我的实验环境为:webeye v2000的usb摄像头,redhat的linux系统RHEL5(系统中已经集成了ov511的驱动程序,当系统检测到摄像头时,会在/dev下有一个vedio0的设备名)。
经过充分的考虑,整个项目我分为四个步骤来完成,第一步:先通过只有ov511芯片的摄像头来抓取一张图片,并且可以保存为bmp格式;第二步:利用背景减除法来实现动态图像的检测,实现每隔10s保存一张背景图片,对超出一定阈值的像素的图片进行保存;第三步:优化程序,将图片检测算法进行优化,同时优化缩短图片采集的时间,并且对保存的图片进行压缩,基本的将采用jpeg格式;第四步:将我的整个项目移植到arm9系列的mini2440开发板上。
现阶段, 第一步已经基本上完成了,由于我使用的时webeye v2000的摄像头,只能够抓取YUV420P格式的图像流,这个就已经花了我很多的时间来查资料进行格式的转换,结果搞了半个月,图片是出来了,但是保存的640*480的bmp格式的图片有将近900k那么大。第二、第三步和第四步还在进行中,如果有同道中人,或者已经有了相关经验的好友,能够留下只言片语,小弟在此感激不尽。
下面是小弟编写的代码,小弟不才,代码比较乱(代码总共分为3个文件,main.c v4l.h v4l.c):v4l.h:该头文件主要是包含了一些BMP格式图片的头文件信息和图片文件信息以及一些V4L2的编程接口函数等。
#ifndef _V4L_H_ #define _V4L_H_ #include <stdio.h> #include <sys/types.h> #include <linux/videodev.h> #include <unistd.h> #define NTSC_WIDTH 640 //设置获取图像的大小 #define NTSC_HEIGHT 480 #define DEFAULT_PALETTE VIDEO_PALETTE_YUV420P struct _v4l_device { int fd;//设备号 struct video_capability capability;//摄像头属性 struct video_picture picture;//图像的属性,亮度、色度、对比度、灰度、编码属性 struct video_window window;//包含capture area 的信息 struct video_channel channel[8];//采集的通道 struct video_mbuf mbuf;//利用mmap映射得侦信息 struct video_capture capture; struct video_buffer buffer; struct video_mmap mmap; unsigned char *map; int frame; int framestat[2]; }; /*******************add by linhui in 2011 8.1**********************/ #define WIDTHBYTES(i) ((i+31)/32*4) #define FREE(x) if((x)){free((x));(x)=NULL;} typedef unsigned char BYTE; typedef unsigned short WORD; typedef unsigned long DWORD; typedef struct tagBITMAPFILEHEADER{ WORD bfType; // the flag of bmp, value is "BM" DWORD bfSize; // size BMP file ,unit is bytes DWORD bfReserved1; // 0 DWORD bfReserved2; // 0 DWORD bfOffBits; // must be 54 }BITMAPFILEHEADER; //这个结构中占据14字节 typedef struct tagBITMAPINFOHEADER{ DWORD biSize; // must be 0x28 DWORD biWidth; // DWORD biHeight; // WORD biPlanes; // must be 1 WORD biBitCount; // DWORD biCompression; // DWORD biSizeImage; // DWORD biXPelsPerMeter; // DWORD biYPelsPerMeter; // DWORD biClrUsed; // DWORD biClrImportant; // }BITMAPINFOHEADER; //这个结构占据40个字节 typedef struct tagRGBQUAD{ BYTE rgbBlue; BYTE rgbGreen; BYTE rgbRed; BYTE rgbReserved; }RGBQUAD; /****************************************************************/ typedef struct _v4l_device v4ldevice; extern int v4l_open(char *,v4ldevice *); extern int v4l_set_norm(v4ldevice *, int); extern int v4l_get_capability(v4ldevice *); extern int v4l_get_window(v4ldevice *); extern int v4l_set_window(v4ldevice *); extern int v4l_get_picture(v4ldevice *); extern int v4l_mmap_init(v4ldevice *); extern int v4l_grab_init(v4ldevice *,int ,int); extern int v4l_grab_start(v4ldevice *,int ); extern int v4l_grab_sync(v4ldevice *,int); extern int v4l_get_capture(v4ldevice *); extern int v4l_set_capture(v4ldevice *); extern unsigned char *v4l_get_address(v4ldevice *); extern int v4l_close(v4ldevice *); extern void set(v4ldevice *); //Conversion from YUV420 to RGB24 void InitConvertTable(); void ConvertYUV2RGB(unsigned char *src0,unsigned char *src1,unsigned char *src2,unsigned char *dst_ori, int width,int height); #endif
下面是V4l的实现函数v4l.c,在我整个项目的开始,都被如何将YUV420P格式的图像信息转为RGB24的格式所困扰,经过几番努力后,采用比较经典的YUV420P转RGB的两个函数实现。至于个中的算法原理,小弟还没有完全吃透,那个高手可以指点一二,小弟万分感激。
#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <sys/mman.h> #include <sys/ioctl.h> #include <error.h> #include <fcntl.h> #include <sys/types.h> #include <linux/videodev.h> #include "v4l.h" #define DEFAULT_DEVICE "/dev/video0" int v4l_open(char *dev,v4ldevice *vd) { if (!dev) dev = DEFAULT_DEVICE; if((vd->fd=open(dev,O_RDWR,10705))<0) { return -1; } if(v4l_get_capability(vd)<0) return -1; if(v4l_init_window(vd)<0) return -1; if(v4l_get_picture(vd)<0) return -1; return 0; } int v4l_get_capability(v4ldevice *vd) { if(ioctl(vd->fd,VIDIOCGCAP,&(vd->capability))<0) { perror("v4l_get_capability:"); return -1; } /* printf("devicename ----->%s\n",vd->capability.name); printf("devicetype ----->%d\n",vd->capability.type); printf("channels ------->%d\n",vd->capability.channels); printf("maxwidth ------->%d\n",vd->capability.maxwidth); printf("maxheith ------->%d\n",vd->capability.maxheight); printf("minwidth ------->%d\n",vd->capability.minwidth); printf("minheith ------->%d\n\n",vd->capability.minheight);*/ return 0; } int v4l_init_window(v4ldevice *vd) { if( ioctl(vd->fd,VIDIOCGWIN,&(vd->window))<0 ) { printf("ERROR:VIDIOCGWIN\n"); } /*printf("window x ------->%s\n",vd->window.x); printf("window y ------->%d\n",vd->window.y); printf("window width --->%d\n",vd->window.width); printf("window height -->%d\n\n",vd->window.height);*/ vd->window.x = 0; //windows中的原点坐标 vd->window.y = 0; //windows中的原点坐标 vd->window.width = 640; //capture area 宽度 vd->window.height = 480; //capture area 高度 /*使用IOCTL命令VIDIOCSWIN,设置摄像头的基本信息*/ if (ioctl(vd->fd, VIDIOCSWIN, &(vd->window)) < 0) { printf("ERROR:VIDIOCSWIN\n"); } } int v4l_get_picture(v4ldevice *vd) { if(ioctl(vd->fd,VIDIOCGPICT,&(vd->picture))<0) { perror("v4l_get_picture"); return -1; } /*printf("Brightness ----->%d\n",vd->picture.brightness); printf("Hue ------------>%d\n",vd->picture.hue); printf("Colour --------->%d\n",vd->picture.colour); printf("Contrast ------->%d\n",vd->picture.contrast); printf("Whiteness ------>%d\n",vd->picture.whiteness); printf("Capture depth -->%d\n",vd->picture.depth); printf("Palette -------->%d\n\n",vd->picture.palette);*/ return 0; } int v4l_set_norm(v4ldevice *vd, int norm) { int i; for (i = 0; i < vd->capability.channels; i++) vd->channel[i].norm = norm; return 0; } int v4l_grab_init(v4ldevice *vd,int width,int height) { vd->mmap.width=width; vd->mmap.height=height; vd->mmap.format=vd->picture.palette; vd->frame=0; vd->framestat[0]=0; vd->framestat[1]=0; return 0; } int v4l_mmap_init(v4ldevice *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) { return -1; } return 0; } int v4l_get_mbuf(v4ldevice *vd)//查询实际可用的缓存数 { if(ioctl(vd->fd,VIDIOCGMBUF,&(vd->mbuf))<0) { perror("v4l_get_mbuf:"); return -1; } printf("\nsize=%d\n",vd->mbuf.size); printf("Frames:%d\n\n",vd->mbuf.frames); return 0; } int v4l_grab_start(v4ldevice *vd,int frame) { vd->mmap.frame=frame; if(ioctl(vd->fd,VIDIOCMCAPTURE,&(vd->mmap))<0)////////////////////// { exit(-1); return -1; } vd->framestat[frame]=1; return 0; } int v4l_grab_sync(v4ldevice *vd,int frame) { if(ioctl(vd->fd,VIDIOCSYNC,&frame)<0) { return -1; } vd->framestat[frame]=0; return 0; } int v4l_close(v4ldevice *vd) { close(vd->fd); return 0; } unsigned char * v4l_get_address(v4ldevice *vd) { return (vd->map+vd->mbuf.offsets[vd->frame]); } /********************YUV420P to RGB24*************************/ // Conversion from YUV420 to RGB24 static long int crv_tab[256]; static long int cbu_tab[256]; static long int cgu_tab[256]; static long int cgv_tab[256]; static long int tab_76309[256]; static unsigned char clp[1024]; //for clip in CCIR601 static unsigned char convert_uu[640*480]; static unsigned char convert_vv[640*480]; // //Initialize conversion table for YUV420 to RGB // void InitConvertTable() { long int crv,cbu,cgu,cgv; int i,ind; crv = 104597; cbu = 132201; /* fra matrise i global.h */ cgu = 25675; cgv = 53279; for (i = 0; i < 256; i++) { crv_tab[i] = (i-128) * crv; cbu_tab[i] = (i-128) * cbu; cgu_tab[i] = (i-128) * cgu; cgv_tab[i] = (i-128) * cgv; tab_76309[i] = 76309*(i-16); } for (i=0; i<384; i++) clp[i] =0; ind=384; for (i=0;i<256; i++) clp[ind++]=i; ind=640; for (i=0;i<384;i++) clp[ind++]=255; } // // Convert from YUV420 to RGB24 // void ConvertYUV2RGB(unsigned char *src0,unsigned char *src1,unsigned char *src2,unsigned char *dst_ori,int width,int height) { int y1,y2,u,v; unsigned char *py1,*py2; int i,j, c1, c2, c3, c4; unsigned char *d1, *d2; py1=src0; py2=py1+width; d1=dst_ori; d2=d1+3*width; for (j = 0; j < height; j += 2) { for (i = 0; i < width; i += 2) { u = *src1++; v = *src2++; c1 = crv_tab[v]; c2 = cgu_tab[u]; c3 = cgv_tab[v]; c4 = cbu_tab[u]; //up-left y1 = tab_76309[*py1++]; *d1++ = clp[384+((y1 + c1)>>16)]; *d1++ = clp[384+((y1 - c2 - c3)>>16)]; *d1++ = clp[384+((y1 + c4)>>16)]; //down-left y2 = tab_76309[*py2++]; *d2++ = clp[384+((y2 + c1)>>16)]; *d2++ = clp[384+((y2 - c2 - c3)>>16)]; *d2++ = clp[384+((y2 + c4)>>16)]; //up-right y1 = tab_76309[*py1++]; *d1++ = clp[384+((y1 + c1)>>16)]; *d1++ = clp[384+((y1 - c2 - c3)>>16)]; *d1++ = clp[384+((y1 + c4)>>16)]; //down-right y2 = tab_76309[*py2++]; *d2++ = clp[384+((y2 + c1)>>16)]; *d2++ = clp[384+((y2 - c2 - c3)>>16)]; *d2++ = clp[384+((y2 + c4)>>16)]; } d1 += 3*width; d2 += 3*width; py1+= width; py2+= width; } }
这个是主程序:main.c,主要是保存三张图片,是bmp格式的图片,非常的占地方。比较讨厌,但是可以保存三张近似动态的图片。
#include "v4l.h" #include "v4l.c" #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <fcntl.h> #include <string.h> #define norm VIDEO_MODE_NTSC /************************/ #define BMP "image.bmp" #define IMAGEWIDTH 640 //图片的宽度 #define IMAGEHEIGHT 480 //图片的高度 /*******************************/ int main() { char *buffer=NULL; v4ldevice VD; v4ldevice *vd=&VD; int frame=0; //int f_d; /********************************/ unsigned char *word; unsigned char *lpbuffer; int i,j; int tmp = 0; //unsigned char* buffer=(unsigned char*)malloc(IMAGEHEIGHT*IMAGEWIDTH*3/2); //存放YUV格式的图片 unsigned char*y; unsigned char*u; unsigned char*v; unsigned char*dst=(unsigned char*)malloc(IMAGEWIDTH*IMAGEHEIGHT*3); //存放RGB格式的图片数据 FILE * fp[3]; BITMAPFILEHEADER bf; //bmp文件头 BITMAPINFOHEADER bi; //bmp信息头 //Set BITMAPINFOHEADER 设置bmp图片的信息头格式 bi.biSize = 40; //位图信息头大小 bi.biWidth = IMAGEWIDTH; //图像宽度 bi.biHeight = IMAGEHEIGHT; //图像高度 bi.biPlanes = 1; //位平面树=1 bi.biBitCount = 24; //单位像素的位数 bi.biCompression = 0; //图片的压缩属性,bmp不压缩,等于0 //bi.biSizeImage = WIDTHBYTES(bi.biWidth * bi.biBitCount) * bi.biHeight; bi.biSizeImage = IMAGEWIDTH * IMAGEHEIGHT * bi.biBitCount; //表示bmp图片数据区的大小,当上一个属性biCompression等于0时,这里的值可以省略不填 bi.biXPelsPerMeter = 0; //水平分辨率 bi.biYPelsPerMeter = 0; //垂直分辨率 bi.biClrUsed = 0; //表示使用了多少哥颜色索引表,一般biBitCount属性小于16才会用到,等于0时表示有2^biBitCount个颜色索引表 bi.biClrImportant = 0; //表示有多少个重要的颜色,等于0时表示所有颜色都很重要 //Set BITMAPFILEHEADER 设置bmp图片的文件头格式 bf.bfType = 0x4d42; //2个字节,恒等于0x4d42,ascii字符“BM” bf.bfSize = 54 + bi.biSizeImage; //文件大小,以4个字节为单位 bf.bfReserved1 = 0; //备用 bf.bfReserved2 = 0; //备用 bf.bfOffBits = 54; //数据区在文件中的位置偏移量 /*******************************/ if(0==v4l_open("/dev/video0",vd)) //打开设备 printf("open success!\n"); else printf("open failure\n"); if(0==v4l_set_norm(vd,norm)) printf("set_norm success\n"); else printf("set_norm failure\n"); if(0==v4l_grab_init(vd,IMAGEWIDTH,IMAGEHEIGHT))//初始化设备,定义获取图像的大小 printf("init success!\n"); else printf("init failure\n"); if(0==v4l_mmap_init(vd))//内存映射 printf("memory map success!\n"); else printf("memory map failure\n"); //////////////////////////////////////////////////// //开始一帧数据的采集 fp[0] = fopen("linhui0.bmp", "wb"); fp[1] = fopen("linhui1.bmp", "wb"); fp[2] = fopen("linhui2.bmp", "wb"); for(tmp=0;tmp<3;tmp++) { /*fp = fopen(BMP, "wb"); if(!fp) { perror(BMP); exit(1); }*/ if(0==v4l_grab_start(vd,frame))//开始获取图像 printf("get picture success!\n"); else printf("get picture failure\n"); v4l_grab_sync(vd,frame);//等待传完一帧 buffer=(char *)v4l_get_address(vd);//得到这一帧的地址 printf("img address %p\n",buffer); /*************************************/ y=buffer; u=buffer+IMAGEWIDTH*IMAGEHEIGHT; v=u+IMAGEWIDTH*IMAGEHEIGHT/4; /*fwrite(&bf, 14, 1, fp); //向文件中写入图片文件头 fwrite(&bi, 40, 1, fp); //向文件中写入图片信息头*/ fwrite(&bf, 14, 1, fp[tmp]); //向文件中写入图片文件头 fwrite(&bi, 40, 1, fp[tmp]); //向文件中写入图片信息头 //================================================== InitConvertTable(); ConvertYUV2RGB(y,u,v,dst,IMAGEWIDTH,IMAGEHEIGHT); //fseek(fp, 0x36, SEEK_SET); fseek(fp[tmp], 0x36, SEEK_SET); lpbuffer = dst+IMAGEWIDTH*3*(IMAGEHEIGHT - 1); for(i=0; i<IMAGEHEIGHT; i++) //bmp file scan line is arraned by BGR|BGR|BGR|........ { word = lpbuffer; for(j=0; j<IMAGEWIDTH; j++) { /*************add in 2011 8.2 23:04*****************/ if(tmp == 10) { tmp = 0; fputc( *(word+2), fp[tmp]); // B fputc( *(word+1), fp[tmp]); // G fputc( *(word+0), fp[tmp]); // R }else{ fputc( *(word+2), fp[tmp]); // B fputc( *(word+1), fp[tmp]); // G fputc( *(word+0), fp[tmp]); // R } /***********************************************/ /*fputc( *(word+2), fp); // B fputc( *(word+1), fp); // G fputc( *(word+0), fp); // R*/ word+=3; } lpbuffer -= IMAGEWIDTH*3; // 指针转到上一行的开始 } fclose(fp[tmp]); } //////////////////////////////////////////////////// //fwrite(buffer, bi.biSizeImage, 1, fp); //将buffer中的图片信息写入文件中/ //fwrite(buffer, IMAGEWIDTH*IMAGEHEIGHT*3, 1, fp); //fwrite(buffer, IMAGEWIDTH*IMAGEHEIGHT*3, 1, fp); printf("get bmp form video\t[OK]\n"); /***************************************/ fclose(fp); v4l_close(vd); return 0; }
小弟是经过了将近10天的时间才搞清楚了YUV420转RGB24格式的基本原理,同时也参考了网上的代码,已经能够满足抓取一张图片的要求,但是比较占地方,如果那个高手能够帮我压缩一下图片的大小,小弟将万分感激。(大只辉 come from scnu )
下面是实验结果:
本文装载自:http://linhui.568.blog.163.com/blog/static/962652682011746211864/