Qt C++——多线程视频帧变换

Posted

技术标签:

【中文标题】Qt C++——多线程视频帧变换【英文标题】:Qt C++ - multithread video frame transformation 【发布时间】:2015-05-06 12:46:07 【问题描述】:

我必须编写一个应用程序,在一个 Widget 中显示加载的视频,拉取帧,转换它们,然后将它们放入另一个 Widget,最好是 QGraphicsView,并希望同步。同步它们对它们来说非常重要。

我写了一个测试线程,主要的肉是这样的:

void HSV::display() 
Ui::MainWindow ui; //1
int j=0;
QTimer *timer = new QTimer;
for(int i=1; i<4; i++)
    QString numer = QString::number(i);
    QImage imageTest(numer+".jpg");
    QGraphicsPixmapItem* test = new QGraphicsPixmapItem(QPixmap::fromImage(imageTest));
    QGraphicsScene* sceneHSV = new QGraphicsScene;
    sceneHSV->addItem(test);
    //2
    ui.graphicsViewKalibracjaHSV->setScene(sceneHSV);
    ui.graphicsViewKalibracjaHSV->fitInView(sceneHSV->sceneRect(),Qt::KeepAspectRatio);
    ui.graphicsViewKalibracjaHSV->show();
    timer->start(500);
    if(i==3) 
        i=1;
    
    j++;
    if (j>40) 
        break;
    
    

这不是我需要的,但这只是一个测试。如果我把文字放在那里,它会很好地通过,继续显示。但是这些图像......当到达第一行 ui 时立即崩溃( //2 )。我毫不怀疑这种做事方式非常糟糕。

如果我在 //1 删除 Ui::MainWindow ui;,它会告诉我 ui is not declared in this scope

所以我的问题是,如何从单独的线程访问和修改主窗口中的 ui 元素?

编辑: 好吧,一个更实际的想法。我将简单地将视频文件 URL 从 GUI 线程传递到工作线程,在那里执行所有操作并将 2 个处理后的图像传回(对于每个视频帧),每个 QGraphicsView 一个。一个正常的,带有标记的点,一个转换的。我现在知道如何将东西从工作线程传递到 GUI 线程,但是如何将 QString 从 GUI 传递到工作线程,以及如何接收它?

【问题讨论】:

我没有看到 ui->setupUi(this); 【参考方案1】:

简短的回答:你不能。

Qt 帮助:线程基础:

GUI线程和工作线程

如前所述,每个程序在启动时都有一个线程。这 线程被称为“主线程”(在 Qt 应用程序)。 Qt GUI 必须在这个线程中运行。所有小部件和 几个相关的类,例如 QPixmap,在辅助中不起作用 线程。辅助线程通常称为“工作线程” 线程”,因为它用于从主线程中卸载处理工作 线程。

你可以做什么:你可以在主线程中使用ui,通过信号/槽系统将QImage(或其他与ui无关的数据)传递到单独的线程中,在那里处理并传回。

更新我该怎么做?我的意思是,在线程之间来回传递 QImage 并同时连续显示两个视频?- Petersaber

我不是视频专家,但我可以给你一些提示。假设您有一个Widget,它接受QImage 并且必须显示它。 Processor 是一个对象,它处理图像并将其返回给 gui 线程。 Processor 应该派生自 QObject

Widget.h

class Widget : public QWidget, private Ui::Widget

    Q_OBJECT
public:
    explicit Widget(QWidget *parent = 0);
    void enqueueImage(const QImage &img);

signals:
    void process(const QImage &img);

private:
    void showNextImage();

private slots:
    void onProcessed(const QImage &img);

private:
    QThread *mThread;
    QScopedPointer < Processor > mProcessor;
    QList < QImage > mImagesPool;
;

Widget.cpp

Widget::Widget(QWidget *parent) :
    QWidget(parent)
  , mThread(new QThread(this))
  , mProcessor(new Processor)

    setupUi(this);
    mProcessor->moveToThread(mThread);
    connect(this, SIGNAL(process(QImage)), mProcessor, SLOT(process(QImage)));
    connect(mProcessor, SIGNAL(processed(QImage)), SLOT(onProcessed(QImage)));


void Widget::enqueueImage(const QImage &img)

    mImagesPool.append(img);
    showNextImage();


void Widget::showNextImage()

    if (mImagesPool.isEmpty())
        return;
    emit process(mImagesPool.first());


void Widget::onProcessed(const QImage &img)

    if (mImagesPool.isEmpty())
        return;
    rawImage->setPixmap(QPixmap::fromImage(mImagesPool.takeFirst()));
    processedImage->setPixmap(QPixmap::fromImage(img));
    showNextImage();

Processor.cpp

void Processor::process(const QImage &img)

    QImage newImg = img;
    // Do stuff
    emit processed(newImg);

【讨论】:

这是“如何从单独的线程访问和修改主窗口中的 ui 元素?”。那为什么说他不行呢? ;-) 不,这是您可以在单独的线程中处理数据的方式。您不能实际上从其他线程访问和修改 ui 元素,您必须在主线程中完成所有这些操作。可能是我有点严格:) gui 必须在主线程中运行。但这与修改或访问 ui 元素无关。这些也只是数据。可以像通过线程边界访问任何函数/数据一样访问。 我不同意。例如,当您阅读 Qt 对QImage 的帮助时,第一行中的一行写着Note: All functions in this class are reentrant.,例如,QWidget 没有这样的注释。同样的帮助说:If a function is not marked as thread-safe or reentrant, it should not be used from different threads. If a class is not marked as thread-safe or reentrant then a specific instance of that class should not be accessed from different threads. 这意味着 您不应该从单独的线程访问 ui 数据 什么时候函数必须是线程安全的才能从另一个线程访问?

以上是关于Qt C++——多线程视频帧变换的主要内容,如果未能解决你的问题,请参考以下文章

C++中的多线程实现

使用多线程处理和保存图像

在多线程应用程序中使用 opencv waitKey()

QT+ffmpeg+多线程的视频播放器的基本使用

纯 C++ 中的多线程?

当 CPU 负载为 100%(主要使用 C++ 和 Qt)时,如何保持 UI 响应?