TA的每日心情 | 慵懒 2024-5-31 23:20 |
---|
签到天数: 302 天 连续签到: 2 天 [LV.8]以坛为家I
|
(一)普通USB摄像头图像采集
关于一般的摄像头视频流采集,前面的帖子有提到:
【米尔FZ3深度学习计算卡】视频流识别测试
【米尔FZ3深度学习计算卡】(三)V4L2采集视频流识别
当时测试时使用opencv的>>重载符读取视频流出现mmap错误,一开始以为是opencv没有适配v4l2相关驱动,于是转向v4l2开发。
但后来参考其他试用者的意见,发现opencv的>>重载符是可以读取视频流的。
这时候我又尝试了一遍opencv的>>重载符,发现果然神奇地可行了,并且再也无法复现mmap错误,至今没有找到mmap错误产生的原因。
这时候我转向了opencv的>>重载符的开发
(二)opencv的>>重载符的双目采集
经过单目的问题后,双目的开发只是简单地重复,应该是理所当然地势如破竹。
但是现实又告诉事情没有那么简单:
VIDIOC_STREAMON: No space left on device opencv抛出了这么一个错误。
我尝试降低分辨率:
- if(!m_VideoCaptureLeft.isOpened()) m_VideoCaptureLeft.open(2);
- m_VideoCaptureLeft.set(CV_CAP_PROP_FRAME_WIDTH, 320);//宽度
- m_VideoCaptureLeft.set(CV_CAP_PROP_FRAME_HEIGHT, 240);//高度
- assert(m_VideoCaptureLeft.isOpened());
- if(!m_VideoCaptureRight.isOpened()) m_VideoCaptureRight.open(3);
- m_VideoCaptureRight.set(CV_CAP_PROP_FRAME_WIDTH, 320);//宽度
- m_VideoCaptureRight.set(CV_CAP_PROP_FRAME_HEIGHT, 240);//高度
- assert(m_VideoCaptureRight.isOpened());
复制代码
结果错误依旧。
按照像素计算带宽,单个摄像头 640x480 是两个摄像头 320x240 的两倍,之前单个摄像头采用640x480分辨率也是可以正常采集的,那么两个摄像头 320x240应该不成问题。那只能怀疑opencv的降分辨率的操作没有真正影响到带宽。
这时候有其他试用者指出opencv应该是封装好、直接操作v4l2底层的,不应该出现这种问题。但事实就是无法驱动两个摄像头,于是只能转向v4l2进行最后一博。
(三)v4l2的双目采集
1-首先封装v4l2的初始化流程:见文末
封装了之后处理就简单了,只需要把缓冲指针和设备描述符传进去就可以了:
- m_fdVideoLeft=open("/dev/video2",O_RDWR);// 打开设备
- init_uvc(m_fdVideoLeft, m_bufVideoLeft);
- m_fdVideoRight=open("/dev/video3",O_RDWR);// 打开设备
- init_uvc(m_fdVideoRight, m_bufVideoRight);
复制代码
2-采集只需要通过缓冲指针取出来即可:
- void DectMea::getFrame()
- {
- while(!m_exitFlag)
- {
- if(m_vecFrameLeft.size()<m_frameMaxSize){
- {
- struct v4l2_buffer buf;
- buf.type =V4L2_BUF_TYPE_VIDEO_CAPTURE;
- buf.memory =V4L2_MEMORY_MMAP;
- // 从缓冲区取出一个缓冲帧
- ioctl (m_fdVideoLeft,VIDIOC_DQBUF, &buf);
- cv::Mat image(m_videoHeight, m_videoWidth, CV_8UC2, (unsigned char*)m_bufVideoLeft[buf.index].start);
- cv::Mat img2;
- cv::cvtColor(image, img2, CV_YUV2BGR_YUYV);
- cv::imshow("1", img2);
- m_vecFrameLeft.push(img2.clone());
- // 将取出的缓冲帧放回缓冲区
- ioctl (m_fdVideoLeft, VIDIOC_QBUF,&buf);
- }
- {
- struct v4l2_buffer buf;
- buf.type =V4L2_BUF_TYPE_VIDEO_CAPTURE;
- buf.memory =V4L2_MEMORY_MMAP;
- // 从缓冲区取出一个缓冲帧
- ioctl (m_fdVideoRight,VIDIOC_DQBUF, &buf);
- cv::Mat image(m_videoHeight, m_videoWidth, CV_8UC2, (unsigned char*)m_bufVideoRight[buf.index].start);
- cv::Mat img2;
- cv::cvtColor(image, img2, CV_YUV2BGR_YUYV);
- cv::imshow("2", img2);
- m_vecFrameRight.push(img2.clone());
- // 将取出的缓冲帧放回缓冲区
- ioctl (m_fdVideoRight, VIDIOC_QBUF,&buf);
- }
- }
- }
- }
复制代码
3-然而到这里就出问题了,左摄像头正常,但右摄像头显示出来是一片绿幕。
修改分辨率:
- fmt.fmt.pix.width = 320;
- fmt.fmt.pix.height = 240;
复制代码
结果还是无济于事。
4-后来尝试把两个摄像头启动顺序调动了一下:
- m_fdVideoRight=open("/dev/video3",O_RDWR);// 打开设备
- init_uvc(m_fdVideoRight, m_bufVideoRight);
- m_fdVideoLeft=open("/dev/video2",O_RDWR);// 打开设备
- init_uvc(m_fdVideoLeft, m_bufVideoLeft);
复制代码
结果变成左摄像头一片绿幕。
由此猜测可能驱动层面就是无法支持双usb摄像头,即使把上下两个usb接口反过来也没有任何变化。
5-对于这种问题,只能够停掉其中一个才能读取另一个了。
停止视频流函数封装:
- void DectMea::stopBuf(int &fd)
- {
- enum v4l2_buf_type type;
- type =V4L2_BUF_TYPE_VIDEO_CAPTURE;
- ioctl (fd,VIDIOC_STREAMOFF, &type);
- }
复制代码
重新启动的函数:
- void DectMea::startBuf(int &fd)
- {
- //3-把四个缓冲帧放入队列,并启动数据流
- unsigned int i;
- enum v4l2_buf_type type;
- // 将缓冲帧放入队列
- for (i = 0; i< n_bufSize; ++i)
- {
- struct v4l2_buffer buf;
- buf.type =V4L2_BUF_TYPE_VIDEO_CAPTURE;
- buf.memory =V4L2_MEMORY_MMAP;
- buf.index = i;
- ioctl (fd,VIDIOC_QBUF, &buf);
- // usleep(300000);
- }
- type =V4L2_BUF_TYPE_VIDEO_CAPTURE;
- ioctl (fd,VIDIOC_STREAMON, &type);
- }
复制代码
6-重写读取视频流:
- void DectMea::getFrame()
- {
- while(!m_exitFlag)
- {
- if(m_vecFrameLeft.size()<m_frameMaxSize){
- {
- struct v4l2_buffer buf;
- buf.type =V4L2_BUF_TYPE_VIDEO_CAPTURE;
- buf.memory =V4L2_MEMORY_MMAP;
- // 从缓冲区取出一个缓冲帧
- ioctl (m_fdVideoLeft,VIDIOC_DQBUF, &buf);
- cv::Mat image(m_videoHeight, m_videoWidth, CV_8UC2, (unsigned char*)m_bufVideoLeft[buf.index].start);
- cv::Mat img2;
- cv::cvtColor(image, img2, CV_YUV2BGR_YUYV);
- cv::imshow("1", img2);
- m_vecFrameLeft.push(img2.clone());
- // 将取出的缓冲帧放回缓冲区
- ioctl (m_fdVideoLeft, VIDIOC_QBUF,&buf);
- }
- stopBuf(m_fdVideoLeft);
- startBuf(m_fdVideoRight);
- {
- struct v4l2_buffer buf;
- buf.type =V4L2_BUF_TYPE_VIDEO_CAPTURE;
- buf.memory =V4L2_MEMORY_MMAP;
- // 从缓冲区取出一个缓冲帧
- ioctl (m_fdVideoRight,VIDIOC_DQBUF, &buf);
- cv::Mat image(m_videoHeight, m_videoWidth, CV_8UC2, (unsigned char*)m_bufVideoRight[buf.index].start);
- cv::Mat img2;
- cv::cvtColor(image, img2, CV_YUV2BGR_YUYV);
- cv::imshow("2", img2);
- m_vecFrameRight.push(img2.clone());
- // 将取出的缓冲帧放回缓冲区
- ioctl (m_fdVideoRight, VIDIOC_QBUF,&buf);
- }
- stopBuf(m_fdVideoRight);
- startBuf(m_fdVideoLeft);
- }
- }
- }
复制代码
7-读取效果
由于需要时间启动摄像头,因此每次读取左右两个摄像头都需要1s。显然FZ3开发板并不适合两个拼接起来的usb摄像头。
附录
封装v4l2的初始化流程:
- bool DectMea::init_uvc(int &fd, v4l2Buf* &m_bufVideoSingal)
- {
- struct v4l2_capability cap;
- //2-查询设备属性
- ioctl(fd,VIDIOC_QUERYCAP,&cap);
- printf("DriverName\t%s\r\nCard Name\t%s\r\nBus info\t%s\r\nDriverVersion\t%u.%u.%u\r\n",
- cap.driver,cap.card,cap.bus_info,(cap.version>>16)&0XFF,(cap.version>>8)&0XFF,(cap.version)&0xFF);
- //*
- //3-帧格式-显示所有支持的格式
- struct v4l2_fmtdesc fmtdesc;
- unsigned int min = 0;
- fmtdesc.index=0;
- fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
- printf("Supportformat:\r\n");
- while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc)!=-1)
- {
- printf("\t%d.%s\r\n",fmtdesc.index+1,fmtdesc.description);
- fmtdesc.index++;
- }
- //4-显示当前帧的相关信息
- struct v4l2_format fmt;
- fmt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
- // fmt.fmt.pix.width = 1280;
- // fmt.fmt.pix.height = 720;
- fmt.fmt.pix.width = m_videoWidth;
- fmt.fmt.pix.height = m_videoHeight;
- fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
- fmt.fmt.pix.field = V4L2_FIELD_NONE;
- ioctl(fd,VIDIOC_S_FMT,&fmt);
- ioctl(fd,VIDIOC_G_FMT,&fmt);
- printf("Currentdata format information:\r\n\twidth:%d\r\n\theight:%d\r\n", fmt.fmt.pix.width, fmt.fmt.pix.height);
- // struct v4l2_fmtdesc fmtdesc;
- fmtdesc.index=1;
- fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
- while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc)!=-1)
- {
- if(fmtdesc.pixelformat& fmt.fmt.pix.pixelformat)
- {
- printf("\tformat:%s\n",fmtdesc.description);
- break;
- }
- fmtdesc.index++;
- }
- // init_device(fd);
- //1-申请一个拥有四个缓冲帧的缓冲区
- struct v4l2_requestbuffers req;
- req.count=n_bufSize;
- req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
- req.memory=V4L2_MEMORY_MMAP;
- ioctl(fd,VIDIOC_REQBUFS,&req);
- m_bufVideoSingal =(v4l2Buf*)calloc (req.count, sizeof (*m_bufVideoSingal));
- assert(m_bufVideoSingal);
- // if (!m_bufVideoLeft) {
- // fprintf (stderr,"Out of memory\n");
- // return false;
- // }
- //2-映射内存
- for (unsigned int n_bufSize = 0; n_bufSize < req.count; ++n_bufSize) {
- struct v4l2_buffer buf;
- memset(&buf,0,sizeof(buf));
- buf.type =V4L2_BUF_TYPE_VIDEO_CAPTURE;
- buf.memory =V4L2_MEMORY_MMAP;
- buf.index =n_bufSize;
- // 查询序号为n_bufSize 的缓冲区,得到其起始物理地址和大小
- if (-1 == ioctl(fd, VIDIOC_QUERYBUF, &buf))
- return false;
- m_bufVideoSingal[n_bufSize].length= buf.length;
- // 映射内存
- m_bufVideoSingal[n_bufSize].start=
- mmap (
- NULL,
- buf.length,
- PROT_READ | PROT_WRITE ,
- MAP_SHARED,
- fd,
- buf.m.offset);
- if (MAP_FAILED== m_bufVideoSingal[n_bufSize].start)
- return false;
- }
- // init_mmap(fd);
- //3-把四个缓冲帧放入队列,并启动数据流
- unsigned int i;
- enum v4l2_buf_type type;
- // 将缓冲帧放入队列
- for (i = 0; i< n_bufSize; ++i)
- {
- struct v4l2_buffer buf;
- buf.type =V4L2_BUF_TYPE_VIDEO_CAPTURE;
- buf.memory =V4L2_MEMORY_MMAP;
- buf.index = i;
- ioctl (fd,VIDIOC_QBUF, &buf);
- // usleep(300000);
- }
- type =V4L2_BUF_TYPE_VIDEO_CAPTURE;
- ioctl (fd,VIDIOC_STREAMON, &type);
- }
复制代码
|
|