转眼间,V4L2已经搞了很长时间,从最开始的一窍不通,到后来的渐渐熟悉,从最开始照猫画虎的使用YUYV格式之间转换,到后来使用MJPEG格式读取,中间颇有周折。趁任务完成间隙,来简单总结下V4L2的使用。(文章只主要写了过程,完整程序已经附在最后)
有读者要问,opencv已经有相关的读取摄像头的函数,为什么要使用V4L2这么麻烦呢。其实主要是因为后面要将程序移植到板子上,而在板子上不能直接使用opencv中读取摄像头的函数的,所以需要借助V4L2来实现读取视频的功能。还有,既然YUYV格式这么简单(见文章末尾附的大神的博客),为什么要用MJPEG格式呢?这一点主要是考虑到YUYV数据量较大,影响摄像头读取速度,也会影响到后面都视频数据传输的扩展。
V4L2主要应用于linux读取USB摄像头,有关它的介绍网上有很多资料,这里不再赘述。结合主题,本文主要讲述如何使用MJPEG格式读取并将其转换为OpenCV中的IplImage格式,以方便进行图像处理,最后以视频的形式进行实时显示。截至到今天晚上,已经使得程序能够实时以MJPEG格式读取并显示,图像分辨率为1920*1080,速度100ms每帧。由于项目需要,所以尽可能使用较大分辨率,导致速度有点慢,如果使用普通的640*480,速度比较会比较快。
有点啰嗦,下面进入主题:使用MJPEG格式读取视频,并实时显示。整个过程主要分为以下几个步骤:
1. 打开视频设备文件,并进行参数初始化,设置采集分辨率、格式等;
该步骤中主要使用函数:
Open(“/dev/video0”,O_RDWR);//打开USB摄像头
ioctl(fd,VIDIOC_QUERYCAP,&cap);//查询设备的信息
ioctl(fd,VIDIOC_S_STD,&fmt);//设置视频的格式
2. 申请帧缓冲区,并将其映射到用户空间;
ioctl(fd,VIDIOC_REQBUFS,&req);//申请缓冲帧
malloc(req.count*sizeof(*buffer));
ioctl(fd,VIDIOC_QUERYBUF,&buf);//将申请到的帧缓冲映射到用户空间
3. 将申请到的帧缓冲区在视频采集输入队列排队,并启动视频采集;
ioctl(fd,VIDIOC_QBUF,&buf);//将申请到的帧缓冲全部加入队列
ioctl(fd,VIDIOC_STREAMON,&byte);//开始采集
4. 应用程序从视频采集输入队列取出帧缓冲区,将其转换为OpenCV中的通用数据格式,然后显示,之后重新放入视频采集队列,循环该过程;
ioctl(fd,VIDIOC_DQBUF,&buf);//取缓冲帧
CvMatcvmat=cvMat(IMAGEHEIGHT,IMAGEWIDTH,CV_8UC3,(void*)buffer);//将帧内容赋值给CvMat格式的数据
IplImage img =cvDecodeImage(&cvmat,1);//解码,这一步将数据转换为IplImage格式
cvShowImage(“one”,img);//显示图像
cvReleaseImage(&img);//释放图像空间
ioctl(fd,VIDIOC_QBUF,&buf);//将缓冲重新加入队尾
循环上面的步骤,就可以形成视频啦
5. 停止视频采集,关闭设备文件。
ioctl(fd,VIDIOC_STREAMOFF,&byte);
close(fd);
由于时间关系,上面只是简单介绍了一下整个过程,相关函数的使用和函数中的结构体参数网上都有很多资料可供参考,当然也可以看下官方给的API手册,讲的很详细,但是有点长,且是英文的。下面列出我学习时找到的一些资料,
最后附上我自己的程序,目前程序能够实现基本的功能,但是本人还想进一步优化以提高效率,有其他好的想法的朋友可以与我联系。Mail:[email protected]
一个大神的博客,最开始我就是按照这个学习的
网址:
V4L2的官方手册:
网址:
我自己的程序:
网址:
程序是在linux环境下使用QT编译的,需要自己安装OPENCV,并在.pro文件中配置,我的.pro文件中已经将相关路径写入,读者使用时可以按照自己安装OPENCV的路径修改。
最后,加上效果图展示,由于分辨率较大,屏幕不能显示完整窗口: