简体   繁体   English

多线程opencv视频处理Qt / C ++

[英]Multithreaded opencv video processing Qt/C++

Hope your are doing good ! 希望你一切都好! I'm strugguling from yesterday on how to implement a multithreaded video processing program with opencv. 从昨天开始,我在为如何使用opencv实现多线程视频处理程序而苦苦挣扎。

I understand how threads actually work, how to use a simple mutex etc...But when it comes to implementation, i'm completely lost. 我了解线程实际上是如何工作的,如何使用简单的互斥锁等...但是谈到实现,我完全迷失了。

My goal is to create an interface with Qt and display two labels, one showing original video feed and the other showing the processed one, each one handeled by a thread. 我的目标是创建一个带有Qt的界面,并显示两个标签,一个显示原始视频提要,另一个显示已处理的标签,每个标签由一个线程处理。

For now i'm just trying to get on thread to display images, but i really struggle with it. 现在,我只是试图进入显示图像的线程,但我真的为此感到挣扎。

Here is what i've done so far : 到目前为止,这是我所做的:

CaptureThread a class inheriting from QThread : It is supposed to handle the start of cam capture etc... CaptureThread是从QThread继承的类:应该用来处理cam捕获的开始等。

#ifndef CAPTURETHREAD_H
#define CAPTURETHREAD_H

#include <QThread>

#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include "opencv2/videoio.hpp"

#include <QTimer>

class CaptureThread : public QThread
{
    Q_OBJECT

public:
    explicit CaptureThread(QObject *parent);


protected:
    void run();

signals:
    void frameCaptured(cv::Mat);

public slots:
    void captureFrame();

private:
    QTimer* tmrTimer;
    cv::VideoCapture capWebam;
    cv::Mat capturedFrame;

};

#endif // CAPTURETHREAD_H

And this is its implementation : 这是它的实现:

#include "capturethread.h"
#include <QDebug>

CaptureThread::CaptureThread(QObject* parent):QThread(parent)
{
    capWebam.open(0);
}

void CaptureThread::run()
{
    tmrTimer = new QTimer(this);
    QObject::connect(tmrTimer, SIGNAL(timeout()), this, SLOT(captureFrame()));

    tmrTimer->start(10);
    exec();
}

void CaptureThread::captureFrame()
{
    if(capWebam.isOpened()){
        capWebam.read(capturedFrame);

        emit frameCaptured(capturedFrame);
    }

}

MainWindow use to display the camera feed etc... MainWindow用于显示摄像头提要等...

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include "opencv2/videoio.hpp"
#include <capturethread.h>

#include <QTimer>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

public slots:
    void pricessFrameAndUpdateGUI(cv::Mat matOriginal);

private slots:
    void on_button_clicked();

private:
    Ui::MainWindow *ui;
    QImage toGrayscale(QImage image);

    cv::VideoCapture capWebcam;
    cv::Mat matOriginal;
    cv::Mat matProcessed;

    QImage qimgOriginal;
    QImage qimgProcessed;

    QTimer* tmrTimer;

    CaptureThread* cpThread;
};

#endif // MAINWINDOW_H

And its implentation : 及其实现:

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QtCore>
#include <cv.h>
#include <QColor>
#include <opencv/highgui.h>

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

    cpThread = new CaptureThread(this);
    QObject::connect(cpThread, SIGNAL(frameCaptured(cv::Mat)),this, SLOT(pricessFrameAndUpdateGUI(cv::Mat)));
    cpThread->start();

}

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

void MainWindow::pricessFrameAndUpdateGUI(cv::Mat matOriginal)
{


    cv::Canny(matOriginal, matProcessed, 100, 300);

    cv::cvtColor(matOriginal, matOriginal, CV_BGR2RGB);
    QImage qimgOriginal((uchar*)matOriginal.data, matOriginal.cols, matOriginal.rows, matOriginal.step, QImage::Format_RGB888);
    QImage qimgProcessed((uchar*)matProcessed.data, matProcessed.cols, matProcessed.rows, matProcessed.step,QImage::Format_Indexed8);

    ui->original->setPixmap(QPixmap::fromImage(qimgOriginal));
    ui->modified->setPixmap(QPixmap::fromImage(qimgProcessed));

}

After compiling and executing the program i get this error : 编译并执行程序后,出现此错误:

Starting /media/wassim/BLAZER/Workspace/CPP/build-firstCV-Desktop_Qt_5_3_GCC_64bit-Debug/firstCV...

VIDEOIO ERROR: V4L/V4L2: VIDIOC_S_CROP

QObject: Cannot create children for a parent that is in a different thread.

(Parent is CaptureThread(0xc02be0), parent's thread is QThread(0xae82c0), current thread is CaptureThread(0xc02be0)

The program has unexpectedly finished.

/media/wassim/BLAZER/Workspace/CPP/build-firstCV-Desktop_Qt_5_3_GCC_64bit-Debug/firstCV crashed

Thanks you guys for your help ! 谢谢你们的帮助!

The error you see is caused by the QTimer being passed a parent that lives in another thread. 您看到的错误是由于QTimer被传递给另一个线程中的父级而引起的。 CaptureThread lives in the UI thread. CaptureThread位于UI线程中。 The QTimer is created in another thread (it is in the run() method). QTimer在另一个线程中创建(它在run()方法中)。

Simplest solution: move the instantiation of the QTimer (and the start call) into the ctor: 最简单的解决方案:将QTimer的实例化(和开始调用)移到ctor中:

tmrTimer = new QTimer(this);
QObject::connect(tmrTimer, SIGNAL(timeout()),
                 this, SLOT(captureFrame()));
tmrTimer->start(40);

This should work. 这应该工作。 But it won't work as it should. 但这并不能正常工作。 The timeout() signal will queue a message in the thread in which CaptureThread lives, which is the UI thread. timeout()信号将在UI线程所在的线程中将消息排队。 So everything will be done in the UI thread, not only the post-processing. 因此,所有事情都将在UI线程中完成,而不仅仅是后处理。 Quickest solution: 最快的解决方案:

CaptureThread::CaptureThread() : QObject()
{
    capWebam.open(0);

    QThread* t = new QThread();
    moveToThread(t);
    t->start();

    tmrTimer = new QTimer;
    QObject::connect(tmrTimer, SIGNAL(timeout()),
                     this, SLOT(captureFrame()));
    tmrTimer->start(40);
}

CaptureThread is moved to a new QThread (it is not a subclass of QThread). CaptureThread被移动到新的QThread(它不是QThread的子类)。 Also, move the post-processing to this thread. 另外,将后处理移至该线程。 This is just the concept, then you'll have to handle cleanup, register the meta-type etc... 这只是概念,那么您将不得不进行清理,注册元类型等。

EDIT: Ok, just a quick test code (tested on Mac OS), may be broken and need optimization, I didn't check memory cleanup etc... (also pay attention to how you're hiding matOriginal in your code, you're not passing the class member to the QImage, but the local instance it seems): 编辑:好的,只是一个快速的测试代码(在Mac OS上进行了测试),可能已损坏并且需要优化,我没有检查内存清理等...(也请注意如何在代码中隐藏matOriginal,没有将类成员传递给QImage,而是本地实例):

main.cpp main.cpp中

#include <QApplication>
#include <QLabel>
#include <QTimer>
#include <QThread>

#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>

#include "src.h"

int main(int argc, char* argv[])
{
   QApplication a(argc, argv);

   cv::VideoCapture vc;
   if (!vc.open(0))
      return 1;

   QTimer t;
   QThread th;
   CaptureHandler handler(&vc);
   handler.moveToThread(&th);
   th.start();

   MainWidget w1;
   w1.resize(100, 100);
   w1.show();

   MainWidget w2;
   w2.resize(100, 100);
   w2.show();

   QObject::connect(&t, SIGNAL(timeout()),
                    &handler, SLOT(handleFrame()));
   QObject::connect(&handler, SIGNAL(frameReady(QImage)),
                    &w1, SLOT(onFrame(QImage)));
   QObject::connect(&handler, SIGNAL(framePpReady(QImage)),
                    &w2, SLOT(onFrame(QImage)));
   t.start(20);

   return a.exec();
}

src.h src.h

#ifndef SRC_H
#define SRC_H

#include <QObject>
#include <QImage>
#include <QLabel>

#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/core/core.hpp>

class CaptureHandler : public QObject
{
   Q_OBJECT
public:
   CaptureHandler(cv::VideoCapture* vc);
signals:
   void frameReady(QImage frame);
   void framePpReady(QImage frame);
public slots:
   void handleFrame();
private:
   cv::VideoCapture* vc;
};

class MainWidget : public QLabel
{
   Q_OBJECT
public slots:
   void onFrame(QImage frame);
};

#endif // SRC_H

src.cpp src.cpp

#include <QObject>
#include <QImage>
#include <QLabel>

#include "src.h"

void cleanup_mat(void* info)
{
   delete (cv::Mat*)info;
}

CaptureHandler::CaptureHandler(cv::VideoCapture* vc) : QObject(), vc(vc) {}

void CaptureHandler::handleFrame() {
   cv::Mat* original = new cv::Mat;
   if (!vc->read(*original))
      return;

   cv::Mat* processed = new cv::Mat;
   cv::Canny(*original, *processed, 100, 300);
   cv::cvtColor(*original, *original, CV_BGR2RGB);
   QImage qimgOriginal((uchar*)original->data,
                       original->cols,
                       original->rows,
                       original->step,
                       QImage::Format_RGB888, cleanup_mat, original);
   QImage qimgProcessed((uchar*)processed->data,
                        processed->cols,
                        processed->rows,
                        processed->step,
                        QImage::Format_Indexed8, cleanup_mat, processed);

   emit frameReady(qimgOriginal);
   emit framePpReady(qimgProcessed);
}

void MainWidget::onFrame(QImage frame) {
   setPixmap(QPixmap::fromImage(frame));
}

Shot :-) 射击:-)

在此处输入图片说明

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM