查看: 8659|回复: 0

一步一步学ZedBoard & Zynq(七):Zedboard上的USB摄像头(V4L2接口)...

[复制链接]
  • TA的每日心情
    奋斗
    2020-9-28 10:10
  • 签到天数: 1018 天

    连续签到: 1 天

    [LV.10]以坛为家III

    发表于 2013-1-8 13:59:10 | 显示全部楼层 |阅读模式
    分享到:
    本帖最后由 xinxincaijq 于 2013-1-9 10:25 编辑

    一步一步学ZedBoard & Zynq(七):Zedboard上的USB摄像头(V4L2接口)的图片采集

    转自博客:@超群天晴 http://www.cnblogs.com/surpassal/   

    一直想把USB摄像头接到Zedboard上,搭建一个简易视频监控系统方便后续做视频处理。Xilinx官方给出了一个Webcam摄像头监控的例子,跑的是linaro,不知道是我的SD卡问题还是摄像头的问题,播放视频的时候总是会很卡,而且突然系统就死掉了。还是很喜欢自己动手,能学到新东西。Digilent官方给的OOB设计,那个精简的linux足够做简单的linux开发了,而且OOB设计中USB驱动和V4L(Video for Linux)都提供好了。这几天找了一些的V4L的资料,完成了摄像头的单帧图片采集,接下来要做的是QT界面显示和视频流的显示了,最终的计划是完成视频采集、编码、存储和以太网传输。希望能有时间和精力完成这么多。先把这几天的做的东西整理出来和大家分享。为了方便大家对程序的理解,先简单介绍一下linux下的V4L2的一些知识,然后再详细介绍V4L2编程。


    更多更新请关注我的博客:@超群天晴 http://www.cnblogs.com/surpassal/

    硬件平台:Digilent ZedBoard + USB 摄像头

    开发环境:Windows XP 32 bit + Wmare 8.0 + Ubuntu 10.04 + arm-linux-xilinx-gnueabi交叉编译环境

    Zedboard linux: Digilent OOB Design
    一、一些知识

    1、V4L和V4L2。
    V4L是Linux环境下开发视频采集设备驱动程序的一套规范(API),它为驱动程序的编写提供统一的接口,并将所有的视频采集设备的驱动程序都纳入其的管理之中。V4L不仅给驱动程序编写者带来极大的方便,同时也方便了应用程序的编写和移植。V4L2是V4L的升级版,由于我们使用的OOB是3.3的内核,不再支持V4L,因而编程不再考虑V4L的api和参数定义。
    2、YUYV与RGB24
    RGB是一种颜色的表示法,计算机中一般采用24位来存储,每个颜色占8位。YUV也是一种颜色空间,为什么要出现YUV,主要有两个原因,一个是为了让彩色信号兼容黑白电视机,另外一个原因是为了减少传输的带宽。YUV中,Y表示亮度,U和V表示色度,总之它是将RGB信号进行了一种处理,根据人对亮度更敏感些,增加亮度的信号,减少颜色的信号,以这样“欺骗”人的眼睛的手段来节省空间。YUV到RGB颜色空间转换关系是:
    R = Y + 1.042*(V-128);
    G = Y - 0.34414*(U-128) - 0.71414*(V-128);
    B = Y + 1.772*(U-128);

    YUV的格式也很多,不过常见的就是422、420等。YUYV就是422形式,简单来说就是,两个像素点P1、P2本应该有Y1、U1、V1和Y2、U2、V2这六个分量,但是实际只保留Y1、U1、Y2、V2。
    19092859-d84d713612864ecc995f84a2894ed6e5.jpg
    图1 YUYV像素

    二、应用程序设计
    先定义一些宏和结构体,方便后续编程
    1. #define  TRUE    1
    2. #define  FALSE    0

    3. #define FILE_VIDEO     "/dev/video0"
    4. #define BMP          "/usr/image_bmp.bmp"
    5. #define YUV            "/usr/image_yuv.yuv"

    6. #define  IMAGEWIDTH    640
    7. #define  IMAGEHEIGHT   480

    8. static   int      fd;
    9. static   struct   v4l2_capability   cap;
    10. struct v4l2_fmtdesc fmtdesc;
    11. struct v4l2_format fmt,fmtack;
    12. struct v4l2_streamparm setfps;  
    13. struct v4l2_requestbuffers req;
    14. struct v4l2_buffer buf;
    15. enum v4l2_buf_type type;
    16. unsigned char frame_buffer[IMAGEWIDTH*IMAGEHEIGHT*3];
    复制代码
    其中
    1. #define FILE_VIDEO     "/dev/video0"
    复制代码
    是要访问的摄像头设备,默人都是/dev/video0
    1. #define BMP          "/usr/image_bmp.bmp"
    2. #define YUV          "/usr/image_yuv.yuv"
    复制代码
    是采集后存储的图片,为了方便测试,这里将直接获取的yuv格式数据也保存成文件,可以通过yuvviewer等查看器查看。
    1. static   int      fd;
    2. static   struct   v4l2_capability   cap;
    3. struct v4l2_fmtdesc fmtdesc;
    4. struct v4l2_format fmt,fmtack;
    5. struct v4l2_streamparm setfps;  
    6. struct v4l2_requestbuffers req;
    7. struct v4l2_buffer buf;
    8. enum v4l2_buf_type type;
    复制代码
    这些结构体的定义都可以从/usr/include/linux/videodev2.h中找到定义,具体含义在后续编程会做相应解释。
    #define  IMAGEWIDTH    640
    #define  IMAGEHEIGHT   480

    为采集图像的大小。

    定义一个frame_buffer,用来缓存RGB颜色数据
    1. unsigned char frame_buffer[IMAGEWIDTH*IMAGEHEIGHT*3]
    复制代码
    这些宏和定义结束后,就可以开始编程配置摄像头并采集图像了。一般来说V4L2采集视频数据分为五个步骤:首先,打开视频设备文件,进行视频采集的参数初始化,通过V4L2接口设置视频图像的采集窗口、采集的点阵大小和格式;其次,申请若干视频采集的帧缓冲区,并将这些帧缓冲区从内核空间映射到用户空间,便于应用程序读取/处理视频数据;第三,将申请到的帧缓冲区在视频采集输入队列排队,并启动视频采集;第四,驱动开始视频数据的采集,应用程序从视频采集输出队列取出帧缓冲区,处理完后,将帧缓冲区重新放入视频采集输入队列,循环往复采集连续的视频数据;第五,停止视频采集。在本次设计中,定义了三个函数实现对摄像头的配置和采集。
    1. int init_v4l2(void);
    2. int v4l2_grab(void);
    3. int close_v4l2(void);
    复制代码
    同时由于采集到的图像数据是YUYV格式,需要进行颜色空间转换,定义了转换函数。
    1. int yuyv_2_rgb888(void);
    复制代码
    下面就详细介绍这几个函数的实现。
    1、初始化V4l2
    (1)打开视频。linux对摄像头的访问和普通设备一样,使用open函数就可以,返回值是设备的id。
    1. if ((fd = open(FILE_VIDEO, O_RDWR)) == -1)
    2. {
    3.      printf("Error opening V4L interface\n");
    4.      return (FALSE);
    5. }
    复制代码
    (2)读video_capability中信息。通过调用IOCTL函数和接口命令VIDIOC_QUERYCAP查询摄像头的信息,结构体v4l2_capability中有包括驱动名称driver、card、bus_info、version以及属性capabilities。这里我们需要检查一下是否是为视频采集设备V4L2_CAP_VIDEO_CAPTURE以及是否支持流IO操作V4L2_CAP_STREAMING。
    1. if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == -1)
    2. {
    3.      printf("Error opening device %s: unable to query device.\n",FILE_VIDEO);
    4.      return (FALSE);
    5. }
    6. else
    7. {
    8.       printf("driver:\t\t%s\n",cap.driver);
    9.       printf("card:\t\t%s\n",cap.card);
    10.       printf("bus_info:\t%s\n",cap.bus_info);
    11.       printf("version:\t%d\n",cap.version);
    12.       printf("capabilities:\t%x\n",cap.capabilities);
    13.       
    14.       if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == V4L2_CAP_VIDEO_CAPTURE)
    15.       {
    16.          printf("Device %s: supports capture.\n",FILE_VIDEO);
    17.      }

    18.      if ((cap.capabilities & V4L2_CAP_STREAMING) == V4L2_CAP_STREAMING)
    19.      {
    20.          printf("Device %s: supports streaming.\n",FILE_VIDEO);
    21.      }
    22. }
    复制代码
    (3)列举摄像头所支持像素格式。使用命令VIDIOC_ENUM_FMT,获取到的信息通过结构体v4l2_fmtdesc查询。这步很关键,不同的摄像头可能支持的格式不一样,V4L2可以支持的格式很多,/usr/include/linux/videodev2.h文件中可以看到。
    1. fmtdesc.index=0;
    2. fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
    3. printf("Support format:\n");
    4. while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc)!=-1)
    5. {
    6.      printf("\t%d.%s\n",fmtdesc.index+1,fmtdesc.description);
    7.      fmtdesc.index++;
    8. }
    复制代码
    (4)设置像素格式。一般的USB摄像头都会支持YUYV,有些还支持其他的格式。通过前一步对摄像头所支持像素格式查询,下面需要对格式进行设置。命令为VIDIOC_S_FMT,通过结构体v4l2_format把图像的像素格式设置为V4L2_PIX_FMT_YUYV,高度和宽度设置为IMAGEHEIGHT和IMAGEWIDTH。一般情况下一个摄像头所支持的格式是不可以随便更改的,我尝试把把一个只支持YUYV和MJPEG的摄像头格式改为RGB24或者JPEG,都没有成功。
    1. fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    2. fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
    3. fmt.fmt.pix.height = IMAGEHEIGHT;
    4. fmt.fmt.pix.width = IMAGEWIDTH;
    5. fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;

    6. if(ioctl(fd, VIDIOC_S_FMT, &fmt) == -1)
    7. {
    8.      printf("Unable to set format\n");
    9.      return FALSE;
    10. }
    复制代码
    为了确保设置的格式作用到摄像头上,再通过命令VIDIOC_G_FMT将摄像头设置读取回来。
    1. if(ioctl(fd, VIDIOC_G_FMT, &fmt) == -1)
    2. {
    3.      printf("Unable to get format\n");
    4.      return FALSE;
    5. }
    6. {
    7.       printf("fmt.type:\t\t%d\n",fmt.type);
    8.       printf("pix.pixelformat:\t%c%c%c%c\n",fmt.fmt.pix.pixelformat & 0xFF, (fmt.fmt.pix.pixelformat >> 8) & 0xFF,(fmt.fmt.pix.pixelformat >> 16) & 0xFF, (fmt.fmt.pix.pixelformat >> 24) & 0xFF);
    9.       printf("pix.height:\t\t%d\n",fmt.fmt.pix.height);
    10.       printf("pix.width:\t\t%d\n",fmt.fmt.pix.width);
    11.       printf("pix.field:\t\t%d\n",fmt.fmt.pix.field);
    12. }
    复制代码
    完整的初始化代码如下:
    1. View Code
    2. int init_v4l2(void)
    3. {
    4.      int i;
    5.      int ret = 0;
    6.      
    7.      //opendev
    8.      if ((fd = open(FILE_VIDEO, O_RDWR)) == -1)
    9.      {
    10.          printf("Error opening V4L interface\n");
    11.          return (FALSE);
    12.      }

    13.      //query cap
    14.      if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == -1)
    15.      {
    16.          printf("Error opening device %s: unable to query device.\n",FILE_VIDEO);
    17.          return (FALSE);
    18.      }
    19.      else
    20.      {
    21.           printf("driver:\t\t%s\n",cap.driver);
    22.           printf("card:\t\t%s\n",cap.card);
    23.           printf("bus_info:\t%s\n",cap.bus_info);
    24.           printf("version:\t%d\n",cap.version);
    25.           printf("capabilities:\t%x\n",cap.capabilities);
    26.          
    27.           if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == V4L2_CAP_VIDEO_CAPTURE)
    28.           {
    29.              printf("Device %s: supports capture.\n",FILE_VIDEO);
    30.          }

    31.          if ((cap.capabilities & V4L2_CAP_STREAMING) == V4L2_CAP_STREAMING)
    32.          {
    33.              printf("Device %s: supports streaming.\n",FILE_VIDEO);
    34.          }
    35.      }
    36.      
    37.      //emu all support fmt
    38.      fmtdesc.index=0;
    39.      fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
    40.      printf("Support format:\n");
    41.      while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc)!=-1)
    42.      {
    43.          printf("\t%d.%s\n",fmtdesc.index+1,fmtdesc.description);
    44.          fmtdesc.index++;
    45.      }
    46.      
    47.      //set fmt
    48.      fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    49.      fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
    50.      fmt.fmt.pix.height = IMAGEHEIGHT;
    51.      fmt.fmt.pix.width = IMAGEWIDTH;
    52.      fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
    53.      
    54.      if(ioctl(fd, VIDIOC_S_FMT, &fmt) == -1)
    55.      {
    56.          printf("Unable to set format\n");
    57.          return FALSE;
    58.      }     
    59.      if(ioctl(fd, VIDIOC_G_FMT, &fmt) == -1)
    60.      {
    61.          printf("Unable to get format\n");
    62.          return FALSE;
    63.      }
    64.      {
    65.           printf("fmt.type:\t\t%d\n",fmt.type);
    66.           printf("pix.pixelformat:\t%c%c%c%c\n",fmt.fmt.pix.pixelformat & 0xFF, (fmt.fmt.pix.pixelformat >> 8) & 0xFF,(fmt.fmt.pix.pixelformat >> 16) & 0xFF, (fmt.fmt.pix.pixelformat >> 24) & 0xFF);
    67.           printf("pix.height:\t\t%d\n",fmt.fmt.pix.height);
    68.           printf("pix.width:\t\t%d\n",fmt.fmt.pix.width);
    69.           printf("pix.field:\t\t%d\n",fmt.fmt.pix.field);
    70.      }
    71.      //set fps
    72.      setfps.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    73.      setfps.parm.capture.timeperframe.numerator = 10;
    74.      setfps.parm.capture.timeperframe.denominator = 10;
    75.      
    76.      printf("init %s \t[OK]\n",FILE_VIDEO);
    77.          
    78.      return TRUE;
    79. }
    复制代码
    2、图像采集
    (1)申请缓存区。使用参数VIDIOC_REQBUFS和结构体v4l2_requestbuffers。v4l2_requestbuffers结构中定义了缓存的数量,系统会据此申请对应数量的视频缓存。
    1. req.count=4;
    2. req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
    3. req.memory=V4L2_MEMORY_MMAP;
    4. if(ioctl(fd,VIDIOC_REQBUFS,&req)==-1)
    5. {
    6.      printf("request for buffers error\n");

    7. }
    复制代码
    (2)获取每个缓存的信息,并mmap到用户空间。定义结构体
    1. struct buffer
    2. {
    3.     void * start;
    4.     unsigned int length;
    5. } * buffers;
    复制代码
    来存储mmap后的地址信息。需要说明的是由于mmap函数定义时返回的地址是个void *,因而这里面的start也是个 void *。实际地址在运行的时候会自动分配。
    1. for (n_buffers = 0; n_buffers < req.count; n_buffers++)
    2. {
    3.      buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    4.      buf.memory = V4L2_MEMORY_MMAP;
    5.      buf.index = n_buffers;
    6.      //query buffers
    7.      if (ioctl (fd, VIDIOC_QUERYBUF, &buf) == -1)
    8.      {
    9.          printf("query buffer error\n");
    10.          return(FALSE);
    11.      }

    12.      buffers[n_buffers].length = buf.length;
    13.      //map
    14.      buffers[n_buffers].start = mmap(NULL,buf.length,PROT_READ |PROT_WRITE, MAP_SHARED, fd, buf.m.offset);
    15.      if (buffers[n_buffers].start == MAP_FAILED)
    16.      {
    17.          printf("buffer map error\n");
    18.          return(FALSE);
    19.      }
    20. }
    复制代码
    (3) 之后就可以开始采集视频了。使用命令VIDIOC_STREAMON。
    1. type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    2. ioctl (fd, VIDIOC_STREAMON, &type);
    复制代码
    (4)取出缓存中已经采样的缓存。使用命令VIDIOC_DQBUF。视频数据存放的位置是buffers[n_buffers].start的地址处。
    1. ioctl(fd, VIDIOC_DQBUF, &buf);
    复制代码
    完整的采集代码:
    1. View Code
    2. int v4l2_grab(void)
    3. {
    4.      unsigned int n_buffers;
    5.      
    6.      //request for 4 buffers
    7.      req.count=4;
    8.      req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
    9.      req.memory=V4L2_MEMORY_MMAP;
    10.      if(ioctl(fd,VIDIOC_REQBUFS,&req)==-1)
    11.      {
    12.          printf("request for buffers error\n");
    13.      }

    14.      //mmap for buffers
    15.      buffers = malloc(req.count*sizeof (*buffers));
    16.      if (!buffers)
    17.      {
    18.          printf ("Out of memory\n");
    19.          return(FALSE);
    20.      }
    21.      
    22.      for (n_buffers = 0; n_buffers < req.count; n_buffers++)
    23.      {
    24.          buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    25.          buf.memory = V4L2_MEMORY_MMAP;
    26.          buf.index = n_buffers;
    27.          //query buffers
    28.          if (ioctl (fd, VIDIOC_QUERYBUF, &buf) == -1)
    29.          {
    30.              printf("query buffer error\n");
    31.              return(FALSE);
    32.          }

    33.          buffers[n_buffers].length = buf.length;
    34.          //map
    35.          buffers[n_buffers].start = mmap(NULL,buf.length,PROT_READ |PROT_WRITE, MAP_SHARED, fd, buf.m.offset);
    36.          if (buffers[n_buffers].start == MAP_FAILED)
    37.          {
    38.              printf("buffer map error\n");
    39.              return(FALSE);
    40.          }
    41.      }
    42.          
    43.      //queue
    44.      for (n_buffers = 0; n_buffers < req.count; n_buffers++)
    45.      {
    46.          buf.index = n_buffers;
    47.          ioctl(fd, VIDIOC_QBUF, &buf);
    48.      }
    49.      
    50.      type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    51.      ioctl (fd, VIDIOC_STREAMON, &type);
    52.      
    53.      ioctl(fd, VIDIOC_DQBUF, &buf);

    54.      printf("grab yuyv OK\n");
    55.      return(TRUE);
    56. }
    复制代码
    3、YUYV转RGB24
    由于摄像头采集的数据格式为YUYV,为了方便后续设计,需要转变为RGB24,并将转换完成的数据存储到frame_buffer中。值得一提的是,由于定义的时候buffers[index].start是个void *,没有办法进行+1这样的操作,需要强制转换为
    1. char * pointer
    2. pointer = buffers[0].start
    复制代码
    由于后续RGB的数据要存储到BMP中,而BMP文件中颜色数据是“倒序”,即从下到上,从左到右,因而在向frame_buffer写数据时是从最后一行最左测开始写,每写满一行行数减一。
    1. View Code
    2. int yuyv_2_rgb888(void)
    3. {
    4.      int           i,j;
    5.      unsigned char y1,y2,u,v;
    6.      int r1,g1,b1,r2,g2,b2;
    7.      char * pointer;
    8.      
    9.      pointer = buffers[0].start;
    10.      
    11.      for(i=0;i<480;i++)
    12.      {
    13.          for(j=0;j<320;j++)
    14.          {
    15.              y1 = *( pointer + (i*320+j)*4);
    16.              u  = *( pointer + (i*320+j)*4 + 1);
    17.              y2 = *( pointer + (i*320+j)*4 + 2);
    18.              v  = *( pointer + (i*320+j)*4 + 3);
    19.             
    20.              r1 = y1 + 1.042*(v-128);
    21.              g1 = y1 - 0.34414*(u-128) - 0.71414*(v-128);
    22.              b1 = y1 + 1.772*(u-128);
    23.             
    24.              r2 = y2 + 1.042*(v-128);
    25.              g2 = y2 - 0.34414*(u-128) - 0.71414*(v-128);
    26.              b2 = y2 + 1.772*(u-128);
    27.             
    28.              if(r1>255)
    29.                  r1 = 255;
    30.              else if(r1<0)
    31.                  r1 = 0;
    32.             
    33.              if(b1>255)
    34.                  b1 = 255;
    35.              else if(b1<0)
    36.                  b1 = 0;   
    37.             
    38.              if(g1>255)
    39.                  g1 = 255;
    40.              else if(g1<0)
    41.                  g1 = 0;   
    42.                  
    43.              if(r2>255)
    44.                  r2 = 255;
    45.              else if(r2<0)
    46.                  r2 = 0;
    47.             
    48.              if(b2>255)
    49.                  b2 = 255;
    50.              else if(b2<0)
    51.                  b2 = 0;   
    52.             
    53.              if(g2>255)
    54.                  g2 = 255;
    55.              else if(g2<0)
    56.                  g2 = 0;        
    57.                  
    58.              *(frame_buffer + ((480-1-i)*320+j)*6    ) = (unsigned char)b1;
    59.              *(frame_buffer + ((480-1-i)*320+j)*6 + 1) = (unsigned char)g1;
    60.              *(frame_buffer + ((480-1-i)*320+j)*6 + 2) = (unsigned char)r1;
    61.              *(frame_buffer + ((480-1-i)*320+j)*6 + 3) = (unsigned char)b2;
    62.              *(frame_buffer + ((480-1-i)*320+j)*6 + 4) = (unsigned char)g2;
    63.              *(frame_buffer + ((480-1-i)*320+j)*6 + 5) = (unsigned char)r2;
    64.          }
    65.      }
    66.      printf("change to RGB OK \n");
    67. }
    复制代码
    4、停止采集和关闭设备
    使用命令VIDIOC_STREAMOFF停止视频采集,并关闭设备。
    1. int close_v4l2(void)
    2. {
    3.      ioctl(fd, VIDIOC_STREAMOFF, &buf_type);     
    4.      if(fd != -1)
    5.       {
    6.           close(fd);
    7.           return (TRUE);
    8.       }
    9.       return (FALSE);
    10. }
    复制代码
    5、主函数
    需要把我们采集到图像数据存储成图片,为了方便调试,先将原始的数据存储为yuv格式文件,再将转换成RGB后的数据存储为BMP。定义BMP头结构体
    1. typedef struct tagBITMAPFILEHEADER{
    2.       WORD    bfType;                // the flag of bmp, value is "BM"
    3.       DWORD    bfSize;                // size BMP file ,unit is bytes
    4.       DWORD    bfReserved;            // 0
    5.       DWORD    bfOffBits;             // must be 54

    6. }BITMAPFILEHEADER;

    7.   
    8. typedef struct tagBITMAPINFOHEADER{
    9.       DWORD    biSize;                // must be 0x28
    10.       DWORD    biWidth;           //
    11.       DWORD    biHeight;          //
    12.       WORD        biPlanes;          // must be 1
    13.       WORD        biBitCount;            //
    14.       DWORD    biCompression;         //
    15.       DWORD    biSizeImage;       //
    16.       DWORD    biXPelsPerMeter;   //
    17.       DWORD    biYPelsPerMeter;   //
    18.       DWORD    biClrUsed;             //
    19.       DWORD    biClrImportant;        //
    20. }BITMAPINFOHEADER;
    复制代码
    完整的主函数
    1. //@超群天晴
    2. //http://www.cnblogs.com/surpassal/
    3. int main(void)
    4. {

    5.     FILE * fp1,* fp2;

    6.     BITMAPFILEHEADER   bf;
    7.     BITMAPINFOHEADER   bi;
    8.    

    9.     fp1 = fopen(BMP, "wb");
    10.     if(!fp1)
    11.     {
    12.         printf("open "BMP"error\n");
    13.         return(FALSE);
    14.     }
    15.    
    16.     fp2 = fopen(YUV, "wb");
    17.     if(!fp2)
    18.     {
    19.         printf("open "YUV"error\n");
    20.         return(FALSE);
    21.     }

    22.     if(init_v4l2() == FALSE)
    23.     {
    24.          return(FALSE);
    25.     }
    26.    
    27.     //Set BITMAPINFOHEADER
    28.     bi.biSize = 40;
    29.     bi.biWidth = IMAGEWIDTH;
    30.     bi.biHeight = IMAGEHEIGHT;
    31.     bi.biPlanes = 1;
    32.     bi.biBitCount = 24;
    33.     bi.biCompression = 0;
    34.     bi.biSizeImage = IMAGEWIDTH*IMAGEHEIGHT*3;
    35.     bi.biXPelsPerMeter = 0;
    36.     bi.biYPelsPerMeter = 0;
    37.     bi.biClrUsed = 0;
    38.     bi.biClrImportant = 0;


    39.     //Set BITMAPFILEHEADER
    40.     bf.bfType = 0x4d42;
    41.     bf.bfSize = 54 + bi.biSizeImage;     
    42.     bf.bfReserved = 0;
    43.     bf.bfOffBits = 54;
    44.    
    45.     v4l2_grab();
    46.     fwrite(buffers[0].start, 640*480*2, 1, fp2);
    47.     printf("save "YUV"OK\n");
    48.    
    49.     yuyv_2_rgb888();
    50.     fwrite(&bf, 14, 1, fp1);
    51.     fwrite(&bi, 40, 1, fp1);   
    52.     fwrite(frame_buffer, bi.biSizeImage, 1, fp1);
    53.     printf("save "BMP"OK\n");
    54.    
    55.    
    56.     fclose(fp1);
    57.     fclose(fp2);
    58.     close_v4l2();
    59.    
    60.     return(TRUE);
    61. }
    复制代码
    三、PC测试
    程序编写完后,可以先在PC上做测试(实际整个调试过程都是在PC上,直道最后PC上能实现功能再挪到ZedBoard上的)。PC上测试的结果
    18213947-6e021e4b2d7040aa9c539421de5e2bee.jpg
    在/usr目录下可以查看到采集到的图片
    18220422-8e19cd189f5e406b91903a370ae221bf.jpg
    18220437-f133cefb64d240929e9697dbd6836fea.jpg

    四、Zedboard测试
    PC上测试OK后,可以“挪”到ZedBoard上了。使用arm-xilinx-linux交叉编译环境对源文件进行交叉编译,将生成的可执行文件拷贝到ZedBoard上运行即可。
    使用命令
    1. arm-xilinx-linux-gnueabi-gcc v4l2grab.c -o zed-camera
    复制代码
    对程序进行编译,编译通过后将生成的可执行文件zed-camera拷贝到到ZedBoard上,并将USB摄像头连接到ZedBoard上,通过命令
    1. ls /dev
    复制代码
    查看dev目录下的是否有video0设备。如果有,可以运行可执行文件了。在运行前我比较习惯获得可执行文件的权限,使用命令
    1. chmod +x zed-camera
    复制代码
    参数+x的意思是这个文件对于当前用户是可执行的。也可以使用
    1. chmod 777 zed-camera
    复制代码
    这样所有用户都有读写执行的权限。使用命令
    1. ./zed-camera
    复制代码
    执行可执行程序,程序运行,并输出以下信息:
    1. zynq> ./zed-camera
    2. [  318.290000] usb 1-1.3: reset high-speed USB device number 3 using xusbps-ehci

    3. driver:         uvcvideo
    4. card:           UVC Camera (046d:0825)
    5. bus_info:       usb-xusbps-ehci.0-1.3
    6. version:        197376
    7. capabilities:   4000001
    8. Device /dev/video0: supports capture.
    9. Device /dev/video0: supports streaming.
    10. Support format:
    11. .YUV 4:2:2 (YUYV)
    12. .MJPEG
    13. fmt.type:               1
    14. pix.pixelformat:        YUYV
    15. pix.height:             480
    16. pix.width:              640
    17. pix.field:              1
    18. init /dev/video0        [OK]
    19. grab yuyv OK
    20. save /usr/image_yuv.yuv OK
    21. change to RGB OK
    22. save /usr/image_bmp.bmp OK
    复制代码
    可以看到我使用的USB摄像支持YUYV和MJPEG两种格式。我也试过其他USB摄像头,大部分都只支持YUYV而不支持MJPEG或者RGB24。

    采集到的图片默认是在/usr目录下的,将其拷贝出来
    1. cp /usr/image* /mnt
    复制代码
    再PC上查看,效果还不错
    18215244-f79342eaa17f40e7879c45bd2a6b4bcd.jpg
    =============================

    完整工程和代码: lab_v4l2_yuyv.zip (8.39 KB, 下载次数: 115)
    回复

    使用道具 举报

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

    本版积分规则

    关闭

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



    手机版|小黑屋|与非网

    GMT+8, 2024-12-25 20:58 , Processed in 0.111286 second(s), 15 queries , MemCache On.

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

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.