Chinaunix首页 | 论坛 | 博客
  • 博客访问: 95390
  • 博文数量: 41
  • 博客积分: 866
  • 博客等级: 准尉
  • 技术积分: 282
  • 用 户 组: 普通用户
  • 注册时间: 2011-11-22 22:49
文章分类

全部博文(41)

文章存档

2011年(41)

我的朋友

分类: LINUX

2011-11-30 22:14:40

要实现在lcd上实时显示usb摄像头采集的图像,前面已经能够采集到图像了,并保存为jpg文件,现在要在lcd上显示这个图片,有几种方法:一种是移植好minigui,然后使用minigui提供的函数FillBoxWithBitmap显示图像;一种是直接将图像数据写入framebuffer中,不管使用哪种前提是要把jpg文件解压得到RGB24数据流。最后决定使用framebuffer,这样可以了解一些底层的东西。

   这里先介绍如何在framebuffer上绘图,首先有两个结构体struct fb_fix_screeninfo 和struct fb_var_screeninfo,应用程序通过这两个结构体可以获取framebuffer设备相关参数。定义两个变量

struct fb_fix_screeninfo   finfo;

struct fb_var_screeninfo vinfo;

int fb = open("/dev/fb0", O_RDWR);

ioctl ( fb, FBIOGET_FSCREENINFO, &finfo)//获取与Framebuffer有关的固定的信息

ioctl( fb, FBIOGET_VSCREENINFO, &vinfo)//获取与Framebuffer有关的可变信息

应用程序中通常要用到struct fb_var_screeninfo的下面这几个字段:xres、yres、bits_per_pixel,分别表示x轴的分辨率、y轴的分辨率以及每像素的颜色深度(每个像素占用的比特数)。

     与Framebuffer设备有关的IO通常都是通过mmap()系统调用来完成的。系统调用mmap()用来实现内存映射IO。所谓内存映射IO,是指将一个磁盘文件的内容与内存中的一个空间相映射。当从这个映射内存空间中取数据时,就相当于从文件中读取相应的字节,而当向此映射内存空间写入数据时,就相当于向磁盘文件中写入数据。这就是内存映射IO的含义。

mmap()的函数原型如下

void  *  mmap(void *start, size_t length, int prot , int flags, int fd,off_t offset);
具体到对mmap()而言,当调用成功时,返回值就是与磁盘文件建立了映射关系的内存空间的起始地址,当调用失败时,mmap()的返回值是-1。第一个参数start通常设置为0,表示由系统选择映射内存空间;第二个参数length指定了要映射的字节数;第三个参数指明了映射内存空间的保护属性,对于Framebuffer通常将其设置为PROT_READ | PROT_WRITE,表示既可读也可写;第四个参数flags指明了影响映射内存空间行为的标志,对于Framebuffer编程而言,要将flags设置为MAP_SHARED,表明当向映射内存空间写入数据时,将数据写入磁盘文件中;第五个参数fd是要映射的文件的文件描述符;第六个参数offset指明了要映射的字节在文件中的偏移量。

#define WIDTH 320
#define HEIGHT  240
#define BYTE_PER_PIXEL 2

我的LCD的bpp模式设置为RGB565

定义全局变量static unsigned char *fb_mem = NULL;

  fb_mem = mmap( NULL, WIDTH * HEIGHT * BYTE_PER_PIXEL,
           PROT_READ | PROT_WRITE, MAP_SHARED, fb, 0);

如果mmap()调用成功,就可以在程序中对得到的映射内存空间进行读写操作了。所有的读写都将由操作系统内核转换成IO操作。

int munmap(void *start, size_t length);

在使用完映射内存空间之后,应当将其释放,这是通过munmap()系统调用完成的。munmap()的第一个参数是映射内存空间的起始地址,第二个参数length是映射内存空间的长度,单位为字节。如果释放成功,munmap()返回0,否则返回-1。

初始化framebufer程序:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#define WIDTH 320
#define HEIGHT  240
#define BYTE_PER_PIXEL 2
#define MAX_X       WIDTH
#define MAX_Y       HEIGHT
#define FB_DEVICE "/dev/fb0"

static unsigned char *fb_mem = NULL;

int init_framebuffer(void)
{
  struct fb_fix_screeninfo finfo;
  struct fb_var_screeninfo vinfo;
  int fb;

  fb = open (FB_DEVICE, O_RDWR);
  if ( fb < 0)
  {
    printf("Can't open framebuffer device %s\n",FB_DEVICE);
    return -1;
  }
 
  if ( ioctl ( fb, FBIOGET_FSCREENINFO, &finfo) )
  {
    printf("read fixed framebuffer infomation error\n");
    return -2;
  }

  printf("printing fixed screen infomation:\n");
  printf("id:%s\n",finfo.id);
  printf("smem_start:%d\n",finfo.smem_start);
  printf("smem_len:%d\n",finfo.smem_len);
  printf("type:%d\n",finfo.type);
  printf("type_aux:%d\n",finfo.type_aux);
  printf("visual:%d\n",finfo.visual);
  printf("xpanstep:%d\n",finfo.xpanstep);
  printf("ypanstep:%d\n",finfo.ypanstep);
  printf("ywrapstep:%d\n",finfo.ywrapstep);
  printf("line_length:%d\n",finfo.line_length);
  printf("mmio_start:%d\n",finfo.mmio_start);
  printf("mmio_len:%d\n",finfo.mmio_len);
  printf("accel:%d\n",finfo.accel);

  if ( ioctl( fb, FBIOGET_VSCREENINFO, &vinfo) )
  {
    printf("read variable framebuffer information\n");
    return -3;
  }
  printf("printing variable screen information\n");
  printf("xres:%d\n",vinfo.xres);
  printf("yres:%d\n",vinfo.yres);
  printf("xres_virtual:%d\n",vinfo.xres_virtual);
  printf("yres_virtual:%d\n",vinfo.yres_virtual);
  printf("xoffset:%d\n",vinfo.xoffset);
  printf("yoffset:%d\n",vinfo.yoffset);
  printf("bits_per_pixel:%d\n",vinfo.bits_per_pixel);
  printf("grayscale:%d\n",vinfo.grayscale);
  printf("nonstd:%d\n",vinfo.nonstd);
  printf("activate:%d\n",vinfo.activate);
  printf("height:%d\n",vinfo.height);
  printf("width:%d\n",vinfo.width);
  printf("accel_flags:%d\n",vinfo.accel_flags);
  printf("pixclock:%d\n",vinfo.pixclock);
  printf("left_margin:%d\n",vinfo.left_margin);
  printf("right_margin:%d\n",vinfo.right_margin);
  printf("upper_margin:%d\n",vinfo.upper_margin);
  printf("lower_margin:%d\n",vinfo.lower_margin);
  printf("hsyns_len:%d\n",vinfo.hsync_len);
  printf("vsync_len:%d\n",vinfo.vsync_len);
  printf("sync:%d\n",vinfo.sync);
  printf("vmode:%d\n",vinfo.vmode);
  //printf("rotate:%d\n",vinfo.rotate);

  fb_mem = mmap( NULL, WIDTH * HEIGHT * BYTE_PER_PIXEL,
           PROT_READ | PROT_WRITE, MAP_SHARED, fb, 0);
  if ( fb_mem < 0 )
  {
    printf("failed to map framebuffer device to memory\n");
    return -4;
  }
  memset( fb_mem, 0, WIDTH * HEIGHT * BYTE_PER_PIXEL);
 
  return fb;

}

现在直接向得到的映射内存空间写入数据就可以绘图了,下面的函数控制向某个像素点的颜色

#define BYTE_PER_PIXEL 2
#define MAX_X 320

#define MAX_Y 240

void putpixel( unsigned int x, unsigned int y,unsigned short color)
{
  unsigned long offset;

  offset = ( y * MAX_X * BYTE_PER_PIXEL) + ( x * BYTE_PER_PIXEL);

  *(unsigned short *)(fb_mem + offset ) = color;
}

下面这个函数填充某个区域,输入参数中x1,x2的范围是0~319,y1,y2的范围是0~239.

void fillrect(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2,
              unsigned short color)
{
  int i , j;
  int xlen;
  unsigned long offset;
  if ( x1 >= MAX_X ) {
    x1 = MAX_X - 1;
  }
  if ( x2 >= MAX_X ) {
    x2 = MAX_X - 1;
  }  
  if ( y1 >= MAX_Y ) {
    y1 = MAX_Y - 1;
  }
  if ( y2 >= MAX_Y ) {
    y2 = MAX_Y -1;
  }
  if ( ( x2 - x1 ) < 0 ) {
    i = x2; x2 = x1; x1 = i;
  }
  if ( ( y2 - y1 ) < 0 ) {
    i = y2; y2 = y1; y1 = i;
  }
 
  offset = ( y1 * MAX_X * BYTE_PER_PIXEL ) + (x1 * BYTE_PER_PIXEL);
  xlen = (x2 - x1 + 1) * BYTE_PER_PIXEL;
  for ( i = y1; i <= y2; i++) {
    for (j = 0; j < xlen; j += 2) {
      *(unsigned short *)(fb_mem + offset + j) = color;
    }
    offset += MAX_X * BYTE_PER_PIXEL;
  }
}

这里主要是要搞清楚坐标和内存空间地址的转换。

在lcd上显示bmp图像

实现需要了解bmp图像文件的格式

 

位图文件 位图文件头 14 字节
位图信息头 40 字节
彩色表(调色板) 4N 字节
位图数据 x  字节
主要是几个结构体

 

 

位图文件头包含文件类型、文件大小、存放位置等信息。结构定义如下:

  typedef struct tagBITMAPFILEHEADER
  {
  UNIT bfType;
  DWORD bfSize;
  UINT bfReserved1;
  UINT bfReserved2;
  DWORD bfOffBits;
  }BITMAPFILEHEADER;

其中:
bfType    说明文件类型,在windows系统中为BM。
bfSize    说明文件大小。
bfReserved1 bfReserved2 保留,设置为0。
bfOffBits 说明实际图形数据的偏移量。

位图信息头包含位图的大小、压缩类型、和颜色格式,结构定义如下:

  typedef struct tagBITMAPINFOHEADER
  {
  DWORD biSize;
  LONG biWidth;
  LONG biHeight;
  WORD biPlanes;
  WORD biBitCount;
  DWORD biCompression;
  DWORD biSizeImage;
  LONG biXPelsPerMerer;
  LONG biYPelsPerMerer;
  DWORD biClrUsed;
  DWORD biClrImportant;
  }BITMAPINFOHEADER;

其中: 
biSize    说明BITMAPINFOHEADER结构所需字节数,在windows系统中为28h
biWidth   说明图像宽度
biHeight  说明图像高度
biPlanes  为目标设备说明位面数,其值设为1
biBitCount每个像素的位数,单色位图为1,256色为8,24bit为24。
biCompression压缩说明,BI_RGB:无压缩,BI_RLE8:8位RLE压缩,BI_RLE4:4位RLE压缩
biSizeImage说明图像大小,如无压缩,可设为0
biXPelsPerMeter水平分辨率
biYPelsPerMeter垂直分辨率
biClrUsed 位图使用的颜色数
biImportant重要颜色数目

彩色表包含的元素与位图所具有的颜色数目相同,像素颜色用结构RGBQUAD来表示:

typedef struct tagRGBQUAD { BYTE rgbBlue; BYTE rgbGreen; BYTE rgbRed; BYTE rgbReserved; }RGBQUAD;

其中:
rgbBlue    指定蓝色强度
rgbGreen   指定绿色强度
rgbRed     指定红色强度
rgbReserved保留,设为0

位图数据

紧跟在彩色表后的是图像数据阵列,图像每一扫描行有连续的字节组成,扫描行由底向上存储,阵列中第一字节为左下角像素,最后一字节为右上角像素

显示bmp图像程序如下(显示一幅320*240的bmp图像):

#define BYTE_PER_PIXEL 2
#define MAX_X 320

#define MAX_Y 240

#pragma pack(1)

typedef  struct  
 
      short      bfType;                          
      long       bfSize;                        
      short      bfReserved1;               
      short      bfReserved2;             
      long       bfoffBits;                
}  BITMAPFILEHEADER;

#pragma pack()

int showbmp(char *bmpfile)
{
  int fp_bmp;
  long height;
  long width;
  int  length, offset = 0;
  int i, y1 = 0, x1 = 0;
  BITMAPFILEHEADER bfile;

  unsigned char *pDIBData = NULL;
  unsigned char r = 0, g = 0, b = 0;
  unsigned short R = 0, G = 0, B = 0;
  unsigned short color = 0;

  printf("open bmp file\n");
  fp_bmp = open( bmpfile, O_RDONLY);//打开文件
  if ( fp_bmp < 0 )
  {
    printf("can not open file %s\n",bmpfile);
    return 0;
  }
  read( fp_bmp, &bfile, sizeof(bfile) );//读取文件头信息
 
  printf("bfsize=%d, bfoffbits=%d\n",
         bfile.bfSize, bfile.bfoffBits);
  
  lseek( fp_bmp, 18, SEEK_SET );
  read( fp_bmp, (void *)&width, sizeof(long) );//获取文件的宽度
  read( fp_bmp, (void *)&height, sizeof(long) );//获取文件的高度

  printf("width=%d\n",width);
  printf("height=%d\n",height);

//分配图像数据存储空间,实际的图像数据大小为文件大小  减去  图像数据与文件头的偏移量

  pDIBData = (unsigned char *)malloc(bfile.bfSize -  bfile.bfoffBits);
  
  if(pDIBData == NULL)
  {
    printf("mem allocate failed\n");
    return 0;
  }
  lseek( fp_bmp, bfile.bfoffBits, SEEK_SET );
  read( fp_bmp, pDIBData, bfile.bfSize -  bfile.bfoffBits);//读取图像数据至分配的存储空间

    //显示图像,这里需要注意如何将RGB24转换为RGB565

    //从bmp文件读出的图像数据是RGB24格式,LCD显示需要RGB565数据格式
  for (y1 = 0; y1 < MAX_Y; y1++)
    for (x1 = 0; x1 < MAX_X; x1++)
    {

          //注意从bmp文件中读出的图像数据连续的三个字节中第一个字节为蓝色分量

         //第二个字节为绿色分量 第三个字节为红色分量
          b = *pDIBData ; g = *(pDIBData + 1) ; r = *(pDIBData + 2);

           //下面5跳语句实现了RGB24转换为RGB565

          //红色r占5为屏蔽低三位  绿色g占6为屏蔽低两位   蓝色b占5为屏蔽低三位
          r = r & (~(0x07)); g = g & (~(0x03)); b = b & (~(0x7));
          R = (unsigned short)r << 8;
          G = (unsigned short)g << 3;
          B = (unsigned short)b >> 3;
          color = R | G | B;

         //计算偏移量 ,注意最先读出的三个字节对应的是图像中的左下角的那个像素,最后三个字节对应图像

            //    中右上 角 的那个像素

          offset = (MAX_Y - y1 -1) * MAX_X * BYTE_PER_PIXEL + (x1 * BYTE_PER_PIXEL);

         //将颜色数据写入显存
         *(unsigned short *)(fb_mem + offset) = color;
         pDIBData += 3;
    }

  close(fp_bmp);
  free(pDIBData);

}


阅读(3114) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~