Qt C ++ - 多线程视频帧变换 [英] Qt C++ - multithread video frame transformation
问题描述
我必须编写一个应用程序,显示一个加载的视频在一个窗口小部件,拉框架,转换他们,并把这些在另一个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)。
如果我删除 Ui :: MainWindow ui;
在// 1,它告诉我 ui未在此范围中声明
。
如何从一个单独的线程访问和修改Mainwindow中的ui元素?
编辑:
好吧,一个更实用的想法。我将简单地从GUI线程传递视频文件URL到工作线程,执行所有操作,并传递2个处理的图像(每个视频帧),每个QGraphicsView一个。一个正常,有点标记,一个转换。我现在知道如何将工作线程传递给GUI线程,但是如何从GUI传递QString到工作线程,我该如何收到呢?
简短的答案:你不能。
Qt帮助:线程基础:
GUI线程和工作线程
如前所述,每个程序在启动时都有一个线程。这个
线程被称为主线程(在
Qt应用程序中也称为GUI线程)。 Qt GUI必须在这个线程中运行。所有窗口小部件和
几个相关的类,例如QPixmap,不能在辅助
线程中工作。辅助线程通常被称为worker
线程,因为它用于从主
线程卸载处理工作。
你可以做什么:你可以在主线程中使用ui,通过 QImage
(或其他数据,与ui无关)
我不是视频专家,但我可以给你暗示。让我们假设你有一个 Widget
,它接受 QImage
并显示它。 处理器
是一个对象,它处理图像并将其返回到gui线程。 处理器
应源自 QObject
。
Widget.h
class Widget:public QWidget,private Ui :: Widget
{
Q_OBJECT
public:
explicit Widget(QWidget * parent = 0);
void enqueueImage(const QImage& img);
信号:
void process(const QImage& img);
private:
void showNextImage();
私有插槽:
void onProcessed(const QImage& img);
private:
QThread * mThread;
QScopedPointer<处理器> 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(已处理(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);
}
I have to write an app that displays a loaded video in one Widget, pulls frames, transforms them, and puts these in another Widget, preferably QGraphicsView, and hopefully synchronized. It's pretty important for them to be synchronized.
I've written a test thread, and the main meat is this:
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;
}
}
}
it's not what I need, but it's just a test. If I put text there, it goes through just fine, keeps displaying. But these images... immediatly a crash when the first ui line is reached ( //2 ). I have no doubt this way of doing things is really bad.
If I remove Ui::MainWindow ui;
at //1 , it tells me that ui is not declared in this scope
.
So my question is, how can I access and modify ui elements in the Mainwindow from a separate thread?
edit: Okay, a more practical idea. I'm going to simply pass video file URL from the GUI thread to the worker thread, do all the operations there and pass 2 processed images back (for each video frame), one for each QGraphicsView. One normal, with points marked, and one transformed. I now know how to pass things from worker thread to GUI thread, but how do I pass QString from GUI to the worker thread, and how do I receive it?
Short answer: you can't.
Qt Help: Threading Basics:
GUI Thread and Worker Thread
As mentioned, each program has one thread when it is started. This thread is called the "main thread" (also known as the "GUI thread" in Qt applications). The Qt GUI must run in this thread. All widgets and several related classes, for example QPixmap, don't work in secondary threads. A secondary thread is commonly referred to as a "worker thread" because it is used to offload processing work from the main thread.
What you can do: you can work with ui in main thread, pass QImage
(or other data, not related with ui) into separate thread through signal/slot system, process it there and pass it back.
UPDATE (how can I do that? I mean, pass QImage back and forth between threads and display both videos simultaneously and continually? – Petersaber)
I'm not a video expert, but I can give you I hint. Let's assume, you have a Widget
, which accepts QImage
and have to show it. Processor
is an object, which processes an image and returns it back to gui thread. Processor
should be derived from 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);
}
这篇关于Qt C ++ - 多线程视频帧变换的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!