用QThread及QObject的movetothread两种多线程方法实现在QT控件上播放opencv视频
程序员文章站
2022-05-10 12:10:05
...
视频截图如下
使用一个单独的线程处理和显示视频源的输入帧和输出帧,有助于使GUI线程(主线程)保持空闲及可响应性,而用第二线程处理更密集的进程。
准备工作:
创建含两个label的QT控件应用程序,并在.pro文件中配置opencv:
INCLUDEPATH += /usr/local/include \
/usr/local/include/opencv \
/usr/local/include/opencv2
LIBS += /usr/local/lib64/libopencv*
第一种方法(官方已不推荐的老方法):子类化QThread。 思路如下:
一,创建类继承Qthread: class videoProcessorThread : public QThread
重写run函数,作为新线程入口。run函数中1打开视频,2设定时时器延时读取视频帧,3给主框架发送显示帧图片消息,4加上this->exec();支持信号槽。
二,主框架MainWindow的构造函数中,1建立显示图片的信号槽连接,2启动线程。析构函数中3结束线程。
代码如下:
videoprocessorthread.h
#ifndef VIDEOPROCESSORTHREAD_H
#define VIDEOPROCESSORTHREAD_H
#include <QObject>
#include <QThread>
#include <QPixmap>
#include <opencv2/opencv.hpp>
using namespace cv;
class videoProcessorThread : public QThread
{
Q_OBJECT
public:
explicit videoProcessorThread(QObject *parent = nullptr);
void showCamera();
void stopVideo();
signals:
void inDisplay(QPixmap pixmap);
void outDisplay(QPixmap pixmap);
public slots:
private:
void run() override;
VideoCapture camera;
Mat inFrame,outFrame;
QTimer *timer;
};
#endif // VIDEOPROCESSORTHREAD_H
videoprocessorthread.cpp
#include "videoprocessorthread.h"
#include <QDebug>
#include "QTimer"
videoProcessorThread::videoProcessorThread(QObject *parent) : QThread(parent)
{
}
void videoProcessorThread::run()
{
camera = VideoCapture("/home/jello/myprojects/images/bike.avi");
double rate = camera.get(CV_CAP_PROP_FPS);
int delay = 1000/rate;
timer = new QTimer();
connect(timer,&QTimer::timeout,this,&videoProcessorThread::showCamera);
if(camera.isOpened())
{
timer->start(delay);
this->exec();
}
}
void videoProcessorThread::showCamera()
{
camera >> inFrame;
bitwise_not(inFrame, outFrame);
emit inDisplay(
QPixmap::fromImage(
QImage(
inFrame.data,
inFrame.cols,
inFrame.rows,
inFrame.step,
QImage::Format_RGB888)
.rgbSwapped()));
emit outDisplay(QPixmap::fromImage(
QImage(
outFrame.data,
outFrame.cols,
outFrame.rows,
outFrame.step,
QImage::Format_RGB888)));
}
void videoProcessorThread::stopVideo()
{
timer->stop();
delete timer;
}
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "videoprocessorthread.h"
#include "videoprocessor.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
protected:
void closeEvent(QCloseEvent *event);
private:
Ui::MainWindow *ui;
videoProcessorThread processor;
// VideoProcessor *processor1;
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QCloseEvent>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(&processor,
SIGNAL(inDisplay(QPixmap)),
ui->inVideo,
SLOT(setPixmap(QPixmap)));
connect(&processor,
SIGNAL(outDisplay(QPixmap)),
ui->outVideo,
SLOT(setPixmap(QPixmap)));
processor.start();
}
void MainWindow::closeEvent(QCloseEvent *event)
{
processor.stopVideo();
processor.quit();
processor.wait();
event->accept();
}
MainWindow::~MainWindow()
{
delete ui;
}
第二种方法:movetothread
线程处理类直接继承QObject
#ifndef VIDEOPROCESSOR_H
#define VIDEOPROCESSOR_H
#include <QObject>
#include <QPixmap>
#include "opencv2/opencv.hpp"
using namespace cv;
class VideoProcessor : public QObject
{
Q_OBJECT
public:
explicit VideoProcessor(QObject *parent = nullptr);
void showCamera();
signals:
void inDisplay(QPixmap pixmap);
void outDisplay(QPixmap pixmap);
public slots:
void startVideo();
void stopVideo();
private:
bool stopped;
VideoCapture camera;
Mat inFrame, outFrame;
QTimer *timer;
};
#endif // VIDEOPROCESSOR_H
构造函数中没有分配任何父函数,有父对象的对象不能移动到新的线程中。
#include "videoprocessor.h"
#include <QTimer>
VideoProcessor::VideoProcessor(QObject *parent) : QObject(parent)
{
}
void VideoProcessor::startVideo()
{
camera = VideoCapture("/home/jello/myprojects/images/bike.avi");
stopped = false;
double rate = camera.get(CV_CAP_PROP_FPS);
int delay = 1000/rate;
timer = new QTimer();
connect(timer,&QTimer::timeout,this,&VideoProcessor::showCamera);
if(camera.isOpened())
timer->start(delay);
}
void VideoProcessor::showCamera()
{
camera >> inFrame;
bitwise_not(inFrame, outFrame);
emit inDisplay(QPixmap::fromImage(
QImage(
inFrame.data,
inFrame.cols,
inFrame.rows,
inFrame.step,
QImage::Format_RGB888)));
emit outDisplay(QPixmap::fromImage(
QImage(
outFrame.data,
outFrame.cols,
outFrame.rows,
outFrame.step,
QImage::Format_RGB888)));
}
void VideoProcessor::stopVideo()
{
timer->stop();
delete timer;
}
主框架中创建VideoProcessor的实例,并定义为指针。
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "videoprocessor.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
VideoProcessor *processor1;
};
#endif // MAINWINDOW_H
不应该直接调用VideoProcessor的startVideo函数,而应该将一个适合的信号连接到它进行调用。线程结束信号连接到deleteLater槽函数。
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
processor1 = new VideoProcessor();
processor1->moveToThread(new QThread(this));
connect(processor1->thread(),SIGNAL(started()),processor1,SLOT(startVideo()));
connect(processor1->thread(),SIGNAL(finnished()),processor1,SLOT(deleteLater()));
connect(processor1,SIGNAL(inDisplay(QPixmap)),ui->inVideo,SLOT(setPixmap(QPixmap)));
connect(processor1,SIGNAL(outDisplay(QPixmap)),ui->outVideo,SLOT(setPixmap(QPixmap)));
processor1->thread()->start();
}
MainWindow::~MainWindow()
{
processor1->stopVideo();
processor1->thread()->quit();
processor1->thread()->wait();
delete ui;
}