2013-07-29 29 views
1

我想爲我的應用程序(opencv-opengl集成)添加一些多線程,並且我從this answer中找到的結構開始。現在有一個線程抓取視頻幀並將其發送到MainWindow,僅此而已。用循環停止QThread的正確方法(從opencv讀取視頻)

我試圖搜索一點點,但沒有明確說明,但只是使事情更加晦澀。

即使我讀an article,說沒有繼承QThread但使用moveToThread()我讀到另一篇文章的地方(其他然後one of the official example),該說的去做。

如果我運行該應用程序,然後關閉它崩潰。 如果我運行的應用程序,我打電話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(); 
    } 
} 

回答

0

不要繼承QThread。創建一個包含插槽的QObject的子類。這個槽應該包含你的無限循環迭代的代碼(循環應該被刪除)。創建一個QThread對象,並使用moveToThread將對象移動到此線程。創建一個QTimer,將其連接到對象的插槽並以期望的間隔(20毫秒)啓動它。由於對象屬於其他線程,它的插槽將在該線程中定期執行。當您想停止執行時,請在線程上撥打quit()wait()。它會停止執行。

+0

no'run()'?沒有'bAbort'標誌?只需啓動/停止定時器即可處理線程的啓動和停止。似乎大大簡化了事情。但是說,我想暫停視頻:停止定時器並等待...並重新啓動?只需重新啓動計時器?不需要喚醒線程? – nkint

+0

請注意,您需要將'video'設爲類成員變量。它將在插槽調用之間保留。如果您想重新啓動視頻,您應該創建另一個插槽來重置「video」值。另外,只有'stop()'可以'啓動()'計時器來暫停執行。當定時器停止時,線程繼續執行但不執行任何操作。此外,現在我認爲20毫秒定時器可能會產生很多開銷。最好將其間隔增加到100-500毫秒,並在插槽中執行多次迭代(而不是一次迭代)。 –