利用ov511的webeye v2000摄像头实现YUV420P格式转RGB24格式来抓取一张图片

最近因为做了国嵌的那个高级项目里的基于H.264的视频采集项目和动态图像检测项目。在充分理解了利用V4L2进行图像采集的过程后,又因为老师的一个要求,希望我能够完成一个小的动态图像检测的项目(要求要移植到arm 9开发板上去),以便能够推荐我到深圳那边的一家安防公司干活,又由于我对嵌入式的热爱和对图像处理的固执。我决定进行自己的图像采集实验。原本老师的要求是在7月中旬的时候要求我能够做出来的,但是因为我的时间问题,现在又要实习,整天的工作都是投入到工作中,每天都对着android系统,驱动和硬件抽象层,感觉挺累的,所以我为自己定了一个目标。也就是整个小项目分几个步骤来完成。

       我的实验环境为: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  )

下面是实验结果:

利用ov511的webeye v2000摄像头实现YUV420P格式转RGB24格式来抓取一张图片_第1张图片

本文装载自: http://linhui.568.blog.163.com/blog/static/962652682011746211864/

你可能感兴趣的:(struct,video,buffer,byte,FP,DST)