BMP说明
BMP是BitMap的缩写,即位图,是一种非压缩格式,图像文件一般比较大,Windows系统内部各图像绘制操作都是以BMP为基础的。
BMP图像组成
BMP文件由四部分:- 文件头信息,固定大小,结构体见BitmapFileHeader
- 图像信息头,固定大小,结构见BitmapInfoHeader
- 调色板,可选的,大小根据不同位数不同。
- 位图RGB数据,大小可以根据位数,高宽计算出来。
BMP数据结构
typedef unsigned short U16;
typedef unsigned long U32;
typedef struct tagBitmapFileHeader
{
U16 bfType; /* windows下该值必需是0x4D42,即字符'BM'*/
U32 bfSize; /* bmp文件大小,包含bmp文件头,信息头,调色板大小,数据大小 */
U16 bfReserved1; /* 保留,必须设置为0 */
U16 bfReserved2; /* 保留,必须设置为0 */
U32 bfOffBits; /* rgb数据相对文件头偏移量 */
} BitmapFileHeader;
typedef struct
{
U32 biSize; /* 信息头大小sizeof(BitmapInfoHeader) */
U32 biWidth; /* 图象的宽度,以象素为单位 */
U32 biHeight; /* 图象的高度,以象素为单位,正值倒向位图,负值正向位图 */
U16 biPlanes; /* 位面数,设为1 */
U16 biBitCount; /* 位图位数,可以为32,24,16,8,4,1 */
U32 biCompression; /* 说明图象数据压缩的类型,BI_RGB(0) BI_BITFIELDS(3)等 */
U32 biSizeImage; /* 图像数据大小,包括位图信息大小和数据大小 */
U32 biXPelsPerMeter;/* 水平分辨率,一般可填0 */
U32 biYPelsPerMeter;/* 垂直分辨率,一般可填0*/
U32 biClrUsed; /* 颜色索引使用数,一般填0,表示都使用 */
U32 biClrImportant; /* 重要颜色索引数,一般填0,表示都重要 */
} BitmapInfoHeader;
数据长度计算:
int iLineByteCnt = (((biWidth*biBitCount) + 31) >> 5) << 2;
iRGBDataSize = iLineByteCnt * biHeight;
这个公式主要做了数据行4字节对齐工作,先加31右移5位(除32)做对齐工作,左移2(乘4)转成字节。
biBitCount与biCompression
24位色与32位色biBitCount分部为24及32,biCompression固定为BI_RGB,一个像素占用3个及4个字节.
16位色,biBitCount为16,每个像素占用2个字节,为表示2个字节RGB的位置,分为多种情况
- biCompression为BI_RGB时,表明为RGB555格式,最高位为0,RGB各占5位,这种16位色格式无调色板信息。
- biCompression为BI_BITFIELDS时,则表明使用掩码方式,BitmapInfoHeader后面12字节(3个int)分别为RGB的掩码,555格式下,掩码为0x7C00、0x03E0、0x001F,565格式下,它们则分别为:0xF800、0x07E0、0x001F,掩码方式挺灵活,甚至都可以自己定义16位色格式如RGB844,当然意义不大。
BMP数据结构比较简单,调试板信息是可变项,其他都是固定的,较易处理, 有几个地方需要注意:
- BitmapFileHeader结构不是4字节对齐,需注意使用#pragma pack(2)或__attribute__((aligned(2)))方式进行2直接对齐。
- BMP图片存储时RGB数据时行一般是倒序的,填充的时候注意倒序填充。
BMP转YUV
YUV和BMP都是非压缩格式,不存在解码过程,只需要做像素点到像素点的转换,YUV转bmp公式较多,应用到不同场景,下面是ITU-R BT.601建议在数字视频领域使用的公式(带有伽马校正,图像增强效果):R' = 1.164*(Y’-16) + 1.596*(Cr'-128)
G' = 1.164*(Y’-16) - 0.813*(Cr'-128) - 0.392*(Cb'-128)
B' = 1.164*(Y’-16) + 2.017*(Cb'-128)
效果肯定是最好的,但运算量也是最大的,如果在嵌入式设备上使用,可以使用下面的简化公式:
R = Y + 1.4075 *(V-128)
G = Y – 0.3455 *(U –128) – 0.7169 *(V –128)
B = Y + 1.779 *(U – 128)
在代码中实现该公式时,为提高计算效率,浮点运算需转为整形运算,公式可以转化为如下形式(统一先左移20,再右移20):
R = (Y<<20 + 1475871 *(V-128)) >> 20
G = (Y<<20 – 362283 *(U –128) – 751724 *(V –128)) >> 20
B = (Y <<20 + 1865417 *(U – 128)) >> 20
由于YUV每个分量都使用1个字节来存储(YUV420格式UV分量会重复使用),每个值的取值范围为(0~255),为减少乘法运算,可以把乘法部分用局部查表法实现,如下:
int V1475871[256] = {0};
int V751724[256] = {0};
int U362283[256] = {0};
int U1865417[256] = {0};
for (i = 0; i < 256; i++)
{
V1475871[i] = (i - 128) * 1475871;
V751724[i] = (i - 128) * 751724;
U362283[i] = (i - 128) * 362283;
U1865417[i] = (i - 128) * 1865417;
}
/* 使用局部查表法,公式转成如下形式: */
R = (Y<<20 + V1475871[V]) >> 20;
G = (Y<<20 – U362283[U] – V751724[V]) >> 20;
B = (Y <<20 + U1865417[U]) >> 20;
24位色及32位色一般采用局部查表法优化效率,采用完全查表法内存使用代价太大,如果16位色,则可以采用完全查表法。
局部查表法优化了乘法操作,优化效果有限(多个平台测试结果都在10%左右)。
测试程序
下面是一个yuv420转bmp的小测试程序,包含yuv420p转bmp8888,bmp888,bmp1555,bmp565这几种格式,工具和源码地址:http://download.csdn.net/detail/t3swing/9909075
测试工程为vs2008,测试代码效率并非最优,尤其使用了函数指针,降低了10%左右的转换速度,大家可以参考并优化。