用循环(从opencv读取视频)停止QThread的正确方法

The proper way to stop a QThread with a loop (that read a video from opencv)

本文关键字:QThread 停止 方法 视频 循环 opencv 读取      更新时间:2023-10-16

我想为我的应用程序添加一些多线程(opencv opengl集成),我从这个答案中的结构开始。现在有一个线程可以获取视频帧并将其发送到主窗口,仅此而已。

我试着搜索了一下,但没有什么能让它变得清晰,只会让事情变得更加模糊。

即使我读到一篇文章说不要将QThread子类化,而是使用moveToThread(),我也会在某个地方读到另一篇文章(除了一个官方例子)说要这样做

如果我运行应用程序然后关闭它,它就会崩溃。如果我运行应用程序并调用endCapture,然后重新启动。。它再次崩溃。

感谢您的每一种帮助或评论!

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

这里是主要的:

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

不要将QThread子类化。创建包含插槽的QObject的子类。这个槽应该包含无限循环的一次迭代的代码(循环应该被删除)。创建一个QThread对象,并使用moveToThread将对象移动到此线程。创建一个QTimer,将其连接到对象的插槽,并以所需的间隔(20毫秒)启动。由于对象属于其他线程,因此它的槽将在该线程中定期执行。当您想停止执行时,在线程上调用quit()wait()。它将停止执行。