主流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。所以我们要用一些流行的算法对图片进行压缩才便于存储,