我的实验环境为:webeye v2000的usb摄像头,redhat的linux系统RHEL5(系统中已经集成了ov511的驱动程序,当系统检测到摄像头时,会在/dev下有一个vedio0的设备名)。
经过充分的考虑,整个项目我分为四个步骤来完成,第一步:先通过只有ov511芯片的摄像头来抓取一张图片,并且可以保存为bmp格式;第二步:利用背景减除法来实现动态图像的检测,实现每隔10s保存一张背景图片,对超出一定阈值的像素的图片进行保存;第三步:优化程序,将图片检测算法进行优化,同时优化缩短图片采集的时间,并且对保存的图片进行压缩,基本的将采用jpeg格式;第四步:将我的整个项目移植到arm9系列的mini2440开发板上。
现阶段, 第一步已经基本上完成了,由于我使用的时webeye v2000的摄像头,只能够抓取YUV420P格式的图像流,这个就已经花了我很多的时间来查资料进行格式的转换,结果搞了半个月,图片是出来了,但是保存的640*480的bmp格式的图片有将近900k那么大。第二、第三步和第四步还在进行中,如果有同道中人,或者已经有了相关经验的好友,能够留下只言片语,小弟在此感激不尽。
本文属于原创,欢迎转载:http://linhui.568.blog.163.com/blog/static/962652682011746211864/ 请尊重原作者的劳动成果!
下面是小弟编写的代码,小弟不才,代码比较乱(代码总共分为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
本文属于原创,欢迎转载:http://linhui.568.blog.163.com/blog/static/962652682011746211864/
下面是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]);
}
本文属于原创,欢迎转载:http://linhui.568.blog.163.com/blog/static/962652682011746211864/
/********************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;
}
//本文属于原创,欢迎转载:http://linhui.568.blog.163.com/blog/static/962652682011746211864/
// 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;
}
}
本文属于原创,欢迎转载:http://linhui.568.blog.163.com/blog/static/962652682011746211864/
这个是主程序:main.c,主要是保存三张图片,是bmp格式的图片,非常的占地方。比较讨厌,但是可以保存三张近似动态的图片。
#include "v4l.h"
#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信息头
本文属于原创,欢迎转载:http://linhui.568.blog.163.com/blog/static/962652682011746211864/
//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++)
{
本文属于原创,欢迎转载:http://linhui.568.blog.163.com/blog/static/962652682011746211864/
/*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);
本文属于原创,欢迎转载:http://linhui.568.blog.163.com/blog/static/962652682011746211864/
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 )