2014-12-26 210 views
1

希望你做得很好! 我从昨天开始就如何使用opencv实现多线程视频处理程序而挣扎。多线程opencv视频处理Qt/C++

我明白线程是如何工作的,如何使用一个简单的互斥等......但是当涉及到实现时,我完全失去了。

我的目标是创建一个与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); 
    } 

} 

主窗口使用可显示CA梅拉饲料等...

#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 

而其implentation:

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 

感谢你们的帮助:

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

} 

编译和执行程序我得到这个错误后!

+0

当您收到工作者线程上的新形象,对ima进行所有的处理然后将它发回主线程,如果你想多线程然后根据宽度将图像分割成托管子集IE 1280/4 = 320 px会给你4个线程来处理处理,确保所有线程都已完成在那里工作,然后重建输出框架并将其发送到主线程以供显示 – Alex

+0

谢谢@亚历克斯! 但我以前认为,但我的主要问题,是我遇到的关于我的程序执行的错误? 你能帮我解决吗?或者你有任何线索如何解决这个问题? – IgZiStO

+0

我相信你应该只从** main **线程执行捕获。请尝试。 – karlphillip

回答

2

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

简单的解决方案:移动QTimer(并开始通话)的实例为构造函数:

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,但看起来像本地实例):

主要是

。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

+0

太棒了!非常感谢 !! 我的主要问题是在QThreads上没有太多文档(或者我不知道如何搜索:)),所以我从来没有理解他们如何实际工作,但现在它更清晰! – IgZiStO