femtoCAM/idscam.cpp
2025-12-14 22:11:08 +01:00

303 lines
10 KiB
C++

#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;
if(av_trig_sources.at(idx) == "Software")
{
arv_camera_set_string(camera, "TriggerMode", "Off", &error);
set_frame_rate(frame_rate);
}
else
arv_camera_set_string(camera, "TriggerMode", "On", &error);
arv_camera_set_string(camera, "TriggerSelector", "FrameStart", &error);
arv_camera_set_string(camera, "TriggerActivation", "RisingEdge", &error);
}
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);
fps_timer.start();
timer.start(50);
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();
}
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);
if(i == batch_results_n-1)
{
emit new_hist_av(cur->hit_hist);
cv::addWeighted(average_all, 0.9, cur->batch_composit, 0.1, 0, average_all);
cv::Mat* data_ptr = new cv::Mat(cur->batch_composit.clone());
emit new_batch_view(data_ptr); // Cleanup handled by reciever!
emit new_avg_view(&average_all);
}
delete cur;
}
results.results.clear();
}
if(fps_timer.elapsed() > 3000)
{
float delta_t = fps_timer.restart();
float delta_n = n - last_n;
last_n = n;
fps = delta_n / (delta_t/1000);
}
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 new_status_text(QString("Total: %1 | Buf: %2/%3 | Hits: %4 | %5 fps").arg(n).arg(pending_n).arg(MAX_BATCHES).arg(res.extended_data.size()).arg(fps));
emit vis_new_frame(drawHitsOnGrayImage(image, res));
}
//emit new_status_text(QString("Total: %1 | Buf: %2/%3 | Hits: %4 | %5 fps").arg(n).arg(pending_n).arg(MAX_BATCHES).arg(0).arg(fps));
//emit new_status_text(QString("Total: %1 | Buf: %2/%3 | Hits: -").arg(n).arg(pending_n).arg(MAX_BATCHES));
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, &results);
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;
}
}