BMP圖像格式


BMP(全稱Bitmap)是Window操作系統中的標准圖像文件格式,可以分成兩類:設備相關位圖(DDB)和設備無關位圖(DIB),使用非常廣。它采用位映射存儲格式,除了圖像深度可選以外,不采用其他任何壓縮,因此,BMP文件所占用的空間很大。BMP文件的圖像深度可選lbit、4bit、8bit及24bit。BMP文件存儲數據時,圖像的掃描方式是按從左到右、從下到上的順序。由於BMP文件格式是Windows環境中交換與圖有關的數據的一種標准,因此在Windows環境中運行的圖形圖像軟件都支持BMP圖像格式。

文件格式

格式組成

典型的BMP圖像文件由四部分組成:

  • 位圖頭文件數據結構,它包含BMP圖像文件的類型、顯示內容等信息;
  • 位圖信息數據結構,它包含有BMP圖像的寬、高、壓縮方法,以及定義顏色等信息;
  • 調色板,這個部分是可選的,有些位圖需要調色板,有些位圖,比如真彩色圖(24位的BMP)就不需要調色板;
  • 位圖數據,這部分的內容根據BMP位圖使用的位數不同而不同,在24位圖中直接使用RGB,而其他的小於24位的使用調色板中顏色索引值。

格式類型

位圖一共有兩種類型,即:設備相關位圖(DDB)和設備無關位圖(DIB)。DDB位圖在早期的Windows系統(Windows 3.0以前)中是很普遍的,事實上它也是唯一的。然而,隨着顯示器制造技術的進步,以及顯示設備的多樣化,DDB位圖的一些固有的問題開始浮現出來了。比如,它不能夠存儲(或者說獲取)創建這張圖片的原始設備的分辨率,這樣,應用程序就不能快速的判斷客戶機的顯示設備是否適合顯示這張圖片。為了解決這一難題,微軟創建了DIB位圖格式。
設備無關位圖 (Device-Independent Bitmap)
DIB位圖包含下列的顏色和尺寸信息:

  • 原始設備(即創建圖片的設備)的顏色格式。
  • 原始設備的分辨率。
  • 原始設備的調色板
  • 一個位數組,由紅、綠、藍(RGB)三個值代表一個像素。
  •  一個數組壓縮標志,用於表明數據的壓縮方案(如果需要的話)。

以上這些信息保存在BITMAPINFO結構中,該結構由BITMAPINFOHEADER結構和兩個或更多個RGBQUAD結構所組成。BITMAPINFOHEADER結構所包含的成員表明了圖像的尺寸、原始設備的顏色格式、以及數據壓縮方案等信息。RGBQUAD結構標識了像素所用到的顏色數據。
DIB位圖也有兩種形式,即:底到上型DIB(bottom-up),和頂到下型DIB(top-down)。底到上型DIB的原點(origin)在圖像的左下角,而頂到下型DIB的原點在圖像的左上角。如果DIB的高度值(由BITMAPINFOHEADER結構中的biHeight成員標識)是一個正值,那么就表明這個DIB是一個底到上型DIB,如果高度值是一個負值,那么它就是一個頂到下型DIB。注意:頂到下型的DIB位圖是不能被壓縮的。
位圖的顏色格式是通過顏色面板值(planes)和顏色位值(bitcount)計算得來的,顏色面板值永遠是1,而顏色位值則可以是1、4、8、16、24、32其中的一個。如果它是1,則表示位圖是一張單色位圖(譯者注:通常是黑白位圖,只有黑和白兩種顏色,當然它也可以是任意兩種指定的顏色),如果它是4,則表示這是一張VGA位圖,如果它是8、16、24、或是32,則表示該位圖是其他設備所產生的位圖。如果應用程序想獲取當前顯示設備(或打印機)的顏色位值(或稱位深度),可調用API函數GetDeviceCaps(),並將第二個參數設為BITSPIXEL即可。

顯示設備的分辨率是以每米多少個像素來表明的,應用程序可以通過以下三個步驟來獲取顯示設備或打印機的水平分辨率:

  • 調用GetDeviceCaps()函數,指定第二個參數為HORZRES。
  • 再次調用GetDeviceCaps()函數,指定第二個參數為HORZSIZE。
  • 用第一個返回值除以第二個返回值。即:GetDeviceCaps(hDC,HORZRES)/GetDeviceCaps(hDC,HORZSIZE);

應用程序也可以使用相同的三個步驟來獲取設備的垂直分辨率,不同之處只是要將HORZRES替換為VERTRES,把HORZSIZE替換為VERTSIZE,即可。
調色板是被保存在一個RGBQUAD結構的數組中,該結構指出了每一種顏色的紅、綠、藍的分量值。位數組中的每一個索引都對應於一個調色板項(即一個RGBQUAD結構),應用程序將根據這種對應關系,將像素索引值轉換為像素RGB值(真實的像素顏色)。應用程序也可以通過調用GetDeviceCaps()函數來獲取當前顯示設備的調色板尺寸(將該函數的第二個參數設為NUMCOLORS即可)。
Win32 API支持位數據的壓縮(只對8位和4位的底到上型DIB位圖)。壓縮方法是采用運行長度編碼方案(RLE),RLE使用兩個字節來描述一個句法,第一個字節表示重復像素的個數,第二個字節表示重復像素的索引值。有關壓縮位圖的詳細信息請參見對BITMAPINFOHEADER結構的解釋。
應用程序可以從一個DDB位圖創建出一個DIB位圖,步驟是,先初始化一些必要的結構,然后再調用GetDIBits()函數。不過,有些顯示設備有可能不支持這個函數,你可以通過調用GetDeviceCaps()函數來確定一下(GetDeviceCaps()函數在調用時指定RC_DI_BITMAP作為RASTERCAPS的標志)。
應用程序可以用DIB去設置顯示設備上的像素(譯者注:也就是顯示DIB),方法是調用SetDIBitsToDevice()函數或調用StretchDIBits()函數。同樣,有些顯示設備也有可能不支持以上這兩個函數,這時你可以指定RC_DIBTODEV作為RASTERCAPS標志,然后調用GetDeviceCaps()函數來判斷該設備是否支持SetDIBitsToDevice()函數。也可以指定RC_STRETCHDIB作為RASTERCAPS標志來調用GetDeviceCaps()函數,來判斷該設備是否支持StretchDIBits()函數。
如果應用程序只是要簡單的顯示一個已經存在的DIB位圖,那么它只要調用SetDIBitsToDevice()函數就可以。比如一個電子表格軟件,它可以打開一個圖表文件,在窗口中簡單的調用SetDIBitsToDevice()函數,將圖形顯示在窗口中。但如果應用程序要重復的繪制位圖的話,則應該使用BitBlt()函數,因為BitBlt()函數的執行速度要比SetDIBitsToDevice()函數快很多。
設備相關位圖 (Device-Dependent Bitmaps)
設備相關位圖(DDB)之所以現在還被系統支持,只是為了兼容舊的Windows 3.0軟件,如果程序員現在要開發一個與位圖有關的程序,則應該盡量使用或生成DIB格式的位圖。
DDB位圖是被一個單個結構BITMAP所描述,這個結構的成員標明了該位圖的寬度、高度、設備的顏色格式等信息。
DDB位圖也有兩種類型,即:可廢棄的(discardable)DDB和不可廢棄的(nondiscardable)DDB。可廢棄的DDB位圖就是一種當系統內存缺乏,並且該位圖也沒有被選入設備描述表(DC)的時候,系統就會把該DDB位圖從內存中清除(即廢棄)。不可廢棄的DDB則是無論系統內存多少都不會被系統清除的DDB。API函數CreateDiscardableBitmap()函數可用於創建可廢棄位圖。而函數CreateBitmap()、CreateCompatibleBitmap()、和CreateBitmapIndirect()可用於創建不可廢棄的位圖。
應用程序可以通過一個DIB位圖而創建一個DDB位圖,只要先初始化一些必要的結構,然后再調用CreateDIBitmap()函數就可以。如果在調用該函數時指定了CBM_INIT標志,那么這一次調用就等價於先調用CreateCompatibleBitmap()創建當前設備格式的DDB位圖,然后又調用SetDIBits()函數轉換DIB格式到DDB格式。(可能有些設備並不支持SetDIBits()函數,你可以指定RC_DI_BITMAP作為RASTERCAPS的標志,然后調用GetDeviceCaps()函數來判斷一下)。
對應數據結構

  • BMP文件組成

BMP文件由文件頭、位圖信息頭、顏色信息和圖形數據四部分組成。

  • BMP文件頭(14字節)

BMP文件頭數據結構含有BMP文件的類型、文件大小和位圖起始位置等信息。
其結構定義如下:
typedef struct tagBITMAPFILEHEADER
{
WORD bfType; // 位圖文件的類型,必須為BM(1-2字節)
DWORD bfSize; // 位圖文件的大小,以字節為單位(3-6字節)
WORD bfReserved1; // 位圖文件保留字,必須為0(7-8字節)
WORD bfReserved2; // 位圖文件保留字,必須為0(9-10字節)
DWORD bfOffBits; // 位圖數據的起始位置,以相對於位圖(11-14字節)
// 文件頭的偏移量表示,以字節為單位
} BITMAPFILEHEADER;

  • 位圖信息頭(40字節)

BMP位圖信息頭數據用於說明位圖的尺寸等信息。
typedef struct tagBITMAPINFOHEADER{
DWORD biSize; // 本結構所占用字節數(15-18字節)
LONG biWidth; // 位圖的寬度,以像素為單位(19-22字節)
LONG biHeight; // 位圖的高度,以像素為單位(23-26字節)
WORD biPlanes; // 目標設備的級別,必須為1(27-28字節)
WORD biBitCount;// 每個像素所需的位數,必須是1(雙色),(29-30字節)
// 4(16色),8(256色)或24(真彩色)之一
DWORD biCompression; // 位圖壓縮類型,必須是 0(不壓縮),(31-34字節)
// 1(BI_RLE8壓縮類型)或2(BI_RLE4壓縮類型)之一
DWORD biSizeImage; // 位圖的大小,以字節為單位(35-38字節)
LONG biXPelsPerMeter; // 位圖水平分辨率,每米像素數(39-42字節)
LONG biYPelsPerMeter; // 位圖垂直分辨率,每米像素數(43-46字節)
DWORD biClrUsed;// 位圖實際使用的顏色表中的顏色數(47-50字節)
DWORD biClrImportant;// 位圖顯示過程中重要的顏色數(51-54字節)
} BITMAPINFOHEADER;

  • 顏色表

顏色表用於說明位圖中的顏色,它有若干個表項,每一個表項是一個RGBQUAD類型的結構,定義一種顏色。RGBQUAD結構的定義如下:
typedef struct tagRGBQUAD {
BYTE rgbBlue;// 藍色的亮度(值范圍為0-255)
BYTE rgbGreen; // 綠色的亮度(值范圍為0-255)
BYTE rgbRed; // 紅色的亮度(值范圍為0-255)
BYTE rgbReserved;// 保留,必須為0
} RGBQUAD;
顏色表中RGBQUAD結構數據的個數有biBitCount來確定:
當biBitCount=1,4,8時,分別有2,16,256個表項;
當biBitCount=24時,沒有顏色表項。
位圖信息頭和顏色表組成位圖信息,BITMAPINFO結構定義如下:
typedef struct tagBITMAPINFO {
BITMAPINFOHEADER bmiHeader; // 位圖信息頭
RGBQUAD bmiColors[1]; // 顏色表
} BITMAPINFO;

  • 位圖數據

位圖數據記錄了位圖的每一個像素值,記錄順序是在掃描行內是從左到右,掃描行之間是從下到上。位圖的一個像素值所占的字節數:
當biBitCount=1時,8個像素占1個字節;
當biBitCount=4時,2個像素占1個字節;
當biBitCount=8時,1個像素占1個字節;
當biBitCount=24時,1個像素占3個字節;
Windows規定一個掃描行所占的字節數必須是
4的倍數(即以long為單位),不足的以0填充,
biSizeImage = ((((bi.biWidth * bi.biBitCount) + 31) & ~31) / 8) * bi.biHeight;
具體數據舉例:
如某BMP文件開頭:
424D 4690 0000 0000 0000 4600 0000 2800 0000 8000 0000 9000 0000 0100*1000 0300 0000 0090 0000 A00F 0000 A00F 0000 0000 0000 0000 0000*00F8 E007 1F00 0000*02F1 84F1 04F1 84F1 84F1 06F2 84F1 06F2 04F2 86F2 06F2 86F2 86F2 …. ….
讀取方法

1 /*
2 功能:在圖片的第50行畫一條黑線
3 為簡化代碼,只支持24位色的圖片
4 codeblocks下正確運行。VC下需要將二維數組img改為malloc動態分配。
5 */
6 #include
7 #include
8 typedef struct{
9 BYTE b;
10 BYTE g;
11 BYTE r;
12 }RGB;
13 int main( void )
14 {
15 BITMAPFILEHEADER fileHeader;
16 BITMAPINFOHEADER infoHeader;
17 FILE* pfin =fopen("原始圖像.bmp","rb");
18 FILE* pfout = fopen( "修改后的圖像.bmp" , "wb");
19 //Read the Bitmap file header;
20 fread(&fileHeader,sizeof(BITMAPFILEHEADER),1,pfin);
21 //Read the Bitmap info header;
22 fread(&infoHeader,sizeof(BITMAPINFOHEADER),1,pfin);
23 //為簡化代碼,只處理24位彩色
24 if( infoHeader.biBitCount == 24 )
25 {
26 int size = infoHeader.biWidth*infoHeader.biHeight;
27 RGB img[infoHeader.biHeight][infoHeader.biWidth];
28 fread( img , sizeof(RGB) , size , pfin );
29 //把第50行染成黑色
30 int i = 0;
31 for( ; i < infoHeader.biWidth ; i++ )
32 {
33 img[50][i].b =img[50][i].g=img[50][i].r= 0;
34 }
35 //將修改后的圖片保存到文件
36 fwrite( &fileHeader , sizeof(fileHeader) , 1 , pfout );
37 fwrite( &infoHeader , sizeof(infoHeader) , 1 , pfout );
38 fwrite( img , sizeof(RGB) , size , pfout );
39 }
40 }

文件部分

圖像文件頭

1)1-2:(這里的數字代表的是”字”,即兩個字節,下同)圖像文件頭。0x4d42=’BM’,表示是Windows支持的BMP格式。(注意:查ascii表B 0×42,M0x4d,bfType 為兩個字節,B為low字節,M為high字節所以bfType=0x4D42,而不是0x424D,但注意)
2)3-6:整個文件大小。4690 0000,為00009046h=36934。
3)7-8:保留,必須設置為0。
4)9-10:保留,必須設置為0。
5)11-14:從文件開始到位圖數據之間的偏移量(14+40+4*(2^biBitCount))。4600 0000,為00000046h=70,上面的文件頭就是35字=70字節。

位圖信息頭

6)15-18:位圖圖信息頭長度。
7) 19-22:位圖寬度,以像素為單位。8000 0000,為00000080h=128。
8)23-26:位圖高度,以像素為單位。9000 0000,為00000090h=144。
9)27-28:位圖的位面數,該值總是1。0100,為0001h=1。
10)29-30:每個像素的位數。有1(單色),4(16色),8(256色),16(64K色,高彩色),24(16M色,真彩色),32(4096M色,增強型真彩色)。1000為0010h=16。
11)31-34:壓縮說明:有0(不壓縮),1(RLE 8,8位RLE壓縮),2(RLE 4,4位RLE壓縮,3(Bitfields,位域存放)。RLE簡單地說是采用像素數+像素值的方式進行壓縮。T408采用的是位域存放方式,用兩個字節表示一個像素,位域分配為r5b6g5。圖中0300 0000為00000003h=3。
12)35-38:用字節數表示的位圖數據的大小,該數必須是4的倍數,數值上等於(≥位圖寬度的最小的4的倍數)×位圖高度×每個像素位數。0090 0000為00009000h=80×90×2h=36864。
13)39-42:用象素/米表示的水平分辨率。A00F 0000為0000 0FA0h=4000。
14)43-46:用象素/米表示的垂直分辨率。A00F 0000為0000 0FA0h=4000。
15)47-50:位圖使用的顏色索引數。設為0的話,則說明使用所有調色板項。
16)51-54:對圖象顯示有重要影響的顏色索引的數目。如果是0,表示都重要。

彩色板

17)(55+0)到(50-1+2^biBitCount):彩色板規范。對於調色板中的每個表項,用下述方法來描述RGB的值:
1字節用於藍色分量
1字節用於綠色分量
1字節用於紅色分量
1字節用於填充符(設置為0)
對於24-位真彩色圖像就不使用彩色板,因為位圖中的RGB值就代表了每個象素的顏色。
如,彩色板為00F8 0000 E007 0000 1F00 0000 0000 0000,其中:
00F8為F800h = 1111 1000 0000 0000(二進制),是藍色分量的掩碼。
E007 為 07E0h = 0000 0111 1110 0000(二進制),是綠色分量的掩碼。
1F00為001Fh = 0000 0000 0001 [1]1111(二進制),是紅色分量的掩碼。
0000 總設置為0。
將掩碼跟像素值進行“與”運算再進行移位操作就可以得到各色分量值。看看掩碼,就可以明白事實上在每個像素值的兩個字節16位中,按從高到低取5、6、5位分別就是r、g、b分量值。取出分量值后把r、g、b值分別乘以8、4、8就可以補齊第個分量為一個字節,再把這三個字節按rgb組合,放入存儲器(同樣要反序),就可以轉換為24位標准BMP格式了。
圖像數據陣列
18)55(無調色板)-bfSize:每兩個字節表示一個像素。陣列中的第一個字節表示位圖左下角的象素,而最后一個字節表示位圖右上角的象素。

圖像數據陣列

每兩個字節表示一個像素。陣列中的第一個字節表示位圖左下角的象素,而最后一個字節表示位圖右上角的象素。

存儲算法

BMP文件通常是不壓縮的,所以它們通常比同一幅圖像的壓縮圖像文件格式要大很多。例如,一個800×600的24位幾乎占據1.4MB空間。因此它們通常不適合在因特網或者其它低速或者有容量限制的媒介上進行傳輸。根據顏色深度的不同,圖像上的一個像素可以用一個或者多個字節表示,它由n/8所確定(n是位深度,1字節包含8個數據位)。圖片瀏覽器等基於字節的ASCII值計算像素的顏色,然后從調色板中讀出相應的值。更為詳細的信息請參閱下面關於位圖文件的部分。n位2n種顏色的位圖近似字節數可以用下面的公式計算:BMP文件大小約等於 54+4*2的n次方+(w*h*n)/8,其中高度和寬度都是像素數。需要注意的是上面公式中的54是位圖文件的文件頭,是彩色調色板的大小。另外需要注意的是這是一個近似值,對於n位的位圖圖像來說,盡管可能有最多2n中顏色,一個特定的圖像可能並不會使用這些所有的顏色。由於彩色調色板僅僅定義了圖像所用的顏色,所以實際的彩色調色板將小於。如果想知道這些值是如何得到的,請參考下面文件格式的部分。由於存儲算法本身決定的因素,根據幾個圖像參數的不同計算出的大小與實際的文件大小將會有一些細小的差別。

存儲序列

圖象數據BGRA:默認的BMP是不支持ALPHA通道的,但對32位BMP而言,每個象素用32位(4個字節)表示,前三個字節表示RGB分量,最后一個字節可以做為ALPHA通道的值,因此32位位圖可以存儲帶ALPHA通道的圖像,在文件中,各分量的存儲順序為BGRA,BGRA,BGRA,BGRA…
另外要注意的是,BMP圖像的象素存儲順序是從下到上


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM