在多种文件格式中,BMP是最基本的文件格式,因为BMP文件格式是没有经过压缩的图像,今天我们就来看看BMP到底是个啥东东。
BMP(Bitmap-File)图形文件,又叫位图文件,是Windows采用的图形文件格式,在Windows环境下运行的所有图象处理软件都支持BMP图象文件格式。Windows系统内部各图像绘制操作都是以BMP为基础的。一个BMP文件由四部分组成:文件信息头、位图信息头、调色板、位图数据。
位图文件头(bitmap-file header)包含了图像类型、图像大小、图像数据存放地址和两个保留未使用的字段。
打开WINGDI.h文件,搜索"BITMAPFILEHEADER"就可以定位到BMP文件的位图文件头的数据结构定义。
structtagBITMAPFILEHEADER { // bmfh
WORD bfType; (说明文件的类型,该值必需是0x4D42,也就是字符'BM',否则表示根本不是BMP)
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
各个字段所占字节数及描述如下:
字 段 名 |
大小(单位:字节) |
描 述 |
bfType |
2 |
说明文件的类型,该值必需是0x4D42,也就是字符'BM',否则表示根本不是BMP |
bfSize |
4 |
说明该位图文件的大小,用字节为单位 |
bfReserved1 |
2 |
保留,必须设置为0 |
bfReserved2 |
2 |
保留,必须设置为0 |
bfOffBits |
4 |
说明从文件头开始到实际的图象数据之间的字节的偏移量。这个参数是非常有用的,因为位图信息头和调色板的长度会根据不同情况而变化,所以你可以用这个偏移值迅速的从文件中读取到位数据,以字节为单位。 |
图1 示例图片
使用UtraEdit打开示例图片1,可以看到文件第一、二个字节分别为42和4D,这正是BM的ascii码值。38、04、01、00代表了该文件的大小,这里的换算关系是:
Ox00010438(Byte)=16*16*16*16+4*16*16+16*3+8=66616≈65KB (如图3中所示,此处要留意二进制文件的解析及换算方式)
这里细心的小伙伴会发现图3中有大小和占用空间两个数量,这是由于windows 系统的文件管理方式带来的差别,有兴趣可以看看windows系统中簇的概念。其他变量以此类推,在此不一一介绍。
图2 二进制文件
图3 bmp文件大小
位图信息头(bitmap-information header)包含了位图信息头的大小、图像的宽高、图像的色深、压缩说明图像数据的大小和其他一些参数。
打开WINGDI.h文件,搜索"tagBITMAPINFOHEADER"就可以定位到BMP文件的位图信息头的数据结构定义。
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;
字 段 名 |
大小 (单位: 字节) |
描 述 |
biSize |
4 |
本结构所占用的字节数大小, |
biWidth |
4 |
BMP图像的宽度,单位像素 |
biHeight |
4 |
BMP图像的高度,单位像素 |
biPlanes |
2 |
总为1,目标设备的级别 |
biBitCount |
2 |
BMP图像的色深,即一个像素用多少位表示,常见有1、4、8、16、24和32,分别对应单色、16色、256色、16位高彩色、24位真彩色和32位增强型真彩色 |
biCompression |
4 |
说明图象数据压缩的类型,其中: BI_RGB:没有压缩 BI_RLE8:每个象素8比特的RLE压缩编码,压缩格式由2字节组成(重复象素计数和颜色索引); BI_RLE4:每个象素4比特的RLE压缩编码,压缩格式由2字节组成 BI_BITFIELDS:每个象素的比特由指定的掩码决定。 BI_JPEG:JPEG格式 |
biSizeImage |
4 |
BMP图像数据大小,必须是4的倍数,图像数据大小不是4的倍数时用0填充补足,若BI_RGB格式时即未压缩,可设置为0 |
biXPelsPerMeter |
4 |
水平分辨率,单位像素/m |
biYPelsPerMeter |
4 |
垂直分辨率,单位像素/m |
biClrUsed |
4 |
BMP图像使用的颜色,0表示使用全部颜色,对于256色位图来说,此值为100h=256 |
biClrImportant |
4 |
重要的颜色数,此值为0时所有颜色都重要,对于使用调色板的BMP图像来说,当显卡不能够显示所有颜色时,此值将辅助驱动程序显示颜色 |
在图2中,第一行的e,f对应列的28和00代表biSize的数值,40个字节,其他的以此类推。
在本图片中
biSize=40
biWidth=256
biPlanes=1
biBitCount=8
biCompression=0
biSizeImage=0
biXPelsPerMeter=0x000B12
biYPelsPerMeter=0x000B12
biClrUsed=0
biClrImportant=0
注意:windows规定一个扫描行所占的字节数必须是4的倍数,不足的以0填充,一个扫描行所占字节数计算方法如下
DataSizePerLine=(biWidth*biBitCount+31)/8(这里是+31是非常聪明的一种写法,保证每行必须有四个字节而且能被4整除)。
那么究竟调色板是什么东西?有什么用?为什么我们这种图片不带调试板呢?
我们先来说说三元色RGB概念。我们知道,自然界中的所有颜色都可以由红、绿、蓝(R,G,B)组合而成。有的颜色含有红色成分多一些,如深红;有的含有红色成分少一些,如浅红。针对含有红色成分的多少,可以分成0到255共256个等级,0级表示不含红色成分;255级表示含有100%的红色成分。同样,绿色和蓝色也被分成256级。这种分级概念称为量化。
表1.1 常见颜色的RGB组合值
当一幅图中每个象素赋予不同的RGB值时,能呈现出五彩缤纷的颜色了,这样就形成了彩色图。
让我们举例说明什么是调色板?为什么需要调色板?
有一个长宽各为200个象素,颜色数为16色的彩色图,每一个象素都用R、G、B三个分量表示。因为每个分量有256个级别,要用8位(bit),即一个字节(byte)来表示,所以每个象素需要用3个字节。整个图象要用200×200×3,约120k字节,可不是一个小数目呀!如果我们用下面的方法,就能省的多。
因为是一个16色图,也就是说这幅图中最多只有16种颜色,我们可以用一个表:表中的每一行记录一种颜色的R、G、B值。这样当我们表示一个象素的颜色时,只需要指出该颜色是在第几行,即该颜色在表中的索引值。举个例子,如果表的第0行为255,0,0(红色),那么当某个象素为红色时,只需要标明0即可。
让我们再来计算一下:16种状态可以用4位(bit)表示,所以一个象素要用半个字节。整个图象要用200×200×0.5,约20k字节,再加上表占用的字节为3×16=48字节.整个占用的字节数约为前面的1/6,省很多吧?
这张R、G、B的表,就是我们常说的调色板(Palette),另一种叫法是颜色查找表LUT(Look Up Table),似乎更确切一些。调色板在windows里的结构定义如下:
typedef structtagPALETTEENTRY { // pe
BYTE peRed;
BYTE peGreen;
BYTE peBlue;
BYTE peFlags;
} PALETTEENTRY;
表1.2 tagRGBTRIPLE结构
字 段 名 |
大小(单位:字节) |
描 述 |
rgbBlue |
1 |
蓝色值 |
rgbGreen |
1 |
绿色值 |
rgbRed |
1 |
红色值 |
rgbReserved |
1 |
保留,总为0 |
调色板中RGBQUAD结构的个数由bitBitCount来确定,当等于1.4.8时候,分别有2,16,256个表项;当bitBit=24,没有颜色表项。
这是因为当BMP是24位真彩色的BMP,它的颜色数高达256×256×256种,也就是说包含我们上述提到的R、G、B颜色表示方法中所有的颜色。真彩色图并不是说一幅图包含了所有的颜色,而是说它具有显示所有颜色的能力,即最多可以包含所有的颜色。表示真彩色图时,每个象素直接用R、G、B三个分量字节表示,而不采用调色板技术。原因很明显:如果用调色板,表示一个象素也要用24位,这是因为每种颜色的索引要用24位(因为总共有256×256×256种颜色,即调色板256×256×256行),和直接用R,G,B三个分量表示用的字节数一样,不但没有任何便宜,还要加上一个256×256×256×3个字节的大调色板。所以真彩色图直接用R、G、B三个分量表示,它又叫做24位色图。
这么看来BMP文件不能一概而论了,其是否用调色板或者是RGB掩码,位图数据中的数据的真正含义直接与biBitCount 有关,不同类型的位图,其中的设计原理也不同,下面对此作一个对比:
通过计算得出bfOffBits为0x000436=1078,而sizeof(BITMAPFILEHEADER )+sizeof(BITMAPINFOHEADER)=14+40=54(bite)
剩下的就是1078-54=1024,说明该结构是有调色版的,调色版的字节数为1024。根据以上分析我们可以得到biBitCount代表有256种颜色,而每个tagRGBTRIPLE结构是4个字节,则256*4=1024,这样就对应了起来。
4、位图数据
如果图像是1、4和8位色,则紧跟着调色板的是位图数据,位图数据是指向调色板的索引序号。
如果位图是16位、24位和32位色,则图像文件中不保留调色板,即不存在调色板,图像的颜色直接在位图数据中给出。
16位图像使用2字节保存颜色值,常见有两种格式:5位红5位绿5位蓝和5位红6位绿5位蓝,即555格式和565格式。555格式只使用了15 位,最后一位保留,设为0。
24位图像使用3字节保存颜色值,每一个字节代表一种颜色,按红、绿、蓝排列。
32位图像使用4字节保存颜色值,每一个字节代表一种颜色,除了原来的红、绿、蓝,还有Alpha通道,即透明色。
如果图像带有调色板,则位图数据可以根据需要选择压缩与不压缩,如果选择压缩,则根据BMP图像是16色或256色,采用RLE4或RLE8压缩算法压缩。
参考链接
1、http://blog.csdn.net/lanbing510/article/details/8176231
2、http://www.cnblogs.com/tiandsp/archive/2012/10/22/2734552.html
3、http://www.cnblogs.com/lzlsky/archive/2012/08/16/2641698.html
4、http://blog.csdn.net/lanbing510/article/details/8176231
5、VisualC++ 数字图像处理典型案例详解 机械工业出版社 沈晶、刘海波等著