實戰——讀取並分析BMP圖像的十六進制數據


 


【內容概述】

  • 圖像壓縮
  • 在ubuntu系統下讀出BMP圖像的十六進制數據
  • 分析24位真彩BMP文件的文件頭、圖像頭、調色板、位圖數據
  • 截取圖像中心32×32區域,並灰度化
  • 分析灰度化後帶來的改變
  • 位圖數據存儲方式與像素位置的關係
     
    接下來我們就開始叭 ~ (* ^ ω ^ *)

【圖像壓縮】

這是一個非必要步驟,如果你的原圖很小就不用啦。但是如果原圖很大的話,建議壓縮一下,不然數據量會hin大的。可以用格式工廠做(有點大材小用嘞但是我首先就想到這個方法).具體步驟可參考這個說明。我通過壓縮把原本4032×3016的圖像壓縮成128×95的了。看看這個變化叭:

【壓縮前】
在這裏插入圖片描述
【壓縮後】
在這裏插入圖片描述
 


【獲取16進制數據】

這一步我是在Ubuntu系統下進行的,我把原圖命名爲【ballon.bmp】(因爲是2019跨年時候在珠海長隆拍的小丑氣球)在圖片所在文件夾打開終端,輸入$ xxd -i ballon.bmp ballon.h並回車,具體操作可以參考這個說明。然後可以看到文件夾下新生成了一個.h文件,打開之後就可以看到十六進制的數組啦。
在這裏插入圖片描述
在這裏插入圖片描述


【文件頭】

0x42, 0x4d, 0xb6, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00,
0x00, 0x00

BMP圖像的文件頭由14個字節組成。其中:
1-2:42 4D分別表示B和M,說明這是一個BMP格式的圖像。
3-6:b6 8e 00 00,從低位到高位的尋址方式,十進制是36534,表示圖片的大小,即所佔的字節數。
7-10:00 00 00 00,特定應用程序使用,這裏不用,默認爲0.
11-14:36 00 00 00,十進制是54,實際位圖數據開始的偏移地址。


【位圖頭】

              0x28, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x5f, 0x00,
  0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0xc4, 0x0e, 0x00, 0x00, 0xc4, 0x0e, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

位圖頭由40個字節組成。其中:
1-4:28 00 00 00,十進制是40,表示圖像頭信息佔用的字節數。
5-8:80 00 00 00,十進制是128,表示圖像的寬度。
9-12:5f 00 00 00,十進制是95,表示圖像的高度。
13-14:01 00,使用的彩色平面數,都爲1.
15-16:18 00,十進制24,表示圖像的位深度,24爲真彩色。
17-20:00 00 00 00,規定像素位的掩碼。
21-24:00 00 00 00,位圖全部像素佔用的字節數,24位爲0.
25-28:c4 0e 00 00,十進制爲3780,表示水平分辨率3780像素/米。
29-32:c4 0e 00 00,十進制爲3780,表示垂直分辨率3780像素/米。
33-36:00 00 00 00,位圖使用的顏色數,爲0表示顏色數爲2的位深度次冪。
37-40:00 00 00 00,重要的顏色數,0代表所有顏色都重要。


【調色板】

因爲這個是24位的BMP圖像,是沒有調色板的,所以從第55個字節開始就是位圖數據。這一點和前面的【文件頭】裏【第11-14字節】顯示的【實際位圖數據開始的偏移地址】爲【54】相符。
順帶一提,如果是8位的圖像,就會存在調色板,並且位圖數據是從第1079位開始的。
 


【24位真彩圖的位圖數據】

即使是128×95大小的圖像,數據還是很多的,我只截取一行(128×3=384個字節)的數據放出來看看。爲什麼要乘個3呢,因爲每個像素是24位,所以一個像素需要三個字節來表示,三個字節依次表示的是B(blue)、G(green)、R(red)的值。

                                      0x33, 0x38, 0x39, 0x31, 0x37, 0x32,
  0x3f, 0x49, 0x49, 0x36, 0x40, 0x40, 0x2e, 0x37, 0x3a, 0x48, 0x46, 0x45,
  0x37, 0x3e, 0x41, 0x37, 0x43, 0x43, 0x3a, 0x43, 0x47, 0x2e, 0x36, 0x36,
  0x33, 0x39, 0x34, 0x37, 0x38, 0x3c, 0x41, 0x40, 0x42, 0x36, 0x3a, 0x3f,
  0x3b, 0x47, 0x47, 0x37, 0x3e, 0x41, 0x39, 0x3c, 0x41, 0x35, 0x3b, 0x42,
  0x3b, 0x44, 0x47, 0x38, 0x47, 0x4a, 0x51, 0x6e, 0x77, 0x52, 0x7f, 0x8a,
  0x44, 0x62, 0x67, 0x33, 0x3a, 0x3d, 0x30, 0x3b, 0x3f, 0x45, 0x67, 0x66,
  0x3e, 0x67, 0x70, 0x3b, 0x65, 0x6a, 0x33, 0x39, 0x44, 0x37, 0x3d, 0x42,
  0x37, 0x42, 0x46, 0x37, 0x43, 0x47, 0x3d, 0x49, 0x4d, 0x47, 0x51, 0x58,
  0x41, 0x4e, 0x56, 0x49, 0x54, 0x58, 0x3d, 0x4d, 0x54, 0x33, 0x3c, 0x45,
  0x3d, 0x42, 0x45, 0x30, 0x27, 0x24, 0x35, 0x28, 0x2a, 0x36, 0x30, 0x31,
  0x30, 0x27, 0x24, 0x31, 0x25, 0x23, 0x2f, 0x26, 0x23, 0x33, 0x46, 0x4e,
  0x39, 0x4f, 0x61, 0x33, 0x48, 0x5e, 0x31, 0x47, 0x52, 0x40, 0x5d, 0x6c,
  0x2b, 0x47, 0x7d, 0x30, 0x4e, 0xad, 0x2e, 0x50, 0xa9, 0x31, 0x60, 0xb1,
  0x2b, 0x5f, 0xb2, 0x37, 0x5e, 0xb3, 0x36, 0x66, 0xb4, 0x5c, 0x8e, 0xca,
  0x67, 0x9b, 0xd0, 0x70, 0x9c, 0xd7, 0x7c, 0xa4, 0xd4, 0x84, 0xa9, 0xdb,
  0x82, 0xac, 0xdb, 0x93, 0xb6, 0xe2, 0x9f, 0xbe, 0xe5, 0x9f, 0xbd, 0xe6,
  0xa0, 0xbd, 0xe2, 0x92, 0xb9, 0xdf, 0xa1, 0xbe, 0xe5, 0x9f, 0xb5, 0xdf,
  0x96, 0xae, 0xdc, 0x83, 0xa3, 0xd4, 0x71, 0x88, 0xc0, 0x64, 0x7f, 0xb8,
  0x35, 0x77, 0xac, 0x24, 0x79, 0xa6, 0x24, 0x98, 0xb7, 0x23, 0xa0, 0xbc,
  0x1f, 0x9c, 0xb8, 0x23, 0xa4, 0xbd, 0x23, 0xae, 0xc1, 0x1f, 0xb2, 0xc2,
  0x1f, 0xad, 0xbe, 0x21, 0xad, 0xbe, 0x20, 0xab, 0xbe, 0x20, 0xaa, 0xbc,
  0x20, 0xa7, 0xbb, 0x21, 0xaa, 0xba, 0x21, 0xa4, 0xb2, 0x27, 0xa4, 0xb2,
  0x2d, 0x6e, 0xa5, 0x33, 0x74, 0xab, 0x47, 0x80, 0xb7, 0x3d, 0x82, 0xbb,
  0x32, 0x42, 0xa2, 0x21, 0x9e, 0xad, 0x1e, 0xa5, 0xb3, 0x1f, 0xad, 0xb8,
  0x1c, 0xb6, 0xc1, 0x22, 0xbd, 0xc6, 0x23, 0xb6, 0xc0, 0x22, 0xb5, 0xbf,
  0x20, 0xb2, 0xbe, 0x21, 0xae, 0xbb, 0x21, 0xb8, 0xc6, 0x23, 0xb2, 0xc1,
  0x24, 0x9c, 0xb2, 0x21, 0x9d, 0xb5, 0x22, 0x99, 0xb2, 0x21, 0x9b, 0xb3,
  0x20, 0x9c, 0xb4, 0x26, 0xa6, 0xbd, 0x22, 0xa0, 0xb9, 0x21, 0xa8, 0xbc,
  0x21, 0xaa, 0xba, 0x26, 0x39, 0x40, 0x34, 0x33, 0x3c, 0x34, 0x38, 0x43,
  0x39, 0x42, 0x46, 0x3d, 0x40, 0x45, 0x41, 0x43, 0x4e, 0x3b, 0x3d, 0x45,
  0x40, 0x34, 0x32, 0x3b, 0x2d, 0x2e, 0x42, 0x33, 0x31, 0x37, 0x35, 0x34,
  0x40, 0x34, 0x32, 0x3e, 0x37, 0x3a,

 


【圖像灰度化】

要分析亮度和數據值的關係,就需要把圖像灰度化。爲了減少數據量(實際就是偷懶),我們在灰度化之前,先裁剪一下圖片。

  • 圖片裁剪
    一般來說,圖片中間的顏色變化會比圖片邊緣的明顯一些。我們在matlab裏用imcrop函數對原圖像裁剪,得到中間32×32的區域。
%將ballon.bmp裁剪成32×32大小並保存爲cutballon.bmp
x1 = imread('ballon.bmp');
x2=imcrop(x1,[48,29,31,31]);
figure
imshow(x2);
imwrite(x2,'cutballon.bmp');

運行代碼之後我們就可以在目錄下看到新生成了”cutballon.bmp".
 
在這裏插入圖片描述
 

我們繼續在matlab裏,把彩色圖像變成灰度圖像(就是常說的“黑白圖像”)

%把裁剪後的圖像灰度化
x1 = imread('cutballon.bmp');
x2=rgb2gray(x1);
imwrite(x2,'greyballon.bmp');

運行代碼之後我們就可以在目錄下看到新生成了”greyballon.bmp".
在這裏插入圖片描述

  • 讀取greyballon.bmp的十六進制數據
    像前面讀取ballon.bmp一樣,在Ubuntu系統裏獲取到greyballon.h,打開之後長這樣:
    在這裏插入圖片描述

【灰度化後數據格式的變化】

  • 我們注意到,這個新的數據中,文件頭第11-14位(紅色框部分)的數據變成了:36 04 00 00,十進制爲1078,而前面分析時候得到的是54,說明經過matlab的灰度化之後,這個圖象的位圖數據起始位置變了。
  • 再看位圖頭的第15-16位(綠色框部分)數據變成了:08 00,十進制爲8,說明圖片的位深度變成了8。那麼在後面的位圖數據中,一個字節就表示一個像素。
  • 結合分析,不難猜出,因爲位深度的變化,讓圖片的信息數據中多了【調色板】(文件頭和位圖頭都是固定不變的長度),24位圖無調色板,而8位圖則存在調色板,且調色板佔用的字節數爲1024.

【讀32×32灰度圖片的位圖數據】

從第1079個字節開始,就是我們的位圖數據了。Ubuntu是默認一行顯示12個字節嗎?。。。我也不知道能不能調整,這樣數下去在91行63列的位置,往後就是位圖數據了。
在這裏插入圖片描述
在這裏插入圖片描述
位圖數據有多少個字節呢,我們算一算,一個像素佔一個字節的話,應該是32×32=1024個。而1024=2+85×12+2,所以從91行最後兩個字節一直到177行頭兩個字節,都是位圖數據。
從上面的圖片也可以看到,第177行只有兩個字節,之後這整個數據就結束了。

所以BMP圖像的十六進制數據就是【文件頭】+【位圖頭】+【調色板】+【位圖數據】。
 

【位圖數據的存儲和像素位置的關係】

 
【手動調整一行顯示的數據】

一行12個字節的話,我們很難直觀地和原圖對比(因爲原圖一行32個像素),所以就調整一下,以一行32個字節的方式呈現。順便把每個字節前面的“0x”去掉,只剩下十六進制的值。害,這個調整過程超級煩的,所以就不具體說明,大家需要的話評論一下我再加叭)
這樣看就清爽多了:
在這裏插入圖片描述
默認一個知識:數值越大,對應像素的亮度越大。我們把數據做以下處理:

  • 把亮度值大於176(十六進制大於B0)的用”–”代替。
    在這裏插入圖片描述
    分析:
    從數據圖像上看到,亮度大於176的區域主要集中在圖片中間,分爲偏右上的一個圓形和中間偏下的一個類似橢圓形。而直觀從原始圖像上看(下圖經過放大處理),並沒有找到合適的絕對對應關係。

在這裏插入圖片描述
但是換個角度,把圖片做“上下顛倒”,會發現對應亮度的位置大致是吻合的。可以初步斷定,位圖數據的存儲,是從圖片的左下方開始的,並一直到右上方結束
在這裏插入圖片描述
 

  • 把亮度值小於48(十六進制小於30)的用”–”代替
    在這裏插入圖片描述
    分析:
    再從亮度小的區域佐證一下上面的判斷。直觀上看,最暗的區域在左下方,即紅色圈出來的區域。但在位圖數據上,看到的亮度值最小的區域卻是在左上方,如下圖所示。不難想象把圖片上下顛倒後,就符合預設,這就驗證了先前的猜想。
    在這裏插入圖片描述

【總結】

BMP圖像的數據可以解析成四部分,分別是文件頭(14字節)、圖像頭(40字節)、調色板和位圖數據。其中特別需要注意的是,位圖數據是從最後一行最左邊的像素開始,往右記錄到盡頭,然後回到倒數第二行的最左像素,從左往右、從下往上,直至記錄完第一行的最右一個像素。

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