gpt4 book ai didi

c++ - 使用 QT + OpenCV 的多线程

转载 作者:行者123 更新时间:2023-11-28 05:10:40 31 4
gpt4 key购买 nike

我正在尝试编写一个简单的程序,该程序使用 3 个不同的线程读取三个视频文件(实际上是同一房间内的 3 个摄像头)。我使用的代码如下:

主窗口.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 的不同类的 UI 中即可。
但是,当我包含 BackgroundSubstraction 方法时,UI 不会为三个摄像头显示相同的帧编号,可能 Camera1 正在计算第 100 帧,而 Camera2 和 Camera3 在第 110 帧。
这是因为某些帧的计算速度比其他帧快,这会导致同步问题。
我是 QT 中使用线程的新手,所以我想在线程之间进行一些同步,所以我知道何时处理了三个不同的帧以调用 displayFrame 方法,因此,三个相同的帧显示在完全相同的时间。

编辑:
我认为最简单的方法是使用 Barriers。
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:已解决似乎改变了

的值(value)
QThread::msleep(5);

QThread::msleep(35);

解决了同步问题,虽然我不太明白原因。

最佳答案

即使没有背景减法,您也需要一些同步来确保每个线程处理相同的帧号。

在 Qt 中,最简单(恕我直言)的方法是移除无限循环,而是在所有线程发出其 frameFinished 信号后,调用每个线程的槽来计算下一张图像。

您可以进一步使用一些缓冲来预先计算线程中的图像,然后从该缓冲区加载它们。在这种情况下,您可以执行以下操作:

  1. 只要有可用的空闲缓冲区空间,您的每个线程就会无限循环地填充他的缓冲区。如果缓冲区已满,线程将等待直到缓冲区空间被释放。

  2. 当您的 gui 显示并等待一段时间后,它会发送一个连接到每个线程槽的信号,如 sendMeANewImage。

  3. 每个线程从其缓冲区发送下一个可用图像,或者如果缓冲区为空,则等待(无限循环或条件等待)图像。然后发出 frameFinished 信号并释放使用的缓冲区空间。

  4. 当每个线程都发出信号时,显示所有图像,等待一段时间并再次发出 sendMeANewImage。

这还不是线程安全的,您将拥有从缓冲区读取和写入的关键部分。对于每个缓冲区,创建一个 QMutex 并在从该缓冲区读取或写入或询问大小等时调用 mutex.lock() 。之后立即调用 mutex.unlock()。

当一个互斥锁被锁定并且另一个线程(甚至同一个线程)试图再次锁定它时,该线程将在那里等待,直到另一个线程解锁了互斥锁。这样,只有一个线程可以进入临界区。

关于c++ - 使用 QT + OpenCV 的多线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43570995/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com