我試圖編寫一個簡單的程序,使用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幀,並將其顯示到用戶界面由另一個使用QWidgets的不同類使用得很好。但是,當我包含BackgroundSubstraction方法時,UI不顯示三個攝像頭的相同幀號,也許Camera1計算幀100,Camera2和Camera3位於幀110中。
這是因爲某些幀計算速度更快比其他,這導致了諧振問題。
我很新使用QT中的線程,所以我想在線程之間做一些synconization,所以我知道何時三個不同的幀已經被處理以調用displayFrame方法,並且因此,顯示三個相同的幀在同一時間。
編輯:
我假設最簡單的方法是使用障礙。
http://www.boost.org/doc/libs/1_55_0/doc/html/thread/synchronization.html#thread.synchronization.barriers。但我不知道如何做到這一點。
編輯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
在開始的同步化是完美的工作,但後來似乎屏障不能正常使用線程不等待各其他...
編輯3:解決 看來的
QThread::msleep(5);
的值更改爲
QThread::msleep(35);
解決了同步問題,雖然我不太明白原因。
即使沒有背景減法,你也需要一些同步來確保每個線程處理相同的幀號。在Qt中,最簡單的方法是去除無限循環,然後調用每個線程的槽來計算下一個圖像,在所有線程發出信號frameFinished之後。您可以進一步使用一些緩衝來預先計算線程中的映像,並從緩衝區中加載它們。 – Micka