C / C++ Bitmap(BMP) 圖檔讀寫範例 與 檔頭詳細解析
C/C++ 純軟體讀取修改 bitmap 檔案完整介紹如下,文末附上詳細的原始碼可以參考。
例外也有如何讀取灰階圖gray與彩圖RGB的問題、以及解析度不為4倍數的時候如何處理4byte位元對齊,並且寫成一個萬用的函式,代碼開源(source code)可在文末下載,可直接直接編譯並執行程式碼不需外掛函式庫。
例外也有如何讀取灰階圖gray與彩圖RGB的問題、以及解析度不為4倍數的時候如何處理4byte位元對齊,並且寫成一個萬用的函式,代碼開源(source code)可在文末下載,可直接直接編譯並執行程式碼不需外掛函式庫。
介紹
BMP圖檔大致分為四大部分下面會依序解說
- 檔案檔頭(14byte)
- 圖片檔頭(40byte)
- 調色盤(1024byte)
- Raw檔
這裡的byte看成是一個字元(char),如果是字元與Raw檔讀取就順著讀而已,比較特別是數字讀取的時候要一起讀;比如說根據表頭某4byte是一組的他們是
00 FF 01 0A
那你要反著讀變成 0A 01 FF 00
而不是直接讀取。(注意看細微變化 01 不會變成 10)有興趣的可以查查這個詞 Endianness:https://goo.gl/xu9uYB
建議除錯的使用二進位軟體查看,比如說 HxD 來看(阿榮載點)
檔案檔頭(14byte)
範例代碼:(占用位元可以從代碼上看到)
struct BmpFileHeader{
uint16_t bfTybe = 0x424D;
uint32_t bfSize;
uint16_t bfReserved1 = 0;
uint16_t bfReserved2 = 0;
uint32_t bfOffBits = 54;
};
其中有寫數值等於多少的地方是 C++的類內初始化。C要刪除等號與數值才能編譯。
第一個 bfTybe 比較特別的是他是BM,你也可以寫成
unsigned char type[2]= {'B', 'M'};
,他就是固定的識別代碼,標準規定。
再來 bfSize 是指檔案大小,就是包含上述全部資料大小,這也會是你直接對BMP右鍵內容看到的容量大小。
再來 bfReserved 兩項直接填0就好了,不影響一般操作,詳細規格可以參考維基百科。
再來最後面 bfOffBits 的54是固定的就是14+40,不過如果你是用灰階圖記得要加上1024(調色盤占用的)。
類內初始化:C++11標準才可以使用
使用 g++ 加上-std=c++11
參數即可,如g++ -std=c++11 source.cpp
Visual Studio 2017 以上就直接是 C++11 了,不需要設置。
圖片檔頭(40byte)
範例代碼:(占用位元可以從代碼上看到)
struct BmpInfoHeader{
uint32_t biSize = 40;
uint32_t biWidth;
uint32_t biHeight;
uint16_t biPlanes = 1; // 1=defeaul, 0=custom
uint16_t biBitCount;
uint32_t biCompression = 0;
uint32_t biSizeImage = 0;
uint32_t biXPelsPerMeter = 0; // 72dpi=2835, 96dpi=3780
uint32_t biYPelsPerMeter = 0; // 120dpi=4724, 300dpi=11811
uint32_t biClrUsed = 0;
uint32_t biClrImportant = 0;
};
- biSize 檔頭的大小
- biWidth 圖片的寬
- biHeight 圖片的長
- biBitCount 位元數,彩圖要設RGB
8*3=24
,灰階圖設8
- biSizeImage 指的是總共有幾個像素,如果是彩圖有RGB,除了長x寬還要再
*3
實測有些軟體存出來的bmp的biSizeImage會是0,建議用長x寬自己算像素值。 - biClrUsed 指的是調色盤的顏色數,彩圖設 0 即可,灰階圖要自訂調色盤要設 256
- PelsPerMeter指的是密度,一般軟體好像也不看都是設0即可,windwos電腦預設是96dpi,大概就差別在你把這張圖拉上PPT上設越高會縮的越小。
調色盤(1024byte)
這個是可選的不一定會有,圖是RGB彩色不用留空(如
00
),直接就是沒有才是正確的。
如過圖片是灰階圖你就要自己補上調色盤,調色盤意思其實也很簡單就是
00 00 00 00~ FF FF FF 00
而已,這是因為灰階圖只需用一個byte表示,但是整張BMP需要用 RGB 表示,所以調色盤的功用就是把看到的RAW檔,就比如看到 00
變成 00 00 00
看到 FF
變成 FF FF FF
。總的來說意思是自訂規則從 “單一通道” 解析出 “RGB三通道”。
你可以一次做好整份調色盤在寫入,也可以用for迴圈寫入,數量不多不會太損效能可以用for比較整潔些,範例如下寫法。
for(unsigned i = 0; i < 256; ++i)
img << uch(i) << uch(i) << uch(i) << uch(0);
Raw檔
比較特別的是讀取的方式是從圖的左下角開始往右邊上面讀取,而且數據是BGR反過來的,並且他們的行會對齊4byte,如果圖片是40x42那麼你寫入的時候要寫成40x44不足的2位元補空白0。
bmp 4bit 對齊的公式代碼如下
size_t RealWidth = Width * bits/8;
size_t alig = (RealWidth*3)%4;
算出來是2的話就是說每行的結尾要塞 2 個
算出來是4的話就是說每行的結尾要塞 3 個
char(0)
就是 0x0000算出來是4的話就是說每行的結尾要塞 3 個
char(0)
就是 0x000000值得注意的是公式要針對不同的位元的圖片做不同的處理
可以想像成假設同一張3x3的圖片那麼,對黑白圖來說就是每行結尾要補1個空白,對於彩色的圖來說每個點個別有RGB,也就是實際寬度總共是9個點,9個點的話4位元對齊就需要補3個空白。
總結一下萬用公式就是
圖的實際位元寬度*3 % 4
這樣。注意
- 灰階圖要多寫調色盤,有三個地方要更動
FileHeader 的 size +1024 FileHeader 的 headSize +1024 InfoHeader 的 size +1024 InfoHeader 的 ncolours =256
- 讀RAW檔的時候(也就是像素值),檔案指標要跳過調色盤(建議是直接抓 headSize 比較省事)。
- 讀寫RAW檔區域的時候,記得跳過4byte對齊,對齊算法是
size_t alig = ((width*bits/8)*3) % 4;
範例 SourceCode
- 純C簡化版功能:https://goo.gl/z6wC1G
- C++函式庫:https://github.com/hunandy14/OpenBMP
C++的版本多了不少功能,後來維護到一半荒廢了QQ 留下不少測試代碼~
還有許多未完成的功能~覺得好用的話可以在這或gihub上留言~
建議其他功能或是bug回報也好,有人留言可能哪天就有動力大改版了XDD
還有許多未完成的功能~覺得好用的話可以在這或gihub上留言~
建議其他功能或是bug回報也好,有人留言可能哪天就有動力大改版了XDD
Hello!Thank you.
回覆刪除不會~有問題可以一起討論^^
刪除收益良多,感謝你
回覆刪除不會~有問題可以一起討論^^
刪除收益良多,想問一下,blog主有沒有寫過bmp的縮放,想了解一下它的原理和程序
回覆刪除可以參考這一篇
刪除https://charlottehong.blogspot.com/2017/11/bilinear.html