基于ZedBoard的Webcam设计Word格式.docx
《基于ZedBoard的Webcam设计Word格式.docx》由会员分享,可在线阅读,更多相关《基于ZedBoard的Webcam设计Word格式.docx(22页珍藏版)》请在冰豆网上搜索。
8#defineIMAGEWIDTH640
9#defineIMAGEHEIGHT480
10
11staticintfd;
12staticstructv4l2_capabilitycap;
13structv4l2_fmtdescfmtdesc;
14structv4l2_formatfmt,fmtack;
15structv4l2_streamparmsetfps;
16structv4l2_requestbuffersreq;
17structv4l2_bufferbuf;
18enumv4l2_buf_typetype;
19unsignedcharframe_buffer[IMAGEWIDTH*IMAGEHEIGHT*3];
其中
#defineFILE_VIDEO"
是要访问的摄像头设备,默人都是/dev/video0
#defineBMP"
#defineYUV"
是采集后存储的图片,为了方便测试,这里将直接获取的yuv格式数据也保存成文件,可以通过yuvviewer等查看器查看。
staticintfd;
staticstructv4l2_capabilitycap;
structv4l2_fmtdescfmtdesc;
structv4l2_formatfmt,fmtack;
structv4l2_streamparmsetfps;
structv4l2_requestbuffersreq;
structv4l2_bufferbuf;
enumv4l2_buf_typetype;
这些结构体的定义都可以从/usr/include/linux/videodev2.h中找到定义,具体含义在后续编程会做相应解释。
#defineIMAGEWIDTH640
#defineIMAGEHEIGHT480
为采集图像的大小。
定义一个frame_buffer,用来缓存RGB颜色数据
unsignedcharframe_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("
ErroropeningV4Linterface\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。
1if(ioctl(fd,VIDIOC_QUERYCAP,&
cap)==-1)
2{
3printf("
Erroropeningdevice%s:
unabletoquerydevice.\n"
FILE_VIDEO);
4return(FALSE);
5}
6else
7{
8printf("
driver:
\t\t%s\n"
cap.driver);
9printf("
card:
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
14if((cap.capabilities&
V4L2_CAP_VIDEO_CAPTURE)==V4L2_CAP_VIDEO_CAPTURE)
15{
16printf("
Device%s:
supportscapture.\n"
17}
18
19if((cap.capabilities&
V4L2_CAP_STREAMING)==V4L2_CAP_STREAMING)
20{
21printf("
supportsstreaming.\n"
22}
23}
(3)列举摄像头所支持像素格式。
使用命令VIDIOC_ENUM_FMT,获取到的信息通过结构体v4l2_fmtdesc查询。
这步很关键,不同的摄像头可能支持的格式不一样,V4L2可以支持的格式很多,/usr/include/linux/videodev2.h文件中可以看到。
1fmtdesc.index=0;
2fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
Supportformat:
\n"
4while(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,都没有成功。
1fmt.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
7if(ioctl(fd,VIDIOC_S_FMT,&
fmt)==-1)
8{
Unabletosetformat\n"
10returnFALSE;
11}
为了确保设置的格式作用到摄像头上,再通过命令VIDIOC_G_FMT将摄像头设置读取回来。
1if(ioctl(fd,VIDIOC_G_FMT,&
Unabletogetformat\n"
4returnFALSE;
6{
7printf("
fmt.type:
\t\t%d\n"
fmt.type);
pix.pixelformat:
\t%c%c%c%c\n"
fmt.fmt.pix.pixelformat&
0xFF,(fmt.fmt.pix.pixelformat>
>
8)&
0xFF,(fmt.fmt.pix.pixelformat>
16)&
24)&
0xFF);
pix.height:
fmt.fmt.pix.height);
pix.width:
fmt.fmt.pix.width);
pix.field:
fmt.fmt.pix.field);
12}
完整的初始化代码如下:
1intinit_v4l2(void)
3inti;
4intret=0;
5
6//opendev
7if((fd=open(FILE_VIDEO,O_RDWR))==-1)
8{
10return(FALSE);
12
13//querycap
14if(ioctl(fd,VIDIOC_QUERYCAP,&
17return(FALSE);
18}
19else
22printf("
23printf("
24printf("
25printf("
26
27if((cap.capabilities&
28{
29printf("
30}
31
32if((cap.capabilities&
33{
34printf("
35}
36}
37
38//emuallsupportfmt
39fmtdesc.index=0;
40fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
41printf("
42while(ioctl(fd,VIDIOC_ENUM_FMT,&
43{
44printf("
45fmtdesc.index++;
46}
47
48//setfmt
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
55if(ioctl(fd,VIDIOC_S_FMT,&
56{
57printf("
58returnFALSE;
59}
60if(ioctl(fd,VIDIOC_G_FMT,&
61{
62printf("
63returnFALSE;
64}
65{
66printf("
67printf("
68printf("
69printf("
70printf("
71}
72//setfps
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"
78
79returnTRUE;
80}
2、图像采集
(1)申请缓存区。
使用参数VIDIOC_REQBUFS和结构体v4l2_requestbuffers。
v4l2_requestbuffers结构中定义了缓存的数量,系统会据此申请对应数量的视频缓存。
1req.count=4;
2req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
3req.memory=V4L2_MEMORY_MMAP;
4if(ioctl(fd,VIDIOC_REQBUFS,&
req)==-1)
requestforbufferserror\n"
7
(2)获取每个缓存的信息,并mmap到用户空间。
定义结构体
structbuffer
{
void*start;
unsignedintlength;
}*buffers;
来存储mmap后的地址信息。
需要说明的是由于mmap函数定义时返回的地址是个void*,因而这里面的start也是个void*。
实际地址在运行的时候会自动分配。
1for(n_buffers=0;
n_buffers<
req.count;
n_buffers++)
3buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
4buf.memory=V4L2_MEMORY_MMAP;
5buf.index=n_buffers;
6//querybuffers
7if(ioctl(fd,VIDIOC_QUERYBUF,&
buf)==-1)
querybuffererror\n"
10return(FALSE);
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);
16if(buffers[n_buffers].start==MAP_FAILED)
17{
18printf("
buffermaperror\n"
19return(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);
完整的采集代码:
1intv4l2_grab(void)
3unsignedintn_buffers;
4
5//requestfor4buffers
6req.count=4;
7req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
8req.memory=V4L2_MEMORY_MMAP;
9if(ioctl(fd,VIDIOC_REQBUFS,&
10{
14//mmapforbuffers
15buffers=malloc(req.count*sizeof(*buffers));
16if(!
buffers)
18printf("
Outofmemory\n"
21
22for(n_buffers=0;
23{
24buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
25buf.memory=V4L2_MEMORY_MMAP;
26buf.index=n_buffers;
27//querybuffers
28if(ioctl(fd,VIDIOC_QUERYBUF,&
29{
30printf("
31return(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);
37if(buffers[n_buffers].start==MAP_FAILED)
38{
39printf("
40return(FALSE);
41}
42}
43
44//queue
45for(n_buffers=0;
n_buffers++)
46{
47buf.index=n_buffers;
48ioctl(fd,VIDIOC_QBUF,&
49}
50
51type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
52ioctl(fd,VIDIOC_STREAMON,&
53
54ioctl(fd,VIDIOC_DQBUF,&
55
56printf("
grabyuyvOK\n"
57return(TRUE);
58}
3、YUYV转RGB24
由于摄像头采集的数据格式为YUYV,为了方便后续设计,需要转变为RGB24,并将转换完成的数据存储到frame_buffer中。
值得一提的