【學習總結】BMP格式

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;

調色板其實是一張映射表,標識顏色索引號與其代表的顏色的對應關係。它在文件中的佈局就像一個二維數組palette[N][4]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色圖像的調色板長
度爲242*4爲8字節。

當biBitCount=4時,爲16色圖像,BMP位圖中有16個數據結構RGBQUAD,
一個調色板佔用4字節數據,故16像的調色板長度爲16416*4爲64字節。

當biBitCount=8時,爲256色圖像,BMP位圖中有256個數據結構RGBQUAD,
一個調色板佔用4字節數據,故256色圖像的調
色板長度爲2564256*4爲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填充,數據對齊滿足以上要求,對數據的獲取速
度等都是有很大的增益。
//一個掃描行所佔的字節數計算方法:
DataSizePerLine=(biWidthbiBitCount+31)/8;DataSizePerLine= (biWidth* biBitCount+31)/8;DataSizePerLine=(biWidthbiBitCount+31)/324;DataSizePerLine= (biWidth* biBitCount+31)/32*4;

1Byte = 8Bits 想得到的就是BYTES,但是計算中間單位是4Bytes,這個明白吧?就是說即使是1Bits也作爲4Bytes來看待,因爲是
“大於或等於biWidth的,離4最近的整倍數”。 4Bytes的單位,就是32Bits的單位,所以,讓它除以32就得到了當下的4的整倍數,
但是,如果有餘數就必須給結果再增加1,既然這樣,不如在做除法之前就把它加進去,這樣,給bits加上31,確保只要有餘數,結
果就會加1。最後要得到Bytes值,需要給這個中間單位再4*4.
位圖數據的大小(不壓縮情況下):
DataSize=DataSizePerLinebiHeight;DataSize= DataSizePerLine* biHeight;

顏色表接下來位爲位圖文件的圖像數據區,在此部分記錄着每點像素對應的顏色號,其記錄方式也隨顏色模式而定,既2色圖像每點
佔1位(8位爲1字節);16色圖像每點佔4位(半字節);256色圖像每點佔8位(1字節);真彩色圖像每點佔24位(3字節)。所以,
整個數據區的大小也會隨之變化。究其規律而言,可的出如下計算公式:
=/8圖像數據信息大小=(圖像寬度*圖像高度*記錄像素的位數)/8

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");
}

1.7 參考說明

BMP數據格式詳解
代碼參考
BMP文件格式詳解(BMP file format)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章