在QT中使用V4L2获取传上来的yuyv(4:2:2)的数据转换为QImage显示在屏幕上

背景

项目需要用到OV3703 USB免驱摄像头在I.MX6ULL的平台上,但是勾八QCamera的库只能去处理RGB格式的数据,yuyv的处理不了,所以只能自己去把yuyv(4:2:2)的数据转换为RGB去显示。幸好有个德国牙医写了个V4L2的中间件可以获取到yuyv数据,respect…

1.初始化接收定时器

//每隔固定的时间显示一帧camera_capture_timer = new QTimer();connect(camera_capture_timer, SIGNAL(timeout()), this, SLOT(videoShow()));if(0 == v4l2_open()){printf("打开相机成功!\n");// 由于摄像头默认30帧每秒,虽然10ms定时执行一次,但实际上1秒内最多有30次可以执行成功// 其余都会在ioctl处阻塞camera_capture_timer->start(10);}

2. 初始化V4L2


int MainWindow::v4l2_open()
{unsigned int i = 0;int j = 0;video_fd = open("/dev/video1", O_RDWR);if(video_fd < 0){perror("open camera failed");return -1;}/* 2.获取摄像头的能力 (VIDIOC_QUERYCAP:是否支持视频采集、内存映射等) */struct v4l2_capability capability;if(0 == ioctl(video_fd, VIDIOC_QUERYCAP, &capability)){if((capability.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0){perror("This camera don't support camera collect!");::close(video_fd);return -2;}if((capability.capabilities & V4L2_MEMORY_MMAP) == 0){perror("This camera do not support mmap!");::close(video_fd);return -3;}}/* 3.枚举摄像头支持的格式           (VIDIOC_ENUM_FMT:MJPG、YUYV等)列举出每种格式下支持的分辨率      (VIDIOC_ENUM_FRAMESIZES) */struct v4l2_fmtdesc fmtdesc;memset(&fmtdesc, 0, sizeof(fmtdesc));fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  //设置视频采集设备类型while(1) {fmtdesc.index = i++;// 获取支持格式if(0 == ioctl(video_fd, VIDIOC_ENUM_FMT, &fmtdesc)){printf("支持格式:%s, %c%c%c%c\n", fmtdesc.description,fmtdesc.pixelformat & 0xff,fmtdesc.pixelformat >> 8 & 0xff,fmtdesc.pixelformat >> 16 & 0xff,fmtdesc.pixelformat >> 24 & 0xff);// 列出该格式下支持的分辨率             VIDIOC_ENUM_FRAMESIZES & 默认帧率 VIDIOC_G_PARM// 1.默认帧率struct v4l2_streamparm streamparm;streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;if(0 == ioctl(video_fd, VIDIOC_G_PARM, &streamparm)) {printf("该格式默认帧率 %d fps\n", streamparm.parm.capture.timeperframe.denominator);}// 2.循环列出支持的分辨率struct v4l2_frmsizeenum frmsizeenum;frmsizeenum.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;frmsizeenum.pixel_format = fmtdesc.pixelformat;   //设置成对应的格式printf("支持的分辨率有:\n");while(1){frmsizeenum.index = j++;if(0 == ioctl(video_fd, VIDIOC_ENUM_FRAMESIZES, &frmsizeenum))printf("%d x %d\n", frmsizeenum.discrete.width, frmsizeenum.discrete.height);else break;}printf("\n");}else {break;}}/* 4.设置摄像头类型为捕获、设置分辨率、视频采集格式 (VIDIOC_S_FMT) */struct v4l2_format format;format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;   /* 视频采集 */format.fmt.pix.width = VIDEO_WIDTH;          /* 宽 */format.fmt.pix.height = VIDEO_HEIGHT;    	 /* 高 */format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;   /* 设置输出类型:YUYV */format.fmt.pix.field = V4L2_FIELD_INTERLACED;if(0 > ioctl(video_fd, VIDIOC_S_FMT, &format)){perror("设置摄像头参数失败!");::close(video_fd);return -4;}
#if 0printf("===============G================\n");printf("fmt.type:\t\t%d\n",format.type);printf("pix.pixelformat:\t%c%c%c%c\n", \format.fmt.pix.pixelformat & 0xFF,\(format.fmt.pix.pixelformat >> 8) & 0xFF, \(format.fmt.pix.pixelformat >> 16) & 0xFF,\(format.fmt.pix.pixelformat >> 24) & 0xFF);printf("pix.width:\t\t%d\n",format.fmt.pix.width);printf("pix.height:\t\t%d\n",format.fmt.pix.height);printf("pix.field:\t\t%d\n",format.fmt.pix.field);printf("===================================\n\n");printf("=================S===================\n");if(-1 == ioctl(video_fd, VIDIOC_G_FMT, &format)){//得到图片格式perror("set format failed!");return -1;}printf("fmt.type:\t\t%d\n",format.type);printf("pix.pixelformat:\t%c%c%c%c\n", \format.fmt.pix.pixelformat & 0xFF,\(format.fmt.pix.pixelformat >> 8) & 0xFF, \(format.fmt.pix.pixelformat >> 16) & 0xFF,\(format.fmt.pix.pixelformat >> 24) & 0xFF);printf("pix.width:\t\t%d\n",format.fmt.pix.width);printf("pix.height:\t\t%d\n",format.fmt.pix.height);printf("pix.field:\t\t%d\n",format.fmt.pix.field);printf("=====================================\n");
#endif/* 5.向内核申请内存 (VIDIOC_REQBUFS:个数、映射方式为mmap)将申请到的缓存加入内核队列 (VIDIOC_QBUF)将内核内存映射到用户空间 (mmap) */struct v4l2_requestbuffers requestbuffers;requestbuffers.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;requestbuffers.count = 4;    //申请缓存个数requestbuffers.memory = V4L2_MEMORY_MMAP;     //申请为物理连续的内存空间if(0 == ioctl(video_fd, VIDIOC_REQBUFS, &requestbuffers)){/* 申请到内存后 */for(i = 0; i < requestbuffers.count; i++){/* 将申请到的缓存加入内核队列 (VIDIOC_QBUF)              */struct v4l2_buffer buffer;buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buffer.index = i;buffer.memory = V4L2_MEMORY_MMAP;if(0 == ioctl(video_fd, VIDIOC_QBUF, &buffer)){/* 加入内核队列成功后,将内存映射到用户空间 (mmap) */userbuff[i] = (char *)mmap(NULL, buffer.length, PROT_READ | PROT_WRITE, MAP_SHARED, video_fd, buffer.m.offset);userbuff_length[i] = buffer.length;}}}else{perror("申请内存失败!");::close(video_fd);return -5;}int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;if(0 > ioctl(video_fd, VIDIOC_STREAMON, &type)){perror("打开视频流失败!");return -6;}return 0;
}

3.接收定时器回调函数

 void MainWindow::videoShow()
{QPixmap pix;image = new QImage(VIDEO_WIDTH, VIDEO_HEIGHT, QImage::Format_RGB16);/* 采集图片数据 *///定义结构体变量,用于获取内核队列数据struct v4l2_buffer buffer;buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;/* 从内核中捕获好的输出队列中取出一个 */if(0 == ioctl(video_fd, VIDIOC_DQBUF, &buffer)){*image = yuv422ToQImage(userbuff[buffer.index], VIDEO_WIDTH, VIDEO_HEIGHT);if (take_photo_flag == 1) {// 保存图像if (image != NULL) {if (saveImageToPhotoFolder(*image, PHOTO_SAVE_PATH, QString("%1.png").arg(para->record_number))) {qDebug() << "Image saved successfully.";} else {qDebug() << "Failed to save image.";}}if(0 == v4l2_close()) {camera_capture_timer->stop();return;}} else {/* 显示在label控件上 */pix = QPixmap::fromImage(*image);camera_content_show_label->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);camera_content_show_label->setPixmap(pix);}}/* 将使用后的缓冲区放回到内核的输入队列中 (VIDIOC_QBUF) */if(0 > ioctl(video_fd, VIDIOC_QBUF, &buffer)){perror("返回队列失败!");}
}

4.yuyv转QImage函数

// 将YUV 4:2:2数据转换为QImage的函数
QImage MainWindow::yuv422ToQImage(const char* yuvData, int width, int height)
{// 创建一个空的QImage,使用RGB16格式QImage image(width, height, QImage::Format_RGB16);for (int y = 0; y < height; ++y) {for (int x = 0; x < width; x += 2) {// 提取YUYV分量uchar y0 = yuvData[y * width * 2 + x * 2];uchar u = yuvData[y * width * 2 + x * 2 + 1];uchar y1 = yuvData[y * width * 2 + x * 2 + 2];uchar v = yuvData[y * width * 2 + x * 2 + 3];// 调整U和V的范围int u_offset = u - 128;int v_offset = v - 128;// 计算RGB分量int r0 = y0 + v_offset + (v_offset >> 2) + (v_offset >> 3) + (v_offset >> 5);int g0 = y0 - (u_offset >> 2) - (u_offset >> 4) - (u_offset >> 5) - (v_offset >> 1) + (v_offset >> 3) + (v_offset >> 4) + (v_offset >> 5);int b0 = y0 + u_offset + (u_offset >> 1) + (u_offset >> 2) + (u_offset >> 6);int r1 = y1 + v_offset + (v_offset >> 2) + (v_offset >> 3) + (v_offset >> 5);int g1 = y1 - (u_offset >> 2) - (u_offset >> 4) - (u_offset >> 5) - (v_offset >> 1) + (v_offset >> 3) + (v_offset >> 4) + (v_offset >> 5);int b1 = y1 + u_offset + (u_offset >> 1) + (u_offset >> 2) + (u_offset >> 6);// 裁剪RGB分量以防止溢出r0 = qBound(0, r0, 255);g0 = qBound(0, g0, 255);b0 = qBound(0, b0, 255);r1 = qBound(0, r1, 255);g1 = qBound(0, g1, 255);b1 = qBound(0, b1, 255);// 将RGB分量打包为RGB565格式ushort rgb565_1 = RGB565(r0, g0, b0);ushort rgb565_2 = RGB565(r1, g1, b1);// 存储RGB565值*(ushort*)(image.bits() + y * image.bytesPerLine() + x * 2) = rgb565_1;if (x + 1 < width) {*(ushort*)(image.bits() + y * image.bytesPerLine() + (x + 1) * 2) = rgb565_2;}}}// 创建一个旋转矩阵QTransform transform;transform.rotate(270);// 使用旋转矩阵创建一个新的QImageQImage rotatedImage = image.transformed(transform);return rotatedImage;
}

5.V4L2关闭函数

int MainWindow::v4l2_close()
{/* 8.停止采集,关闭视频流 (VIDIOC_STREAMOFF)关闭摄像头设备 & 关闭LCD设备 */int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;if(0 == ioctl(video_fd, VIDIOC_STREAMOFF, &type)){/* 9.释放映射 */for(int i = 0; i < 4; i++)munmap(userbuff[i], userbuff_length[i]);::close(video_fd);printf("关闭相机成功!\n");return 0;}return -1;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/450770.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

day46|72. 编辑距离647. 回文子串516.最长回文子序列 5 最长回文子串

文章目录 前言72. 编辑距离思路方法一647. 回文子串思路方法一方法二516.最长回文子序列思路方法一5 最长回文子串总结前言 72. 编辑距离 思路 总体思路:dp定义直接为操作数,递推公式分情况讨论,如果两个元素相等,那操作数不变;如果不相等,那么操作数就会改变–三种情况…

免费证件照app哪个好?哪个效果比较好?

在日常生活中&#xff0c;证件照的需求无处不在&#xff0c;尤其是在求职、签证和考试等场合。 许多人可能会觉得制作证件照需要花费不少费用&#xff0c;但其实市场上有许多免费的证件照制作软件&#xff0c;能够轻松满足你的需求。 这些软件不仅操作简单&#xff0c;更具备…

如何在word里面给文字加拼音?

如何在word里面给文字加拼音&#xff1f;在现代社会&#xff0c;阅读已经成为了我们日常生活中不可或缺的一部分。尤其是在学习汉语的过程中&#xff0c;拼音的帮助显得尤为重要。为了帮助大家更好地理解和掌握汉字的发音&#xff0c;许多教师和学生都希望能够在Word文档中为文…

什么是网络代理

了解网络代理 网络代理是一种特殊的网络服务&#xff0c;它允许一个网络终端&#xff08;通常指客户端&#xff09;通过这个服务与另一个网络终端&#xff08;通常指服务器&#xff09;进行非直接的连接。网络代理服务器位于发送主机和接收主机之间&#xff0c;接收网络请求&a…

使用人体关键点驱动FBX格式虚拟人原理【详解】

文章目录 1、使用人体关键点数据驱动FBX格式虚拟人的总流程2、使用mediapipe检测人体关键点和插值平滑2.1 mediapipe检测人体关键点2.2 人体关键点的插值平滑 3、将2d关键点转为3d关键点4、旋转矩阵4.1 旋转矩阵4.2 旋转矩阵转为四元数 5、将旋转矩阵用于虚拟人的驱动5.1 基础旋…

高分SCI发文利器!植物脂质代谢数据库——CLAIR

脂质是全球重要的大宗商品和工业原料。世界上消耗的24种脂质中&#xff0c;大部分来自植物。全面了解不同油料作物中与脂质生物合成相关的基因和机制&#xff0c;对于通过分子生物学和育种来提高这些作物的含油性状至关重要。 2024年2月&#xff0c;Plant Communications在线发…

台积电Q3业绩猛增,市值破万亿美元!

KlipC报道&#xff1a;全球晶圆代工龙头台积电发布第三季度财报&#xff0c;财报显示&#xff0c;三季度营收7596.9亿新台币&#xff08;约235亿美元&#xff09;&#xff0c;同比增长39%&#xff0c;市场预期7421.66亿元新台币&#xff1b;净利润达到3252.58亿新台币&#xff…

【可答疑】基于51单片机的智能衣柜(含仿真、代码、报告、演示视频等)

✨哈喽大家好&#xff0c;这里是每天一杯冰美式oh&#xff0c;985电子本硕&#xff0c;大厂嵌入式在职0.3年&#xff0c;业余时间做做单片机小项目&#xff0c;有需要也可以提供就业指导&#xff08;免费&#xff09;~ &#x1f431;‍&#x1f409;这是51单片机毕业设计100篇…

云计算第四阶段: cloud二周目 07-08

cloud 07 一、k8s服务管理 创建服务 # 资源清单文件 [rootmaster ~]# kubectl create service clusterip websvc --tcp80:80 --dry-runclient -o yaml [rootmaster ~]# vim websvc.yaml --- kind: Service apiVersion: v1 metadata:name: websvc spec:type: ClusterIPselector…

汽车建模用什么软件最好?汽车建模渲染建议!

在汽车建模和渲染领域&#xff0c;选择合适的软件对于实现精确的设计与高质量的视觉效果至关重要。那么不少的汽车设计师如何选择合适的建模软件与渲染方案呢&#xff0c;一起来简单看看吧&#xff01; 一、汽车建模用软件推荐 1、Alias Autodesk旗下的Alias系列软件是汽车设…

数据结构实验十二 图的遍历及应用

数据结构实验十二 图的遍历及应用 一、【实验目的】 1、 理解图的存储结构与基本操作&#xff1b; 2、熟悉图的深度度优先遍历和广度优先遍历算法 3、掌握图的单源最短路径算法 二、【实验内容】 1.根据下图&#xff08;图见实验11&#xff09;邻接矩阵&#xff0c;编程实…

刚刚,ChatGPT推出Windows客户端!

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;专注于分享AI全维度知识&#xff0c;包括但不限于AI科普&#xff0c;AI工…

C# + SQLiteExpert 进行(cipher)加密数据库开发+Costura.Fody 清爽发布

一&#xff1a;让 SQLiteExpert 支持&#xff08;cipher&#xff09;加密数据库 SQLiteExpert 作为SQlite 的管理工具&#xff0c;默认不支持加密数据库的&#xff0c;使其成为支持&#xff08;cipher&#xff09;加密数据库的管理工具&#xff0c;需要添加e_sqlcipher.dll &…

1997-2022年各省农作物总播种面积数据(无缺失)

1997-2022年各省农作物总播种面积数据 1、时间&#xff1a;1997-2022年 2、来源&#xff1a;国家统计局、统计年鉴 3、指标&#xff1a;农作物总播种面积(千公顷) 4、范围&#xff1a;31省 5、缺失情况&#xff1a;无缺失 6、指标解释&#xff1a;农作物播种面积指农业生…

PCL 点云配准-改进的RANSAC算法(粗配准)

目录 一、概述 1.1原理 1.2实现步骤 1.3应用场景 二、代码实现 2.1关键函数 2.1.1 计算FPFH特征 2.1.2 RANSAC配准 2.1.3 可视化点云 2.2完整代码 三、实现效果 PCL点云算法汇总及实战案例汇总的目录地址链接&#xff1a; PCL点云算法与项目实战案例汇总&#xff0…

基于SSM高校课程评价的设计

教师账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;指标信息管理&#xff0c;课程信息管理&#xff0c;教师自评管理 学生账号功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;课程信息管理&#xff0c;学生评价管理 开发系统&#xff1a;…

不坑盒子在哪儿下载?

不坑盒子是一款Office办公软件的插件&#xff0c;支持MicroSoft Office和WPS的三件套&#xff08;Word、Excel、PPT&#xff09;。 可以为你的Office软件增加数百个实用功能&#xff0c;比如&#xff1a;自动排版、智能写作、仿手写、全文加拼音、稿子模板、一键删除、数据分发…

SAP物料凭证报表字段调整

业务场景&#xff1a; 报表MB51的输入和输出字段调整&#xff1a; 输入&#xff08;选择界面&#xff09; 输出界面 可以看到在这是没有布局调整的 后台路径&#xff1a; SPRO-物料管理-库存管理和实际库存-报表-定义物料凭证列表的字段选择 事务码&#xff1a;SM30-V_MMI…

docker构建jar镜像

文章目录 构建 DockerFile将jar包上传到创建的目录当中在目录中创建 Dockerfile 文件构建镜像创建并启动容器说明 构建 DockerFile [root192 /]# mkdir my [root192 /]# cd my [root192 my]# 将jar包上传到创建的目录当中 在目录中创建 Dockerfile 文件 vi Dockerfile FROM …

MFC工控项目实例二十四模拟量校正值输入

承接专栏《MFC工控项目实例二十三模拟量输入设置界面》 对模拟量输入的零点校正值及满量程对应的电压值进行输入。 1、在SenSet.h文件中添加代码 #include "BtnST.h" #include "ShadeButtonST.h"/ // SenSet dialogclass SenSet : public CDialog { // Co…