查看: 2597|回复: 0

【米尔FZ3深度学习计算卡】(四)双目采集

[复制链接]
  • TA的每日心情
    慵懒
    2024-5-31 23:20
  • 签到天数: 302 天

    连续签到: 2 天

    [LV.8]以坛为家I

    发表于 2021-1-10 21:14:47 | 显示全部楼层 |阅读模式
    分享到:
    (一)普通USB摄像头图像采集
    关于一般的摄像头视频流采集,前面的帖子有提到:
    【米尔FZ3深度学习计算卡】视频流识别测试
    【米尔FZ3深度学习计算卡】(三)V4L2采集视频流识别
    当时测试时使用opencv的>>重载符读取视频流出现mmap错误,一开始以为是opencv没有适配v4l2相关驱动,于是转向v4l2开发。
    但后来参考其他试用者的意见,发现opencv的>>重载符是可以读取视频流的。
    这时候我又尝试了一遍opencv的>>重载符,发现果然神奇地可行了,并且再也无法复现mmap错误,至今没有找到mmap错误产生的原因。
    这时候我转向了opencv的>>重载符的开发


    (二)opencv的>>重载符的双目采集
    经过单目的问题后,双目的开发只是简单地重复,应该是理所当然地势如破竹。
    但是现实又告诉事情没有那么简单:
    VIDIOC_STREAMON: No space left on device
    opencv抛出了这么一个错误。
    我尝试降低分辨率:
    1.     if(!m_VideoCaptureLeft.isOpened()) m_VideoCaptureLeft.open(2);
    2.     m_VideoCaptureLeft.set(CV_CAP_PROP_FRAME_WIDTH, 320);//宽度
    3.     m_VideoCaptureLeft.set(CV_CAP_PROP_FRAME_HEIGHT, 240);//高度
    4.     assert(m_VideoCaptureLeft.isOpened());

    5.     if(!m_VideoCaptureRight.isOpened()) m_VideoCaptureRight.open(3);
    6.     m_VideoCaptureRight.set(CV_CAP_PROP_FRAME_WIDTH, 320);//宽度
    7.     m_VideoCaptureRight.set(CV_CAP_PROP_FRAME_HEIGHT, 240);//高度
    8.     assert(m_VideoCaptureRight.isOpened());
    复制代码


    结果错误依旧。
    按照像素计算带宽,单个摄像头 640x480 是两个摄像头 320x240 的两倍,之前单个摄像头采用640x480分辨率也是可以正常采集的,那么两个摄像头 320x240应该不成问题。那只能怀疑opencv的降分辨率的操作没有真正影响到带宽。

    这时候有其他试用者指出opencv应该是封装好、直接操作v4l2底层的,不应该出现这种问题。但事实就是无法驱动两个摄像头,于是只能转向v4l2进行最后一博。

    (三)v4l2的双目采集
    1-首先封装v4l2的初始化流程:见文末
    封装了之后处理就简单了,只需要把缓冲指针和设备描述符传进去就可以了:
    1.     m_fdVideoLeft=open("/dev/video2",O_RDWR);// 打开设备
    2.     init_uvc(m_fdVideoLeft, m_bufVideoLeft);
    3.     m_fdVideoRight=open("/dev/video3",O_RDWR);// 打开设备
    4.     init_uvc(m_fdVideoRight, m_bufVideoRight);
    复制代码


    2-采集只需要通过缓冲指针取出来即可:
    1. void DectMea::getFrame()
    2. {
    3.     while(!m_exitFlag)
    4.     {
    5.         if(m_vecFrameLeft.size()<m_frameMaxSize){
    6.             {
    7.                 struct v4l2_buffer buf;
    8.                 buf.type =V4L2_BUF_TYPE_VIDEO_CAPTURE;
    9.                 buf.memory =V4L2_MEMORY_MMAP;
    10.                 // 从缓冲区取出一个缓冲帧
    11.                 ioctl (m_fdVideoLeft,VIDIOC_DQBUF, &buf);
    12.                 cv::Mat image(m_videoHeight, m_videoWidth, CV_8UC2, (unsigned char*)m_bufVideoLeft[buf.index].start);
    13.                 cv::Mat img2;
    14.                 cv::cvtColor(image, img2, CV_YUV2BGR_YUYV);
    15.                 cv::imshow("1", img2);
    16.                 m_vecFrameLeft.push(img2.clone());
    17.                 // 将取出的缓冲帧放回缓冲区
    18.                 ioctl (m_fdVideoLeft, VIDIOC_QBUF,&buf);
    19.             }
    20.             {
    21.                 struct v4l2_buffer buf;
    22.                 buf.type =V4L2_BUF_TYPE_VIDEO_CAPTURE;
    23.                 buf.memory =V4L2_MEMORY_MMAP;
    24.                 // 从缓冲区取出一个缓冲帧
    25.                 ioctl (m_fdVideoRight,VIDIOC_DQBUF, &buf);
    26.                 cv::Mat image(m_videoHeight, m_videoWidth, CV_8UC2, (unsigned char*)m_bufVideoRight[buf.index].start);
    27.                 cv::Mat img2;
    28.                 cv::cvtColor(image, img2, CV_YUV2BGR_YUYV);
    29.                 cv::imshow("2", img2);
    30.                 m_vecFrameRight.push(img2.clone());
    31.                 // 将取出的缓冲帧放回缓冲区
    32.                 ioctl (m_fdVideoRight, VIDIOC_QBUF,&buf);
    33.             }
    34.         }
    35.     }
    36. }
    复制代码

    3-然而到这里就出问题了,左摄像头正常,但右摄像头显示出来是一片绿幕。
    修改分辨率:
    1. fmt.fmt.pix.width = 320;
    2. fmt.fmt.pix.height = 240;
    复制代码

    结果还是无济于事。


    4-后来尝试把两个摄像头启动顺序调动了一下:
    1. m_fdVideoRight=open("/dev/video3",O_RDWR);// 打开设备
    2. init_uvc(m_fdVideoRight, m_bufVideoRight);
    3. m_fdVideoLeft=open("/dev/video2",O_RDWR);// 打开设备
    4. init_uvc(m_fdVideoLeft, m_bufVideoLeft);
    复制代码

    结果变成左摄像头一片绿幕。
    由此猜测可能驱动层面就是无法支持双usb摄像头,即使把上下两个usb接口反过来也没有任何变化。

    5-对于这种问题,只能够停掉其中一个才能读取另一个了。
    停止视频流函数封装:
    1. void DectMea::stopBuf(int &fd)
    2. {
    3.     enum v4l2_buf_type type;
    4.     type =V4L2_BUF_TYPE_VIDEO_CAPTURE;
    5.     ioctl (fd,VIDIOC_STREAMOFF, &type);
    6. }
    复制代码

    重新启动的函数:
    1. void DectMea::startBuf(int &fd)
    2. {
    3.     //3-把四个缓冲帧放入队列,并启动数据流
    4.     unsigned int i;
    5.     enum v4l2_buf_type type;
    6.     // 将缓冲帧放入队列
    7.     for (i = 0; i< n_bufSize; ++i)
    8.     {
    9.         struct v4l2_buffer buf;
    10.         buf.type =V4L2_BUF_TYPE_VIDEO_CAPTURE;
    11.         buf.memory =V4L2_MEMORY_MMAP;
    12.         buf.index = i;
    13.         ioctl (fd,VIDIOC_QBUF, &buf);
    14. //        usleep(300000);
    15.     }
    16.     type =V4L2_BUF_TYPE_VIDEO_CAPTURE;
    17.     ioctl (fd,VIDIOC_STREAMON, &type);
    18. }
    复制代码

    6-重写读取视频流:
    1. void DectMea::getFrame()
    2. {
    3. while(!m_exitFlag)
    4. {
    5. if(m_vecFrameLeft.size()<m_frameMaxSize){
    6. {
    7. struct v4l2_buffer buf;
    8. buf.type =V4L2_BUF_TYPE_VIDEO_CAPTURE;
    9. buf.memory =V4L2_MEMORY_MMAP;
    10. // 从缓冲区取出一个缓冲帧
    11. ioctl (m_fdVideoLeft,VIDIOC_DQBUF, &buf);
    12. cv::Mat image(m_videoHeight, m_videoWidth, CV_8UC2, (unsigned char*)m_bufVideoLeft[buf.index].start);
    13. cv::Mat img2;
    14. cv::cvtColor(image, img2, CV_YUV2BGR_YUYV);
    15. cv::imshow("1", img2);
    16. m_vecFrameLeft.push(img2.clone());
    17. // 将取出的缓冲帧放回缓冲区
    18. ioctl (m_fdVideoLeft, VIDIOC_QBUF,&buf);
    19. }
    20. stopBuf(m_fdVideoLeft);
    21. startBuf(m_fdVideoRight);
    22. {
    23. struct v4l2_buffer buf;
    24. buf.type =V4L2_BUF_TYPE_VIDEO_CAPTURE;
    25. buf.memory =V4L2_MEMORY_MMAP;
    26. // 从缓冲区取出一个缓冲帧
    27. ioctl (m_fdVideoRight,VIDIOC_DQBUF, &buf);
    28. cv::Mat image(m_videoHeight, m_videoWidth, CV_8UC2, (unsigned char*)m_bufVideoRight[buf.index].start);
    29. cv::Mat img2;
    30. cv::cvtColor(image, img2, CV_YUV2BGR_YUYV);
    31. cv::imshow("2", img2);
    32. m_vecFrameRight.push(img2.clone());
    33. // 将取出的缓冲帧放回缓冲区
    34. ioctl (m_fdVideoRight, VIDIOC_QBUF,&buf);
    35. }
    36. stopBuf(m_fdVideoRight);
    37. startBuf(m_fdVideoLeft);
    38. }
    39. }
    40. }
    复制代码

    7-读取效果
    由于需要时间启动摄像头,因此每次读取左右两个摄像头都需要1s。显然FZ3开发板并不适合两个拼接起来的usb摄像头。




    附录

    封装v4l2的初始化流程:

    1. bool DectMea::init_uvc(int &fd, v4l2Buf* &m_bufVideoSingal)
    2. {
    3.     struct v4l2_capability cap;

    4.     //2-查询设备属性
    5.     ioctl(fd,VIDIOC_QUERYCAP,&cap);
    6.     printf("DriverName\t%s\r\nCard Name\t%s\r\nBus info\t%s\r\nDriverVersion\t%u.%u.%u\r\n",
    7.            cap.driver,cap.card,cap.bus_info,(cap.version>>16)&0XFF,(cap.version>>8)&0XFF,(cap.version)&0xFF);

    8.     //*
    9.     //3-帧格式-显示所有支持的格式
    10.     struct v4l2_fmtdesc fmtdesc;
    11.     unsigned int min = 0;

    12.     fmtdesc.index=0;
    13.     fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
    14.     printf("Supportformat:\r\n");
    15.     while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc)!=-1)
    16.     {
    17.         printf("\t%d.%s\r\n",fmtdesc.index+1,fmtdesc.description);
    18.         fmtdesc.index++;
    19.     }

    20.     //4-显示当前帧的相关信息
    21.     struct v4l2_format fmt;
    22.     fmt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;

    23. //    fmt.fmt.pix.width       = 1280;
    24. //    fmt.fmt.pix.height      = 720;
    25.     fmt.fmt.pix.width       = m_videoWidth;
    26.     fmt.fmt.pix.height      = m_videoHeight;
    27.     fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
    28.     fmt.fmt.pix.field       = V4L2_FIELD_NONE;

    29.     ioctl(fd,VIDIOC_S_FMT,&fmt);
    30.     ioctl(fd,VIDIOC_G_FMT,&fmt);

    31.     printf("Currentdata format information:\r\n\twidth:%d\r\n\theight:%d\r\n", fmt.fmt.pix.width, fmt.fmt.pix.height);
    32. //    struct v4l2_fmtdesc fmtdesc;
    33.     fmtdesc.index=1;
    34.     fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
    35.     while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc)!=-1)
    36.     {
    37.         if(fmtdesc.pixelformat& fmt.fmt.pix.pixelformat)
    38.         {
    39.             printf("\tformat:%s\n",fmtdesc.description);
    40.             break;
    41.         }
    42.         fmtdesc.index++;
    43.     }

    44. //    init_device(fd);

    45.     //1-申请一个拥有四个缓冲帧的缓冲区
    46.     struct v4l2_requestbuffers req;
    47.     req.count=n_bufSize;
    48.     req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
    49.     req.memory=V4L2_MEMORY_MMAP;
    50.     ioctl(fd,VIDIOC_REQBUFS,&req);

    51.     m_bufVideoSingal =(v4l2Buf*)calloc (req.count, sizeof (*m_bufVideoSingal));
    52.     assert(m_bufVideoSingal);
    53. //    if (!m_bufVideoLeft) {
    54. //        fprintf (stderr,"Out of memory\n");
    55. //        return false;
    56. //    }

    57.     //2-映射内存
    58.     for (unsigned int n_bufSize = 0; n_bufSize < req.count; ++n_bufSize) {
    59.         struct v4l2_buffer buf;
    60.         memset(&buf,0,sizeof(buf));
    61.         buf.type =V4L2_BUF_TYPE_VIDEO_CAPTURE;
    62.         buf.memory =V4L2_MEMORY_MMAP;
    63.         buf.index =n_bufSize;
    64.         // 查询序号为n_bufSize 的缓冲区,得到其起始物理地址和大小
    65.         if (-1 == ioctl(fd, VIDIOC_QUERYBUF, &buf))
    66.             return false;
    67.         m_bufVideoSingal[n_bufSize].length= buf.length;
    68.         // 映射内存
    69.         m_bufVideoSingal[n_bufSize].start=
    70.                 mmap (
    71.                     NULL,
    72.                     buf.length,
    73.                     PROT_READ | PROT_WRITE ,
    74.                     MAP_SHARED,
    75.                     fd,
    76.                     buf.m.offset);
    77.         if (MAP_FAILED== m_bufVideoSingal[n_bufSize].start)
    78.             return false;
    79.     }
    80. //    init_mmap(fd);

    81.     //3-把四个缓冲帧放入队列,并启动数据流
    82.     unsigned int i;
    83.     enum v4l2_buf_type type;
    84.     // 将缓冲帧放入队列
    85.     for (i = 0; i< n_bufSize; ++i)
    86.     {
    87.         struct v4l2_buffer buf;
    88.         buf.type =V4L2_BUF_TYPE_VIDEO_CAPTURE;
    89.         buf.memory =V4L2_MEMORY_MMAP;
    90.         buf.index = i;
    91.         ioctl (fd,VIDIOC_QBUF, &buf);
    92. //        usleep(300000);
    93.     }
    94.     type =V4L2_BUF_TYPE_VIDEO_CAPTURE;
    95.     ioctl (fd,VIDIOC_STREAMON, &type);
    96. }
    复制代码




    回复

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /4 下一条

    手机版|小黑屋|与非网

    GMT+8, 2024-11-19 12:16 , Processed in 0.109749 second(s), 15 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.