BMP图像数据格式详解
BMP(Bitmap-File)位图文件是Windows采用的图形文件格式,在Windows环境下运行的所有图像处理软件都支持BMP图像文件格式。Windows系统内部各图像绘制操作都是以BMP为基础的。
Windows 3.0以前:
BMP图像件格式与显示设备有关,因此把这种BMP图像文件格式称为设备相关位图DDB(device-dependent bitmap)文件格式。
Windows 3.0以后:
BMP图像文件与显示设备无关,因此把这种BMP图像文件格式称为设备无关位图DIB(device-independent bitmap)文件格式。
注:Windows 3.0以后,在系统中仍然存在DDB位图,像BitBlt()这种函数就是基于DDB位图的,BMP位图文件默认的文件扩展名是BMP或者bmp(有时它也会以.DIB或.RLE作扩展名)。
一、BMP格式结构
BMP文件的数据按照从文件头开始的先后顺序分为四个部分:
◆ 位图文件头(bmp file header): 提供文件的格式、大小等信息
◆ 位图信息头(bitmap information):提供图像数据的尺寸、位平面数、压缩方式、颜色索引等信息
◆ 调色板(color palette):可选,如使用索引来表示图像,调色板就是索引与其对应的颜色的映射表
◆ 位图数据(bitmap data):图像数据。
数据段名称 | 大小(byte) | 开始地址 | 结束地址 |
---|---|---|---|
位图文件头(bitmap-file header) | 14 | 0000h | 000Dh |
位图信息头(bitmap-information header) | 40 | 000Eh | 0035h |
调色板(color table) | 由biBitCount决定 | 0036h | 未知 |
图片点阵数据(bitmap data) | 由图片大小和颜色定 | 未知 | 未知 |
在BMP文件中,如果一个数据需要用几个字节来表示的话,那么该数据的存放字节顺序为“低地址存放低位数据,高地址存放高位数据”。如 0x1756在内存中的存储顺序为:
这种存储方式称为小端方式(little endian),与之相反的是大端方式(big endian)。
BMP格式比较简单,它只包含两个重要参数:编码格式(Encoding)和像素位数(bpp, bit-per-pixel)。到目前为止,BMP格式所支持的所有像素位数与编码格式的组合如下:
序号 | 像素位数(bpp) | 编码格式(Encoding) |
---|---|---|
1 | 1 | bit |
2 | 4 | bgr(blue-green-red) |
3 | 4 | rle(run-length encode) |
4 | 8 | bgr |
5 | 8 | rle |
6 | grayscale | bgr |
7 | grayscale | rle |
8 | 16 | bgr |
9 | 16 | bitfields-555 |
10 | 16 | bitfields-565 |
11 | 16 | bitfields-customized |
12 | 24 | bgr |
13 | 32 | bgr |
14 | 32 | bitfields-888 |
15 | 32 | bitfields-customized |
其中24bpp称为真彩(true-color)图像,应用最为广泛。16bpp的bmp图像拥有存储空间小,解析速度快,仿真彩效果好等特点,经常出现在游戏软件中。grayscale(灰度)图像其实是8bpp的一种情况。
二、BMP文件头
BMP文件结构体定义如下:
1 2 3 4 5 6 7 8 | typedef struct tagBITMAPFILEHEARER { UINT16 bfType; //2Bytes,必须为“BM”,即0x424D 才是Windows位图文件 DWORD bfSize; //4Bytes,整个BMP文件的大小 UINT16 bfReserved1; //2Bytes,保留,为0 UINT16 bfReserved2; //2Bytes,保留,为0 DWORD bfOffBits; //4Bytes,文件起始位置到图像像素数据的字节偏移量 }BITMAPFILEHEADER; |
变量名 | 地址偏移 | 大小 | 作用说明 |
---|---|---|---|
bfType | 0000h | 2Bytes | 文件标识符,必须为“BM”,即0x424D才是Windows位图文件 ’BM’:Windows 3.1x,95,NT,… |
bfSize | 0002h | 4Bytes | 整个BMP文件的大小(以位B位单位)。 |
bfReserved1 | 0006h | 2Bytes | 保留,必须设置为0。 |
bfReserved2 | 0008h | 2Bytes | 保留,必须设置为0。 |
bfOffBits | 000Ah | 4Bytes | 说明从文件头0000h开始到图像像素数据的字节偏移量(以字节Bytes为单位), 调色板长度根据位图格式不同而变化,可以用这个偏移量快速从文件中读取图像数据。 |
三、BMP信息头
BMP信息头结构体定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | typedef struct _tagBMP_INFOHEADER { DWORD biSize; //4Bytes,INFOHEADER结构体大小,存在其他版本INFOHEADER,用作区分 LONG biWidth; //4Bytes,图像宽度(以像素为单位) LONG biHeight; //4Bytes,图像高度,+:图像存储顺序为Bottom2Top,-:Top2Bottom WORD biPlanes; //2Bytes,图像数据平面,BMP存储RGB数据,因此总为1 WORD biBitCount; //2Bytes,图像像素位数 DWORD biCompression; //4Bytes,0:不压缩,1:RLE8,2:RLE4 DWORD biSizeImage; //4Bytes,4字节对齐的图像数据大小 LONG biXPelsPerMeter; //4Bytes,用象素/米表示的水平分辨率 LONG biYPelsPerMeter; //4Bytes,用象素/米表示的垂直分辨率 DWORD biClrUsed; //4Bytes,实际使用的调色板索引数,0:使用所有的调色板索引 DWORD biClrImportant; //4Bytes,重要的调色板索引数,0:所有的调色板索引都重要 }BMP_INFOHEADER; |
变量名 | 地址偏移 | 大小 | 作用说明 |
---|---|---|---|
biSize | 000Eh | 4Bytes | BMP信息头即BMP_INFOHEADER结构体所需要的字节数(以字节为单位)。 |
biWidth | 0012h | 4Bytes | 图像的宽度(以像素为单位)。 |
biHeight | 0016h | 4Bytes | 图像的高度(以像素为单位)。 还有一个用处,指明图像是正向的位图还是倒向的位图, 值是正数说明图像是倒向的即图像存储是由下到上; 值是负数说明图像是倒向的即图像存储是由上到下。 大多数BMP位图是倒向的位图,所以此值是正值。 |
biPlanes | 001Ah | 2Bytes | 为目标设备说明位面数,其值总设置为1。 |
biBitCount | 001Ch | 2Bytes | 说明一个像素点占几位(以比特位/像素为单位),其值可为1,4,8,16,24或32。 |
biCompression | 001Eh | 4Bytes | 说明图像数据的压缩类型,取值范围为: 0 BI_RGB 不压缩(最常用) 1 BI_RLE8 8比特游程编码(BLE),只用于8位位图 2 BI_RLE4 4比特游程编码(BLE),只用于4位位图 3 BI_BITFIELDS比特域(BLE),只用于16/32位位图 |
biSizeImage | 0022h | 4Bytes | 说明图像的大小,以字节为单位。当用BI_RGB格式时,总设置为0 |
biXPelsPerMeter | 0026h | 4Bytes | 说明水平分辨率,用像素/米表示,有符号整数。 |
biYPelsPerMeter | 002Ah | 4Bytes | 说明垂直分辨率,用像素/米表示,有符号整数。 |
biClrUsed | 002Eh | 4Bytes | 说明位图实际使用的调色板索引数,0:使用所有的调色板索引。 |
biClrImportant | 0032h | 4Bytes | 说明对图像显示有重要影响的颜色索引的数目,如果是0,表示都重要。 |
四、BMP调色板
BMP调色板结构体定义如下:
1 2 3 4 5 6 7 | typedef struct _tagRGBQUAD { BYTE rgbBlue; //指定蓝色强度 BYTE rgbGreen; //指定绿色强度 BYTE rgbRed; //指定红色强度 BYTE rgbReserved; //保留,设置为0 } RGBQUAD; |
调色板其实是一张映射表,标识颜色索引号与其代表的颜色的对应关系。它在文件中的布局就像一个二维数组
palette[N][4],其中N表示总的颜色索引数,每行的四个元素分别表示该索引对应的B、G、R、和Alpha的值,每个分量占一个字节。如不设透明通道时,Alpha为0。N为256。
索引:(蓝,绿,红,Alpha)
说明:使用调色板是为了减少文件的存储大小,如果其索引需要的位数和直接存储一样没有什么减少,那么也就没有必要使用调色板;调色板最多只需要256项,因此,8位以上则不需要调色板。
1,4,8位图像才会使用调色板数据,16,24,32位图像不需要调色板数据,即调色板最多只需要256项(索引0 - 255)。
颜色表的大小根据所使用的颜色模式而定:2色图像为8字节;16色图像位64字节;256色图像为1024字节。
其中,每4字节表示一种颜色,并以B(蓝色)、G(绿色)、R(红色)、alpha(32位位图的透明度值,一般不需要)。
即首先4字节表示颜色号1的颜色,接下来表示颜色号2的颜色,依此类推。
颜色表中RGBQUAD结构数据的个数有biBitCount来确定,
当biBitCount=1,4,8时,分别有2,16,256个表项。
当biBitCount=1时,为2色图像,BMP位图中有2个数据结构RGBQUAD,
一个调色板占用4字节数据,故2色图像的调色板长度为
2?4为8字节。
当biBitCount=4时,为16色图像,BMP位图中有16个数据结构RGBQUAD,
一个调色板占用4字节数据,故16像的调色板长度为
16?4为64字节。
当biBitCount=8时,为256色图像,BMP位图中有256个数据结构RGBQUAD,
一个调色板占用4字节数据,故256色图像的调色板长度为
256?4为1024字节。
当biBitCount=16,24或32时,没有颜色表。
五、BMP图像数据区
位图数据记录了位图的每一个像素值,记录顺序是在扫描行内是从左到右,扫描行之间是从下到上,又称为Bottom_Up位图。
位图的一个像素值所占的字节数:
当biBitCount=1时,8个像素占1个字节;
当biBitCount=4时,2个像素占1个字节;
当biBitCount=8时,1个像素占1个字节;
当biBitCount=24时,1个像素占3个字节;
也就是说,一个像素所占的字节数是biBitCount/8
对齐规则
Windows规定一个扫描行所占的字节数必须是4的倍数(即以long为单位),不足的以0填充,数据对齐满足以上要求,对数据的获取速度等都是有很大的增益。
//一个扫描行所占的字节数计算方法:
DataSizePerLine=(biWidth?biBitCount+31)/8;即
DataSizePerLine=(biWidth?biBitCount+31)/32?4;
1Byte = 8Bits 想得到的就是BYTES,但是计算中间单位是4Bytes,这个明白吧?就是说即使是1Bits也作为4Bytes来看待,因为是“大于或等于biWidth的,离4最近的整倍数”。 4Bytes的单位,就是32Bits的单位,所以,让它除以32就得到了当下的4的整倍数,但是,如果有余数就必须给结果再增加1,既然这样,不如在做除法之前就把它加进去,这样,给bits加上31,确保只要有余数,结果就会加1。最后要得到Bytes值,需要给这个中间单位再
?4.
位图数据的大小(不压缩情况下):
DataSize=DataSizePerLine?biHeight;
颜色表接下来位为位图文件的图像数据区,在此部分记录着每点像素对应的颜色号,其记录方式也随颜色模式而定,既2色图像每点占1位(8位为1字节);16色图像每点占4位(半字节);256色图像每点占8位(1字节);真彩色图像每点占24位(3字节)。所以,整个数据区的大小也会随之变化。究其规律而言,可的出如下计算公式:
图像数据信息大小=(图像宽度?图像高度?记录像素的位数)/8。
六、识别BMP图片包含的数据内容
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | #include <stdio.h> #include <stdlib.h> #include <iostream> using namespace std; typedef unsigned char BYTE; typedef unsigned short WORD; typedef unsigned int DWORD; typedef unsigned short UINT16; typedef long LONG; typedef struct tagBITMAPFILEHEARER { //UINT16 bfType; DWORD bfSize; UINT16 bfReserved1; UINT16 bfReserved2; DWORD bfOffBits; }BITMAPFILEHEADER; typedef struct _tagBMP_INFOHEADER { DWORD biSize; LONG biWidth; LONG biHeight; WORD biPlanes; WORD biBitCount; DWORD biCompression; DWORD biSizeImage; LONG biXPelsPerMeter; LONG biYPelsPerMeter; DWORD biClrUsed; DWORD biClrImportant; }BMP_INFOHEADER; typedef struct _tagRGBQUAD { BYTE rgbBlue; BYTE rgbGreen; BYTE rgbRed; BYTE rgbReserved; } RGBQUAD; typedef struct tagIMAGEDATA { BYTE red; BYTE green; BYTE blue; }IMAGEDATA; BITMAPFILEHEADER strHead; RGBQUAD strPla[256]; BMP_INFOHEADER strInfo; void showBmpHead(BITMAPFILEHEADER pBmpHead) { cout << "Bitmap file header" << endl << endl; cout << "File size:" << pBmpHead.bfSize << endl; cout << "Reserved1:" << pBmpHead.bfReserved1 << endl; cout << "Reserved2:" << pBmpHead.bfReserved2 << endl; cout << "The number of bytes offset from the actual bitmap data:" << pBmpHead.bfOffBits << endl << endl; } void showBmpInforHead(BMP_INFOHEADER pBmpInforHead) { cout << "Bitmap information header" << endl << endl; cout << "The length of the structure:" << pBmpInforHead.biSize << endl; cout << "Width:" << pBmpInforHead.biWidth << endl; cout << "Height:" << pBmpInforHead.biHeight << endl; cout << "biPlanes:" << pBmpInforHead.biPlanes << endl; cout << "biBitCount:" << pBmpInforHead.biBitCount << endl; cout << "Compression:" << pBmpInforHead.biCompression << endl; cout << "The number of bytes consumed by the actual bitmap data:" << pBmpInforHead.biSizeImage << endl; cout << "X:" << pBmpInforHead.biXPelsPerMeter << endl; cout << "Y:" << pBmpInforHead.biYPelsPerMeter << endl; cout << "Clr:" << pBmpInforHead.biClrUsed << endl; cout << "Significant color number:" << pBmpInforHead.biClrImportant << endl; } RGBQUAD* ReadFile(char *strFile) { FILE *fpBMP; fpBMP = fopen(strFile, "rb"); if (fpBMP != NULL) { WORD bfType; fread(&bfType, 1, sizeof(WORD), fpBMP); if (0x4d42 != bfType) { cout << "the file is not a bmp file!" << endl; return NULL; } fread(&strHead, 1, sizeof(BITMAPFILEHEADER), fpBMP); showBmpHead(strHead); fread(&strInfo, 1, sizeof(BMP_INFOHEADER), fpBMP); showBmpInforHead(strInfo); cout << endl; int i; IMAGEDATA **imagedata; imagedata = (IMAGEDATA **)malloc(sizeof(IMAGEDATA*)*strInfo.biHeight); for (i = 0; i < strInfo.biHeight; i++) *(imagedata + i) = (IMAGEDATA*)malloc(sizeof(IMAGEDATA)*strInfo.biWidth); for (i = 0; i < strInfo.biHeight; i++) { for (int j = 0; j < strInfo.biWidth; j++) { fread(*(imagedata + i) + j, sizeof(IMAGEDATA), 1, fpBMP); } } cout << "==================red======================" << endl; for (i = 0; i < strInfo.biHeight; i++) { for (int j = 0; j < strInfo.biWidth; j++) { printf("%d ", imagedata[i][j].red); } cout << endl; } cout << "===================green=====================" << endl; for (i = 0; i < strInfo.biHeight; i++) { for (int j = 0; j < strInfo.biWidth; j++) { printf("%d ", imagedata[i][j].green); } cout << endl; } cout << "====================blue====================" << endl; for (i = 0; i < strInfo.biHeight; i++) { for (int j = 0; j < strInfo.biWidth; j++) { printf("%d ", imagedata[i][j].blue); } cout << endl; } fclose(fpBMP); } else { cout << "file open error!" << endl; return NULL; } } int main() { char strFile[30] = "..//test.bmp"; cout << strFile << endl; ReadFile(strFile); system("pause"); } |
参考说明
BMP数据格式详解
代码参考
BMP文件格式详解(BMP file format)