基于ZedBoard的Webcam设计.docx

上传人:b****6 文档编号:3653475 上传时间:2022-11-24 格式:DOCX 页数:22 大小:302.10KB
下载 相关 举报
基于ZedBoard的Webcam设计.docx_第1页
第1页 / 共22页
基于ZedBoard的Webcam设计.docx_第2页
第2页 / 共22页
基于ZedBoard的Webcam设计.docx_第3页
第3页 / 共22页
基于ZedBoard的Webcam设计.docx_第4页
第4页 / 共22页
基于ZedBoard的Webcam设计.docx_第5页
第5页 / 共22页
点击查看更多>>
下载资源
资源描述

基于ZedBoard的Webcam设计.docx

《基于ZedBoard的Webcam设计.docx》由会员分享,可在线阅读,更多相关《基于ZedBoard的Webcam设计.docx(22页珍藏版)》请在冰豆网上搜索。

基于ZedBoard的Webcam设计.docx

基于ZedBoard的Webcam设计

基于ZedBoard的Webcam设计

(一):

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

硬件平台:

DigilentZedBoard+USB摄像头

开发环境:

WindowsXP32bit+Wmare8.0+Ubuntu10.04+arm-linux-xilinx-gnueabi交叉编译环境

Zedboardlinux:

DigilentOOBDesign 

 

一、一些知识

 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。

图1YUYV像素

二、应用程序设计

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

1#defineTRUE1

2#defineFALSE0

3

4#defineFILE_VIDEO"/dev/video0"

5#defineBMP"/usr/image_bmp.bmp"

6#defineYUV"/usr/image_yuv.yuv"

7

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"

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

#defineBMP"/usr/image_bmp.bmp"

#defineYUV"/usr/image_yuv.yuv"

是采集后存储的图片,为了方便测试,这里将直接获取的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:

\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

14if((cap.capabilities&V4L2_CAP_VIDEO_CAPTURE)==V4L2_CAP_VIDEO_CAPTURE)

15{

16printf("Device%s:

supportscapture.\n",FILE_VIDEO);

17}

18

19if((cap.capabilities&V4L2_CAP_STREAMING)==V4L2_CAP_STREAMING)

20{

21printf("Device%s:

supportsstreaming.\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("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{

9printf("Unabletosetformat\n");

10returnFALSE;

11}

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

1if(ioctl(fd,VIDIOC_G_FMT,&fmt)==-1)

2{

3printf("Unabletogetformat\n");

4returnFALSE;

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}

完整的初始化代码如下:

1intinit_v4l2(void)

2{

3inti;

4intret=0;

5

6//opendev

7if((fd=open(FILE_VIDEO,O_RDWR))==-1)

8{

9printf("ErroropeningV4Linterface\n");

10return(FALSE);

11}

12

13//querycap

14if(ioctl(fd,VIDIOC_QUERYCAP,&cap)==-1)

15{

16printf("Erroropeningdevice%s:

unabletoquerydevice.\n",FILE_VIDEO);

17return(FALSE);

18}

19else

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

27if((cap.capabilities&V4L2_CAP_VIDEO_CAPTURE)==V4L2_CAP_VIDEO_CAPTURE)

28{

29printf("Device%s:

supportscapture.\n",FILE_VIDEO);

30}

31

32if((cap.capabilities&V4L2_CAP_STREAMING)==V4L2_CAP_STREAMING)

33{

34printf("Device%s:

supportsstreaming.\n",FILE_VIDEO);

35}

36}

37

38//emuallsupportfmt

39fmtdesc.index=0;

40fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;

41printf("Supportformat:

\n");

42while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc)!

=-1)

43{

44printf("\t%d.%s\n",fmtdesc.index+1,fmtdesc.description);

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,&fmt)==-1)

56{

57printf("Unabletosetformat\n");

58returnFALSE;

59}

60if(ioctl(fd,VIDIOC_G_FMT,&fmt)==-1)

61{

62printf("Unabletogetformat\n");

63returnFALSE;

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//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",FILE_VIDEO);

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)

5{

6printf("requestforbufferserror\n");

7

8}

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

定义结构体

structbuffer

{

void*start;

unsignedintlength;

}*buffers;

来存储mmap后的地址信息。

需要说明的是由于mmap函数定义时返回的地址是个void*,因而这里面的start也是个void*。

实际地址在运行的时候会自动分配。

1for(n_buffers=0;n_buffers

2{

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)

8{

9printf("querybuffererror\n");

10return(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);

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)

2{

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,&req)==-1)

10{

11printf("requestforbufferserror\n");

12}

13

14//mmapforbuffers

15buffers=malloc(req.count*sizeof(*buffers));

16if(!

buffers)

17{

18printf("Outofmemory\n");

19return(FALSE);

20}

21

22for(n_buffers=0;n_buffers

23{

24buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;

25buf.memory=V4L2_MEMORY_MMAP;

26buf.index=n_buffers;

27//querybuffers

28if(ioctl(fd,VIDIOC_QUERYBUF,&buf)==-1)

29{

30printf("querybuffererror\n");

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("buffermaperror\n");

40return(FALSE);

41}

42}

43

44//queue

45for(n_buffers=0;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("grabyuyvOK\n");

57return(TRUE);

58}

3、YUYV转RGB24

由于摄像头采集的数据格式为YUYV,为了方便后续设计,需要转变为RGB24,并将转换完成的数据存储到frame_buffer中。

值得一提的

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高中教育 > 语文

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1