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



 








                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值