BMP文件格式
位圖文件(Bitmap-File,BMP)格式是Windows操作系統中的標準圖像格式。採用位映射存儲,圖像深度可選lbit、4bit、8bit及24bit,圖像的掃描方式是按從左到右、從下到上的順序,即陣列的第一個字節表示圖像最後一行的第一個像素,默認的文件擴展名是bmp或者dib。
BMP文件大體上分爲四個部分:
位圖文件頭BITMAPFILEHEADER |
位圖信息頭 BITMAPINFOHEADER |
調色板Palette |
實際的位圖數據ImageData |
typedef struct tagBITMAPFILEHEADER {
WORD bfType; /* 說明文件的類型 */
DWORD bfSize; /* 說明文件的大小,用字節爲單位 ,注意此處的字節序問題*/
WORD bfReserved1; /* 保留,設置爲0 */
WORD bfReserved2; /* 保留,設置爲0 */
DWORD bfOffBits; /* 說明從BITMAPFILEHEADER 開始到實際的圖像數據之間的字節偏移量 */
}BITMAPFILEHEADER;
WORD:unsigned short 佔2字節
DWORD:unsigned int佔4字節
byType:表示文件類型,16進制:0x4D42,代表bmp文件
位圖信息頭包括:
typedef struct tagBITMAPINFOHEADER
{
DWORD biSize; /* 說明結構體所需字節數 */
long biWidth; /* 以像素爲單位說明圖像的寬度 00 00 02 58*/
long biHeight; /* 以像素爲單位說明圖像的高度 00 00 01 c2*/
WORD biPlanes; /* 說明位面數,必須爲1 00 01*/
WORD biBitCount; /* 說明位數/像素,1、2、4、8、24 00 18*/
DWORD biCompression; /* 說明圖像是否壓縮及壓縮類型 BI_bmp,BI_RLE8,BI_RLE4,BI_BITFIELDS */
DWORD biSizeImage; /* 以字節爲單位說明圖像大小 ,必須是4 的整數倍*/
long biXPelsPerMeter; /* 目標設備的水平分辨率,像素/米 */
long biYPelsPerMeter; /*目標設備的垂直分辨率,像素/米 */
DWORD biClrUsed; /* 說明圖像實際用到的顏色數,如果爲0
則顏色數爲2的biBitCount次方 */
DWORD biClrImportant; /*說明對圖像顯示有重要影響的顏色
索引的數目,如果是0,表示都重要。*/
} BITMAPINFOHEADER;
biWidth:4字節,表示圖像的寬度:如 00 00 02 58 寬度600(單位像素)
biHeight:4字節,表示圖像的高度:如 00 00 01 C2 高度450(單位像素)
biBitCount:2字節,表示位數,如所選擇的bmp爲24位,則爲 00 18
用二進制編輯器打開如下:
調色板
typedef struct tagRGBQUAD {
BYTE rgbBlue; /*指定藍色分量*/
BYTE rgbGreen; /*指定綠色分量*/
BYTE rgbRed; /*指定紅色分量*/
}RGBQUAD
調色板實際上是一個數組,它所包含的元素與位圖所具有的顏色數相同,決定於biClrUsed和biBitCount字段。數組中每個元素的類型是一個RGBQUAD結構。真彩色圖(24位的bmp)無調色板部分。
緊跟在調色板之後的是圖像原始數據Matadata,隨位圖所使用的的位數不同而不同,24位圖中直接使用RGB部分,小於24位的使用調色板顏色索引值。
每一掃描行由表示像素的連續字節組成,每一行的字節數取決於顏色數目和圖像寬度,且必須是4的倍數。
讀取BMP,提取RGB的流程如下:
根據每像素所佔比特的不同,採用不同的處理方法
並調用RGB2YUV的函數實現到YUV的轉換,轉換關係如下:
Y=0.2990*R+0.5870*G+0.1140*B
R-Y=0.7010*R-0.5870*G-0.1140*B
B-Y=-0.2990*R-0.5870*G+0.8860*B
歸一化後:
轉換後的YUV文件是4:2:0格式,即U、V分量的寬度和高度各是Y分量的一半。
部分代碼如下:
BITMAPFILEHEADER File_header;
BITMAPINFOHEADER Info_header;
// read file & info header
if(fread(&File_header,sizeof(BITMAPFILEHEADER),1,bmpFile) != 1)
{
printf("read file header error!");
exit(0);
}
if (File_header.bfType != 0x4D42)
{
printf("Not bmp file!");
exit(0);
}
else
{ printf("this is a bmp\n");
}
if(fread(&Info_header,sizeof(BITMAPINFOHEADER),1,bmpFile) != 1)
{
printf("read info header error!");
exit(0);
}
利用typedef 定義的BITMAPFILEHEADER和 BITMAPINFOHEADER 結構化定義數組 File_header和Info_header數組,存儲讀取的文件頭和信息頭,並進行判斷是否可讀且是BMP文件,以備使用。
if (((Info_header.biWidth/8*Info_header.biBitCount)%4) == 0)
frameWidth= Info_header.biWidth;
else
frameWidth = (Info_header.biWidth*Info_header.biBitCount+31)/32*4;
if ((Info_header.biHeight%2) == 0)
frameHeight = Info_header.biHeight;
else
frameHeight = Info_header.biHeight + 1;
printf("The width is %d\n",frameWidth);
printf("The height is %d\n",frameHeight);
判斷讀取的寬度和高度是否滿足條件,並傳遞參數。
之後調用RGB2YUV的函數以實現到YUV的轉換
實驗中出現的問題如下:
1.讀取參數錯誤:
寬度和高度與文件的實際數據不符,經在網上查詢後,發現是結構體會按照DWORD長度, 即4進行對齊,第一個WORD佔用2字節,文件頭佔用16字節,使後續字節讀取錯誤,所以在定義文件頭結構體和信息頭結構體前面加上:
#pragma pack(1), 使結構體內部按1字節排列,這樣便可讀出正確的文件頭和信息頭。
實驗結果 生成一個包含5張圖像的200幀的yuv文件(24比特),使用YUVviewerPlus播放:
16bit的BMP文件:
16比特的有555和565兩種,默認是555格式,當biCompression成員的值是BI_RGB時,它沒有調色板。16位中,最低的5位表示藍色分量,中間的5位表示綠色分量,高的5位表示紅色分量,一共佔用了15位,最高的一位保留,設爲0,即xrrrrrgggggbbbbb
通過移位進行轉換:
for (Loop = 0;Loop < height * width;Loop +=2)
{
*rgbData = (Data[Loop]&0x1F)<<3;
*(rgbData + 1) = ((Data[Loop]&0xE0)>>2) + ((Data[Loop+1]&0x03)<<6);
*(rgbData + 2) = (Data[Loop+1]&0x7C)<<1;
rgbData +=3;
}
1~8bit
先判斷調色板:
bool MakePalette(FILE * pFile,BITMAPFILEHEADER &file_h,BITMAPINFOHEADER & info_h,RGBQUAD *pRGB_out)
{
if ((file_h.bfOffBits - sizeof(BITMAPFILEHEADER) - info_h.biSize) == sizeof(RGBQUAD)*pow(float(2),info_h.biBitCount))
{
fseek(pFile,sizeof(BITMAPFILEHEADER)+info_h.biSize,0);
fread(pRGB_out,sizeof(RGBQUAD),(unsigned int)pow(float(2),info_h.biBitCount),pFile);
return true;
}
else
return false;
// free(pRGB);
}
RGBQUAD *pRGB = (RGBQUAD *)malloc(sizeof(RGBQUAD)*(unsigned long)pow(float(2),info_h.biBitCount));
int temp = sizeof(pRGB);
if(!MakePalette(pFile,file_h,info_h,pRGB))
printf("No palette!\n\n");
for (Loop=0;Loop<height*width;Loop++)
{
switch(info_h.biBitCount)
{
case 1:
mask = 0x80;
break;
case 2:
mask = 0xC0;
break;
case 4:
mask = 0xF0;
break;
case 8:
mask = 0xFF;
}
int shiftCnt = 1;
while (mask)
{
unsigned char index = mask == 0xFF ? Data[Loop] : ((Data[Loop] & mask)>>(8 - shiftCnt * info_h.biBitCount));
* rgbData = pRGB[index].rgbBlue;
* (rgbData+1) = pRGB[index].rgbGreen;
* (rgbData+2) = pRGB[index].rgbRed;
if(info_h.biBitCount == 8)
mask =0;
else
mask >>= info_h.biBitCount;
rgbData+=3;
shiftCnt ++;
}
}
if(Index_Data)
free(Index_Data);
if(Data)
free(Data);
// if(pRGB)
// free(pRGB);
}
(其實不太理解原理)實驗結果如下:
其中bmp2爲16bit,最後一張爲4bit,其餘爲8bit
16比特及以下實驗中出現的問題:
運行8比特文件時會停止運行,加斷點調試後,發現是
RGBQUAD *pRGB = (RGBQUAD *)malloc(sizeof(RGBQUAD)*(unsigned char)pow(float(2),info_h.biBitCount));
有問題,然後將char改爲long,long爲4字節,而8比特bmp調色板索引值爲0~255,超出了char型所能表示的範圍。通過本次實驗:
1、學習瞭解BMP文件的組成結構
2、複習RGB和YUV文件之間的轉換
3、複習了c語言中的函數的調用和參數傳遞
8bit 16bit 等BMP文件的下載地址:
http://www.hlevkin.com/06testimages.htm