New Status screen added, result processing pipeline started

This commit is contained in:
Sally Marcher 2025-12-13 01:32:35 +01:00
parent 7d15290a58
commit f7ba865d6e
12 changed files with 326 additions and 21 deletions

View file

@ -1,8 +1,8 @@
QT += core gui
QT += core gui charts
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++17
CONFIG += c++25
PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
CONFIG += link_pkgconfig
@ -16,16 +16,19 @@ SOURCES += \
idscam.cpp \
main.cpp \
mainwindow.cpp \
processing.cpp
processing.cpp \
statsview.cpp
HEADERS += \
framebatch.h \
idscam.h \
mainwindow.h \
processing.h
processing.h \
statsview.h
FORMS += \
mainwindow.ui
mainwindow.ui \
statsview.ui
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin

View file

@ -7,6 +7,7 @@
#include <opencv4/opencv2/opencv.hpp>
#include <QList>
#include <QMutex>
#include "arv.h"
struct Hit {
@ -39,4 +40,15 @@ struct alignas(64) FrameBatch {
int count = 0;
};
struct BatchResult {
ProcessingResult result[BATCHSIZE];
quint64 frame_no[BATCHSIZE];
quint64 hit_hist[10] = {};
};
struct ResList {
QList<BatchResult*> results;
QMutex *resultMutex = new QMutex();
};
#endif // FRAMEBATCH_H

View file

@ -175,6 +175,18 @@ void IdsCam::buffer_polling()
pending_n = callback_data.pendingData->length();
}
quint64 batch_results_n;
{
QMutexLocker locker(results.resultMutex);
batch_results_n = results.results.length();
for(int i = 0; i < batch_results_n; i++)
{
BatchResult* cur = results.results.at(i);
emit new_hist_av(cur->hit_hist);
delete cur;
}
results.results.clear();
}
if(fps_timer.elapsed() > 3000)
{
@ -212,7 +224,7 @@ void IdsCam::buffer_polling()
QMutexLocker locker(callback_data.pendingMutex);
batch_ptr = callback_data.pendingData->takeFirst();
}
Worker *w = new Worker(batch_ptr, callback_data.stream);
Worker *w = new Worker(batch_ptr, callback_data.stream, &results);
workers.start(w);
}

View file

@ -67,9 +67,12 @@ private:
QThreadPool workers{};
ProcessingConfig pro_cfg{};
ResList results{};
signals:
void new_status_text(QString text);
void vis_new_frame(QImage img);
void new_hist_av(quint64 hit_hist[10]);
};
#endif // IDSCAM_H

View file

@ -23,8 +23,11 @@ MainWindow::MainWindow(QWidget *parent)
connect(ui->startButton, &QPushButton::clicked, this, &MainWindow::handle_start_button);
connect(ui->statsButton, &QPushButton::clicked, &stats, &StatsView::show);
connect(&cam, &IdsCam::new_status_text, ui->infotext, &QLabel::setText);
connect(&cam, &IdsCam::vis_new_frame, this, &MainWindow::visualize_new_frame);
connect(&cam, &IdsCam::new_hist_av, stats.histogramm, &BarChart::new_batch_data);
}

View file

@ -4,6 +4,7 @@
#include <QMainWindow>
#include <QPixmap>
#include "idscam.h"
#include "statsview.h"
QT_BEGIN_NAMESPACE
namespace Ui {
@ -21,6 +22,8 @@ public:
private:
Ui::MainWindow *ui;
StatsView stats;
IdsCam cam{};
void handle_cam_selector(int i);
void handle_pixel_format_selector(int i);

View file

@ -20,10 +20,10 @@
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
<enum>QFrame::Shadow::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
@ -32,7 +32,7 @@
<string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
</widget>
</item>
@ -48,10 +48,10 @@
</size>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
<enum>QFrame::Shadow::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
@ -68,14 +68,14 @@
<string>Camera Settings</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
</widget>
</item>
@ -85,7 +85,7 @@
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
</widget>
</item>
@ -161,7 +161,7 @@
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
@ -177,16 +177,23 @@
<string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QPushButton" name="statsButton">
<property name="text">
<string>Stats</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>

View file

@ -2,16 +2,27 @@
Worker::Worker(FrameBatch *batch, ArvStream *stream)
:batch(batch), stream(stream)
Worker::Worker(FrameBatch *batch, ArvStream *stream, ResList *result)
:batch(batch), stream(stream), result(result)
{
}
void Worker::run()
{
batch_result = new BatchResult(); // Constructed as a pointer! Will need cleanup when done!
for(int i=0; i<BATCHSIZE; i++)
{
ProcessingResult res = processArvBuffer(batch->frames[i], cfg);
batch_result->result[i] = processArvBuffer(batch->frames[i], cfg);
batch_result->frame_no[i] = batch->frame_no[i]; // TODO: a lot of optimisation could be done!
quint8 hits = std::min(batch_result->result[i].extended_data.length(), 10); // Max 10 hits in histogramm
batch_result->hit_hist[hits]++;
}
// Append Batch Result to Output Data - with mutex!
{
QMutexLocker locker(result->resultMutex);
result->results.append(batch_result);
}
// Cleanup

View file

@ -26,12 +26,15 @@ QImage drawHitsOnGrayImage(const QImage& grayImage, const ProcessingResult& resu
class Worker : public QRunnable
{
public:
Worker(FrameBatch* batch, ArvStream *stream);
Worker(FrameBatch* batch, ArvStream *stream, ResList *result);
void run() override;
private:
FrameBatch* batch;
ArvStream *stream;
ProcessingConfig cfg{};
ResList *result;
BatchResult *batch_result = nullptr;
};
#endif // PROCESSING_H

97
statsview.cpp Normal file
View file

@ -0,0 +1,97 @@
#include "statsview.h"
#include "ui_statsview.h"
StatsView::StatsView(QWidget *parent)
: QDialog(parent)
, ui(new Ui::StatsView)
{
ui->setupUi(this);
connect(ui->closeButton, &QPushButton::clicked, this, &QDialog::close);
counts = new CountChart(ui->counts);
histogramm = new BarChart(ui->histogramm);
}
StatsView::~StatsView()
{
delete ui;
}
CountChart::CountChart(QChartView *view)
:chartView(view)
{
//series.append(0,6);
//series.append(2,4);
chartView->chart()->addSeries(&series);
axisX.setRange(0,10);
axisY.setRange(0,10);
axisX.setTitleText("Frame");
axisY.setTitleText("Counts");
chartView->chart()->addAxis(&axisY, Qt::AlignLeft);
chartView->chart()->addAxis(&axisX, Qt::AlignBottom);
series.attachAxis(&axisX);
series.attachAxis(&axisY);
chartView->chart()->legend()->hide();
chartView->chart()->setTitle("Counts per Frame");
}
BarChart::BarChart(QChartView *view)
:chartView(view)
{
sets.append(new QBarSet{"Last batch"});
sets.append(new QBarSet{"Avg. over batches"});
QStringList categories;
for(int i = 0; i<10; i++)
{
categories << QString("%1").arg(i);
sets.barSets().at(0)->append(0);
sets.barSets().at(1)->append(0);
}
categories.last() = "9+";
chartView->chart()->addSeries(&sets);
chartView->chart()->setTitle("Distribution of Counts per frame");
//chartView->chart()->setAnimationOptions(QChart::SeriesAnimations);
// Axis
axisX.append(categories);
chartView->chart()->addAxis(&axisX, Qt::AlignBottom);
sets.attachAxis(&axisX);
axisY.setRange(0,100);
chartView->chart()->addAxis(&axisY, Qt::AlignLeft);
sets.attachAxis(&axisY);
chartView->chart()->legend()->setVisible(1);
chartView->chart()->legend()->setAlignment(Qt::AlignBottom);
}
void BarChart::new_batch_data(quint64 distr[10])
{
for(int i = 0; i<10; i++)
{
// New set
sets.barSets().at(0)->remove(i);
sets.barSets().at(0)->insert(i, distr[i]);
// Avg
if(n_since_res == 0)
{
sets.barSets().at(1)->remove(i);
sets.barSets().at(1)->insert(i, distr[i]);
}
else
{
float val = sets.barSets().at(1)->at(i);
sets.barSets().at(1)->remove(i);
sets.barSets().at(1)->insert(i, (val * n_since_res + (float)distr[i]) / (float)(n_since_res + 1) );
}
}
n_since_res++;
}

60
statsview.h Normal file
View file

@ -0,0 +1,60 @@
#ifndef STATSVIEW_H
#define STATSVIEW_H
#include <QDialog>
#include <QChart>
#include <QChartView>
#include <QLineSeries>
#include <QValueAxis>
#include <QBarSeries>
#include <QBarSet>
#include <QBarCategoryAxis>
using namespace QtCharts;
namespace Ui {
class StatsView;
}
class CountChart : public QObject
{
Q_OBJECT
public:
CountChart(QChartView* view);
private:
QChartView* chartView = nullptr;
QValueAxis axisX{};
QValueAxis axisY{};
QLineSeries series{};
};
class BarChart : public QObject
{
Q_OBJECT
public:
BarChart(QChartView* view);
private:
QChartView* chartView = nullptr;
QBarCategoryAxis axisX{};
QValueAxis axisY{};
QBarSeries sets{};
quint64 n_since_res = 0;
public slots:
void new_batch_data(quint64 distr[10]);
};
class StatsView : public QDialog
{
Q_OBJECT
public:
explicit StatsView(QWidget *parent = nullptr);
~StatsView();
CountChart *counts;
BarChart *histogramm;
private:
Ui::StatsView *ui;
};
#endif // STATSVIEW_H

91
statsview.ui Normal file
View file

@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>StatsView</class>
<widget class="QDialog" name="StatsView">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>800</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QChartView" name="counts" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QChartView" name="histogramm" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QPushButton" name="buttonSettings">
<property name="text">
<string>Settings</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonReset">
<property name="text">
<string>Reset Stats</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="closeButton">
<property name="text">
<string>Close</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QChartView</class>
<extends>QWidget</extends>
<header>QtCharts</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>