2017-04-23 18 views
0

3つのビデオファイル(実際には、同じ部屋にある3つのカメラ)を読み込み、3つの異なるスレッドを使用する簡単なプログラムを作成しようとしています。QT + OpenCVによるマルチスレッド

mainwindow.cpp

void MainWindow::init() 
{ 
    numCams = 3; 

    // Resize the video for displaying to the size of the widget 
    int WidgetHeight = ui->CVWidget1->height(); 
    int WidgetWidth = ui->CVWidget1->width(); 

    for (int i = 0; i < numCams; i++){ 
     // Create threads 
     threads[i] = new QThread; 

     // Create workers 
     string Path = "/Users/alex/Desktop/PruebasHilos/Videos/" + to_string(i+1) + ".m2v"; 
     workers[i] = new Worker(QString::fromStdString(Path), i, WidgetHeight, WidgetWidth); 

     workers[i]->moveToThread(threads[i]); 

     connectSignals2Slots(threads[i], workers[i]); 

     threads[i]->start(); 
     qDebug() << "Thread from camera " << (i+1) << " started"; 
    } 
} 

void MainWindow::connectSignals2Slots(QThread *thread, Worker *worker) 
{ 
    connect(thread, SIGNAL(started()), worker, SLOT(readVideo())); 
    connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); 
    connect(worker, SIGNAL(frameFinished(Mat, int)), this, SLOT(displayFrame(Mat,int))); 
    connect(worker, SIGNAL(finished(int)), thread, SLOT(quit())); 
    connect(worker, SIGNAL(finished(int)), worker, SLOT(deleteLater())); 
} 

void MainWindow::displayFrame(Mat frame, int index) 
{ 
    if (index == 0) { 
     // Camera 1 
     ui->CVWidget1->showImage(frame); 
    } 
    else if (index == 1) { 
     // Camera 2 
     ui->CVWidget2->showImage(frame); 
    } 
    else if (index == 2) { 
     // Camera 3 
     ui->CVWidget3->showImage(frame); 
    } 
} 

worker.cpp

Worker::Worker(QString path, int id, int WidgetHeight, int WidgetWidth) : filepath(path), index(id), WidgetHeight(WidgetHeight), WidgetWidth(WidgetWidth) { 
} 

Worker::~Worker(){ 
} 

void Worker::readVideo() 
{ 
    VideoCapture cap(filepath.toStdString()); 

    if (! cap.isOpened()) { 
     qDebug() << "Can't open video file " << filepath; 
     emit finished(index); 
     return; 
    } 

    Mat ActualFrame; 
    while (true) { 
     cap >> ActualFrame; 

     if (ActualFrame.empty()) { 
      // Empty frame to display when the video has finished 
      ActualFrame = Mat(Size(720, 576), CV_8UC3, Scalar(192, 0, 0)); 
      emit frameFinished(ActualFrame, index); 

      qDebug() << "Video finished"; 
      break; 
     } 

     // Background Subtraction 
     BackgroundSubtraction(ActualFrame, BackgroundMask); 

     emit frameFinished(ActualFrame.clone(), index); 
     QThread::msleep(35); 
    } 
    emit finished(index); 
} 

void Worker::BackgroundSubtraction(Mat ActualFrame, Mat &BackgroundMask) 
{ 
    pMOG2->apply(ActualFrame, BackgroundMask); 
} 

だけVideoCaptureからフレームを読み込み、UIにそれらを表示する:私が使用しているコードは以下のとおりです。 QWidgetsを使用する別のクラスがうまく動作します。
しかし、BackgroundSubstractionメソッドを含めると、UIは3つのカメラの同じフレーム番号を表示しません。Camera1はフレーム100を計算し、Camera2とCamera3はフレーム110にあります。
これは、他のものよりも多く、これは合成の問題を引き起こす。
私はQTでスレッドを使用しているので、スレッド間でいくつかのsynconizationを行いたいので、displayFrameメソッドを呼び出すために3つの異なるフレームが処理されていることがわかっているので、3つの同じフレームが表示されますまったく同じ時期に

EDIT:
は、私がこれを行う最も簡単な方法は、障壁を使用していることを前提としています。
http://www.boost.org/doc/libs/1_55_0/doc/html/thread/synchronization.html#thread.synchronization.barriers。しかし、私はこれをどうやって行うのかわからない。

EDIT 2: 私はこのSyncronizacion using barriersを実装しているし、今のコードは次のようになります。

barrier.h

#ifndef BARRIER_H 
#define BARRIER_H 

#include <QMutex> 
#include <QWaitCondition> 
#include <QSharedPointer> 

// Data "pimpl" class (not to be used directly) 
class BarrierData 
{ 
public: 
    BarrierData(int count) : count(count) {} 

    void wait() { 
     mutex.lock(); 
     --count; 
     if (count > 0) 
      condition.wait(&mutex); 
     else 
      condition.wakeAll(); 
     mutex.unlock(); 
    } 
private: 
    Q_DISABLE_COPY(BarrierData) 
    int count; 
    QMutex mutex; 
    QWaitCondition condition; 
}; 

class Barrier { 
public: 
    // Create a barrier that will wait for count threads 
    Barrier(int count) : d(new BarrierData(count)) {} 
    void wait() { 
     d->wait(); 
    } 

private: 
    QSharedPointer<BarrierData> d; 
}; 

#endif // BARRIER_H 

更新worker.cpp

void Worker::readVideo() 
{ 
    VideoCapture cap(filepath.toStdString()); 

    int framenumber = 0; 
    if (! cap.isOpened()) { 
     qDebug() << "Can't open video file " << filepath; 
     emit finished(index); 
     return; 
    } 

    Mat ActualFrame; 
    while (true) { 
     cap >> ActualFrame; 

     if (ActualFrame.empty()) { 
      // Empty frame to display when the video has finished 
      ActualFrame = Mat(Size(720, 576), CV_8UC3, Scalar(192, 0, 0)); 
      emit frameFinished(ActualFrame, index); 

      qDebug() << "Video finished"; 
      break; 
     } 

     // Background Subtraction 
     BackgroundSubtraction(ActualFrame, BackgroundMask); 

     QThread::msleep(5); 
     barrier.wait(); 
     qDebug() << "Thread " << index << " processing frame " << framenumber ; 
     emit frameFinished(ActualFrame.clone(), index); 
     framenumber++; 
    } 
    emit finished(index); 
} 

void Worker::BackgroundSubtraction(Mat ActualFrame, Mat &BackgroundMask) 
{ 
    pMOG2->apply(ActualFrame, BackgroundMask); 
} 

しかし、プログラムの出力は以下の通りです、完璧に動作するようです:

Thread 1 processing frame 0 
Thread 0 processing frame 0 
Thread 2 processing frame 0 
Thread 2 processing frame 1 
Thread 1 processing frame 1 
Thread 0 processing frame 1 
Thread 2 processing frame 2 
Thread 1 processing frame 2 
Thread 0 processing frame 2 
Thread 2 processing frame 3 
Thread 1 processing frame 3 
Thread 0 processing frame 3 
Thread 2 processing frame 4 
Thread 1 processing frame 4 
Thread 0 processing frame 4 
Thread 2 processing frame 5 
Thread 0 processing frame 5 
Thread 1 processing frame 5 
Thread 2 processing frame 6 
Thread 1 processing frame 6 
Thread 2 processing frame 7 
Thread 0 processing frame 6 
Thread 1 processing frame 7 
Thread 2 processing frame 8 
Thread 0 processing frame 7 
Thread 1 processing frame 8 
Thread 2 processing frame 9 
Thread 0 processing frame 8 
Thread 1 processing frame 9 
Thread 1 processing frame 10 
Thread 2 processing frame 10 
Thread 0 processing frame 9 
Thread 1 processing frame 11 
Thread 2 processing frame 11 
Thread 0 processing frame 10 
Thread 1 processing frame 12 

のsyncronizationが完璧に働いている冒頭で、しかし、バリアが動作していないとのスレッドがそれぞれに待機していないようです他の...

EDIT 3:解決しよう

QThread::msleep(5); 

の値を変更するようです

QThread::msleep(35); 

は、理由を実際には理解していませんが、同期化の問題を解決します。

+0

バックグラウンドの減算がなくても、同じフレーム番号が各スレッドで処理されるようにするには、同期が必要です。 Qtでは、最も簡単な方法は、無限ループを取り除き、すべてのスレッドがsignalFinishedシグナルを出した後、次の画像を計算するために各スレッドのスロットを呼び出すことです。いくつかのバッファリングを使用して、スレッド内の画像を事前計算し、そのバッファからロードするだけで済みます。 – Micka

答えて

1

バックグラウンドの減算がなくても、同じフレーム番号が各スレッドで処理されるようにするには、同期が必要です。

Qtでは、無限ループを削除し、代わりに、すべてのスレッドが信号frameFinishedを送出した後、次の画像を計算するために各スレッドのスロットを呼び出します。

あなたはさらに、あなたのスレッドで画像を事前に計算し、ちょうどそのバッファからそれらをロードするために、いくつかのバッファリングを使用することができます。そのシナリオでは、次の操作を行うことができます:あなたのスレッドの

  1. それぞれは限り空きバッファスペースが利用可能であるとして無限ループで彼のバッファを埋めます。バッファが満杯の場合、スレッドはバッファ領域が解放されるまで待ちます。あなたのGUIが表示されており、いくつかの時間を待っていたときに

  2. 、それはsendMeANewImageなどの各スレッドのスロットに接続された信号を送信します。バッファが空の場合

  3. 各スレッドは、イメージのためにそのバッファまたは待ち(無限ループまたは条件待ち)から、次の利用可能な画像を送信します。その後、frameFinished信号を送出し、使用されたバッファスペースを解放します。

  4. 各スレッドが信号を放射された、すべての画像を表示する、いくつかの時間を待ってから再度sendMeANewImageを発します。

これはスレッドセーフではありませんが、バッファからの読み取りと書き込みのクリティカルセクションがあります。それぞれのバッファに対して、QMutexを作成し、そのバッファから読み書き、またはサイズの問い合わせなどを行うたびにmutex.lock()を呼び出します。すぐ後にmutex.unlock()を呼び出します。ミューテックスがロックされ、別のスレッド(または同じスレッドが)再びそれをロックしようとした場合、他のスレッドがミューテックスをアンロックするまで

、スレッドは、そこに待機します。これにより、クリティカルセクションに入ることができるスレッドは1つだけです。

+0

私はこの解決策を見つけました[link](http://stackoverflow.com/questions/9637374/qt-synchronization-barrier/9639624#9639624)、バリアを使用して、スレッドが待機すべきコード内のポイントを作成します。これは、最後のスレッドがバリアに達すると、すべてのスレッドが継続することを意味します。 waitを呼び出す前にframeFinished()を呼び出すように実装しました。あなたは正しいと思いますか? – Alex

+0

ただ試してみてください。おそらく動作します。 didntはそれを読んだが、私のアプローチに似て聞こえる。 – Micka

関連する問題