Initial commit

This commit is contained in:
Sally Marcher 2025-10-26 02:16:28 +02:00
commit 2d5f6b625e
12 changed files with 1162 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
build/

33
femtoCAM.pro Normal file
View file

@ -0,0 +1,33 @@
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++17
PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
CONFIG += link_pkgconfig
PKGCONFIG += aravis-0.10 opencv4
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
idscam.cpp \
main.cpp \
mainwindow.cpp \
processing.cpp
HEADERS += \
framebatch.h \
idscam.h \
mainwindow.h \
processing.h
FORMS += \
mainwindow.ui
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

227
femtoCAM.pro.user Normal file
View file

@ -0,0 +1,227 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 17.0.2, 2025-10-24T23:48:06. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
<value type="QByteArray">{be9215e2-482c-41d7-992e-2ab1dc0ca384}</value>
</data>
<data>
<variable>ProjectExplorer.Project.ActiveTarget</variable>
<value type="qlonglong">0</value>
</data>
<data>
<variable>ProjectExplorer.Project.EditorSettings</variable>
<valuemap type="QVariantMap">
<value type="bool" key="EditorConfiguration.AutoDetect">true</value>
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
<value type="QString" key="language">Cpp</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
</valuemap>
</valuemap>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
<value type="QString" key="language">QmlJS</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
</valuemap>
</valuemap>
<value type="qlonglong" key="EditorConfiguration.CodeStyle.Count">2</value>
<value type="QByteArray" key="EditorConfiguration.Codec">UTF-8</value>
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
<value type="int" key="EditorConfiguration.IndentSize">4</value>
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
<value type="int" key="EditorConfiguration.LineEndingBehavior">0</value>
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
<value type="int" key="EditorConfiguration.PreferAfterWhitespaceComments">0</value>
<value type="bool" key="EditorConfiguration.PreferSingleLineComments">false</value>
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">2</value>
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
<value type="int" key="EditorConfiguration.TabSize">8</value>
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
<value type="bool" key="EditorConfiguration.UseIndenter">false</value>
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
<value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
<value type="QString" key="EditorConfiguration.ignoreFileTypes">*.md, *.MD, Makefile</value>
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
<value type="bool" key="EditorConfiguration.skipTrailingWhitespace">true</value>
<value type="bool" key="EditorConfiguration.tintMarginArea">true</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.PluginSettings</variable>
<valuemap type="QVariantMap">
<valuemap type="QVariantMap" key="AutoTest.ActiveFrameworks">
<value type="bool" key="AutoTest.Framework.Boost">true</value>
<value type="bool" key="AutoTest.Framework.CTest">false</value>
<value type="bool" key="AutoTest.Framework.Catch">true</value>
<value type="bool" key="AutoTest.Framework.GTest">true</value>
<value type="bool" key="AutoTest.Framework.QtQuickTest">true</value>
<value type="bool" key="AutoTest.Framework.QtTest">true</value>
</valuemap>
<value type="bool" key="AutoTest.ApplyFilter">false</value>
<valuemap type="QVariantMap" key="AutoTest.CheckStates"/>
<valuelist type="QVariantList" key="AutoTest.PathFilters"/>
<value type="int" key="AutoTest.RunAfterBuild">0</value>
<value type="bool" key="AutoTest.UseGlobal">true</value>
<valuemap type="QVariantMap" key="ClangTools">
<value type="bool" key="ClangTools.AnalyzeOpenFiles">true</value>
<value type="bool" key="ClangTools.BuildBeforeAnalysis">true</value>
<value type="QString" key="ClangTools.DiagnosticConfig">Builtin.DefaultTidyAndClazy</value>
<value type="int" key="ClangTools.ParallelJobs">8</value>
<value type="bool" key="ClangTools.PreferConfigFile">true</value>
<valuelist type="QVariantList" key="ClangTools.SelectedDirs"/>
<valuelist type="QVariantList" key="ClangTools.SelectedFiles"/>
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
<value type="bool" key="ClangTools.UseGlobalSettings">true</value>
</valuemap>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.Target.0</variable>
<valuemap type="QVariantMap">
<value type="QString" key="DeviceType">Desktop</value>
<value type="bool" key="HasPerBcDcs">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{88705c22-1dff-443a-bd85-c7eb89196b97}</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
<value type="int" key="EnableQmlDebugging">0</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/gryhsh/Documents/Projekte/femtoCAM/build/Desktop-Debug</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/home/gryhsh/Documents/Projekte/femtoCAM/build/Desktop-Debug</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
<valuelist type="QVariantList" key="QtProjectManager.QMakeBuildStep.SelectedAbis"/>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Erstellen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Bereinigen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Bereinigen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges">
<value type="QString">PKG_CONFIG_PATH=/usr/local/lib/pkgconfig</value>
</valuelist>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Debug</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deployment</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deployment</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<value type="int" key="Analyzer.Valgrind.Callgrind.CostFormat">0</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<valuelist type="QVariantList" key="CustomOutputParsers"/>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
<value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph dwarf,4096 -F 250</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4RunConfiguration:</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">/home/gryhsh/Documents/Projekte/femtoCAM/femtoCAM.pro</value>
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseLibrarySearchPath">true</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
<value type="QString" key="RunConfiguration.WorkingDirectory.default">/home/gryhsh/Documents/Projekte/femtoCAM/build/Desktop-Debug</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">2</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.BuildConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deployment</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deployment</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<value type="int" key="Analyzer.Valgrind.Callgrind.CostFormat">0</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<valuelist type="QVariantList" key="CustomOutputParsers"/>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
<value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph dwarf,4096 -F 250</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4RunConfiguration:</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">/home/gryhsh/Documents/Projekte/femtoCAM/femtoCAM.pro</value>
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseLibrarySearchPath">true</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
<value type="QString" key="RunConfiguration.WorkingDirectory.default">/home/gryhsh/Documents/Projekte/femtoCAM/build/Desktop-Debug</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.TargetCount</variable>
<value type="qlonglong">1</value>
</data>
<data>
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
<value type="int">22</value>
</data>
<data>
<variable>Version</variable>
<value type="int">22</value>
</data>
</qtcreator>

29
framebatch.h Normal file
View file

@ -0,0 +1,29 @@
#ifndef FRAMEBATCH_H
#define FRAMEBATCH_H
#define BATCHSIZE 100
#define MAX_BATCHES 10
#define N_THREADS 1
#include "arv.h"
struct ProcessingConfig {
int thresholdValue = 128;
bool adaptive = false;
int adaptiveBlocksize = 11;
int adaptiveShift = 2;
int contourMinSize = 3;
int contourMinArea = 5;
int contourMaxArea = 1000;
bool correctDoubleHits = true;
int numDatapoints = 4;
};
struct alignas(64) FrameBatch {
static constexpr int BatchSize = BATCHSIZE;
ArvBuffer* frames[BatchSize];
quint64 frame_no[BatchSize]; // Possibly optional if constructing takes too long
int count = 0;
};
#endif // FRAMEBATCH_H

263
idscam.cpp Normal file
View file

@ -0,0 +1,263 @@
#include "idscam.h"
IdsCam::IdsCam()
{
connect(&timer, &QTimer::timeout, this, &IdsCam::buffer_polling);
workers.setMaxThreadCount(N_THREADS);
}
void IdsCam::update_av_cams()
{
arv_update_device_list();
uint n = arv_get_n_devices();
for(uint i = 0; i<n; i++)
{
av_cams_info.append(QString("%1: %2").arg(arv_get_device_model(i)).arg(arv_get_device_serial_nbr(i)));
qDebug() << "Device: " << av_cams_info.at(i);
}
}
void IdsCam::select_cam(uint id)
{
if(camera != nullptr)
g_clear_object(&camera);
camera = arv_camera_new(arv_get_device_id(id), &error);
uint n;
// Update av. pixel formats
av_pixel_formats.clear();
const char** labels = arv_camera_dup_available_pixel_formats_as_strings(camera, &n, &error);
for (uint i = 0; i<n; i++)
av_pixel_formats.append(labels[i]);
// Update av. trigger sources
av_trig_sources.clear();
const char** trigs = arv_camera_dup_available_trigger_sources(camera, &n, &error);
for (uint i = 0; i<n; i++)
av_trig_sources.append(trigs[i]);
exposure_time = arv_camera_get_exposure_time(camera, &error);
frame_rate = arv_camera_get_frame_rate(camera, &error);
gain = arv_camera_get_gain(camera, &error);
}
void IdsCam::set_pixel_format(int idx)
{
arv_camera_set_pixel_format_from_string(camera, reinterpret_cast<char *>(av_pixel_formats.at(idx).toLocal8Bit().data()), &error);
qDebug() << "Selected new Pixel format " << av_pixel_formats.at(idx);
if(error != nullptr)
qDebug() << error->message;
}
void IdsCam::set_exposure_time(double val)
{
arv_camera_set_exposure_time(camera, val, &error);
qDebug() << "Setting new exposure time: " << val;
if(error != nullptr)
qDebug() << error->message;
exposure_time = arv_camera_get_exposure_time(camera, &error);
}
void IdsCam::set_frame_rate(double val)
{
arv_camera_set_frame_rate(camera, val, &error);
qDebug() << "New Framerate: " << val;
if(error != nullptr)
qDebug() << error->message;
frame_rate = arv_camera_get_frame_rate(camera, &error);
}
void IdsCam::set_gain(double val)
{
arv_camera_set_gain(camera, val, &error);
qDebug() << "New Gain: " << val;
if(error != nullptr)
qDebug() << error->message;
gain = arv_camera_get_gain(camera, &error);
}
void IdsCam::set_trig(uint idx)
{
arv_camera_set_trigger_source(camera, reinterpret_cast<char *>(av_trig_sources.at(idx).toLocal8Bit().data()), &error);
qDebug() << "Selected new Trig source " << av_trig_sources.at(idx);
if(error != nullptr)
qDebug() << error->message;
}
bool IdsCam::setup_buffering()
{
if (ARV_IS_CAMERA (camera)) {
arv_camera_set_acquisition_mode (camera, ARV_ACQUISITION_MODE_CONTINUOUS, &error);
callback_data.counter = 0;
callback_data.counter_total = 0;
callback_data.stream = NULL;
if(callback_data.cur != nullptr)
delete callback_data.cur;
callback_data.cur = new FrameBatch{};
callback_data.pendingData = &pendingData;
callback_data.pendingMutex = &pendingMutex;
if (error == NULL)
/* Create the stream object with callback */
callback_data.stream = arv_camera_create_stream (camera, stream_callback, &callback_data, nullptr, &error);
if (ARV_IS_STREAM (callback_data.stream)) {
int i;
size_t payload;
/* Retrieve the payload size for buffer creation */
payload = arv_camera_get_payload (camera, &error);
if (error == NULL) {
/* Insert some buffers in the stream buffer pool */
for (i = 0; i < BATCHSIZE*MAX_BATCHES; i++)
arv_stream_push_buffer (callback_data.stream, arv_buffer_new (payload, NULL));
}
if (error == NULL)
{
/* Start the acquisition */
arv_camera_start_acquisition (camera, &error);
timer.start(500);
return 1;
}
}
}
return 0;
}
bool IdsCam::stop_buffering()
{
arv_camera_stop_acquisition (camera, &error);
if (error == NULL)
{
// will free all the buffers still in its queues when destroyed => push back all frames that were still pending
qDebug() << "Stopping... Discharding last " << callback_data.counter << " frames";
for(int i=0; i<callback_data.counter; i++)
arv_stream_push_buffer(callback_data.stream, callback_data.cur->frames[i]);
buffer_polling();
// Wait for data analysis finishing TODO: show software is busy
workers.waitForDone();
g_clear_object(&callback_data.stream);
timer.stop();
//delete arv_buffer_get_user_data(callback_data.pendingData->at(0)->frames[0]);
return 1;
}
return 0;
}
void IdsCam::buffer_polling()
{
quint64 n;
int pending_n;
{
QMutexLocker locker(callback_data.pendingMutex);
n = callback_data.counter_total;
pending_n = callback_data.pendingData->length();
}
emit new_status_text(QString("Total: %1 | Buf: %2/%3").arg(n).arg(pending_n).arg(MAX_BATCHES));
if(pending_n > 0)
{
ArvBuffer* ptr = nullptr;
{
QMutexLocker locker(callback_data.pendingMutex);
ptr = callback_data.pendingData->at(0)->frames[0];
}
ProcessingResult res = processArvBuffer(ptr, pro_cfg);
const void* data = arv_buffer_get_data(ptr, nullptr);
guint32 width = arv_buffer_get_image_width(ptr);
guint32 height = arv_buffer_get_image_height(ptr);
//ArvPixelFormat pix_format = arv_buffer_get_image_pixel_format(ptr);
QImage image((uchar*)data, width, height, QImage::Format_Grayscale8);
emit vis_new_frame(drawHitsOnGrayImage(image, res));
}
if(workers.activeThreadCount() < workers.maxThreadCount())
for(int i = 0; i<pending_n; i++)
{
FrameBatch* batch_ptr;
{
QMutexLocker locker(callback_data.pendingMutex);
batch_ptr = callback_data.pendingData->takeFirst();
}
Worker *w = new Worker(batch_ptr, callback_data.stream);
workers.start(w);
}
}
void stream_callback(void *user_data, ArvStreamCallbackType type, ArvBuffer *buffer)
{
ArvStreamCallbackData *callback_data = (ArvStreamCallbackData *) user_data;
/* This code is called from the stream receiving thread, which means all the time spent there is less time
* available for the reception of incoming packets */
switch (type) {
case ARV_STREAM_CALLBACK_TYPE_INIT:
/* Stream thread started.
*
* Here you may want to change the thread priority arv_make_thread_realtime() or
* arv_make_thread_high_priority() */
break;
case ARV_STREAM_CALLBACK_TYPE_START_BUFFER:
/* The first packet of a new frame was received */
break;
case ARV_STREAM_CALLBACK_TYPE_BUFFER_DONE:
/* The buffer is received, successfully or not. It is already pushed in the output FIFO.
*
* You could here signal the new buffer to another thread than the main one, and pull/push the
* buffer from this another thread.
*
* Or use the buffer here. We need to pull it, process it, then push it back for reuse by the
* stream receiving thread */
g_assert (buffer == arv_stream_pop_buffer(callback_data->stream));
g_assert (buffer != NULL);
if (arv_buffer_get_status(buffer) == ARV_BUFFER_STATUS_SUCCESS)
{
//qDebug() << "Acquired buffer " << arv_buffer_get_image_width(buffer) << " x " << arv_buffer_get_image_height(buffer);
callback_data->cur->frames[callback_data->counter] = buffer;
callback_data->cur->frame_no[callback_data->counter] = callback_data->counter_total;
callback_data->counter++;
if(callback_data->counter == BATCHSIZE)
{
{
QMutexLocker locker(callback_data->pendingMutex);
callback_data->pendingData->append(callback_data->cur);
qDebug() << "New Batch started, current batch num: " << callback_data->pendingData->size();
}
callback_data->cur = new FrameBatch{};
callback_data->counter = 0;
}
}
else
{
//qDebug() << "ARV_BUFFER_STATUS was not OK; failed frame! Pushing frame back in buffer " << arv_buffer_get_status(buffer);
arv_stream_push_buffer(callback_data->stream, buffer);
}
callback_data->counter_total++;
//arv_stream_push_buffer(callback_data->stream, buffer); TODO!!
//callback_data->counter++;
break;
case ARV_STREAM_CALLBACK_TYPE_EXIT:
/* Stream thread ended */
break;
}
}

71
idscam.h Normal file
View file

@ -0,0 +1,71 @@
#ifndef IDSCAM_H
#define IDSCAM_H
#include <QString>
#include <QDebug>
#include <QList>
#undef signals
#include <arv.h>
#undef signals
#define signals Q_SIGNALS
#include <QObject>
#include <QMutex>
#include <QTimer>
#include <QImage>
#include "processing.h"
#include "framebatch.h"
typedef struct {
ArvStream *stream;
QList<FrameBatch*> *pendingData;
QMutex *pendingMutex;
uint counter;
quint64 counter_total;
FrameBatch *cur = nullptr;
} ArvStreamCallbackData;
static void stream_callback(void *user_data, ArvStreamCallbackType type, ArvBuffer *buffer);
class IdsCam : public QObject
{
Q_OBJECT
public:
IdsCam();
void update_av_cams();
void select_cam(uint id);
void set_pixel_format(int idx);
void set_exposure_time(double val);
void set_frame_rate(double val);
void set_gain(double val);
void set_trig(uint idx);
bool setup_buffering();
bool stop_buffering();
void buffer_polling();
QList<QString> av_cams_info{};
QList<QString> av_pixel_formats{};
QList<QString> av_trig_sources{};
double exposure_time = 0;
double frame_rate = 0;
double gain = 0;
ArvStream *stream = NULL;
private:
ArvCamera *camera = nullptr;
GError *error = NULL;
ArvStreamCallbackData callback_data;
QMutex pendingMutex;
QList<FrameBatch*> pendingData;
QTimer timer;
QThreadPool workers{};
ProcessingConfig pro_cfg{};
signals:
void new_status_text(QString text);
void vis_new_frame(QImage img);
};
#endif // IDSCAM_H

11
main.cpp Normal file
View file

@ -0,0 +1,11 @@
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}

102
mainwindow.cpp Normal file
View file

@ -0,0 +1,102 @@
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
cam.update_av_cams();
foreach (QString info, cam.av_cams_info)
ui->cam_selector->addItem(info);
handle_cam_selector(0);
ui->liveView->setPixmap(liveview_pixmap);
connect(ui->cam_selector, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &MainWindow::handle_cam_selector);
connect(ui->pixel_format_selector, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &MainWindow::handle_pixel_format_selector);
connect(ui->triggerSource, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &MainWindow::handle_new_trig);
connect(ui->exposureTime, &QDoubleSpinBox::editingFinished, this, &MainWindow::handle_new_exposure_time);
connect(ui->frameRate, &QDoubleSpinBox::editingFinished, this, &MainWindow::handle_new_frame_rate);
connect(ui->gain, &QDoubleSpinBox::editingFinished, this, &MainWindow::handle_new_gain);
connect(ui->startButton, &QPushButton::clicked, this, &MainWindow::handle_start_button);
connect(&cam, &IdsCam::new_status_text, ui->infotext, &QLabel::setText);
connect(&cam, &IdsCam::vis_new_frame, this, &MainWindow::visualize_new_frame);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::handle_cam_selector(int i)
{
cam.select_cam(i);
ui->pixel_format_selector->clear();
foreach(QString mode, cam.av_pixel_formats)
ui->pixel_format_selector->addItem(mode);
ui->triggerSource->clear();
foreach (QString trig, cam.av_trig_sources)
ui->triggerSource->addItem(trig);
ui->exposureTime->setValue(cam.exposure_time);
ui->frameRate->setValue(cam.frame_rate);
ui->gain->setValue(cam.gain);
}
void MainWindow::handle_pixel_format_selector(int i)
{
if(i != -1)
cam.set_pixel_format(i);
}
void MainWindow::handle_new_exposure_time()
{
cam.set_exposure_time(ui->exposureTime->value());
ui->exposureTime->setValue(cam.exposure_time);
}
void MainWindow::handle_new_frame_rate()
{
cam.set_frame_rate(ui->frameRate->value());
ui->frameRate->setValue(cam.frame_rate);
}
void MainWindow::handle_new_gain()
{
cam.set_gain(ui->gain->value());
ui->gain->setValue(cam.gain);
}
void MainWindow::handle_new_trig(int i)
{
if(i != -1)
cam.set_trig(i);
}
void MainWindow::handle_start_button()
{
if (ui->startButton->text() == "Start")
{
qDebug("Setting up Buffering");
if(cam.setup_buffering())
ui->startButton->setText("Stop");
}
else
{
qDebug("Stopping Buffering");
if(cam.stop_buffering())
ui->startButton->setText("Start");
}
}
void MainWindow::visualize_new_frame(QImage img)
{
//QPixmap *map = ui->liveView->setPixmap();
ui->liveView->setPixmap(QPixmap::fromImage(img));
}

37
mainwindow.h Normal file
View file

@ -0,0 +1,37 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QPixmap>
#include "idscam.h"
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
IdsCam cam{};
void handle_cam_selector(int i);
void handle_pixel_format_selector(int i);
void handle_new_exposure_time();
void handle_new_frame_rate();
void handle_new_gain();
void handle_new_trig(int i);
void handle_start_button();
QPixmap liveview_pixmap{};
public slots:
void visualize_new_frame(QImage img);
};
#endif // MAINWINDOW_H

231
mainwindow.ui Normal file
View file

@ -0,0 +1,231 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="liveView">
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_2">
<property name="maximumSize">
<size>
<width>300</width>
<height>16777215</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="font">
<font>
<family>Hack</family>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>Camera Settings</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cam_selector"/>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Pixel Format</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="pixel_format_selector"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Exposure [μs]</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="exposureTime">
<property name="maximum">
<double>10000.000000000000000</double>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Framerate [Hz]</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QDoubleSpinBox" name="frameRate">
<property name="maximum">
<double>800.000000000000000</double>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Gain [dB]</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QDoubleSpinBox" name="gain">
<property name="minimum">
<double>1.000000000000000</double>
</property>
<property name="maximum">
<double>20.000000000000000</double>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Trigger Source</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="triggerSource"/>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="infotext">
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="startButton">
<property name="text">
<string>Start</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>22</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>

114
processing.cpp Normal file
View file

@ -0,0 +1,114 @@
#include "processing.h"
Worker::Worker(FrameBatch *batch, ArvStream *stream)
:batch(batch), stream(stream)
{
}
void Worker::run()
{
for(int i=0; i<BATCHSIZE; i++)
{
ProcessingResult res = processArvBuffer(batch->frames[i], cfg);
}
// Cleanup
for(int i=0; i<BATCHSIZE; i++)
if(ARV_IS_STREAM(stream))
arv_stream_push_buffer(stream, batch->frames[i]);
}
ProcessingResult processArvBuffer(ArvBuffer* buffer, const ProcessingConfig& config)
{
ProcessingResult result;
if (!buffer) {
cerr << "Error: Null ArvBuffer pointer!" << endl;
return result;
}
// --- Get image data from ArvBuffer ---
size_t data_size = 0;
const uint8_t* data = (const uint8_t*)arv_buffer_get_data(buffer, &data_size);
if (!data) {
cerr << "Error: Could not get data from ArvBuffer!" << endl;
return result;
}
int width = arv_buffer_get_image_width(buffer);
int height = arv_buffer_get_image_height(buffer);
if (width <= 0 || height <= 0) {
cerr << "Error: Invalid image dimensions!" << endl;
return result;
}
// --- Wrap raw data in a cv::Mat (no copy) ---
Mat frame(height, width, CV_8UC1, (void*)data);
result.composit = Mat::zeros(height, width, CV_8UC1);
// --- Thresholding ---
Mat frame_threshold;
if (!config.adaptive) {
cv::threshold(frame, frame_threshold, config.thresholdValue, 255, cv::THRESH_BINARY);
} else {
cv::adaptiveThreshold(frame, frame_threshold, 255, cv::ADAPTIVE_THRESH_MEAN_C,
cv::THRESH_BINARY, config.adaptiveBlocksize, -config.adaptiveShift);
}
// --- Find contours ---
vector<vector<Point>> contours;
cv::findContours(frame_threshold, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
uint32_t hits = 0;
for (const auto& contour : contours) {
if ((int)contour.size() < config.contourMinSize) continue;
Moments m = cv::moments(contour);
if (m.m00 == 0) continue;
int cx = (int)(m.m10 / m.m00);
int cy = (int)(m.m01 / m.m00);
int area = (int)cv::contourArea(contour);
if (area < config.contourMinArea || area > config.contourMaxArea) continue;
if (cx < 0 || cy < 0 || cx >= width || cy >= height) continue;
result.composit.at<uint8_t>(cy, cx) += 1;
vector<uint16_t> entry(config.numDatapoints);
entry[0] = cx;
entry[1] = cy;
entry[2] = area;
entry[3] = 0; // placeholder (e.g., for roundness)
result.extended_data.push_back(entry);
hits++;
}
result.hitsPerFrame.push_back(hits);
return result;
}
QImage drawHitsOnGrayImage(const QImage& grayImage, const ProcessingResult& result)
{
QImage output = grayImage.copy(); // make a writable copy
QPainter painter(&output);
QPen pen(Qt::white); // white markers stand out on grayscale
pen.setWidth(2);
painter.setPen(pen);
for (const auto& hit : result.extended_data) {
if (hit.size() >= 2) {
int x = hit[0];
int y = hit[1];
painter.drawEllipse(QPoint(x, y), 3, 3); // small white dot
}
}
painter.end();
return output;
}

43
processing.h Normal file
View file

@ -0,0 +1,43 @@
#ifndef PROCESSING_H
#define PROCESSING_H
#include <QThreadPool>
#include <QDebug>
#include <QPainter>
#include <opencv4/opencv2/imgcodecs.hpp>
#include <opencv4/opencv2/opencv.hpp>
#include <vector>
#include <iostream>
#undef signals
#include <arv.h>
#undef signals
#define signals Q_SIGNALS
#include "idscam.h"
#include "framebatch.h"
using namespace cv;
using namespace std;
struct ProcessingResult {
Mat composit;
vector<vector<uint16_t>> extended_data;
vector<uint32_t> hitsPerFrame;
};
ProcessingResult processArvBuffer(ArvBuffer* buffer, const ProcessingConfig& config);
QImage drawHitsOnGrayImage(const QImage& grayImage, const ProcessingResult& result);
class Worker : public QRunnable
{
public:
Worker(FrameBatch* batch, ArvStream *stream);
void run() override;
private:
FrameBatch* batch;
ArvStream *stream;
ProcessingConfig cfg{};
};
#endif // PROCESSING_H