QT多线程采集摄像头数据处理显示(QVideoProbe+QCamera+QThread)

Aine ·
更新时间:2024-09-21
· 535 次阅读

一、环境介绍

操作系统介绍:ubuntu 18.04 、windows、Android

QT版本:  5.12.6

摄像头:  USB摄像头、虚拟机挂载本机自带摄像头

二、功能介绍

在子线程里通过QVideoProbe捕获摄像头一帧数据,处理之后(加时间水印),再通过信号/槽机制发送给主线程,在UI界面显示。

子线程方式采用moveToThread方式实现,因为需要用到QVideoProbe的槽函数,需要事件机制,使用子类化方式使用子线程不方便,直接用moveToThread方式实现。

三、核心代码

main.cpp

#include "widget.h" #include int main(int argc, char *argv[]) { QApplication a(argc, argv); Widget w; w.show(); return a.exec(); }

Camera.cpp

#include "Camera.h" /* 函数功能: 将YUV数据转为RGB格式 函数参数: unsigned char *yuv_buffer: YUV源数据 unsigned char *rgb_buffer: 转换之后的RGB数据 int iWidth,int iHeight : 图像的宽度和高度 */ void yuyv_to_rgb(unsigned char *yuv_buffer,unsigned char *rgb_buffer,int iWidth,int iHeight) { int x; int z=0; unsigned char *ptr = rgb_buffer; unsigned char *yuyv= yuv_buffer; for (x = 0; x < iWidth*iHeight; x++) { int r, g, b; int y, u, v; if (!z) y = yuyv[0] << 8; else y = yuyv[2] <> 8; g = (y - (88 * u) - (183 * v)) >> 8; b = (y + (454 * u)) >> 8; *(ptr++) = (r > 255) ? 255 : ((r 255) ? 255 : ((g 255) ? 255 : ((b < 0) ? 0 : b); if(z++) { z = 0; yuyv += 4; } } } void NV21_TO_RGB24(unsigned char *yuyv, unsigned char *rgb, int width, int height) { const int nv_start = width * height ; int index = 0, rgb_index = 0; uint8_t y, u, v; int r, g, b, nv_index = 0,i, j; for(i = 0; i < height; i++){ for(j = 0; j 255) r = 255; if(g > 255) g = 255; if(b > 255) b = 255; if(r < 0) r = 0; if(g < 0) g = 0; if(b setSource(camera); // Returns true, hopefully. connect(m_pProbe, SIGNAL(videoFrameProbed(QVideoFrame)),this, SLOT(slotOnProbeFrame(QVideoFrame)), Qt::QueuedConnection); } /*配置摄像头捕 QCamera *camera; QVideoProbe *m_pProbe;获模式为帧捕获模式*/ //camera->setCaptureMode(QCamera::CaptureStillImage); //如果在Linux系统下运行就这样设置 camera->setCaptureMode(QCamera::CaptureVideo);//如果在android系统下运行就这样设置 /*启动摄像头*/ camera->start(); /*设置摄像头的采集帧率和分辨率*/ QCameraViewfinderSettings settings; settings.setPixelFormat(QVideoFrame::Format_YUYV); //设置像素格式 Android上只支持NV21格式 settings.setResolution(QSize(VIDEO_WIDTH,VIDEO_HEIGHT)); //设置摄像头的分辨率 camera->setViewfinderSettings(settings); } void VideoReadThread_0::slotOnProbeFrame(const QVideoFrame &frame) { //qDebug()<<"子线程ProbeFrame槽函数的ID:"<<QThread::currentThreadId(); QVideoFrame cloneFrame(frame); cloneFrame.map(QAbstractVideoBuffer::ReadOnly); unsigned char rgb_buffer[VIDEO_WIDTH*VIDEO_HEIGHT*3]; if(cloneFrame.pixelFormat()==QVideoFrame::Format_YUYV) { yuyv_to_rgb(cloneFrame.bits(),rgb_buffer,cloneFrame.width(),cloneFrame.height()); } else if(cloneFrame.pixelFormat()==QVideoFrame::Format_NV21) { NV21_TO_RGB24(cloneFrame.bits(),rgb_buffer,cloneFrame.width(),cloneFrame.height()); } else { qDebug("当前格式编码为%1,暂时不支持转换.\n"); return; } cloneFrame.unmap(); //加载图片数据 QImage image(rgb_buffer, cloneFrame.width(), cloneFrame.height(), QImage::Format_RGB888); //绘制图片水印 QDateTime dateTime(QDateTime::currentDateTime()); //时间效果: 2020-03-05 16:25::04 周一 QString qStr=""; qStr+=dateTime.toString("yyyy-MM-dd hh:mm:ss ddd"); QPainter pp(&image); QPen pen = QPen(Qt::white); pp.setPen(pen); pp.drawText(QPointF(0,20),qStr); //提取RGB数据 unsigned char *p=rgb_buffer; for(int i=0;i<image.height();i++) { for(int j=0;j<image.width();j++) { QRgb rgb=image.pixel(j,i); *p++=qRed(rgb); *p++=qGreen(rgb); *p++=qBlue(rgb); } } emit VideoDataOutput(image); //发送信号 } //停止视频采集 void VideoReadThread_0::stop() { qDebug()<stop(); delete camera; camera=nullptr; } if(m_pProbe) { delete m_pProbe; m_pProbe=nullptr; } } //执行线程 void VideoReadThread_0::run() { stop(); Camear_Init(); qDebug()<<"摄像头开始采集数据"; qDebug()<<"子线程run槽函数的ID:"<<QThread::currentThreadId(); }

camera.h

#ifndef CAMERA_H #define CAMERA_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //这五个是QT处理音频的库 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //这五个是QT处理音频的库 #include #include #include #include //视频输出尺寸 #define VIDEO_WIDTH 640 #define VIDEO_HEIGHT 480 class VideoReadThread_0:public QObject { Q_OBJECT public: QCamera *camera; QVideoProbe *m_pProbe; VideoReadThread_0(QObject* parent=nullptr):QObject(parent){camera=nullptr;m_pProbe=nullptr;} ~VideoReadThread_0(){} void Camear_Init(void); public slots: void slotOnProbeFrame(const QVideoFrame &frame); void run(); void stop(); signals: void VideoDataOutput(QImage ); //输出信号 }; //视频音频编码类 class VideoAudioEncode { public: /*继续采集标志*/ bool run_flag; bool mode; //0 表示保存视频 1表示推流 /*视频相关*/ QMutex video_encode_mutex; QWaitCondition video_WaitConditon; QCameraInfo camera; //当前选择的摄像头 /*音频相关*/ QAudioDeviceInfo audio; QMutex audio_encode_mutex; QQueue audio_data_queue; }; extern class VideoAudioEncode videoaudioencode_0; #endif // CAMERA_H

mainwindow.cpp

#include "widget.h" #include "ui_widget.h" Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui->setupUi(this); qDebug()<<"主线程的ID:"<setWindowTitle("监控设备"); //启动摄像头的信号 connect(this,SIGNAL(StartWorkThread()),work_class,SLOT(run())); //释放资源-释放摄像头 connect(this,SIGNAL(Stop_VideoAudioEncode_0()),work_class,SLOT(stop())); //连接摄像头采集信号,在主线程实时显示视频画面 connect(work_class,SIGNAL(VideoDataOutput(QImage)),this,SLOT(VideoDataDisplay_0(QImage))); //将类移动到子线程工作 work_class->moveToThread(work_thread); } Widget::~Widget() { delete ui; } //驾驶室:视频刷新显示 void Widget::VideoDataDisplay_0(QImage image) { QPixmap my_pixmap; my_pixmap.convertFromImage(image.copy()); ui->label->setPixmap(my_pixmap); } void Widget::on_pushButton_clicked() { /*2. 获取摄像头列表*/ video_dev_list.clear(); ui->comboBox->clear(); video_dev_list=QCameraInfo::availableCameras(); for(int i=0;i

mainwindow.h

#ifndef WIDGET_H #define WIDGET_H #include #include "Camera.h" QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACE class Widget : public QWidget { Q_OBJECT public: Widget(QWidget *parent = nullptr); ~Widget(); QList video_dev_list; VideoReadThread_0 *work_class; QThread *work_thread; private slots: void on_pushButton_clicked(); void VideoDataDisplay_0(QImage image); void on_pushButton_2_clicked(); void on_pushButton_3_clicked(); signals: void StartWorkThread(); void Stop_VideoAudioEncode_0();//停止线程 private: Ui::Widget *ui; }; #endif // WIDGET_H

四、运行环境说明

在windows、Android、ubuntu下都可正常使用。

主要注意的地方:  在windows下有些摄像头不支持YUYV格式输出设置,需要在代码里根据情况稍微修改。

下面公众号有全套基础QT\C++\C\单片机教程。


作者:DS小龙哥



qt多线程 qthread 数据 摄像 数据处理 摄像头 线程

需要 登录 后方可回复, 如果你还没有账号请 注册新账号