131 lines
4 KiB
C++
131 lines
4 KiB
C++
#include "processing.h"
|
|
|
|
|
|
|
|
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++)
|
|
{
|
|
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(), 9); // Max 10 hits in histogramm
|
|
batch_result->hit_hist[hits]++;
|
|
|
|
foreach(Hit hit, batch_result->result[i].extended_data)
|
|
{
|
|
int cy = std::min((hit.cy*100) / batch_result->result[i].composit.rows, 99); // TODO config size!
|
|
int cx = std::min((hit.cx*100) / batch_result->result[i].composit.cols, 99); // TODO config size!
|
|
batch_result->batch_composit.at<uchar>(cy, cx) += (uint8_t)(255/batch_result->result[i].extended_data.length());
|
|
}
|
|
}
|
|
|
|
// Append Batch Result to Output Data - with mutex!
|
|
{
|
|
QMutexLocker locker(result->resultMutex);
|
|
result->results.append(batch_result);
|
|
}
|
|
|
|
// Cleanup
|
|
for(int i=0; i<BATCHSIZE; i++)
|
|
if(ARV_IS_STREAM(stream))
|
|
arv_stream_push_buffer(stream, batch->frames[i]);
|
|
delete batch;
|
|
}
|
|
|
|
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_8U, (void*)data);
|
|
result.composit = Mat::zeros(height, width, CV_8U);
|
|
|
|
// --- 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);
|
|
|
|
|
|
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;
|
|
|
|
Hit entry;
|
|
entry.cx = cx;
|
|
entry.cy = cy;
|
|
entry.area = area;
|
|
result.extended_data.push_back(entry);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
QImage drawHitsOnGrayImage(const QImage& grayImage, const ProcessingResult& result)
|
|
{
|
|
QImage output = grayImage.copy(); // make a writable copy
|
|
QPainter painter(&output);
|
|
|
|
QPen pen_fg(Qt::white);
|
|
QPen pen_bg(Qt::black);
|
|
pen_fg.setWidth(2);
|
|
pen_bg.setWidth(4);
|
|
|
|
for (const auto& hit : result.extended_data) {
|
|
int x = hit.cx;
|
|
int y = hit.cy;
|
|
painter.setPen(pen_bg);
|
|
painter.drawEllipse(QPoint(x, y), 6, 6);
|
|
painter.setPen(pen_fg);
|
|
painter.drawEllipse(QPoint(x, y), 3, 3);
|
|
}
|
|
|
|
painter.end();
|
|
return output;
|
|
}
|