bmp2yuv

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

歸一化後:

U=-0.1684*R-0.3316*G+0.5*B
V=0.5*R-0.4187*G-0.0813*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_headerInfo_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



 








發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章