303 lines
10 KiB
C++
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;
|
|
}
|
|
}
|