• 14、USB摄像头(V4L2接口)的图片采集


    参考网站http://www.cnblogs.com/surpassal/archive/2012/12/19/zed_webcam_lab1.html

    一、一些知识

     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。

    图1 YUYV像素

    
    

    二、应用程序设计

    先定义一些宏和结构体,方便后续编程

     1 #define  TRUE    1
     2 #define  FALSE    0
     3 
     4 #define FILE_VIDEO     "/dev/video0"
     5 #define BMP          "/usr/image_bmp.bmp"
     6 #define YUV            "/usr/image_yuv.yuv"
     7 
     8 #define  IMAGEWIDTH    640
     9 #define  IMAGEHEIGHT   480
    10 
    11 static   int      fd;
    12 static   struct   v4l2_capability   cap;
    13 struct v4l2_fmtdesc fmtdesc;
    14 struct v4l2_format fmt,fmtack;
    15 struct v4l2_streamparm setfps;  
    16 struct v4l2_requestbuffers req;
    17 struct v4l2_buffer buf;
    18 enum v4l2_buf_type type;
    19 unsigned char frame_buffer[IMAGEWIDTH*IMAGEHEIGHT*3];

    其中

    #define FILE_VIDEO     "/dev/video0"

    是要访问的摄像头设备,默人都是/dev/video0

    #define BMP          "/usr/image_bmp.bmp"
    #define YUV          "/usr/image_yuv.yuv"

    是采集后存储的图片,为了方便测试,这里将直接获取的yuv格式数据也保存成文件,可以通过yuvviewer等查看器查看。

    static   int      fd;
    static   struct   v4l2_capability   cap;
    struct v4l2_fmtdesc fmtdesc;
    struct v4l2_format fmt,fmtack;
    struct v4l2_streamparm setfps;  
    struct v4l2_requestbuffers req;
    struct v4l2_buffer buf;
    enum v4l2_buf_type type;

    这些结构体的定义都可以从/usr/include/linux/videodev2.h中找到定义,具体含义在后续编程会做相应解释。

    #define  IMAGEWIDTH    640
    #define  IMAGEHEIGHT   480

    为采集图像的大小。

    定义一个frame_buffer,用来缓存RGB颜色数据

    unsigned char frame_buffer[IMAGEWIDTH*IMAGEHEIGHT*3]

     这些宏和定义结束后,就可以开始编程配置摄像头并采集图像了。一般来说V4L2采集视频数据分为五个步骤:首先,打开视频设备文件,进行视频采集的参数初始化,通过V4L2接口设置视频图像的采集窗口、采集的点阵大小和格式;其次,申请若干视频采集的帧缓冲区,并将这些帧缓冲区从内核空间映射到用户空间,便于应用程序读取/处理视频数据;第三,将申请到的帧缓冲区在视频采集输入队列排队,并启动视频采集;第四,驱动开始视频数据的采集,应用程序从视频采集输出队列取出帧缓冲区,处理完后,将帧缓冲区重新放入视频采集输入队列,循环往复采集连续的视频数据;第五,停止视频采集。在本次设计中,定义了三个函数实现对摄像头的配置和采集。

    int init_v4l2(void);
    int v4l2_grab(void);
    int close_v4l2(void);

    同时由于采集到的图像数据是YUYV格式,需要进行颜色空间转换,定义了转换函数。

    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
    ");
    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.
    ",FILE_VIDEO);
     4     return (FALSE);
     5 }
     6 else
     7 {
     8      printf("driver:		%s
    ",cap.driver);
     9      printf("card:		%s
    ",cap.card);
    10      printf("bus_info:	%s
    ",cap.bus_info);
    11      printf("version:	%d
    ",cap.version);
    12      printf("capabilities:	%x
    ",cap.capabilities);
    13      
    14      if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == V4L2_CAP_VIDEO_CAPTURE) 
    15      {
    16         printf("Device %s: supports capture.
    ",FILE_VIDEO);
    17     }
    18 
    19     if ((cap.capabilities & V4L2_CAP_STREAMING) == V4L2_CAP_STREAMING) 
    20     {
    21         printf("Device %s: supports streaming.
    ",FILE_VIDEO);
    22     }
    23 }

    (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:
    ");
    4 while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc)!=-1)
    5 {
    6     printf("	%d.%s
    ",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,都没有成功。

    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
    fmt.fmt.pix.height = IMAGEHEIGHT;
    fmt.fmt.pix.width = IMAGEWIDTH;
    fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
    
    if(ioctl(fd, VIDIOC_S_FMT, &fmt) == -1)
    {
        printf("Unable to set format
    ");
        return FALSE;
    }


    为了确保设置的格式作用到摄像头上,再通过命令VIDIOC_G_FMT将摄像头设置读取回来。

     1 if(ioctl(fd, VIDIOC_G_FMT, &fmt) == -1)
     2 {
     3     printf("Unable to get format
    ");
     4     return FALSE;
     5 } 
     6 {
     7      printf("fmt.type:		%d
    ",fmt.type);
     8      printf("pix.pixelformat:	%c%c%c%c
    ",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:		%d
    ",fmt.fmt.pix.height);
    10      printf("pix.		%d
    ",fmt.fmt.pix.width);
    11      printf("pix.field:		%d
    ",fmt.fmt.pix.field);
    12 }

    完整的初始化代码如下:

     1 int init_v4l2(void)
     2 {
     3     int i;
     4     int ret = 0;
     5     
     6     //opendev
     7     if ((fd = open(FILE_VIDEO, O_RDWR)) == -1) 
     8     {
     9         printf("Error opening V4L interface
    ");
    10         return (FALSE);
    11     }
    12 
    13     //query cap
    14     if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == -1) 
    15     {
    16         printf("Error opening device %s: unable to query device.
    ",FILE_VIDEO);
    17         return (FALSE);
    18     }
    19     else
    20     {
    21          printf("driver:		%s
    ",cap.driver);
    22          printf("card:		%s
    ",cap.card);
    23          printf("bus_info:	%s
    ",cap.bus_info);
    24          printf("version:	%d
    ",cap.version);
    25          printf("capabilities:	%x
    ",cap.capabilities);
    26          
    27          if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == V4L2_CAP_VIDEO_CAPTURE) 
    28          {
    29             printf("Device %s: supports capture.
    ",FILE_VIDEO);
    30         }
    31 
    32         if ((cap.capabilities & V4L2_CAP_STREAMING) == V4L2_CAP_STREAMING) 
    33         {
    34             printf("Device %s: supports streaming.
    ",FILE_VIDEO);
    35         }
    36     } 
    37     
    38     //emu all support fmt
    39     fmtdesc.index=0;
    40     fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
    41     printf("Support format:
    ");
    42     while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc)!=-1)
    43     {
    44         printf("	%d.%s
    ",fmtdesc.index+1,fmtdesc.description);
    45         fmtdesc.index++;
    46     }
    47     
    48     //set fmt
    49     fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    50     fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
    51     fmt.fmt.pix.height = IMAGEHEIGHT;
    52     fmt.fmt.pix.width = IMAGEWIDTH;
    53     fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
    54     
    55     if(ioctl(fd, VIDIOC_S_FMT, &fmt) == -1)
    56     {
    57         printf("Unable to set format
    ");
    58         return FALSE;
    59     }     
    60     if(ioctl(fd, VIDIOC_G_FMT, &fmt) == -1)
    61     {
    62         printf("Unable to get format
    ");
    63         return FALSE;
    64     } 
    65     {
    66          printf("fmt.type:		%d
    ",fmt.type);
    67          printf("pix.pixelformat:	%c%c%c%c
    ",fmt.fmt.pix.pixelformat & 0xFF, (fmt.fmt.pix.pixelformat >> 8) & 0xFF,(fmt.fmt.pix.pixelformat >> 16) & 0xFF, (fmt.fmt.pix.pixelformat >> 24) & 0xFF);
    68          printf("pix.height:		%d
    ",fmt.fmt.pix.height);
    69          printf("pix.		%d
    ",fmt.fmt.pix.width);
    70          printf("pix.field:		%d
    ",fmt.fmt.pix.field);
    71     }
    72     //set fps
    73     setfps.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    74     setfps.parm.capture.timeperframe.numerator = 10;
    75     setfps.parm.capture.timeperframe.denominator = 10;
    76     
    77     printf("init %s 	[OK]
    ",FILE_VIDEO);
    78         
    79     return TRUE;
    80 }

    2、图像采集

    (1)申请缓存区。使用参数VIDIOC_REQBUFS和结构体v4l2_requestbuffers。v4l2_requestbuffers结构中定义了缓存的数量,系统会据此申请对应数量的视频缓存。

    req.count=4;
    req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
    req.memory=V4L2_MEMORY_MMAP;
    if(ioctl(fd,VIDIOC_REQBUFS,&req)==-1)
    {
        printf("request for buffers error
    ");
    
    }

    (2)获取每个缓存的信息,并mmap到用户空间。定义结构体

    struct buffer
    {
        void * start;
        unsigned int length;
    } * 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
    ");
    10         return(FALSE);
    11     }
    12 
    13     buffers[n_buffers].length = buf.length;
    14     //map
    15     buffers[n_buffers].start = mmap(NULL,buf.length,PROT_READ |PROT_WRITE, MAP_SHARED, fd, buf.m.offset);
    16     if (buffers[n_buffers].start == MAP_FAILED)
    17     {
    18         printf("buffer map error
    ");
    19         return(FALSE);
    20     }
    21 }

    (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 int v4l2_grab(void)
     2 {
     3     unsigned int n_buffers;
     4     
     5     //request for 4 buffers 
     6     req.count=4;
     7     req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
     8     req.memory=V4L2_MEMORY_MMAP;
     9     if(ioctl(fd,VIDIOC_REQBUFS,&req)==-1)
    10     {
    11         printf("request for buffers error
    ");
    12     }
    13 
    14     //mmap for buffers
    15     buffers = malloc(req.count*sizeof (*buffers));
    16     if (!buffers) 
    17     {
    18         printf ("Out of memory
    ");
    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
    ");
    31             return(FALSE);
    32         }
    33 
    34         buffers[n_buffers].length = buf.length;
    35         //map
    36         buffers[n_buffers].start = mmap(NULL,buf.length,PROT_READ |PROT_WRITE, MAP_SHARED, fd, buf.m.offset);
    37         if (buffers[n_buffers].start == MAP_FAILED)
    38         {
    39             printf("buffer map error
    ");
    40             return(FALSE);
    41         }
    42     }
    43         
    44     //queue
    45     for (n_buffers = 0; n_buffers < req.count; n_buffers++)
    46     {
    47         buf.index = n_buffers;
    48         ioctl(fd, VIDIOC_QBUF, &buf);
    49     } 
    50     
    51     type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    52     ioctl (fd, VIDIOC_STREAMON, &type);
    53     
    54     ioctl(fd, VIDIOC_DQBUF, &buf);
    55 
    56     printf("grab yuyv OK
    ");
    57     return(TRUE);
    58 }

    3、YUYV转RGB24

    由于摄像头采集的数据格式为YUYV,为了方便后续设计,需要转变为RGB24,并将转换完成的数据存储到frame_buffer中。值得一提的是,由于定义的时候buffers[index].start是个void *,没有办法进行+1这样的操作,需要强制转换为

    char * pointer
    pointer = buffers[0].start

    由于后续RGB的数据要存储到BMP中,而BMP文件中颜色数据是“倒序”,即从下到上,从左到右,因而在向frame_buffer写数据时是从最后一行最左测开始写,每写满一行行数减一。

     1 int yuyv_2_rgb888(void)
     2 {
     3     int           i,j;
     4     unsigned char y1,y2,u,v;
     5     int r1,g1,b1,r2,g2,b2;
     6     char * pointer;
     7     
     8     pointer = buffers[0].start;
     9     
    10     for(i=0;i<480;i++)
    11     {
    12         for(j=0;j<320;j++)
    13         {
    14             y1 = *( pointer + (i*320+j)*4);
    15             u  = *( pointer + (i*320+j)*4 + 1);
    16             y2 = *( pointer + (i*320+j)*4 + 2);
    17             v  = *( pointer + (i*320+j)*4 + 3);
    18             
    19             r1 = y1 + 1.042*(v-128);
    20             g1 = y1 - 0.34414*(u-128) - 0.71414*(v-128);
    21             b1 = y1 + 1.772*(u-128);
    22             
    23             r2 = y2 + 1.042*(v-128);
    24             g2 = y2 - 0.34414*(u-128) - 0.71414*(v-128);
    25             b2 = y2 + 1.772*(u-128);
    26             
    27             if(r1>255)
    28                 r1 = 255;
    29             else if(r1<0)
    30                 r1 = 0;
    31             
    32             if(b1>255)
    33                 b1 = 255;
    34             else if(b1<0)
    35                 b1 = 0;    
    36             
    37             if(g1>255)
    38                 g1 = 255;
    39             else if(g1<0)
    40                 g1 = 0;    
    41                 
    42             if(r2>255)
    43                 r2 = 255;
    44             else if(r2<0)
    45                 r2 = 0;
    46             
    47             if(b2>255)
    48                 b2 = 255;
    49             else if(b2<0)
    50                 b2 = 0;    
    51             
    52             if(g2>255)
    53                 g2 = 255;
    54             else if(g2<0)
    55                 g2 = 0;        
    56                 
    57             *(frame_buffer + ((480-1-i)*320+j)*6    ) = (unsigned char)b1;
    58             *(frame_buffer + ((480-1-i)*320+j)*6 + 1) = (unsigned char)g1;
    59             *(frame_buffer + ((480-1-i)*320+j)*6 + 2) = (unsigned char)r1;
    60             *(frame_buffer + ((480-1-i)*320+j)*6 + 3) = (unsigned char)b2;
    61             *(frame_buffer + ((480-1-i)*320+j)*6 + 4) = (unsigned char)g2;
    62             *(frame_buffer + ((480-1-i)*320+j)*6 + 5) = (unsigned char)r2;
    63         }
    64     }
    65     printf("change to RGB OK 
    ");
    66 }

    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 
     7 }BITMAPFILEHEADER;
     8 
     9  
    10 typedef struct tagBITMAPINFOHEADER{
    11      DWORD    biSize;                // must be 0x28
    12      DWORD    biWidth;           //
    13      DWORD    biHeight;          //
    14      WORD        biPlanes;          // must be 1
    15      WORD        biBitCount;            //
    16      DWORD    biCompression;         //
    17      DWORD    biSizeImage;       //
    18      DWORD    biXPelsPerMeter;   //
    19      DWORD    biYPelsPerMeter;   //
    20      DWORD    biClrUsed;             //
    21      DWORD    biClrImportant;        //
    22 }BITMAPINFOHEADER;

    完整的主函数

    {
    
        FILE * fp1,* fp2;
    
        BITMAPFILEHEADER   bf;
        BITMAPINFOHEADER   bi;
       
    
        fp1 = fopen(BMP, "wb");
        if(!fp1)
        {
            printf("open "BMP"error
    ");
            return(FALSE);
        }
        
        fp2 = fopen(YUV, "wb");
        if(!fp2)
        {
            printf("open "YUV"error
    ");
            return(FALSE);
        }
    
        if(init_v4l2() == FALSE) 
        {
             return(FALSE);
        }
        
        //Set BITMAPINFOHEADER
        bi.biSize = 40;
        bi.biWidth = IMAGEWIDTH;
        bi.biHeight = IMAGEHEIGHT;
        bi.biPlanes = 1;
        bi.biBitCount = 24;
        bi.biCompression = 0;
        bi.biSizeImage = IMAGEWIDTH*IMAGEHEIGHT*3;
        bi.biXPelsPerMeter = 0;
        bi.biYPelsPerMeter = 0;
        bi.biClrUsed = 0;
        bi.biClrImportant = 0;
     
    
        //Set BITMAPFILEHEADER
        bf.bfType = 0x4d42;
        bf.bfSize = 54 + bi.biSizeImage;     
        bf.bfReserved = 0;
        bf.bfOffBits = 54;
        
        v4l2_grab();
        fwrite(buffers[0].start, 640*480*2, 1, fp2);
        printf("save "YUV"OK
    ");
        
        yuyv_2_rgb888();
        fwrite(&bf, 14, 1, fp1);
        fwrite(&bi, 40, 1, fp1);    
        fwrite(frame_buffer, bi.biSizeImage, 1, fp1);
        printf("save "BMP"OK
    ");
        
        
        fclose(fp1);
        fclose(fp2);
        close_v4l2();
        
        return(TRUE);
    }

    三、ARM板测试

    PC上测试OK后,可以“挪”到ZedBoard上了。使用arm-xilinx-linux交叉编译环境对源文件进行交叉编译,将生成的可执行文件拷贝到ZedBoard上运行即可。

    使用命令

    arm-xilinx-linux-gnueabi-gcc v4l2grab.c -o zed-camera

    对程序进行编译,编译通过后将生成的可执行文件zed-camera拷贝到到ZedBoard上,并将USB摄像头连接到ZedBoard上,通过命令

    ls /dev 

     查看dev目录下的是否有video0设备。如果有,可以运行可执行文件了。在运行前我比较习惯获得可执行文件的权限,使用命令

    chmod +x zed-camera

    参数+x的意思是这个文件对于当前用户是可执行的。也可以使用

    chmod 777 zed-camera

    这样所有用户都有读写执行的权限。使用命令

    ./zed-camera

    执行可执行程序,程序运行,并输出以下信息:

    driver: uvcvideo
    card: UVC Camera (046d:0825)
    bus_info: usb-xusbps-ehci.0-1.3
    version: 197376
    capabilities: 4000001
    Device /dev/video0: supports capture.
    Device /dev/video0: supports streaming.
    Support format:
    1.YUV 4:2:2 (YUYV)
    2.MJPEG
    fmt.type: 1
    pix.pixelformat: YUYV
    pix.height: 480
    pix. 640
    pix.field: 1
    init /dev/video0 [OK]
    grab yuyv OK
    save /usr/image_yuv.yuv OK
    change to RGB OK
    save /usr/image_bmp.bmp OK

    采集到的图片默认是在/usr目录下的,将其拷贝出来
    cp /usr/image* /mnt
     
    完整工程和代码:https://files.cnblogs.com/files/liusiluandzhangkun/lab_v4l2_yuyv.zip
    可以指定任意分辨率摄像头的代码:https://files.cnblogs.com/files/liusiluandzhangkun/v4l2grab_Anysize.rar
  • 相关阅读:
    Daemon——守护进程
    RTMP
    CR LF CR/LF
    SO_REUSEADDR
    [aac @ ...] more samples than frame size (avcodec_encode_audio2)
    前端向后端传数据的方法
    控制层接受参数
    Swagger2
    net.sf.json------json解析
    springboot
  • 原文地址:https://www.cnblogs.com/liusiluandzhangkun/p/8690379.html
Copyright © 2020-2023  润新知