USB视频摄像头的媒体格式为RGB24和I420,如何把这些数据变成BMP位图存下来呢?请看本文。
首先,我们了解一下BMP的格式,BMP有四部分组成,用表格表示如下:
1. 文件信息头
2. 位图信息头
3. 调色板
4. 位图数据
第一部分,文件信息头的格式如下:
typedef struct tagBITMAPFILEHEADER {
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER, FAR *LPBITMAPFILEHEADER,
*PBITMAPFILEHEADER;
共有14个字节,其中bfType为文件类型,BMP的类型为0×4d42,也就是字母m和b;bfSize是文件大小,为1,2,3,4部分大小的总和;bfReserved1和bfReserved2为1,2,3部分大小的总和。
第二部分,位图信息头,定义如下:
typedef struct tagBITMAPINFOHEADER{
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG
biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER, FAR *LPBITMAPINFOHEADER,
*PBITMAPINFOHEADER;
总共40个字节,字段比较多,可以查看MSDN中的说明,对于RGB24的位图,这个结构体一般定义如下:
BITMAPINFOHEADER bih;
bih.biSize = sizeof(BITMAPINFOHEADER);
bih.biWidth = width;
bih.biHeight = height;
bih.biPlanes = 1;
bih.biBitCount = 24;
bih.biCompression = 0;
bih.biSizeImage = size;
bih.biXPelsPerMeter = 0;
bih.biYPelsPerMeter = 0;
bih.biClrUsed = 0;
bih.biClrImportant = 0;
第三部分是调色板信息,定义如下:
typedef struct tagRGBQUAD {
BYTE rgbBlue;
BYTE rgbGreen;
BYTE rgbRed;
BYTE rgbReserved;
} RGBQUAD;
这部分用来表示RGB各色的强度,一般情况我们不把这一部分写到文件中。
第四部分就是真正的数据,比如宽度和高度分别是320和240,这部分数据的长度应该为320*240*3(每个像素点上有3个字节,分别用来表示b,g,r的颜色)。
根据对BMP格式的说明,我们可以轻易的写出一个生成BMP图像的函数,如下所示:
// pData为rgb24数据,width为位图宽度,height为位图高度,filename为位图文件的名字
void Snapshot( BYTE * pData, int width, int height, const char *
filename )
{
int size = width*height*3; // 每个像素点3个字节
// 位图第一部分,文件信息
BITMAPFILEHEADER bfh;
bfh.bfType = 0×4d42; //bm
bfh.bfSize = size // data size
+ sizeof( BITMAPFILEHEADER ) // first section size
+ sizeof( BITMAPINFOHEADER ) // second section size
;
bfh.bfReserved1 = 0; // reserved
bfh.bfReserved2 = 0; // reserved
bfh.bfOffBits = bfh.bfSize - size;
// 位图第二部分,数据信息
BITMAPINFOHEADER bih;
bih.biSize = sizeof(BITMAPINFOHEADER);
bih.biWidth = width;
bih.biHeight = height;
bih.biPlanes = 1;
bih.biBitCount = 24;
bih.biCompression = 0;
bih.biSizeImage = size;
bih.biXPelsPerMeter = 0;
bih.biYPelsPerMeter = 0;
bih.biClrUsed = 0;
bih.biClrImportant = 0;
FILE * fp = fopen( filename, “wb” );
if( !fp ) return;
fwrite( &bfh, 1, sizeof(BITMAPFILEHEADER), fp
);
fwrite( &bih, 1, sizeof(BITMAPINFOHEADER), fp
);
fwrite( pData, 1, size, fp );
fclose( fp );
}
如果要对摄像头采集到的数据进行截图,这里可以直接把数据送给这个函数,就可以生成一张位图了。当然也可以自己生成一段数据,用图片的形式保存下来。下面我将给一个例子,说明怎么生成位图,本例中说明了如何生成一个320*240的图像,黑色的背景,中部有一个100*100的矩形,代码如下:
void GenerateBMP()
{
int i=0, j=0;
struct {
BYTE b;
BYTE g;
BYTE r;
} pRGB[240][320]; // 定义位图数据
memset( pRGB, 0, sizeof(pRGB) ); // 设置背景为黑色
// 在中间画一个100*100的矩形
for( int i=70;i<170;i++ ){
for( int j=110;j<210;j++ ){
pRGB[i][j].r = 0xff;
}
}
// 生成BMP图片
Snapshot( (BYTE *)pRGB, 320, 240, “C:\rgb.bmp” );
}
生成图片如下所示:
上面所说的都是对于RGB24的数据,对于I420,可以先转换成RGB24,然后在生成位图。
BMP的数据量是很大的,没有经过任何的压缩,我们可以计算一下,对于一副320*240的图像,图片的大小是320*240*3 =
230400 bytes = 225
Kbytes,普通电脑一副屏幕截图就要1024*768*3=2.3M。所以我们要用一些流行的算法对图片进行压缩才便于存储,下一篇文章,我将介绍如何把BMP的文件压缩成JPG的文件,压缩比大概在80倍左右,请关注下文。
11-10补充:
感谢张欣网友把上述代码完整的整理出来,经过我的小的改动,C代码如下:
#include “stdio.h”
#include “stdlib.h”
#include “string.h”
typedef long BOOL;
typedef long LONG;
typedef unsigned char BYTE;
typedef unsigned long DWORD;
typedef unsigned short WORD;
#include
typedef struct {
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BMPFILEHEADER_T;
struct BMPFILEHEADER_S{
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
};
typedef struct{
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BMPINFOHEADER_T;
void Snapshot( BYTE * pData, int width, int
height, char * filename )
{
int size = width*height*3; // 每个像素点3个字节
// 位图第一部分,文件信息
BMPFILEHEADER_T bfh;
bfh.bfType = 0×4d42; //bm
bfh.bfSize = size // data size
+ sizeof( BMPFILEHEADER_T ) // first section size
+ sizeof( BMPINFOHEADER_T ) // second section size
;
bfh.bfReserved1 = 0; // reserved
bfh.bfReserved2 = 0; // reserved
bfh.bfOffBits = bfh.bfSize - size;
// 位图第二部分,数据信息
BMPINFOHEADER_T bih;
bih.biSize = sizeof(BMPINFOHEADER_T);
bih.biWidth = width;
bih.biHeight = height;
bih.biPlanes = 1;
bih.biBitCount = 24;
bih.biCompression = 0;
bih.biSizeImage = size;
bih.biXPelsPerMeter = 0;
bih.biYPelsPerMeter = 0;
bih.biClrUsed = 0;
bih.biClrImportant =
0;
FILE * fp = fopen( filename,”wb” );
if( !fp ) return;
fwrite( &bfh, 1, sizeof(BMPFILEHEADER_T), fp );
fwrite( &bih, 1, sizeof(BMPINFOHEADER_T), fp );
fwrite( pData, 1, size, fp );
fclose( fp );
}
void main()
{
int i=0, j=0;
struct {
BYTE b;
BYTE g;
BYTE r;
} pRGB[240][320]; // 定义位图数据
memset( pRGB, 0, sizeof(pRGB) ); // 设置背景为黑色
// 在中间画一个100*100的矩形
for( i=70;i<170;i++ ){
for( j=110;j<210;j++ ){
pRGB[i][j].r = 0xff;
}
}
// 生成BMP图片
Snapshot( ( BYTE*)pRGB, 320, 240, “c:\rgb.bmp” );
}