Solved Custom Qlabel show Videocamera on different thread
-
Goodmorning,
I'm using a raspberry pi3 board with a custom image built with Yocto, where I've included opencv libraries. Qt version is 5.7.1.
I succeded to show the camera video output on a QLabel, but now I wanted to make the opencv operations on a different thread, to not overload the GUI thread.
Hence, I've created a custom QLabel widget called videocamera, where the opencv task is done by a Worker class.Unfortunately, when I get the error Segmentation fault. Have you got any ideas?
Thank you.videocamera.h
#ifndef VIDEOCAMERA_H #define VIDEOCAMERA_H #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/videoio.hpp> #include <QWidget> #include <QTimer> #include <QLabel> #include <QThread> #include <QSignalMapper> using namespace cv; class Worker : public QObject { Q_OBJECT QImage m_image; VideoCapture cap; public slots: void doWork() { if(cap.open(0)) { Mat image; cap >> image; //conversion from Mat to QImage Mat dest; cvtColor(image, dest,CV_BGR2RGB); m_image = QImage((uchar*) dest.data, dest.cols, dest.rows, dest.step, QImage::Format_RGB888); emit resultReady(m_image); } } void deleteLater() { } signals: void resultReady(const QImage &result); }; class VideoCamera : public QLabel { Q_OBJECT public: explicit VideoCamera(QWidget *parent = nullptr); ~VideoCamera(); public slots: void updatePicture(const QImage&); private: Worker* worker; QThread workerThread; QTimer* timer; }; #endif // VIDEOCAMERA_H
videocamera.cpp
#include "videocamera.h" using namespace cv; VideoCamera::VideoCamera(QWidget *parent): QLabel(parent) { worker=new Worker(); worker->moveToThread(&workerThread); timer = new QTimer(this); connect(timer, SIGNAL(timeout()), worker, SLOT(doWork())); connect(&workerThread, SIGNAL(finished()), worker, SLOT(deleteLater())); connect(worker, SIGNAL(resultReady(const QImage&)), this, SLOT(updatePicture(const QImage&))); workerThread.start(); timer->start(10); } void VideoCamera::updatePicture(const QImage &image) { //show Qimage using QLabel this->setPixmap(QPixmap::fromImage(image)); } VideoCamera::~VideoCamera() { workerThread.quit(); workerThread.wait(); }
-
Hi,
Your original data has likely become invalid by the time you try to create the QPixmap object. You should make a copy before sending it.
-
Hello SGaist,
thank you for your answer!! You are right. I solved passing directly the Pixmap, instead of QImage. Is it what you meant? -
No it's not, can you show the new version of your code ?
-
Hi, @SGaist ,
arn't the
const
,&
references ofvoid resultReady(const QImage &result);
ignored by a Qt::QueuedConnection and a copy is send anyway? The default connection type between threads is QueuedConnection after all.@davidino I had a case where QImage was not properly registerstered in the metatyp system, that caused problems for qued connections. Do you get any warnings in your consol output during run time?
Or better yet, use the new Qt5 Syntax and see if it compiles:
//5th argument Qt::QueuedConnection just to be sure connect(worker, Worker::resultReady, this, VideoCamera::updatePicture, Qt::QueuedConnection);
-
Here it's the QImage construction that is important. It uses the third constructor which doesn't copy the data hence you have to handle the lifetime of the underlying data. In this case there's a need to force a deep copy.
-
Hello Gaist,
thank you very much. Now I got it!
Below the working Worker class.class Worker : public QObject { Q_OBJECT Mat dest; QImage m_image; VideoCapture cap; QObject* parent; public slots: void doColor() { if(cap.isOpened()) { Mat image; cap >> image; //conversion from Mat to QImage cvtColor(image, dest,CV_BGR2RGB); m_image = QImage((uchar*) dest.data, dest.cols, dest.rows, dest.step, QImage::Format_RGB888); emit resultReady(m_image); } else cap = VideoCapture(0); }
-
Hello @J-Hilk,
thank you for your email.
The only console outputs that I have are the following, but they don't bother the program.evdevkeyboard: Could not read from input device (No such device)
evdevkeyboard: Failed to query led statesI'll pass to Qt5 connect Syntax for sure, thank you.
-
There's another solution worth investigating. Since your images are likely not going to change size, you could make m_image directly the right size and then wrap the data from it in the dest Mat (see this constructor). Doing so you you can remove the
m_image = etc.
line.