2

I'd like to add some multithreading to my application (opencv-opengl integration), and I'm starting from the structure found in this answer. For now there is a thread that grab the video frame and send it to the MainWindow, nothing more.

I have tried to search a little bit but nothing makes it clear but only make things more obscure.

Even if I read an article that says not to subclass QThread but use moveToThread() I read another article somewhere (other then one of the official example) that say to do it.

If I run the application and then close it crashes. If I run the application and I call the endCapture and then start again.. it crashes again.

Every kind of help or comment is appreciated!

Here is the VideoThread.h:

#ifndef VIDEOTHREAD_H
#define VIDEOTHREAD_H

#include <QMutex>
#include <QImage>
#include <QThread>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

class VideoThread : public QThread
{
    Q_OBJECT
public:
    explicit VideoThread(QObject *parent = 0);
    ~VideoThread();

protected:
    void run();

private:
    cv::VideoCapture video;

    QMutex m_AbortCaptureLock;
    bool m_AbortCapture;

signals:
    void sendImage(QImage);

public slots:
    void endCapture();
};

#endif // VIDEOTHREAD_H

VideoThread.cpp:

#include "videothread.h"

#include <QDebug>

VideoThread::VideoThread(QObject *parent) :
    QThread(parent)
{
    qDebug() << "VideoThread > ctor.";
}

VideoThread::~VideoThread()
{
    qDebug() << "VideoThread > dtor";

    if(video.isOpened()) {
        video.release();
        qDebug() << "Camera successfully disconnected.";
    }
}

void VideoThread::run()
{
    m_AbortCapture = false;
    video = cv::VideoCapture(0);
    qDebug() << "VideoThread::run..";

    while(true)
    {
        m_AbortCaptureLock.lock();
        if (m_AbortCapture) {
            qDebug() << "VideoThread::run > abort capture..";
            break;
        }
        m_AbortCaptureLock.unlock();

        cv::Mat cvFrame;
        video >> cvFrame;
        if(cvFrame.empty()) continue;

        // convert the Mat to a QImage
        QImage qtFrame(cvFrame.data, cvFrame.size().width, cvFrame.size().height, cvFrame.step, QImage::Format_RGB888);
        qtFrame = qtFrame.rgbSwapped();

        // queue the image to the gui
        emit sendImage(qtFrame);
        msleep(20);
    }
}

void VideoThread::endCapture()
{
    qDebug() << "VideoThread::endCapture()";

    m_AbortCaptureLock.lock();
    m_AbortCapture = true;
    m_AbortCaptureLock.unlock();
}

And here the main:

#include "mainwindow.h"
#include "ui_mainwindow.h"

#include "opencv_glwidget.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    OpenCV_GLWidget *w = new OpenCV_GLWidget();
    w->setParent(this->centralWidget());

    connect(ui->checkBox, SIGNAL(toggled(bool)),
            this, SLOT(toggle(bool)));
    ui->checkBox->toggle();

    connect(&thread, SIGNAL(sendImage(QImage)),
            w, SLOT(renderImage(QImage)));
    thread.start();
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::toggle(bool n)
{
    if(n) {
        thread.start();
    } else {
        thread.endCapture();
    }
}
Community
  • 1
  • 1
nkint
  • 11,513
  • 31
  • 103
  • 174

1 Answers1

0

Do not subclass QThread. Create a subclass of QObject that contain a slot. This slot should contain the code for one iteration of your infinite loop (the loop should be removed). Create a QThread object and move your object to this thread using moveToThread. Create a QTimer, connect it to your object's slot and start it with the desired interval (20 msec). Since the object belongs to other thread, its slot will be executed periodically in that thread. When you want to stop the executing, call quit() and wait() on the thread. It will stop executing.

Pavel Strakhov
  • 39,123
  • 5
  • 88
  • 127
  • no `run()`? no `bAbort` flag? Handle the start and the stop of the thread just with starting/stoping timer. Seems to greatly simplify the thing. But say that I want to pause the video: stop to timer and wait.. and for restarting? just restart the timer? no need to awake the thread? – nkint Jul 29 '13 at 22:38
  • Note that you need to make `video` a class member variable. It will be preserved between slots calls. If you want to restart video, you should create another slot that will reset `video` value. Also, just `stop()` can `start()` the timer to pause execution. When the timer is stopped, the thread continues executing but doesn't perform any operations. Also, now I think that 20 msec timer can create much overhead. It's better to increase its interval to 100-500 msec and perform several iterations (instead of one iteration) in the slot. – Pavel Strakhov Jul 30 '13 at 10:11