1. 程式人生 > >BMP檔案格式詳解(BMP file format)[圖文解說]

BMP檔案格式詳解(BMP file format)[圖文解說]

BMP檔案格式,又稱為Bitmap(點陣圖)或是DIB(Device-Independent Device,裝置無關點陣圖),是Windows系統中廣泛使用的影象檔案格式。由於它可以不作任何變換地儲存影象畫素域的資料,因此成為我們取得RAW資料的重要來源。Windows的圖形使用者介面(graphical user interfaces)也在它的內建影象子系統GDI中對BMP格式提供了支援。

下面以Notepad++為分析工具,結合Windows的點陣圖資料結構對BMP檔案格式進行一個深度的剖析。

BMP檔案的資料按照從檔案頭開始的先後順序分為四個部分:

Ø         bmp檔案頭(bmp file header):提供檔案的格式、大小等資訊

Ø         點陣圖資訊頭(bitmap information):提供影象資料的尺寸、位平面數、壓縮方式、顏色索引等資訊

Ø         調色盤(color palette):可選,如使用索引來表示影象,調色盤就是索引與其對應的顏色的對映表

Ø         點陣圖資料(bitmap data):就是影象資料啦^_^

下面結合Windows結構體的定義,通過一個表來分析這四個部分。


我們一般見到的影象以24點陣圖像為主,即R、G、B三種顏色各用8個bit來表示,這樣的影象我們稱為真彩色,這種情況下是不需要調色盤的,也就是所點陣圖資訊頭後面緊跟的就是點陣圖資料了。因此,我們常常見到有這樣一種說法:點陣圖檔案從檔案頭開始偏移54個位元組就是點陣圖資料了,這其實說的是24或32點陣圖的情況。這也就解釋了我們按照這種程式寫出來的程式為什麼對某些點陣圖檔案沒用了。

下面針對一幅特定的影象進行分析,來看看在點陣圖檔案中這四個資料段的排布以及組成。

我們使用的影象顯示如下:

                


   這是一幅16位的點陣圖檔案,因此它是含有調色盤的。

   在拉出影象資料進行分析之前,我們首先進行幾個約定:

   1. 在BMP檔案中,如果一個數據需要用幾個位元組來表示的話,那麼該資料的存放位元組順序為“低地址村存放低位資料,高地址存放高位資料”。如資料0x1756在記憶體中的儲存順序為:

     

這種儲存方式稱為小端方式(little endian) , 與之相反的是大端方式(big endian)。對兩者的使用情況有興趣的可以深究一下,其中還是有學問的。

2. 以下所有分析均以位元組為序號單位進行。

   下面我們對從檔案中拉出來的資料進行剖析:

  


一、bmp檔案頭
Windows為bmp檔案頭定義瞭如下結構體:

   typedef struct tagBITMAPFILEHEADER
{
UINT16 bfType;   
DWORD bfSize;
UINT16 bfReserved1;
UINT16 bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER;

其中:

  


對照檔案資料我們看到:


1-2 :424dh = 'BM',表示這是Windows支援的點陣圖格式。有很多聲稱開頭兩個位元組必須為'BM'才是點陣圖檔案,從上表來看應為開頭兩個位元組必須為'BM'才是Windows點陣圖檔案。

3-5 :00010436h = 66614 B = 65.05 kB,通過查詢檔案屬性發現一致。

6-9 :這是兩個保留段,為0。

A-D:00000436h = 1078。即從檔案頭到點陣圖資料需偏移1078位元組。我們稍後將驗證這個資料。

共有14個位元組。

二、點陣圖資訊頭
同樣地,Windows為點陣圖資訊頭定義瞭如下結構體:

程式碼
typedef struct tagBITMAPINFOHEADER
{
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER;


對照資料檔案:


0E-11:00000028h = 40,這就是說我這個點陣圖資訊頭的大小為40個位元組。前面我們已經說過點陣圖資訊頭一般有40個位元組,既然是這樣,為什麼這裡還要給一個欄位來說明呢?這裡涉及到一些歷史,其實點陣圖資訊頭原本有很多大小的版本的。我們看一下下表:


              

    出於相容性的考慮,大多數應用使用了舊版的點陣圖資訊頭來儲存檔案。而 OS/2 已經過時了,因此現在最常用的格式就僅有V3 header了。因此,我們在前面說點陣圖資訊頭的大小為40位元組。

12-15:00000100h = 256,影象寬為255畫素,與檔案屬性一致。

16-19:00000100h = 256,影象高為255畫素,與檔案屬性一致。這是一個正數,說明影象資料是從影象左下角到右上角排列的。

1A-1B:0001h, 該值總為1。

1C-1D:0008h = 8, 表示每個畫素佔8個位元,即該影象共有256種顏色。

1E-21:00000000h,BI_RGB, 說明本影象不壓縮。

22-25:00000000h,影象的大小,因為使用BI_RGB,所以設定為0。

26-29:00000000h,水平解析度,預設。

2A-2D:00000000h,垂直解析度,預設。

2E-31:00000100h = 256,說明本點陣圖實際使用的顏色索引數為256,與1C-ID得到的結論一致。

32-35:00000100h = 256,說明本點陣圖重要的顏色索引數為256,與前面得到的結論一致。

三、調色盤
下面的資料就是調色盤了。前面也已經提過,調色盤其實是一張對映表,標識顏色索引號與其代表的顏色的對應關係。它在檔案中的佈局就像一個二維陣列palette[N][4],其中N表示總的顏色索引數,每行的四個元素分別表示該索引對應的B、G、R和Alpha的值,每個分量佔一個位元組。如不設透明通道時,Alpha為0。因為前面知道,本圖有256個顏色索引,因此N = 256。索引號就是所在行的行號,對應的顏色就是所在行的四個元素。這裡擷取一些資料來說明:


索引:(藍,綠,紅,Alpha)

0號:(fe,fa,fd,00)

1號:(fd,f3,fc,00)

2號:(f4,f3,fc,00)

3號:(fc,f2,f4,00)

4號:(f6,f2,f2,00)

                                                           5號:(fb,f9,f6,00) 等等。

一共有256種顏色,每個顏色佔用4個位元組,就是一共1024個位元組,再加上前面的檔案資訊頭和點陣圖資訊頭的54個位元組加起來一共是1078個位元組。也就是說在點陣圖資料出現之前一共有1078個位元組,與我們在檔案資訊頭得到的資訊:檔案頭到文圖資料區的偏移為1078個位元組一致!

四、點陣圖資料

下面就是點陣圖資料了,每個畫素佔一個位元組,取得這個位元組後,以該位元組為索引查詢相應的顏色,並顯示到相應的顯示裝置上就可以了。

注意:由於點陣圖資訊頭中的影象高度是正數,所以點陣圖資料在檔案中的排列順序是從左下角到右上角,以行為主序排列的。


      

也即我們見到的第一個畫素60是影象最左下角的資料,第二個人畫素60為影象最後一行第二列的資料,…一直到最後一行的最後一列資料,後面緊接的是倒數第二行的第一列的資料,依此類推。

如果影象是24位或是32位資料的點陣圖的話,點陣圖資料區就不是索引而是實際的畫素值了。下面說明一下,此時點陣圖資料區的每個畫素的RGB顏色陣列排布:

24位RGB按照BGR的順序來儲存每個畫素的各顏色通道的值,一個畫素的所有顏色分量值都存完後才存下一個下一個畫素,不進行交織儲存。

32位資料按照BGRA的順序儲存,其餘與24位點陣圖的方式一樣。

畫素的排布規則與前述一致。

對齊規則

講完了畫素的排列規則以及各畫素的顏色分量的排列規則,最後我們談談資料的對齊規則。我們知道Windows預設的掃描的最小單位是4位元組,如果資料對齊滿足這個值的話對於資料的獲取速度等都是有很大的增益的。因此,BMP影象順應了這個要求,要求每行的資料的長度必須是4的倍數,如果不夠需要進行位元填充(以0填充),這樣可以達到按行的快速存取。這時,點陣圖資料區的大小就未必是 圖片寬×每畫素位元組數×圖片高 能表示的了,因為每行可能還需要進行位元填充。

填充後的每行的位元組數為:

    ,其中BPP(Bits Per Pixel)為每畫素的位元數。

在程式中,我們可以表示為:

int iLineByteCnt = (((m_iImageWidth * m_iBitsPerPixel) + 31) >> 5) << 2;

這樣,點陣圖資料區的大小為:

m_iImageDataSize = iLineByteCnt * m_iImageHeight;

我們在掃描完一行資料後,也可能接下來的資料並不是下一行的資料,可能需要跳過一段填充資料:

skip = 4 - ((m_iImageWidth * m_iBitsPerPixel)>>3) & 3;

五、拾遺

至此,我們通過分析一個具體的點陣圖檔案例子詳細地剖析了點陣圖檔案的組成。需要注意的是:我們講的主要是PC機上的點陣圖檔案的構成,對於嵌入式平臺,可能在調色盤資料段與PC機的不同。如在嵌入式平臺上常見的16位r5g6b5點陣圖實際上採用的掩模的方式而不是索引的方式來表示影象。此時,在調色盤資料段共有四個部分,每個部分為四個位元組,實際表示的是彩色版規範。即:

第一個部分是紅色分量的掩模

第二個部分是綠色分量的掩模

第三個部分是藍色分量的掩模

第四個部分是Alpha分量的掩模(預設為0)

典型的調色盤規範在檔案中的順序為為:

00F8 0000 E007 0000 1F00 0000 0000 0000

其中

    00F8 0000為FB00h=1111100000000000(二進位制),是藍紅分量的掩碼。
  E007 0000為 07E0h=0000011111100000(二進位制),是綠色分量的掩碼。
  1F00 0000為001Fh=0000000000011111(二進位制),是藍色分量的掩碼。
    0000 0000設定為0。

將掩碼跟畫素值進行“與”運算再進行移位操作就可以得到各色分量值。看看掩碼,就可以明白事實上在每個畫素值的兩個位元組16位中,按從高到低取5、6、5位分別就是r、g、b分量值。取出分量值後把r、g、b值分別乘以8、4、8就可以補齊每個分量為一個位元組,再把這三個位元組按BGR組合,放入儲存器,就可以轉換為24位標準BMP格式了。

這樣我們假設在點陣圖資料區有一個畫素的資料在檔案中表示為02 F1。這個資料實際上應為F102:

r = (F102 AND F800) >> 8 = F0h = 240

g= (F102 AND 07E0)>> 3 = 20h = 32
  b=(F102 AND 001F) << 3 = 10h =16

至此我們就可以顯示了。(本文結束)

參考資源:

1.      wiki百科 bmp file format