作者:曾永刚

转自:http://zyg0227.blog.51cto.com/1043164/271994


4步获得采集到的图像数据

这一步是使用v4l比较重要的一步,涉及到几个函数的编写。当然使用v4l就是为了要获得图像,所以这一步很关键,但是当你获得了图像数据后,还需要根据你想要达到的目的和具体情况做进一步的处理,也就是第5步所做的事情,这些内容将在后面第三部分提到。这里讲如何获得采集到的数据。 如前所述获得图像的方式有两种,分别是直接读取设备和使用mmap内存映射,而通常大家使用的方法都是后者。 1.直接读取设备 直接读设备的方式就是使用read()函数,我们先前定义的 externintv4l_grab_picture(v4l_device*,unsignedint);函数就是完成这个工作的,它的实现也很简单。 intv4l_grab_picture(v4l_device*vd,unsighedintsize) { if(read(vd-fd,&(vd->map),size)==0)return-1; return0 } 该函数的使用也很简单,就是给出图像数据的大小,vd->map所指向的数据就是图像数据。而图像数据的大小你要根据设备的属性自己计算获得。 2.使用mmap内存映射来获取图像 在这部分涉及到下面几个函数,它们配合来完成最终图像采集的功能。 externintv4l_mmap_init(v4l_device*);该函数把摄像头图像数据映射到进程内存中,也就是只要使用vd->map指针就可以使用采集到的图像数据(下文详细说明) externintv4l_grab_init(v4l_device*,int,int);该函数完成图像采集前的初始化工作。 externintv4l_grab_frame(v4l_device*,int);该函数是真正完成图像采集的一步,在本文使用了一个通常都会使用的一个小技巧,可以在处理一帧数据时同时采集下一帧的数据,因为通常我们使用的摄像头都可以至少存储两帧的数据。 externintv4l_grab_sync(v4l_device*);该函数用来完成截取图像的同步工作,在截取一帧图像后调用,返回表明一帧截取结束。 下面分别介绍这几个函数。 mmap()系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以像访问普通内存一样对文件进行访问,不必在调用read()write()等操作。两个不同进程AB共享内存的意思是,同一块物理内存被映射到进程AB各自的进程地址空间。进程A可以即时访问进程B对共享内存中数据的更新,反之亦然。 采用共享内存通信的一个显而易见的好处是减少I/O操作提高读取效率,因为使用mmap后进程可以直接读取内存而不需要任何数据的拷贝。 mmap的函数原型如下 void*mmap(void*addr,size_tlen,intprot,intflags,intfd,off_toffset) addr:共享内存的起始地址,一般设为0,表示由系统分配。 len:指定映射内存的大小。在我们这里,该值为摄像头mbuf结构体的size值,即图像数据的总大小。 port:指定共享内存的访问权限PROT_READ(可读)PROT_WRITE(可写) flags:一般设置为MAP_SHARED fd:同享文件的文件描述符。 介绍完了mmap的使用,就可以介绍上文中定义的函数externintv4l_mmap_init(v4l_device*);了。先给出这个函数的代码,再做说明。 intv4l_mmap_init(v4l_device*vd) { if(v4l_get_mbuf(vd)<0) return-1; if((vd->map=mmap(0,vd->mbuf.size,PROT_READ|PROT_WRITE,MAP_SHARED,vd->fd,0))<0){ perror("v4l_mmap_init:mmap"); return-1; } return0; } 这个函数首先使用v4l_get_mbuf(vd)获得一个摄像头重要的参数,就是需要映射内存的大小,即vd->mbuf.size,然后调用mmap,当我们在编程是调用v4l_mmap_init后,vd.map指针所指向的内存空间即为我们将要采集的图像数据。 获得图像前的初始化工作v4l_grab_init();该函数十分简单直接粘上去,其中将。vd->frame_using[0]vd->frame_using[1]都设为FALSE,表示两帧的截取都没有开始。 intv4l_grab_init(v4l_device*vd,intwidth,intheight) { vd->mmap.width=width; vd->mmap.height=height; vd->mmap.format=vd->picture.palette; vd->frame_current=0; vd->frame_using[0]=FALSE; vd->frame_using[1]=FALSE; returnv4l_grab_frame(vd,0); } 真正获得图像的函数externintv4l_grab_frame(v4l_device*,int); intv4l_grab_frame(v4l_device*vd,intframe) { if(vd->frame_using[frame]){ fprintf(stderr,"v4l_grab_frame:frame%disalreadyused.\n",frame); return-1; } vd->mmap.frame=frame; if(ioctl(vd->fd,VIDIOCMCAPTURE,&(vd->mmap))<0){ perror("v4l_grab_frame"); return-1; } vd->frame_using[frame]=TRUE; vd->frame_current=frame; return0; } 读到这里,应该觉得这个函数也是相当的简单。最关键的一步即为调用ioctl(vd->fd,VIDIOCMCAPTURE,&(vd->mmap)),调用后相应的图像就已经获取完毕。其他的代码是为了完成双缓冲就是截取两帧图像用的,可以自己理解下。 在截取图像后还要进行同步操作,就是调用externintv4l_grab_sync(v4l_device*);函数,该函数如下 intv4l_grab_sync(v4l_device*vd) { if(ioctl(vd->fd,VIDIOCSYNC,&(vd->frame_current))<0){ perror("v4l_grab_sync"); } vd->frame_using[vd->frame_current]=FALSE; return0; } 该函数返回0说明你想要获取的图像帧已经获取完毕。 图像存在了哪里? 最终我们使用v4l的目的是为了获取设备中的图像,那么图像存在哪里?从上面的文章可以知道,vd.map指针所指就是你要获得的第一帧图像。图像的位置,存在vd.map+vd.mbuf.offsets[vd.frame_current]处。其中vd.frame_current=0,即为第一帧的位置,vd.frame_current=1,为第二帧的位置。 2上述v4l库使用的方法 给出了上述的一些代码,这里用一些简单的代码表明如何来使用它。上文中已经说过将相关结构体和函数的定义放到一个名为v4l.h的文件中,相关函数的编写放在一个名为v4l.c的文件。 现在我们要使用它们。 使用的方法很简单,你创建一个.c文件,假设叫test.c吧,那么test.c如下 //test.c includev4l.h ... v4l_devicevd; voidmain() { v4l_open(DEFAULT_DEVICE,&vd); v4l_mmap_init(&vd); v4l_grab_init(&vd,320,240); v4l_grab_sync(&vd);//此时就已经获得了一帧的图像,存在vd.map while(1) { vd.frame_current^=1; v4l_grab_frame(&vd,vd.frame_current); v4l_grab_sync(&vd); 图像处理函数(vd.map+vd.vd.map+vd.mbuf.offsets[vd.frame_current]); //循环采集,调用你设计的图像处理函数来处理图像 //其中vd.map+vd.vd.map+vd.mbuf.offsets[vd.frame_current]就是图像所在位置。 } } 3有关获取的图像的一些问题 问:我获取到的图像究竟长什么样? 答:每个摄像头获取的图像数据的格式可能都不尽相同,可以通过picture.palette获得。获得的图像有黑白的,有yuv格式的,RGB格式的,也有直接为jpeg格式的。你要根据实际情况,和你的需要对图像进行处理。比如常见的,如果你要在嵌入式的LCD上显示假设LCDRGB24的,但是你获得图像是YUV格式的那么你就将他转换为RGB24的。具体的转换方法可以上网查找,也可参考前面提到过的effectTV中的相关代码。 问:如何显示图像或将图像保存? 答:假设你采集到的图像为RGB24格式的,我接触过的可以使用SDL库显示(网络上很流行的叫spcaview的软件就是这样的,不过它将图像数据压缩为jpeg的格式后显示,这个软件也被经常的移植到一些嵌入式平台使用,如ARM的)。当然也可以使用嵌入式linuxFramebuffer直接写屏显示。将图像保存可以用libjpeg将其保存为jpeg图片直接存储,相关的使用方法可以上网查找。也可以使用一些视频编码,将其编码保存(我希望学习一下相关的技术因为我对这方面一点不懂,如果你有一些资料可以推荐给我看,我十分想看一看)。 一边写文章一边才发现自己很菜,因为很多都是参考别人的文章,而自己想写出来去一落键盘就写不出什么。就写这么多,因为我只会这么多。高手见笑,新手和我一样我们互相讨论

更多相关文章

  1. 使用带有派生列的SQL排名函数
  2. 数据库截取字符串SUBSTR函数的使用
  3. SQL Server CLR函数类型不匹配。
  4. mysql中MAX()函数MIN()函数
  5. MySQL 中的函数(一:数学函数)
  6. SQL Server 2008使用sproc中的函数
  7. MYSQL存储过程,函数,光标
  8. 如何将图像加载到PictureBox;基于存储在DataBase中的图像位置
  9. 请问mysql中有没有类似math.max(1,2)这种可以比较两个值中最大值

随机推荐

  1. 解析错误:解析软件包时出现问题
  2. Android 动画框架代码分析
  3. This android SDk requires Android deve
  4. 浅谈Android下拉菜单Spinner
  5. Error:(15) No resource identifier foun
  6. android执行外部程序,类似DELPHI里的EXEC
  7. android 读取,写入图片到sd卡源码
  8. Android(安卓)自动更新之状态栏下载
  9. android -------- Android(安卓)Studio 4
  10. Android实现对HOME键的捕获和屏蔽