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格式儲存到磁碟檔案中時,微軟極力推薦你以DIB格式儲存),目的是為了讓Windows能夠在任何型別的顯示裝置上顯示所儲存的圖象。BMP點陣圖檔案預設的副檔名是BMP或者bmp(有時它也會以.DIB或.RLE作副檔名)。
二.BMP格式結構
BMP檔案的資料按照從檔案頭開始的先後順序分為四個部分:
◆ 點陣圖檔案頭(bmp file header): 提供檔案的格式、大小等資訊
◆ 點陣圖資訊頭(bitmap information):提供影象資料的尺寸、位平面數、壓縮方式、顏色索引等資訊
◆ 調色盤(color palette):可選,如使用索引來表示影象,調色盤就是索引與其對應的顏色的對映表
◆ 點陣圖資料(bitmap data):影象資料區
BMP圖片檔案資料表如下:
資料段名稱 |
大小(byte) |
開始地址 |
結束地址 |
點陣圖檔案頭(bitmap-file header) |
14 |
0000h |
000Dh |
點陣圖資訊頭(bitmap-information header) |
40 |
000Eh |
0035h |
調色盤(color table) |
由biBitCount決定 |
0036h |
未知 |
圖片點陣資料(bitmap data) |
由圖片大小和顏色定 |
未知 |
未知 |
三.BMP檔案頭
BMP檔案頭結構體定義如下:
typedef struct tagBITMAPFILEHEADER
{
UINT16 bfType; //2Bytes,必須為"BM",即0x424D 才是Windows點陣圖檔案
DWORD bfSize; //4Bytes,整個BMP檔案的大小
UINT16 bfReserved1; //2Bytes,保留,為0
UINT16 bfReserved2; //2Bytes,保留,為0
DWORD bfOffBits; //4Bytes,檔案起始位置到影象畫素資料的位元組偏移量
} BITMAPFILEHEADER;
BMP檔案頭資料表如下:
變數名 |
地址偏移 |
大小 |
作用說明 |
bfType |
0000h |
2Bytes |
檔案識別符號,必須為"BM",即0x424D 才是Windows點陣圖檔案 ‘BM’:Windows 3.1x, 95, NT,… ‘BA’:OS/2 Bitmap Array ‘CI’:OS/2 Color Icon ‘CP’:OS/2 Color Pointer ‘IC’:OS/2 Icon ‘PT’:OS/2 Pointer 因為OS/2系統並沒有被普及開,所以在程式設計時,你只需判斷第一個標識“BM”就行 |
bfSize |
0002h |
4Bytes |
整個BMP檔案的大小(以位B為單位) |
bfReserved1 |
0006h |
2Bytes |
保留,必須設定為0 |
bfReserved2 |
0008h |
2Bytes |
保留,必須設定為0 |
bfOffBits |
000Ah |
4Bytes |
說明從檔案頭0000h開始到影象畫素資料的位元組偏移量(以位元組Bytes為單位),以為點陣圖的調色盤長度根據點陣圖格式不同而變化,可以用這個偏移量快速從檔案中讀取影象資料 |
四.BMP資訊頭
BMP資訊頭結構體定義如下:
typedef struct _tagBMP_INFOHEADER
{
DWORD biSize; //4Bytes,INFOHEADER結構體大小,存在其他版本I NFOHEADER,用作區分
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; //4 Bytes,用象素/米表示的水平解析度
LONG biYPelsPerMeter; //4 Bytes,用象素/米表示的垂直解析度
DWORD biClrUsed; //4 Bytes,實際使用的調色盤索引數,0:使用所有的調色盤索引
DWORD biClrImportant; //4 Bytes,重要的調色盤索引數,0:所有的調色盤索引都重要
}BMP_INFOHEADER;
BMP資訊頭資料表如下:
變數名 |
地址偏移 |
大小 |
作用說明 |
biSize |
000Eh |
4Bytes |
BNP資訊頭即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位點陣圖 4 |
biSizeImage |
0022h |
4Bytes |
說明影象的大小,以位元組為單位。當用BI_RGB格式時,總設定為0 |
biXPelsPerMeter |
0026h |
4Bytes |
說明水平解析度,用畫素/米表示,有符號整數 |
biYPelsPerMeter |
002Ah |
4Bytes |
說明垂直解析度,用畫素/米表示,有符號整數 |
biClrUsed |
002Eh |
4Bytes |
說明點陣圖實際使用的調色盤索引數,0:使用所有的調色盤索引 |
biClrImportant |
0032h |
4Bytes |
說明對影象顯示有重要影響的顏色索引的數目,如果是0,表示都重要。 |
六.BMP調色盤
BMP調色盤結構體定義如下:
typedef struct _tagRGBQUAD
{
BYTE rgbBlue; //指定藍色強度
BYTE rgbGreen; //指定綠色強度
BYTE rgbRed; //指定紅色強度
BYTE rgbReserved; //保留,設定為0
} RGBQUAD;
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影象資料區
點陣圖資料記錄了點陣圖的每一個畫素值,記錄順序是在掃描行內是從左到右,掃描行之間是從下到上。點陣圖的一個畫素值所佔的位元組數:
當biBitCount=1時,8個畫素佔1個位元組;
當biBitCount=4時,2個畫素佔1個位元組;
當biBitCount=8時,1個畫素佔1個位元組;
當biBitCount=24時,1個畫素佔3個位元組;
Windows規定一個掃描行所佔的位元組數必須是4的倍數(即以long為單位),不足的以0填充,
一個掃描行所佔的位元組數計算方法:
DataSizePerLine= (biWidth* biBitCount+31)/8;
// 一個掃描行所佔的位元組數
DataSizePerLine= DataSizePerLine/4*4; // 位元組數必須是4的倍數
點陣圖資料的大小(不壓縮情況下):
DataSize= DataSizePerLine* biHeight;
顏色表接下來位為點陣圖檔案的影象資料區,在此部分記錄著每點畫素對應的顏色號,其記錄方式也隨顏色模式而定,既2色影象每點佔1位(8位為1位元組);16色影象每點佔4位(半位元組);256色影象每點佔8位(1位元組);真彩色影象每點佔24位(3位元組)。所以,整個資料區的大小也會隨之變化。究其規律而言,可的出如下計算公式:影象資料資訊大小=(影象寬度*影象高度*記錄畫素的位數)/8。
8點陣圖簡單例子:
typedef unsigned char LBYTE;
typedef unsigned short LWORD;
typedef unsigned int LDWORD;
typedef long LLONG;
#pragma pack(2)
typedef struct
{
LWORD bfType; //點陣圖檔案型別,必須是0X4D42
LDWORD bfSize; // 點陣圖檔案大小
LWORD bfReserved1; //windows保留字
LWORD bfReserved2; //windows保留字,暫時不用
LDWORD bfOffBits; //從檔案頭到實際的點陣圖資料的偏移位元組數
}LBITMAPFILEHEADER;
typedef struct
{
LDWORD biSize; //點陣圖資訊頭的長度,40位元組
LLONG biWidth; //點陣圖的寬度
LLONG biHeight; //點陣圖的高度
LWORD biPlanes; //目標裝置級別,必須為1
LWORD biBitCount; // 每個畫素所佔位數(bit),二值影象為1,灰度影象為8,真彩色影象為24
LDWORD biCompression; // 點陣圖壓縮型別
LDWORD biSizeImage; // 實際的點陣圖資料佔用的位元組數
LLONG biXPelsPerMeter; //指定目標裝置的水平解析度
LLONG biYPelsPerMeter; //指定目標裝置的垂直解析度
LDWORD biClrUsed; // 點陣圖實際用到的顏色數
LDWORD biClrImportant; // 點陣圖顯示過程中重要的顏色數
}LBITMAPINFOHEADER;
typedef struct
{
LBYTE rgbBlue; //藍色分量
LBYTE rgbGreen; //綠色分量
LBYTE rgbRed; //紅色分量
LBYTE rgbReserved; //保留位元組,暫時不用
}LRGBQUAD;
void SaveBmp(char * fileName, unsigned char *imgBuffer, int imWidth, int imHeight)
{
if (!imgBuffer)
{
return;
}
int biBitCount = 8;
int colorTablesize = 1024; //灰度影象顏色表
int lineByte = (imWidth * biBitCount / 8 + 3) / 4 * 4;
FILE *fp = fopen(fileName, "wb");
if (!fp)
{
return;
}
LBITMAPFILEHEADER filehead;
filehead.bfType = 0x4D42;
filehead.bfSize = sizeof(LBITMAPFILEHEADER)+sizeof(LBITMAPINFOHEADER)+
colorTablesize + lineByte * imHeight;
filehead.bfReserved1 = 0;
filehead.bfReserved2 = 0;
filehead.bfOffBits = sizeof(LBITMAPFILEHEADER)+sizeof(LBITMAPINFOHEADER)+colorTablesize;
//寫點陣圖檔案頭進檔案
int count = sizeof(LBITMAPFILEHEADER);
fwrite(&filehead, count, 1, fp);
//申請點陣圖檔案資訊頭結構變數, 填寫檔案資訊頭資訊
LBITMAPINFOHEADER infoHead;
infoHead.biBitCount = biBitCount;
infoHead.biClrImportant = 0;
infoHead.biClrUsed = 0;
infoHead.biSize = sizeof(LBITMAPINFOHEADER);
infoHead.biWidth = imWidth;
infoHead.biHeight = imHeight;
infoHead.biPlanes = 1;
infoHead.biCompression = 0;
infoHead.biSizeImage = lineByte * imHeight;
infoHead.biXPelsPerMeter = 0;
infoHead.biYPelsPerMeter = 0;
count = sizeof(LBITMAPINFOHEADER);
fwrite(&infoHead, count, 1, fp);
LRGBQUAD *pColorTable = (LRGBQUAD*)malloc(sizeof(LRGBQUAD)*256);
for (int i = 0; i < 256; i++)
{
pColorTable[i].rgbBlue = i;
pColorTable[i].rgbGreen = i;
pColorTable[i].rgbRed = i;
//pColorTable[i].rgbReserved = 0;
}
count = sizeof(LRGBQUAD);
fwrite(pColorTable, count, 256, fp);
//寫點陣圖資料進檔案
fwrite(imgBuffer, imHeight*lineByte, 1, fp);
fclose(fp);
free(pColorTable);
return 1;
}