BMP(全稱Bitmap)是Windows操作系統中的標準圖像文件格式,可以分成兩類:設備相關位圖(DDB)和設備無關位圖(DIB),使用非常廣。它採用位映射存儲格式,除了圖像深度可選以外,不採用其他任何壓縮,因此,BMP文件所佔用的空間很大。BMP文件的圖像深度可選lbit、4bit、8bit及24bit。BMP文件存儲數據時,圖像的掃描方式是按從左到右、從下到上的順序。由於BMP文件格式是Windows環境中交換與圖有關的數據的一種標準,因此在Windows環境中運行的圖形圖像軟件都支持BMP圖像格式。
先解釋下數據存貯方式:
1、在windows中,顏色順序是:B G R。
2、BMP的內存行順序和圖像顯示的行順序是上下顛倒的。
即:BMP內存第0行,是真實圖像下面的最後一行。
舉例,假如圖像爲2*2大小,像素三顏色按照RGB的順序, 我們看到的圖像爲:
1 2 3, 11 22 33
4 5 6, 44 55 66
內存表示如下:
6 5 4, 66 55 44 (0 0) -- 第0行
3 2 1, 33 22 11 (0 0) -- 第1行
注意,通常內存是需要內存對齊的,所以每行後面可能會有對齊所產生的0.
最常見的就是24位圖,所謂的24位圖,就是說一個像素的顏色信息用24位來表示,也就是說,對於三原色BRG,每一個顏色都用以字節(8)位來表示。除了24位圖,還有1位(單色),2位(4色,CGA),4位(16色,VGA),8位(256色),16位(增強色),24位(真彩色)和32位等
BMP文件的數據按照從文件頭開始的先後順序分爲四個部分:
◆ 位圖文件頭(bmp file header): 提供文件的格式、大小等信息 共14字節
◆ 位圖信息頭(bitmap information):提供圖像數據的尺寸、位平面數、壓縮方式、顏色索引等信息 共40字節
◆ 調色板(color palette):可選,如使用索引來表示圖像,調色板就是索引與其對應的顏色的映射表
◆ 位圖數據(bitmap data):圖像數據區
bmp文件頭包含如下信息:
- bfType:2字節,文件類型;
- bfSize:4字節,文件大小;
- bfReserved1:2字節,保留,必須設置爲0;
- bfReserved2:2字節,保留,必須設置爲0;
- bfOffBits:4字節,從頭到位圖數據的偏移;
下圖的數據就是bmp文件頭:
一共14字節,下面逐個解釋。
0-1:bfType,表示文件類型,BMP格式的文件這兩個字節是0x4D42,10進制就是19778,字符顯示就是‘BM’;
2-5:bfSize,表示文件的大小,這裏的是0x0004B436,十進制是308278,也就是301kb,檢查文件信息,驗證正確;
6-7:bfReserved1,保留位,必須設置爲0;
8-9:bfReserved2,保留位,必須設置爲0;
a-d:bfOffBits,4字節的偏移,表示從文件頭到位圖數據的偏移,這裏是0x00000436,十進制是1078,後面會做驗證;
3、位圖信息頭(bitmap information)
位圖信息頭一共40字節,包含如下內容:
- biSize:4字節,信息頭的大小,即40;
- biWidth:4字節,以像素爲單位說明圖像的寬度;
- biHeight:4字節,以像素爲單位說明圖像的高度,同時如果爲正,說明位圖倒立(即數據表示從圖像的左下角到右上角),如果爲負說明正向;
- biPlanes:2字節,爲目標設備說明顏色平面數,總被設置爲1;
- biBitCount:2字節,說明比特數/像素數,值有1、2、4、8、16、24、32;
- biCompression:4字節,說明圖像的壓縮類型,最常用的就是0(BI_RGB),表示不壓縮;
- biSizeImages:4字節,說明位圖數據的大小,當用BI_RGB格式時,可以設置爲0;
- biXPelsPerMeter:表示水平分辨率,單位是像素/米,有符號整數;
- biYPelsPerMeter:表示垂直分辨率,單位是像素/米,有符號整數;
- biClrUsed:說明位圖使用的調色板中的顏色索引數,爲0說明使用所有;
- biClrImportant:說明對圖像顯示有重要影響的顏色索引數,爲0說明都重要;
下圖數據是位圖信息頭:
一共40字節,解釋如下:
0e-11:4字節的biSize,這裏是0x28,即十進制的40,驗證正確;
12-15:4字節的biWidth,這裏是0x00000280,即十進制的640,用像素表示圖像的寬度,查看文件信息驗證正確;
16-19:4字節的biHeight,這裏是0x000001E0,即十進制的480,用像素表示圖像的高度,查看文件信息驗證正確;同時,這是一個正數,表示圖像是倒立的,即圖像數據是從左下角到右上角排列的;
1a-1b:2字節的biPlanes,值爲0x0001;
1c-1d:2字節的biBitCount,值是0x0008,即8,表示每個像素用8位表示,一共有256個顏色;
1e-21:4字節的biCompression,值是0,即BI_RGB格式,不壓縮;
22-25:4字節的biSizeImage,圖像的大小,值是0x0004B000,十進制爲307200,由上面的bfSize(文件大小)和bfOffBits(文件頭到數據的偏移)分別是308278和1078可以得到,biSizeImage=bfSize-bfOffBits,即圖像大小=文件大小-偏移量;
26-29:4字節的biXPelsPerMeter,水平分辨率,值是0x00000EC4,十進制3780;
2a-2d:4字節的biYPelsPerMeter,垂直分辨率,值是0x00000EC4,十進制3780;
2e-31:4字節的biClrUsed,使用的顏色索引數,值是0x00000100,十進制256,與1c-1d得到的結論一致;
32-35:4字節的biClrImportant,重要的顏色索引數,值是0x00000100,十進制256;
4、調色板(Color Palette)
調色板是可選的,不過這裏的8位色圖有調色板。那麼接下來的數據就是調色板了。調色板就是一個顏色的索引,這裏是8位色圖,一共有256中顏色,由於每個顏色都有RGB三原色,也就是要3個字節表示,這樣的話256個顏色就不能表示所有的顏色,所以就需要一個索引,用一個字節的索引指向4個字節表示的顏色(RGB加上Alpha值)。如果把這4個字節表示爲一個Color類型,那麼調色板就是Color的數組。由於Color類型也是一個數組,調色板就像一個二維數組palette[N][4],其中N是顏色的數量,這裏就是256。因此,這個例子中的調色板的大小就是256x4=1024字節,在調色板之前,有14字節的bmp文件頭,40字節的位圖信息頭,加上1024字節的調色板,一共1078字節,也就是說真正的圖像數據前面有1078字節,這和bmp文件頭中的bfOffBits相符,驗證了我們的討論。
有的圖像沒有調色板,比如下面的24位色圖:
頭部數據如下:
根據上面的討論可以知道,biBitCount是24(0x18),bfOffBits是54(0x36),即沒有調色板,位圖信息頭接下來就是圖像數據了。
調色板中的數據每4字節一組,分別表示藍、綠、紅和Alpha值。按照第一個圖像舉例來說:
索引 | 藍 | 綠 | 紅 | Alpha |
0 | 01 | 10 | 37 | 00 |
1 | 00 | 10 | 49 | 00 |
2 | 00 | 18 | 44 | 00 |
3 | 01 | 1D | 58 | 00 |
5、位圖數據
接下來就是位圖數據了。由於是8位色圖,所以每個像素用1個字節表示,取出每個字節,顯示到相應的設備上就可以了。
注意,這裏的biHeight爲正數,說明圖像倒立,從左下角開始到右上角,以行爲主序排列。
如果是24位色圖,按照BGR的順序排列,32位色圖按照BGRAlpha排列。
位圖數據排列還有一個規則,就是對齊。
Windows默認的掃描的最小單位是4字節,如果數據對齊滿足這個值的話對於數據的獲取速度等都是有很大的增益的。因此,BMP圖像順應了這個要求,要求每行的數據的長度必須是4的倍數,如果不夠需要進行比特填充(以0填充),這樣可以達到按行的快速存取。這樣的話,位圖數據的大小就不一定是寬x高x每像素字節數了,因爲每行還可能有0填充。
填充後的每行數據如下:
其中,BPP是每像素的比特數(Bits Per Pixel),即biBitCount,Width是寬度,單位是像素即bfWidth。
對於我們這個例子,BPP是8,Width是480,正好是4的倍數,也就是沒有填充。來計算一下:
RowSize=4*(8*480/32)=480字節,驗證沒有填充。
那麼以上面第二個圖片24位色圖爲例,按照數據可以得到:
- biBitCount=0x0018=24;
- bfWidth=0x000001c6=454;
- bfHeight=0x00000053=83;
- biSizeImage=0x0001BA3c=113212;
按照沒填充計算:454*83*3=113046 bytes,與真實值相差166字節。
按照填充公式,每行有數據4*(24*454/32)=1364 字節,真正的數據有454*3=1362字節,也就是說每行填充了2字節0,一共83行,共填充83*2=166字節,驗證了我們的討論。
代碼展示:
C語言結構體定義:
struct bmp_file //BMP文件頭結構
{
char type[2]; //位圖文件的類型,必須爲BM,我這裏類型不對,所以顯示有誤。
unsigned int size; //位圖文件的大小,以字節爲單位
short rd1; // 位圖文件保留字,必須爲0
short rd2; // 位圖文件保留字,必須爲0
unsigned int offset; // 位圖數據的起始位置,以相對於位圖
};
struct bmp_info //圖像信息區
{
unsigned int bsize; //本結構體所佔用字節數,即40個字節
int width; // 位圖的寬度,以像素爲單位,像素數量是4字節對齊的
int height; // 位圖的高度,以像素爲單位
unsigned short planes; // 目標設備的級別,必須爲1
unsigned short count; // 每個像素所需的位數,必須是1(雙色)// 4(16色),8(256色)或24(真彩色)之一
unsigned int compression; // 位圖壓縮類型,必須是 0(不壓縮),// 1(BI_RLE8壓縮類型)或2(BI_RLE4壓縮類型)之一
unsigned int sizeimage; // 位圖的大小,以字節爲單位
unsigned int xmeter; // 位圖水平分辨率,每米像素數
unsigned int ymeter; // 位圖垂直分辨率,每米像素數
unsigned int cused; // 位圖實際使用的顏色表中的顏色數
unsigned int cimportant; // 位圖顯示過程中重要的顏色數
};
struct bmp_head {
struct bmp_file file;
struct bmp_info info;
};
struct bmp_attr {
struct bmp_head head;
int xsize, ysize,sizeimage;
int pixel_size;
int line_length;
unsigned int offset;
};
struct bmp_attr *bmp = (struct bmp_attr *)calloc(1, sizeof(struct bmp_attr));
//讀取bmp的文件頭
fread(bmp, sizeof(struct bmp_attr), 1, fp);
bmp->xsize = (bmp->head.info.width * 3 + 3) / 4 * 4; // 4字節補齊
bmp->ysize = bmp->head.info.height;
bmp->sizeimage = bmp->head.info.sizeimage;
bmp->offset = bmp->head.file.offset;
printf("bmp xsize = %d ysize = %d sizeimage = %d offset = %d\n",bmp->xsize,bmp->ysize,bmp->sizeimage,bmp->offset);
可以獲取頭文件的信息,打印出來驗證。
下面關於圖片數據信息的獲取
for (ix = 0; ix < bmp->ysize; ++ix) {
//因爲bmp文件的原點是左下角,所以bmp圖片需要順着y軸的反方向來讀取
fseek(fp, bmp->head.file.offset + (bmp->ysize -1 - ix) * bmp->line_length+1, SEEK_SET); //最後一行開始讀取
//讀取一行像素數
fread(buf, 1, bmp->line_length, fp);
轉載:https://blog.csdn.net/weixin_42164528/article/details/80594667