文章目錄
【內容概述】
- 圖像壓縮
- 在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字節)、調色板和位圖數據。其中特別需要注意的是,位圖數據是從最後一行最左邊的像素開始,往右記錄到盡頭,然後回到倒數第二行的最左像素,從左往右、從下往上,直至記錄完第一行的最右一個像素。