diff --git a/notes.md b/notes.md index c5f6721..f6d2daf 100644 --- a/notes.md +++ b/notes.md @@ -3,12 +3,34 @@ Recorder for up to two Basler cameras. ## TODOs + * implement grabbing from 2 joined cameras * set the region of interest * save properly -## FIXMEs & improvements +## FIXMEs -* make the max image sizes depend on camera +* dualcamwrapper: remove hardcode +* dualcamwrapper: support setting exposure individually +* dualcamwrapper: support setting gain individually +* pylonrecorder somehow remembers the old layout: +* display of images during recording of both cameras. +* +## Improvements +* make the max image sizes depend on camera +* detect framedrops based on the framecounts +* support reading out camera health (temperature) +* add interface to change camera DeviceUserID to provided an user friendly name for the cameras +* code cleanup check import guards +* check private public +* add interface for single- dual- multi-camera support +* same for grabbing from single or multiple devices +* support handling of multiple buffers in GUI (load, pessure) +* remove the old open, save etc. functions. +* improve dry run to not create panik by showing the buffer lad to go up +* support buffer progress for two cams/buffers +* add functionality to adjust the gain per camera (automatically?) +* cleanup setter function on dual and single camera wrappers... +* remove full name from SinglecamWrapper \ No newline at end of file diff --git a/pylonrecorder.cpp b/pylonrecorder.cpp index 298cfbc..9992c8e 100644 --- a/pylonrecorder.cpp +++ b/pylonrecorder.cpp @@ -32,7 +32,12 @@ #endif PylonRecorder::PylonRecorder(QWidget *parent) - : QMainWindow(parent), imageLabel(new QLabel), scrollArea(new QScrollArea), grabber(nullptr), writer(nullptr), buffer(nullptr), pyloncam(nullptr), dryRun(false), cameraOpened(false) + : QMainWindow(parent), imageLabel(new QLabel), scrollArea(new QScrollArea), + singlecamgrabber(nullptr), dualcamgrabber(nullptr), + writer0(nullptr), writer1(nullptr), + buffer0(nullptr), buffer1(nullptr), + singlecam(nullptr), dualcam(nullptr), + dryRun(false), cameraOpened(false), camsconfigured(false) { imageLabel->setBackgroundRole(QPalette::Base); imageLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); @@ -42,24 +47,7 @@ PylonRecorder::PylonRecorder(QWidget *parent) scrollArea->setWidget(imageLabel); scrollArea->setVisible(false); setCentralWidget(scrollArea); - // Pylon::DeviceInfoList_t deviceList = detectCameras(); - // for (auto dev : deviceList) { - // std::cout << dev.GetFullName() << " " << dev.GetFriendlyName() << std::endl; - // } - // std::cout << "peng\n"; - // std::cout << deviceList.size() << std::endl; - // std::cout << "peng\n"; - - // if (deviceList.size() == 0) { - // QErrorMessage message(this); - // message.showMessage("No camera detected!"); - // } else { - // std::cout << "peng\n"; - // buffer = new ImageBuffer(defaultBufferSize); - // grabber = new Grabber(pyloncam, buffer, defaultFrameRate); - // writer = new Writer(buffer); - // connect(writer, &Writer::writingDone, this, &PylonRecorder::writerDone); - // } + frameTimer = new QTimer(this); connect(frameTimer, &QTimer::timeout, this, &PylonRecorder::displaySingleFrame); pressureTimer = new QTimer(this); @@ -118,34 +106,6 @@ PylonRecorder::PylonRecorder(QWidget *parent) applySettings(); } -// void PylonRecorder::setupCameras(){ -// std::cerr << "Setting up cameras!" << std::endl; -// qDebug() << "setting up cameras"; -// for (auto dev : deviceList) { -// std::cout << dev.GetFullName() << " " << dev.GetFriendlyName() << std::endl; -// } -// std::string s = "detected "; -// s += deviceList.size(); -// s += " devices"; -// qDebug() << s.c_str(); - -// if (deviceList.size() == 0) { -// qWarning() << "device list is empty!"; -// QErrorMessage message(this); -// message.showMessage("No camera detected!"); -// } else { -// std::cout << "peng\n"; -// std::string cname = (std::string)deviceList[0].GetFullName(); -// // qDebug() << -// // pyloncam = new PylonWrapper(cname); -// qDebug() << "Creating buffer, grabber, and writer"; -// buffer = new ImageBuffer(defaultBufferSize); -// grabber = new Grabber(pyloncam, buffer, defaultFrameRate); -// writer = new Writer(buffer); -// connect(writer, &Writer::writingDone, this, &PylonRecorder::writerDone); -// } -// } - void PylonRecorder::detectCameras() { qDebug() << "Detecting devices!"; Pylon::CTlFactory& TlFactory = Pylon::CTlFactory::GetInstance(); @@ -155,30 +115,56 @@ void PylonRecorder::detectCameras() { PylonRecorder::~PylonRecorder(){ qDebug() << "Destructing PylonRecorder"; - if (grabber != nullptr && grabber->isRunning()) { - grabber->requestStop(); - grabber->wait(1000); + if (singlecamgrabber != nullptr && singlecamgrabber->isRunning()) { + singlecamgrabber->requestStop(); + singlecamgrabber->wait(1000); } - if (writer != nullptr && writer->isRunning()) { - writer->forceStop(); - writer->wait(1000); + if (writer0 != nullptr && writer0->isRunning()) { + writer0->forceStop(); + writer0->wait(1000); } storeSettings(); - if (pyloncam != nullptr) { - qDebug() << "Deleting pyloncam"; - delete pyloncam; - pyloncam = nullptr; + if (singlecam != nullptr) { + qDebug() << "Deleting singlecam"; + delete singlecam; + singlecam = nullptr; + } + if (dualcam != nullptr) { + qDebug() << "Deleting dualcam"; + delete dualcam; + dualcam = nullptr; } - if (buffer != nullptr) { + if (buffer0 != nullptr) { + qDebug() << "Deleting buffer"; + delete buffer0; + buffer0 = nullptr; + } + if (buffer1 != nullptr) { qDebug() << "Deleting buffer"; - delete buffer; - buffer = nullptr; + delete buffer1; + buffer1 = nullptr; + } + if (singlecamgrabber != nullptr) { + qDebug() << "Deleting grabber"; + delete singlecamgrabber; + singlecamgrabber = nullptr; + } + if (dualcamgrabber != nullptr) { + qDebug() << "Deleting grabber"; + delete dualcamgrabber; + dualcamgrabber = nullptr; + } + if (writer0 != nullptr) { + qDebug() << "Deleting writer"; + delete writer0; + writer0 = nullptr; + } + if (writer1 != nullptr) { + qDebug() << "Deleting writer"; + delete writer1; + writer1 = nullptr; } - qDebug() << "Deleting grabber"; - delete grabber; - qDebug() << "Deleting writer"; - delete writer; qDebug() << "Deleting setting"; delete settings; } @@ -201,6 +187,7 @@ void PylonRecorder::applySettings() { void PylonRecorder::storeSettings() { + // FIXME store cam layout to settings settings->setValue("camera/exposure", exposureSpinner->value()); settings->setValue("camera/framerate", framerateSpinner->value()); settings->setValue("camera/gain", gainSpinner->value()); @@ -232,6 +219,7 @@ bool PylonRecorder::loadFile(const QString &fileName) { void PylonRecorder::setImage(const QImage &newImage) { + //FIXME figure out how to display both images. extract to extra class... image = newImage; // (image.colorSpace().isValid()) // image.convertToColorSpace(QColorSpace::SRgb); @@ -563,6 +551,7 @@ void PylonRecorder::updateActions() { connect_camera_action->setEnabled(true); grab_still_action->setEnabled(deviceList.size() > 0); grab_continuous_action->setEnabled(cameraOpened && !grabbing); + // grab_continuous_action->setEnabled(!grabbing); grab_stop_action->setEnabled(grabbing); } @@ -604,19 +593,30 @@ void PylonRecorder::adjustScrollBar(QScrollBar *scrollBar, double factor) { + ((factor - 1) * scrollBar->pageStep()/2))); } + void PylonRecorder::cameraConfiguration(){ d = new CamConfigurator(deviceList, this); - connect(d, SIGNAL(accepted()), SLOT(camerasetup())); + connect(d, SIGNAL(accepted()), SLOT(cameraConfigurationAccepted())); + connect(d, SIGNAL(rejected()), SLOT(cameraConfigurationAborted())); // QObject::connect(&d, SIGNAL(column_visibility_changed(QString, QString,bool)), this, SLOT(visible_columns_update(QString, QString,bool))); d->exec(); } -void PylonRecorder::camerasetup() { + +void PylonRecorder::cameraConfigurationAccepted() { qDebug() << "Cameras setting " << ((d->result()) ? "Accepted" : "Discarded"); this->layout = d->layout(); + camsconfigured = true; delete d; } + +void PylonRecorder::cameraConfigurationAborted() { + qDebug() << "Camera configuration aborted!"; + camsconfigured = false; +} + + void PylonRecorder::connectCamera() { this->layout = CameraLayout(); std::cerr << this->layout.rois.size() << " " << this->layout.devices.size() << std::endl; @@ -627,29 +627,54 @@ void PylonRecorder::connectCamera() { QString msg = "

No camera device found!


Connect camera and try again!

"; msgBox.setText(msg); msgBox.exec(); - } else { - cameraConfiguration(); - if (layout.mode == CameraMode::single && layout.devices.size() == 1) { - qDebug() << "single camera mode"; - std::string cname = layout.devices[0]; - std::string message; - qDebug() << "connecting to camera " << cname.c_str(); - pyloncam = new PylonWrapper(layout); - bool success = pyloncam->openCamera(message); - if (success) { - cameraConnectedLabel->setText("connected"); - cameraConnectedLabel->setStyleSheet("QLabel { font-size: 10px;font-family: Arial;color: green;}"); - cameraOpened = true; - } else { - QMessageBox msgBox; - QString msg = "

Could not open camera device!

" + QString::fromStdString(message) + "

"; - msgBox.setText(msg); - msgBox.exec(); - cameraOpened = false; - } - statusBar()->showMessage(QString::fromStdString(message)); - updateActions(); + qWarning() << msg.toStdString().c_str(); + return; + } + cameraConfiguration(); + if (!camsconfigured) { + qDebug() << "cameras have not been properly configured!"; + return; + } + if (layout.mode == CameraMode::single && layout.devices.size() == 1) { + qDebug() << "single camera mode"; + std::string cname = layout.devices[0]; + std::string message; + qDebug() << "connecting to camera " << cname.c_str(); + singlecam = new PylonWrapper(layout); + bool success = singlecam->openCamera(message); + if (success) { + cameraConnectedLabel->setText("connected"); + cameraConnectedLabel->setStyleSheet("QLabel { font-size: 10px;font-family: Arial;color: green;}"); + cameraOpened = true; + } else { + QMessageBox msgBox; + QString msg = "

Could not open camera device!

" + QString::fromStdString(message) + "

"; + msgBox.setText(msg); + msgBox.exec(); + cameraOpened = false; } + statusBar()->showMessage(QString::fromStdString(message)); + updateActions(); + } + if (layout.mode == CameraMode::dual && layout.devices.size() == 2) { + qDebug() << "dual camera mode"; + std::string message; + qDebug() << "creating dual cam wrapper"; + dualcam = new DualcamWrapper(layout); + bool success = dualcam->openCameras(message); + if (success) { + cameraConnectedLabel->setText("connected"); + cameraConnectedLabel->setStyleSheet("QLabel { font-size: 10px;font-family: Arial;color: green;}"); + cameraOpened = true; + } else { + QMessageBox msgBox; + QString msg = "

Could not open camera device!

" + QString::fromStdString(message) + "

"; + msgBox.setText(msg); + msgBox.exec(); + cameraOpened = false; + } + statusBar()->showMessage(QString::fromStdString(message)); + updateActions(); } qDebug() << "connecting cam(s) done!"; } @@ -657,13 +682,18 @@ void PylonRecorder::connectCamera() { void PylonRecorder::disconnectCamera() { qDebug() << "disconnecting camera"; - if (pyloncam != nullptr && pyloncam->isOpen()) { - pyloncam->closeCamera(); - statusBar()->showMessage(tr("Camera closed!")); - cameraConnectedLabel->setText("not connected"); - cameraConnectedLabel->setStyleSheet("QLabel { font-size: 10px;font-family: Arial;color: red;}"); - updateActions(); + if (singlecam != nullptr && singlecam->isOpen()) { + singlecam->closeCamera(); + } + if (dualcam != nullptr && dualcam->isOpen()) { + dualcam->closeCameras(); } + statusBar()->showMessage(tr("Camera closed!")); + cameraConnectedLabel->setText("not connected"); + cameraConnectedLabel->setStyleSheet("QLabel { font-size: 10px;font-family: Arial;color: red;}"); + updateActions(); + camsconfigured = false; + cameraOpened = false; qDebug() << "disconnecting cameras done"; } @@ -687,13 +717,14 @@ VideoSpecs PylonRecorder::getVideoSpecs(const ImageSettings &settings) { return s; } -void PylonRecorder::startRecording() { + +void PylonRecorder::startSinglecamRecording() { qDebug() << "start recording!"; std::string filename = createFilename(".mp4"); fileLabel->setText(QString::fromStdString(filename)); qDebug() << "storing to file " << filename.c_str(); - ImageSettings settings = pyloncam->getImageSettings(); + ImageSettings settings = singlecam->getImageSettings(); qDebug() << "got image settings"; VideoSpecs specs = getVideoSpecs(settings); @@ -701,54 +732,70 @@ void PylonRecorder::startRecording() { specs.format = VideoFormat::mp4; qDebug() << "got video specifications"; - if (buffer != nullptr) { - buffer->clear(); - delete buffer; - buffer = nullptr; + if (buffer0 != nullptr) { + buffer0->clear(); + delete buffer0; + buffer0 = nullptr; } qDebug() << "setting image buffer to size " << buffersizeSpinner->value(); - buffer = new ImageBuffer(defaultBufferSize); - if (buffersizeSpinner->value() != static_cast(buffer->capacity())) { - buffer->resize(static_cast(buffersizeSpinner->value())); + buffer0 = new ImageBuffer(defaultBufferSize); + if (buffersizeSpinner->value() != static_cast(buffer0->capacity())) { + buffer0->resize(static_cast(buffersizeSpinner->value())); loadBar->setRange(0, buffersizeSpinner->value()); } qDebug() << "setting up grabber"; - if (grabber != nullptr) { - delete grabber; - grabber = nullptr; + if (singlecamgrabber != nullptr) { + delete singlecamgrabber; + singlecamgrabber = nullptr; } - grabber = new Grabber(pyloncam, buffer, defaultFrameRate); + singlecamgrabber = new Grabber(singlecam, buffer0, defaultFrameRate); - if (framerateSpinner->value() != grabber->currentFramerate()) - grabber->setFrameRate(framerateSpinner->value()); - if (exposureSpinner->value() != int(grabber->currentExposureTime())) - grabber->setExposureTime(static_cast(exposureSpinner->value())); - if (gainSpinner->value() != int(grabber->currentGain())) - grabber->setGain(static_cast(gainSpinner->value())); + if (framerateSpinner->value() != singlecamgrabber->currentFramerate()) + singlecamgrabber->setFrameRate(framerateSpinner->value()); + if (exposureSpinner->value() != int(singlecamgrabber->currentExposureTime())) + singlecamgrabber->setExposureTime(static_cast(exposureSpinner->value())); + if (gainSpinner->value() != int(singlecamgrabber->currentGain())) + singlecamgrabber->setGain(static_cast(gainSpinner->value())); qDebug() << "setup writer"; - if (writer != nullptr) { - delete writer; - writer = nullptr; + if (writer0 != nullptr) { + delete writer0; + writer0 = nullptr; } - writer = new Writer(buffer); - connect(writer, &Writer::writingDone, this, &PylonRecorder::writerDone); - writer->setVideoSpecs(specs); + writer0 = new Writer(buffer0); + connect(writer0, &Writer::writingDone, this, &PylonRecorder::writerDone); + writer0->setVideoSpecs(specs); QSettings s; this->mdata.read(s); - writer->setProjectMetadata(mdata); + writer0->setProjectMetadata(mdata); - buffer->clear(); + buffer0->clear(); if (!dryRunCheckBox->isChecked()) { - writer->start(); + writer0->start(); writing = true; } dryRun = dryRunCheckBox->isChecked(); - grabber->start(); + singlecamgrabber->start(); grabbing = true; - stopRequest = false; + stopRequest = false; +} + + +void PylonRecorder::startDualcamRecording() { + +} + + +void PylonRecorder::startRecording() { + if (layout.mode == CameraMode::single) { + startSinglecamRecording(); + } else if (layout.mode == CameraMode::dual){ + startDualcamRecording(); + } else { + qDebug() << "invalid camera mode!"; + } pressureTimer->start(50); frameTimer->start(100); @@ -763,17 +810,17 @@ void PylonRecorder::stopRecording() { qDebug() << "StopRecording: stop frame timer!"; frameTimer->stop(); qDebug() << "StopRecording: stop grabber!"; - if (grabber !=nullptr) - grabber->requestStop(); + if (singlecamgrabber !=nullptr) + singlecamgrabber->requestStop(); qDebug() << "StopRecording: stop writer!"; - if (writer != nullptr) - writer->requestStop(); + if (writer0 != nullptr) + writer0->requestStop(); grabbing = false; stopRequest = true; grab_stop_action->setEnabled(false); qDebug() << "StopRecording: clear buffer!"; - if(buffer != nullptr) { - buffer->clear(); + if(buffer0 != nullptr) { + buffer0->clear(); writerDone(); } } @@ -789,8 +836,8 @@ void PylonRecorder::writerDone() { labelTimer->stop(); writingLabel->setStyleSheet(inactiveLabelStyle); grabbingLabel->setStyleSheet(inactiveLabelStyle); - grabber->wait(10000); - writer->wait(10000); + singlecamgrabber->wait(10000); + writer0->wait(10000); writing = false; updateActions(); qInfo() << "writer is Done!"; @@ -807,7 +854,7 @@ void PylonRecorder::displayActivity() { void PylonRecorder::displaySingleFrame() { MyImage *img; size_t fc = 0; - img = buffer->readLast(fc); + img = buffer0->readLast(fc); if (img != nullptr){ std::cerr << "display, last frame count " << fc << std::endl; QImage qimg(static_cast(img->data()), img->width(), img->height(), QImage::Format::Format_Grayscale8); @@ -845,22 +892,22 @@ std::string PylonRecorder::createFilename(const std::string &extension) { void PylonRecorder::displayBufferPressure() { - int value = static_cast(round(buffer->bufferPressure())); + int value = static_cast(round(buffer0->bufferPressure())); pressureBar->setValue(value); QColor color = progressColor(value); progressPalette.setBrush(QPalette::Highlight, QBrush(color)); pressureBar->setPalette(progressPalette); - int load = static_cast(buffer->bufferLoad()); + int load = static_cast(buffer0->bufferLoad()); loadBar->setValue(load); } void PylonRecorder::grabStillFromPylon() { qDebug() << "Grab still image form camera!"; - if (pyloncam != nullptr && pyloncam->isOpen()) { + if (singlecam != nullptr && singlecam->isOpen()) { MyImage img; - bool valid = pyloncam->grabFrame(img); + bool valid = singlecam->grabFrame(img); if (valid) { QImage qimg(static_cast(img.data()), img.width(), img.height(), QImage::Format::Format_Grayscale8); diff --git a/pylonrecorder.h b/pylonrecorder.h index 77b3715..9c9ebb5 100644 --- a/pylonrecorder.h +++ b/pylonrecorder.h @@ -8,8 +8,10 @@ #include #include #include "pylonwrapper.h" +#include "dualcamwrapper.h" #include "imagebuffer.h" #include "grabber.h" +#include "dualcamgrabber.h" #include "writer.h" #include "projectsettings.h" #include "camconfig.h" @@ -66,7 +68,8 @@ private slots: void displayActivity(); void writerDone(); void selectStorageLocation(); - void camerasetup(); + void cameraConfigurationAccepted(); + void cameraConfigurationAborted(); private: void createActions(); @@ -81,6 +84,8 @@ private: std::string createFilename(const std::string &extension=".raw"); void cameraConfiguration(); VideoSpecs getVideoSpecs(const ImageSettings &settings); + void startSinglecamRecording(); + void startDualcamRecording(); bool saveFile(const QString &fileName); void setImage(const QImage &newImage); @@ -88,7 +93,6 @@ private: void applyScaling(); void adjustScrollBar(QScrollBar *scrollBar, double factor); void detectCameras(); - // void setupCameras(); int defaultBufferSize = 3000, defaultFrameRate = 30, movieCount = 0, defaultExposureTime = 6000, defaultGain=13; QSettings *settings = new QSettings; QImage image; @@ -99,12 +103,14 @@ private: QProgressBar *loadBar; QScrollArea *scrollArea; double scaleFactor = 1; - PylonWrapper *pyloncam; - ImageBuffer *buffer; - Grabber *grabber; - Writer *writer; + PylonWrapper *singlecam; + DualcamWrapper *dualcam; + ImageBuffer *buffer0, *buffer1; + Grabber *singlecamgrabber; + DualcamGrabber *dualcamgrabber; + Writer *writer0, *writer1; CameraLayout layout; - bool grabbing, stopRequest, writing, labelSwitch, dryRun, cameraOpened; + bool grabbing, stopRequest, writing, labelSwitch, dryRun, cameraOpened, camsconfigured; QPalette progressPalette; QString activeLabelStyleHigh = "QLabel { font-size: 10pt;font-family: Arial; color : red; }"; QString activeLabelStyleLow = "QLabel { font-size: 10pt;font-family: Arial; color : cmyk(0, 255, 255, 0, 50); }";