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

时间:2014-12-26 10:04:16

标签: c++ multithreading qt opencv implementation

希望你做得好! 我是从昨天开始就如何使用opencv实现多线程视频处理程序而努力的。

我理解线程是如何工作的,如何使用简单的互斥锁等......但是当涉及到实现时,我完全迷失了。

我的目标是使用Qt创建一个界面,并显示两个标签,一个显示原始视频,另一个显示已处理的标签,每个标签都由一个帖子处理。

现在我只是试图通过线程来显示图像,但我真的很挣扎。

以下是我迄今所做的事情:

CaptureThread继承自QThread的类: 它应该处理凸轮捕获的开始等...

#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

这是它的实施:

#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用于显示相机Feed ...

#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

及其实施:

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

}

编译并执行程序后,我收到此错误:

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

谢谢你们的帮助!

1 个答案:

答案 0 :(得分:2)

您看到的错误是由QTimer传递给另一个线程中的父级引起的。 CaptureThread存在于UI线程中。 QTimer是在另一个线程中创建的(它在run()方法中)。

最简单的解决方案:将QTimer(和启动调用)的实例化移动到ctor中:

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

这应该有效。但它不会像它应该的那样工作。 timeout()信号将在CaptureThread所在的线程中对消息进行排队,这是UI线程。所以一切都将在UI线程中完成,而不仅仅是后期处理。最快的解决方案:

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被移动到新的QThread(它不是QThread的子类)。此外,将后处理移动到此线程。这只是概念,然后您必须处理清理,注册元类型等...

编辑:好的,只是一个快速的测试代码(在Mac OS上测试过),可能会被破坏并需要优化,我没有检查内存清理等等...(另外注意你是如何做的)在代码中隐藏matOriginal,你没有将类成员传递给QImage,但看起来是本地实例):

的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

#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

#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));
}
拍摄: - )

enter image description here