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();
    }

  • Lifetime Qt Champion

    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?


  • Lifetime Qt Champion

    No it's not, can you show the new version of your code ?



  • Hi, @SGaist ,

    arn't the const ,& references of void 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);
    

  • Lifetime Qt Champion

    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 states

    I'll pass to Qt5 connect Syntax for sure, thank you.


  • Lifetime Qt Champion

    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.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.