图像处理学习之路 -- 认识BMP
因为公司的主要业务是图像识别相关的,因此对图像处理、识别是我学习的重点。虽然写程序也不少年了,但是对于图像处理领域,我还是一个新兵。对很多基础的概念也还是存在盲区,所以想在边学边做的过程中,对一些概念的梳理和学习心得进行记录。
BMP文件格式
BMP(Bitmap-File)图形文件,又叫位图文件,是Windows采用的图形文件格式,在Windows环境下运行的所有图象处理软件都支持BMP图象文件格式。Windows系统内部各图像绘制操作都是以BMP为基础的。一个BMP文件由四部分组成:
- 位图文件头
- 位图信息段
- 调色板
- 位图数据
一个BMP文件,可以用代码表示,如下:
typedef struct tagBITMAP_FILE{
BITMAPFILEHEADER bitmapheader;
BITMAPINFOHEADER bitmapinfoheader;
PALETTEENTRY palette[256];
UCHAR *buffer; //UCHAR 大小1字节(同BYTE), 在VC6下
} BITMAP_FILE;
1、 BMP文件头:BITMAPFILEHEADER
typedef struct tagBITMAPFILEHEADER { // bmfh
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER;
bfType | 说明文件的类型,该值必需是0x4D42,也就是字符'BM',否则表示根本不是BMP |
bfSize | 说明该位图文件的大小,用字节为单位 |
bfReserved1 | 保留,必须设置为0 |
bfReserved2 | 保留,必须设置为0 |
bfOffBits | 说明从文件头开始到实际的图象数据之间的字节的偏移量。这个参数是非常有用的,因为位图信息头和调色板的长度会根据不同情况而变化,所以你可以用这个偏移值迅速的从文件中读取到位数据。 |
下面用Notepad++打开一个BMP文件:
这里:
bfType:0X040d(BM)
bfSize:0X0004a436 == 304182字节 == 297K字节,说明这个位图文件的大小为297K字节,和我看到的符合:
跳过4字节的保留字节,
bfOffBits:0X00000036 == 54字节
2、位图信息段:BITMAPINFOHEADER
typedef struct tagBITMAPINFOHEADER{ // bmih
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER;
biSize | 说明BITMAPINFOHEADER结构所需要的字节数 |
biWidth | 说明图象的宽度,以象素为单位 |
biHeight | 说明图象的高度,以象素为单位。注:这个值除了用于描述图像的高度之外,它还有另一个用处,就是指明该图像是倒向的位图,还是正向的位图。如果该值是一个正数,说明图像是倒向的,即:数据的第一行其实是图像的最后一行,如果该值是一个负数,则说明图像是正向的。大多数的BMP文件都是倒向的位图,也就是时,高度值是一个正数。 |
biPlanes | 表示bmp图片的平面属,显然显示器只有一个平面,所以恒等于1 |
biBitCount | 说明比特数/象素,其值为1、4、8、16、24、或32。 |
biCompression | 说明图象数据压缩的类型,其中:
BI_RGB:没有压缩 BI_RLE8:每个象素8比特的RLE压缩编码,压缩格式由2字节组成(重复象素计数和颜色索引); BI_RLE4:每个象素4比特的RLE压缩编码,压缩格式由2字节组成 BI_BITFIELDS:每个象素的比特由指定的掩码决定。 BI_JPEG:JPEG格式 |
biSizeImage | 说明图象的大小,以字节为单位。当用BI_RGB格式时,可设置为0。 |
biXPelsPerMeter | 说明水平分辨率,用象素/米表示。 |
biYPelsPerMeter | 说明垂直分辨率,用象素/米表示。 |
biClrUsed | 说明位图实际使用的彩色表中的颜色索引数(设为0的话,则说明使用所有调色板项)。 |
biClrImportant | 说明对图象显示有重要影响的颜色索引的数目,如果是0,表示都重要。 |
biSize:位图信息段结构BITMAPINFOHEADER的字节数 0x00000028 ==40字节
biWidth:352像素;
biHeight:288像素;
和图片信息相符:
biPlanes:1
biBitCount :24位图
biCompression:没有压缩;
biSizeImage:0x4a400 == 304128字节 ,当用BI_RGB格式时,可设置为0,为什么这里是304128呢?这里的304128是怎么出来的呢?
原来biSizeImage = biWidth*biHeight*每个像素的字节数
那么我们这里应该是:352*288*24/8 = 304128。(我们这里的位数/像素为24,所以每个像素的字节数为3)
注:这里的biWidth必须是4的倍数,如果不是4的倍数,则需要取4的倍数,比如241,则取244;为什么必须是4的倍数?这里涉及到一个行对齐的问题:
由于Windows在进行行扫描的时候最小的单位为4个字节,所以当
图片宽 X 每个像素的字节数 != 4的整数倍
时要在每行的后面补上缺少的字节,以0填充
biXPelsPerMeter:0x00000000
biYPelsPerMeter:0x00000000
biClrUsed:0x00000000 使用所有调色板项
biClrImportant:00000000
3、调色板
上面这张BMP图片是否有调色板呢?答案是否定的。
因为从bfOffBits = 54字节可以看出,刚好是sizeof(BITMAPFILEHEADER )+sizeof(BITMAPINFOHEADER);
那么究竟调色板是什么东西?有什么用?为什么我们这种图片不带调试板呢?
我们先来说说三元色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 struct tagPALETTEENTRY { // pe
BYTE peRed;
BYTE peGreen;
BYTE peBlue;
BYTE peFlags;
} PALETTEENTRY;
那么为什么我们这张BMP不带调色板呢?
是因为我们这张BMP是24位真彩色的BMP,所谓真彩色图(true color),就是它的颜色数高达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 有关,不同类型的位图,其中的设计原理也不同,下面对此作一个对比:
4、位图数据
上面基本把调色板及相关取色策略梳理清楚了,接着回到我们上面的例子。24位BMP图,每3个字节表示一个像素,3个字节分别表示R、G、B的分量值
这里每3个字节表示一个像素的颜色,注意:由于位图信息头中的图像高度是正数,所以位图数据在文件中的排列顺序是从左下角到右上角,以行为主序排列的。
参考了BMP文件格式详解(BMP file format)的学习思路,对BMP格式进行了梳理和学习。
参考资源: