目录:
一、方法
实现逻辑:使用定时器捕获视频帧,在子线程中将捕获的帧构造后返回给主线程的Label中显示,然后安全的退出线程。
opencv用到的类如下:
QImage* image = new QImage; //图像类
VideoCapture cap; //视频流
Mat frame; //视频帧
cap.open(0); //打开默认摄像头
cap.release(); //释放摄像头
//从视频捕获对象cap中读取一帧视频数据,并将这帧数据存储在Mat类型的对象 frame中。
cap.read(frame);
//获取OpenCV的cv::Mat对象frame ,转为const uchar*类型 ,得到图像数据的首地址
const uchar* pSrc = (const uchar*)frame.data;
QImage qImage(pSrc, frame.cols, frame.rows, frame.step, QImage::Format_RGB888);
//pSrc:指向原始图像数据的指针,图像数据的起始地址;
//frame.cols:这个参数指定了图像的宽度(以像素为单位);
//frame.rows:这个参数指定了图像的高度(以像素为单位);
//frame.step:这个参数指定了图像中一行数据所占的字节数;
//QImage::Format_RGB888:这个参数指定了图像数据的格式;
//目的是从一个OpenCV的cv::Mat对象(假设为frame)创建一个QImage对象,
//其中frame包含RGB格式的图像数据,没有透明度通道。
二、具体实现
QtWidgetsApplication14.h
#pragma once
#include <QtWidgets/QMainWindow>
#include "ui_QtWidgetsApplication14.h"
#include<opencv2/opencv.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/videoio/videoio.hpp>
#include<QTimer>
#include<QThread>
using namespace std;
using namespace cv;
class QtWidgetsApplication14 : public QMainWindow //主界面类
{
Q_OBJECT
public:
QtWidgetsApplication14(QWidget *parent = nullptr);
~QtWidgetsApplication14();
QTimer* timer = new QTimer(this); //定时器
QThread* thread = new QThread(this); //线程
void closeEvent(QCloseEvent *event); //关闭窗口事件
public slots:
void slot_readframe(QImage); //槽函数,用来接收线程传过来的图像
private:
Ui::QtWidgetsApplication14Class ui;
};
class mythread : public QObject //线程类
{
Q_OBJECT // 必须
public:
mythread() //构造函数
{
cap.open(0); //打开摄像头
}
~mythread() //析构函数
{
cap.release(); //释放摄像头
}
void readframe(); //捕获视频帧
VideoCapture cap; //视频流
Mat frame; //视频帧
signals:
void signal_readframe(QImage); //信号,用来发送视频帧给主界面
};
QtWidgetsApplication14.cpp
#include "QtWidgetsApplication14.h"
QtWidgetsApplication14::QtWidgetsApplication14(QWidget* parent)
: QMainWindow(parent)
{
ui.setupUi(this);
mythread* myth = new mythread(); //创建线程
myth->moveToThread(thread); //将线程绑定到线程对象thread 注意myth已在类中创建
//定时器捕获视频帧
connect(timer, &QTimer::timeout, myth, &mythread::readframe);
//子线程将捕获的视频帧返回到主界面Label中
connect(myth, &mythread::signal_sendframe, this, &QtWidgetsApplication14::slot_readframe);
//使用按钮开启线程与定时器
connect(ui.pushButton, &QPushButton::clicked, this, [=]() {
thread->start(); //线程开始
timer->start(33); //定时器开始
ui.label->setScaledContents(true); //调整QPixmap的大小以匹配QLabel的大小
});
}
void QtWidgetsApplication14::closeEvent(QCloseEvent* event) //窗口关闭事件
{
timer->stop(); //停止定时器
thread->quit(); //退出线程
thread->wait(); //等待线程真正退出 (安全)
}
void mythread::readframe() //子线程读取视频帧
{
cap.read(frame); //读一帧
const uchar* pSrc = (const uchar*)frame.data; //得到图像首地址
//从一个原始图像数据(pSrc)创建一个 qImage 对象
QImage qImage(pSrc, frame.cols, frame.rows, frame.step, QImage::Format_RGB888);
//从RGB顺序交换到BGR顺序,返回一个新的 QImage 对象,其颜色通道已经被交换
QImage image = qImage.rgbSwapped();
emit signal_sendframe(image); //发送image信号给主界面
}
void QtWidgetsApplication14::slot_readframe(QImage image) //主界面槽函数
{
//QPixmap pixmap = QPixmap::fromImage(image);
//ui.label->setPixmap(pixmap);
//ui.label->resize(pixmap.size()); // 调整QLabel的大小以匹配QPixmap的大小
ui.label->setPixmap(QPixmap::fromImage(image)); //将image显示在label上
}
QtWidgetsApplication14::~QtWidgetsApplication14() //析构函数
{
}
main.cpp
#include "QtWidgetsApplication14.h"
#include <QtWidgets/QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QtWidgetsApplication14 w;
w.show();
return a.exec();
}