超群天晴

【原创】基于ZedBoard的Webcam设计(一):USB摄像头(V4L2接口)的图片采集

0
阅读(4271)

一直想把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/

相关阅读

(原创)基于ZedBoard的Webcam设计(一):USB摄像头(V4L2接口)的图片采集

(原创)基于ZedBoard的Webcam设计(二):USB摄像头图片采集+QT显示

(原创)基于ZedBoard的Webcam设计(三):视频的采集和动态显示

(原创)基于ZedBoard的Webcam设计(四):MJPG编码和AVI封装

(原创)基于ZedBoard的Webcam设计(五):x264编码在zedboard上的实现(软编码)

硬件平台: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。

图1 YUYV像素

二、应用程序设计

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

1 #defineTRUE 1
2 #defineFALSE 0 3
4 #defineFILE_VIDEO "/dev/video0"
5 #defineBMP "/usr/image_bmp.bmp"
6 #defineYUV "/usr/image_yuv.yuv"
7
8 #defineIMAGEWIDTH 640
9 #defineIMAGEHEIGHT 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 charframe_buffer[IMAGEWIDTH*IMAGEHEIGHT* 3];

其中


#defineFILE_VIDEO "/dev/video0"

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


#defineBMP "/usr/image_bmp.bmp"#defineYUV "/usr/image_yuv.yuv"

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

复制代码
static intfd;
static structv4l2_capability cap;
structv4l2_fmtdesc fmtdesc;
structv4l2_format fmt,fmtack;
structv4l2_streamparm setfps; structv4l2_requestbuffers req;
structv4l2_buffer buf;
enumv4l2_buf_type type;

复制代码

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


#defineIMAGEWIDTH 640#defineIMAGEHEIGHT 480

为采集图像的大小。

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


unsigned charframe_buffer[IMAGEWIDTH*IMAGEHEIGHT*3]

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


intinit_v4l2(void);intv4l2_grab(void);intclose_v4l2(void);

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


intyuyv_2_rgb888(void);

下面就详细介绍这几个函数的实现。

1、初始化V4l2

(1)打开视频。linux对摄像头的访问和普通设备一样,使用open函数就可以,返回值是设备的id。


1if((fd = open(FILE_VIDEO, O_RDWR)) == -1)2{3printf("Error opening V4L interface\n");4return(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 {
3printf( " Error opening device %s: unable to query device.\n " ,FILE_VIDEO);
4 return (FALSE);
5 }
6 else
7 {
8printf( " driver:\t\t%s\n " ,cap.driver);
9printf( " card:\t\t%s\n " ,cap.card);
10printf( " bus_info:\t%s\n " ,cap.bus_info);
11printf( " version:\t%d\n " ,cap.version);
12printf( " capabilities:\t%x\n " ,cap.capabilities);
13
14 if((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == V4L2_CAP_VIDEO_CAPTURE)
15 {
16printf( " Device %s: supports capture.\n " ,FILE_VIDEO);
17 }
18
19 if((cap.capabilities & V4L2_CAP_STREAMING) == V4L2_CAP_STREAMING)
20 {
21printf( " Device %s: supports streaming.\n " ,FILE_VIDEO);
22 }
23}

复制代码

(3)列举摄像头所支持像素格式。使用命令VIDIOC_ENUM_FMT,获取到的信息通过结构体v4l2_fmtdesc查询。这步很关键,不同的摄像头可能支持的格式不一样,V4L2可以支持的格式很多,/usr/include/linux/videodev2.h文件中可以看到。

复制代码
1fmtdesc.index= 0 ;
2fmtdesc.type= V4L2_BUF_TYPE_VIDEO_CAPTURE;
3printf( " Support format:\n " );
4 while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc)!=- 1 )
5 {
6printf( " \t%d.%s\n ",fmtdesc.index+ 1 ,fmtdesc.description);
7fmtdesc.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;
2fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
3fmt.fmt.pix.height = IMAGEHEIGHT;
4fmt.fmt.pix.width = IMAGEWIDTH;
5fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
6
7 if(ioctl(fd, VIDIOC_S_FMT, &fmt) == - 1 )
8 {
9printf( " Unable to set format\n " );
10 return FALSE;
11}

复制代码

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

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

复制代码

完整的初始化代码如下:


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

2、图像采集

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

复制代码
1req.count= 4 ;
2req.type= V4L2_BUF_TYPE_VIDEO_CAPTURE;
3req.memory= V4L2_MEMORY_MMAP;
4 if(ioctl(fd,VIDIOC_REQBUFS,&req)==- 1 )
5 {
6printf( " request for buffers error\n " );
7
8}

复制代码

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


structbuffer {void*start; unsignedintlength; }* buffers;

来存储mmap后的地址信息。需要说明的是由于mmap函数定义时返回的地址是个void *,因而这里面的start也是个 void *。实际地址在运行的时候会自动分配。

复制代码
1 for(n_buffers = 0; n_buffers < req.count; n_buffers++ )
2 {
3buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
4buf.memory = V4L2_MEMORY_MMAP;
5buf.index = n_buffers;
6 // query buffers
7 if(ioctl (fd, VIDIOC_QUERYBUF, &buf) == - 1 )
8 {
9printf( " query buffer error\n " );
10 return (FALSE);
11 }
12
13buffers[n_buffers].length = buf.length;
14 // map
15buffers[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 {
18printf( " buffer map error\n " );
19 return (FALSE);
20 }
21}

复制代码

(3) 之后就可以开始采集视频了。使用命令VIDIOC_STREAMON。


1type =V4L2_BUF_TYPE_VIDEO_CAPTURE;2ioctl (fd, VIDIOC_STREAMON, &type);

(4)取出缓存中已经采样的缓存。使用命令VIDIOC_DQBUF。视频数据存放的位置是buffers[n_buffers].start的地址处。


1ioctl(fd, VIDIOC_DQBUF, &buf);

完整的采集代码:


1 intv4l2_grab( void )
2 {
3unsigned int n_buffers;
4
5 // request for 4 buffers
6req.count= 4 ;
7req.type= V4L2_BUF_TYPE_VIDEO_CAPTURE;
8req.memory= V4L2_MEMORY_MMAP;
9 if(ioctl(fd,VIDIOC_REQBUFS,&req)==- 1 )
10 {
11printf( " request for buffers error\n " );
12 }
13
14 // mmap for buffers
15buffers = malloc(req.count* sizeof(* buffers));
16 if(! buffers)
17 {
18printf ( " Out of memory\n " );
19 return (FALSE);
20 }
21
22 for(n_buffers = 0; n_buffers < req.count; n_buffers++ )
23 {
24buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
25buf.memory = V4L2_MEMORY_MMAP;
26buf.index = n_buffers;
27 // query buffers
28 if(ioctl (fd, VIDIOC_QUERYBUF, &buf) == - 1 )
29 {
30printf( " query buffer error\n " );
31 return (FALSE);
32 }
33
34buffers[n_buffers].length = buf.length;
35 // map
36buffers[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 {
39printf( " buffer map error\n " );
40 return (FALSE);
41 }
42 }
43
44 // queue
45 for(n_buffers = 0; n_buffers < req.count; n_buffers++ )
46 {
47buf.index = n_buffers;
48ioctl(fd, VIDIOC_QBUF, & buf);
49 }
50
51type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
52ioctl (fd, VIDIOC_STREAMON, & type);
53
54ioctl(fd, VIDIOC_DQBUF, & buf);
55
56printf( " grab yuyv OK\n " );
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 intyuyv_2_rgb888( void )
2 {
3 int i,j; 4unsigned char y1,y2,u,v;
5 int r1,g1,b1,r2,g2,b2;
6 char* pointer;
7
8pointer = buffers[ 0 ].start;
9
10 for(i= 0;i< 480;i++ )
11 {
12 for(j= 0;j< 320;j++ )
13 {
14y1 = *( pointer + (i* 320+j)* 4 );
15u = *( pointer + (i* 320+j)* 4+ 1 );
16y2 = *( pointer + (i* 320+j)* 4+ 2 );
17v = *( pointer + (i* 320+j)* 4+ 3 );
18
19r1 = y1 + 1.042*(v- 128 );
20g1 = y1 - 0.34414*(u- 128) - 0.71414*(v- 128 );
21b1 = y1 + 1.772*(u- 128 );
22
23r2 = y2 + 1.042*(v- 128 );
24g2 = y2 - 0.34414*(u- 128) - 0.71414*(v- 128 );
25b2 = y2 + 1.772*(u- 128 );
26
27 if(r1> 255 )
28r1 = 255 ;
29 else if(r1< 0 )
30r1 = 0 ;
31
32 if(b1> 255 )
33b1 = 255 ;
34 else if(b1< 0 )
35b1 = 0 ;
36
37 if(g1> 255 )
38g1 = 255 ;
39 else if(g1< 0 )
40g1 = 0 ;
41
42 if(r2> 255 )
43r2 = 255 ;
44 else if(r2< 0 )
45r2 = 0 ;
46
47 if(b2> 255 )
48b2 = 255 ;
49 else if(b2< 0 )
50b2 = 0 ;
51
52 if(g2> 255 )
53g2 = 255 ;
54 else if(g2< 0 )
55g2 = 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 }
65printf( " change to RGB OK \n " );
66}

4、停止采集和关闭设备

使用命令VIDIOC_STREAMOFF停止视频采集,并关闭设备。

复制代码
1 intclose_v4l2( void )
2 {
3ioctl(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{
2WORD bfType; // the flag of bmp, value is "BM"
3DWORD bfSize; // size BMP file ,unit is bytes
4DWORD bfReserved; // 0
5DWORD bfOffBits; // must be 54
6
7 }BITMAPFILEHEADER;
8
9
10typedef struct tagBITMAPINFOHEADER{
11DWORD biSize; // must be 0x28
12DWORD biWidth; //
13DWORD biHeight; //
14WORD biPlanes; // must be 1
15WORD biBitCount; //
16DWORD biCompression; //
17DWORD biSizeImage; //
18DWORD biXPelsPerMeter; //
19DWORD biYPelsPerMeter; //
20DWORD biClrUsed; //
21DWORD biClrImportant; //
22}BITMAPINFOHEADER;

复制代码

完整的主函数

复制代码
//@超群天晴
//http://www.cnblogs.com/surpassal/
int
main( void )
{

FILE
* fp1,* fp2;

BITMAPFILEHEADER bf;
BITMAPINFOHEADER bi;


fp1
= fopen(BMP, " wb " );
if(! fp1)
{
printf(
" open "BMP " error\n " );
return (FALSE);
}

fp2
= fopen(YUV, " wb " );
if(! fp2)
{
printf(
" open "YUV " error\n " );
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\n " );

yuyv_2_rgb888();
fwrite(
&bf, 14, 1 , fp1);
fwrite(
&bi, 40, 1 , fp1);
fwrite(frame_buffer, bi.biSizeImage,
1 , fp1);
printf(
" save "BMP " OK\n " );


fclose(fp1);
fclose(fp2);
close_v4l2();

return (TRUE);
}

复制代码

三、PC测试

程序编写完后,可以先在PC上做测试(实际整个调试过程都是在PC上,直道最后PC上能实现功能再挪到ZedBoard上的)。PC上测试的结果

在/usr目录下可以查看到采集到的图片

四、Zedboard测试

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

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

复制代码
zynq> ./zed-camera
[ 318.290000] usb 1-1.3: reset high-speed USB device number 3 using xusbps-ehci

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.width: 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

复制代码

可以看到我使用的USB摄像支持YUYV和MJPEG两种格式。我也试过其他USB摄像头,大部分都只支持YUYV而不支持MJPEG或者RGB24。

采集到的图片默认是在/usr目录下的,将其拷贝出来


cp /usr/image* /mnt

再PC上查看,效果还不错

=============================

完整工程和代码:lab_v4l2_yuyv.zip

Baidu
map