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作擴展名)。
1.1 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的一種情況。
1.2 BMP文件頭
BMP文件結構體定義如下:
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爲單位), 調色板長度根據位圖格式不同而變化, |
可以用這個偏移量快速從文件中讀取圖像數據。
1.3 BMP信息頭
BMP信息頭結構體定義如下:
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,表示都重要。 |
1.4 BMP調色板
BMP調色板結構體定義如下:
typedef struct _tagRGBQUAD
{
BYTE rgbBlue; //指定藍色強度
BYTE rgbGreen; //指定綠色強度
BYTE rgbRed; //指定紅色強度
BYTE rgbReserved; //保留,設置爲0
} RGBQUAD;
調色板其實是一張映射表,標識顏色索引號與其代表的顏色的對應關係。它在文件中的佈局就像一個二維數組,
其中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色圖像的調色板長
度爲爲8字節。
當biBitCount=4時,爲16色圖像,BMP位圖中有16個數據結構RGBQUAD,
一個調色板佔用4字節數據,故16像的調色板長度爲爲64字節。
當biBitCount=8時,爲256色圖像,BMP位圖中有256個數據結構RGBQUAD,
一個調色板佔用4字節數據,故256色圖像的調
色板長度爲爲1024字節。
當biBitCount=16,24或32時,沒有顏色表。
1.5 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填充,數據對齊滿足以上要求,對數據的獲取速
度等都是有很大的增益。
//一個掃描行所佔的字節數計算方法:
即
1Byte = 8Bits 想得到的就是BYTES,但是計算中間單位是4Bytes,這個明白吧?就是說即使是1Bits也作爲4Bytes來看待,因爲是
“大於或等於biWidth的,離4最近的整倍數”。 4Bytes的單位,就是32Bits的單位,所以,讓它除以32就得到了當下的4的整倍數,
但是,如果有餘數就必須給結果再增加1,既然這樣,不如在做除法之前就把它加進去,這樣,給bits加上31,確保只要有餘數,結
果就會加1。最後要得到Bytes值,需要給這個中間單位再.
位圖數據的大小(不壓縮情況下):
顏色表接下來位爲位圖文件的圖像數據區,在此部分記錄着每點像素對應的顏色號,其記錄方式也隨顏色模式而定,既2色圖像每點
佔1位(8位爲1字節);16色圖像每點佔4位(半字節);256色圖像每點佔8位(1字節);真彩色圖像每點佔24位(3字節)。所以,
整個數據區的大小也會隨之變化。究其規律而言,可的出如下計算公式:
。
1.6 識別BMP圖片包含的數據內容
代碼如下:
#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");
}