天天看點

BMP檔案結構及圖像每行位元組計算方法

BMP檔案結構

1. BMP檔案組成   BMP檔案由檔案頭、位圖資訊頭、顔色資訊和圖形資料四部分組成。檔案頭主要包含檔案的大小、檔案類型、圖像資料偏離檔案頭的長度等資訊;位圖資訊頭包含圖象的尺寸資訊、圖像用幾個比特數值來表示一個像素、圖像是否壓縮、圖像所用的顔色數等資訊。顔色資訊包含圖像所用到的顔色表,顯示圖像時需用到這個顔色表來生成調色闆,但如果圖像為真彩色,既圖像的每個像素用24個比特來表示,檔案中就沒有這一塊資訊,也就不需要操作調色闆。檔案中的資料塊表示圖像的相應的像素值,需要注意的是:圖像的像素值在檔案中的存放順序為從左到右,從下到上,也就是說,在BMP檔案中首先存放的是圖像的最後一行像素,最後才存儲圖像的第一行像素,但對與同一行的像素,則是按照先左邊後右邊的的順序存儲的;另外一個需要讀者朋友關注的細節是:檔案存儲圖像的每一行像素值時,如果存儲該行像素值所占的位元組數為4的倍數,則正常存儲,否則,需要在後端補0,湊足4的倍數。   

2. BMP檔案頭   BMP檔案頭資料結構含有BMP檔案的類型、檔案大小和位圖起始位置等資訊。其結構定義如下: typedef struct tagBITMAPFILEHEADER { WORD bfType; // 位圖檔案的類型,必須為“BM” DWORD bfSize; // 位圖檔案的大小,以位元組為機關 WORD bfReserved1; // 位圖檔案保留字,必須為0 WORD bfReserved2; // 位圖檔案保留字,必須為0 DWORD bfOffBits; // 位圖資料的起始位置,以相對于位圖檔案頭的偏移量表示,以位元組為機關 } BITMAPFILEHEADER;該結構占據14個位元組。   

3. 位圖資訊頭   BMP位圖資訊頭資料用于說明位圖的尺寸等資訊。其結構如下: typedef struct tagBITMAPINFOHEADER{ DWORD biSize; // 本結構所占用位元組數 LONG biWidth; // 位圖的寬度,以像素為機關 LONG biHeight; // 位圖的高度,以像素為機關 WORD biPlanes; // 目标裝置的平面數不清,必須為1 WORD biBitCount// 每個像素所需的位數,必須是1(雙色), 4(16色),8(256色)或24(真彩色)之一 DWORD biCompression; // 位圖壓縮類型,必須是 0(不壓縮),1(BI_RLE8壓縮類型)或2(BI_RLE4壓縮類型)之一 DWORD biSizeImage; // 位圖的大小,以位元組為機關 LONG biXPelsPerMeter; // 位圖水準分辨率,每米像素數 LONG biYPelsPerMeter; // 位圖垂直分辨率,每米像素數 DWORD biClrUsed;// 位圖實際使用的顔色表中的顔色數 DWORD biClrImportant;// 位圖顯示過程中重要的顔色數 } BITMAPINFOHEADER;該結構占據40個位元組。   注意:對于BMP檔案格式,在處理單色圖像和真彩色圖像的時候,無論圖象資料多麼龐大,都不對圖象資料進行任何壓縮處理,一般情況下,如果位圖采用壓縮格式,那麼16色圖像采用RLE4壓縮算法,256色圖像采用RLE8壓縮算法。  

 4. 顔色表   顔色表用于說明位圖中的顔色,它有若幹個表項,每一個表項是一個RGBQUAD類型的結構,定義一種顔色。RGBQUAD結構的定義如下: typedef struct tagRGBQUAD { BYTErgbBlue;// 藍色的亮度(值範圍為0-255) BYTErgbGreen; // 綠色的亮度(值範圍為0-255) BYTErgbRed; // 紅色的亮度(值範圍為0-255) BYTErgbReserved;// 保留,必須為0 } RGBQUAD;   顔色表中RGBQUAD結構資料的個數由BITMAPINFOHEADER 中的biBitCount項來确定,當biBitCount=1,4,8時,分别有2,16,256個顔色表項,當biBitCount=24時,圖像為真彩色,圖像中每個像素的顔色用三個位元組表示,分别對應R、G、B值,圖像檔案沒有顔色表項。位圖資訊頭和顔色表組成位圖資訊,BITMAPINFO結構定義如下: typedef struct tagBITMAPINFO { BITMAPINFOHEADER bmiHeader; // 位圖資訊頭 RGBQUAD bmiColors[1]; // 顔色表 } BITMAPINFO;   注意:RGBQUAD資料結構中,增加了一個保留字段rgbReserved,它不代表任何顔色,必須取固定的值為“0”,同時,RGBQUAD結構中定義的顔色值中,紅色、綠色和藍色的排列順序與一般真彩色圖像檔案的顔色資料排列順序恰好相反,既:若某個位圖中的一個像素點的顔色的描述為“00,00,ff,00”,則表示該點為紅色,而不是藍色。  

 5. 位圖資料   位圖資料記錄了位圖的每一個像素值或該對應像素的顔色表的索引值,圖像記錄順序是在掃描行内是從左到右,掃描行之間是從下到上。這種格式我們又稱為Bottom_Up位圖,當然與之相對的還有Up_Down形式的位圖,它的記錄順序是從上到下的,對于這種形式的位圖,也不存在壓縮形式。位圖的一個像素值所占的位元組數:當biBitCount=1時,8個像素占1個位元組;當biBitCount=4時,2個像素占1個位元組;當biBitCount=8時,1個像素占1個位元組;當biBitCount=24時,1個像素占3個位元組,此時圖像為真彩色圖像。當圖像不是為真彩色時,圖像檔案中包含顔色表,位圖的資料表示對應像素點在顔色表中相應的索引值,當為真彩色時,每一個像素用三個位元組表示圖像相應像素點彩色值,每個位元組分别對應R、G、B分量的值,這時候圖像檔案中沒有顔色表。上面我已經講過了,Windows規定圖像檔案中一個掃描行所占的位元組數必須是4的倍數(即以字為機關),不足的以0填充,圖像檔案中一個掃描行所占的位元組數計算方法: DataSizePerLine= (biWidth* biBitCount+31)/8;// 一個掃描行所占的位元組數或者 DataSizePerLine= (biWidth* biBitCount+31)/32 * 4;// 一個掃描行所占的位元組數 (如果biBitCount == 8 或24) DataSizePerLine= (biWidth* 3+3)/4*4;// 一個掃描行所占的位元組數或 DataSizePerLine= (biWidth*1+3)/4*4;// 一個掃描行所占的位元組數  位圖資料的大小按下式計算(不壓縮情況下):   DataSize= DataSizePerLine* biHeight。  

 上述是BMP檔案格式的說明,搞清楚了以上的結構,就可以正确的操作圖像檔案,對它進行讀或寫操作了。六、圖像每行位元組數的計算 利用宏:#define WIDTHBYTES(bits) (((bits) + 31) / 32 * 4) bmp檔案的資料塊部分不是直接的一個個像素排列後存儲。為了要保證每行的位元組數都能夠被4整除,往往要在每行資料後面補充1,2或3個位元組的備援資訊。 X*Y大小的24位bmp圖像,每個像素占3個位元組,如果X*3後不能夠被4整除,則每行有可能是X*3+1, X*3+2或X*3+3, 取決于哪個數值可以被4整除。 這裡有一個宏,可以很友善的算出這個位元組數:#define WIDTHBYTES(bits) (((bits) + 31) / 32 * 4),使用方法如下:long lLineBytes = WIDTHBYTES(m_lWidth * 24)。如果是8位的bmp圖像,或4位,2位,則将算式中的24改為相應的位數即可。另外bmp檔案分檔案頭14位元組,資訊40位元組,調色闆若幹位元組,資料lLineBytes*height位元組。 其次,怎樣通路bmp圖像中特定的一個第x行,第y列的像素。如果bmp檔案的檔案頭BITMAPINFOHEADER中的biHeight為正,則檔案的起始位置是圖像的左下角,像素依次從左向右,從下到上排列。如果biHeight為負,則檔案的起始位置是圖像的左上角,像素依次從左向右,從上到下排列。 假設指向bmp檔案的資料區的指針byte* p_bmpdata, bmp檔案為24位,為了通路第x行,第y列,可以用如下公式: b = p_bmpdata[lLineBytes * x + y*3]; g = p_bmpdata[lLineBytes *x + y*3+1]; r = p_bmpdata[lLineBytes * x + y*3+2]; 如果要從第一個像素開始周遊整個資料區,可以用這樣的方式: for (i = 0; i <height; i ++) { for (j = 0; j < width; j ++) { b = p_bmpdata[lLineBytes * i + j*3]; g = p_bmpdata[lLineBytes * i + j*3+1]; r = p_bmpdata[lLineBytes * i + j*3+2]; } } 注意,這裡的height和width是圖像的列像素數和行像素數。而循環内與i相乘的是整行的位元組數,不是行像素數。 最後,當使用自己建立的圖像緩沖區是,周遊的方法又有所不同。假設為上bmp檔案的資料作一個緩沖。 byte *p_bmpbuffer = new byte[height*width*3]; for(i = 0; i< width; i++) { for(j=0; j<height; j++) { p_bmpbuffer[(i*height+j)*3] = p_bmpdata[lLineBytes * i + j*3]; p_bmpbuffer[(i*height+j)*3+1] = p_bmpdata[lLineBytes * i + j*3+1]; p_bmpbuffer[(i*height+j)*3+2] = p_bmpdata[lLineBytes * i + j*3+2]; } } //這裡不用memcpy的原因是p_bmpdata的長度為height*lLineBytes, 而p_bmpbuffer的長度是height*width*3, p_bmpdata在每行末都有1,2或3個位元組的備援。 byte b, g, r; for(i = 0; i< height; i++) { for(j=0; j<width; j++) { b = p_bmpbuffer[(i*width+j)*3]; g = p_bmpbuffer[(i*width+j)*3+1]; r = p_bmpbuffer[(i*width+j)*3+2]; } } //這裡對像素的通路i,j的意義發生變化,之前的循環順序通路下來,圖像的行被列值y中間截斷,雖然可以順序依次通路每個像素,但i,j值沒有實際意義。第二次循環, //i值即對應目前像素y坐标,j值對應目前像素x坐标。這樣在運用模闆濾波時,或是進行相鄰像素計算時是準确的。 看了一本關于數字圖像處理的書,提供的計算每行像素所占位元組數算法如下: #define WIDTHBYTES(bits) (((bits) + 31) / 32 * 4) LineBytes=WIDTHBYTES(m_nWidth*8); //m_nWidth是圖像的寬度朋友們乍看可能不明白為何用此公式,現在解釋一下公式。 BMP檔案在儲存或顯示的時候,要求每行位元組數是4的整數倍。WIDTHBYTES(bits)這個定義的意思就是為了保證每行都是4的倍數,注意必須要這樣,要不然你顯示的圖檔内容就會錯位。 比如灰階圖 吧,每行寬度是191個象素即191bytes,如果要儲存所有圖像資訊,你在寫檔案時每行位元組數必須為192=(191*1+3)/4*4;(也可通過位運算:(191*1+3)&~3);如果是24位的彩色圖,即每個象素有 3個Bytes,隻要把上式中*1換成*3即可。 每一行的位元組數必須是4的整倍數,隻要調用 WIDTHBYTES(bi.biWidth*bi.biBitCount)就能完成這一換算。舉一個例子,對于2色圖,如果圖象寬是31,則每一行需要31位存儲,合3個位元組加7位,因 為位元組數必須是4的整倍數,是以應該是4,而此時的biWidth=31,biBitCount=1,WIDTHBYTES(31*1)=4,和我們設想的一樣。再舉一個256色的例子,如果圖象寬是31,則每一行需要31個位元組存儲,因為字 節數必須是4的整倍數,是以應該是32,而此時的biWidth=31,biBitCount=8,WIDTHBYTES(31*8)=32,和我們設想的一樣。你可以多舉幾個例子來驗證一下LineBytes為每一行的位元組數 LineBytes= (DWORD)WIDTHBYTES(bi.biWidth*bi.biBitCount);不過隻要了解了BMP的格式,自己就可以寫出其他的換算函數。

繼續閱讀