BMP2YUV实验报告
2017-03-21 22:11
351 查看
第二次实验报告
一、实验原理
bmp文件的结构
bmp文件格式 |
---|
位图文件头 BITMAPFILEHEADER |
位图信息头 BITMAPINFOHEADER |
调色板Palette |
实际的位图数据ImageData |
WORD为两字节,DWORD为四字节
typedef struct tagBITMAPFILEHEADER { WORD bfType;/*说明文件的类型*/ DWORD bfSize;/*说明文件的大小,用字节为单位*/ /*注意此处的字节序问题*/ WORD bfReserved1; /* 保留,设置为0 */ WORD bfReserved2; /* 保留,设置为0 */ DWORD bfOffBits; /* 说明从BITMAPFILEHEADER结构开始到实际的图像数据之间的字节偏移量 */ } BITMAPFILEHEADER;
信息头包含的内容
typedef struct tagBITMAPINFOHEADER { DWORD biSize; /* 说明结构体所需字节数 */ LONG biWidth; /* 以像素为单位说明图像的宽度 */ LONG biHeight; /* 以像素为单位说明图像的高速 */ WORD biPlanes; /* 说明位面数,必须为1 */ WORD biBitCount;/*说明位数/像素1、2、4、8、24*/ DWORD biCompression; /* 说明图像是否压缩及压缩类型 BI_RGB,BI_RLE8,BI_RLE4,BI_BITFIELDS */ DWORD biSizeImage; /* 以字节为单位说明图像大小,必须是4的整数倍*/ LONG biXPelsPerMeter; /*目标设备的水平分辨率,像素/米 */ LONG biYPelsPerMeter; /*目标设备的垂直分辨率,像素/米 */ DWORD biClrUsed; /* 说明图像实际用到的颜色数,如果为0则颜色数为2的biBitCount次方 */ DWORD biClrImportant; /*说明对图像显示有重要影响的颜色索引的数目,如果是0,表示都重要。*/ } BITMAPINFOHEADER;
调色板包含的内容
图像位深度小于等于8bit时调用调色板,调色板中不保存每个像素的RGB值,而是将颜色信息制成表格,通过索引来确定像素的RGB值。比如图像位深度为8bit时,表格中则有2^8 256个数值。
typedef struct tagRGBQUAD { BYTE rgbBlue; /*指定蓝色分量*/ BYTE rgbGreen; /*指定绿色分量*/ BYTE rgbRed; /*指定红色分量*/ BYTE rgbReserved; /*保留,指定为0*/ } RGBQUAD;
下面通过具体的bmp文件来进行说明。
图像为24bit的bmp文件,分辨率为256*256。
0x00~0x01:内容为42 4D,标记了此文件类型为BMP文件
0x02~0x05:内容为38 00 03 00,表示该文件大小的十六进制表示为030038个字节(先保存低位,再保存高位)。
0x0a~0x0d:36 00 00 00,因为24bit的bmp文件没有调色板,因此表示bmp文件的文件头和信息头共占了36H字节,表示实际数据字节偏移量。
0x16~0x19:00 01 00 00,表示高度为100000000即256个像素。
0x1c~0x1d:表示图片为深度为18H,即24bit。
图像为8bit的bmp文件,分辨率为256*256。
0x00~0x01:内容为42 4D,标记了此文件类型为BMP文件
0x02~0x05:内容为38 04 01 00,表示该文件大小的十六进制表示为010438个字节(先保存低位,再保存高位)。
0x0a~0x0d:36 04 00 00,因为8bit的bmp文件有调色板,因此表示bmp文件的文件头和信息头共占了36H字节,调色板结构占用了00100000(1024)个字节,,共占用了1078个字节,表示实际数据字节偏移量为1078个字节。
0x16~0x19:00 01 00 00,表示高度为100000000即256个像素。
0x1c~0x1d:表示图片为深度为18H,即24bit。
RGB2YUV转换算法
Y=0.2990R+0.5870G+0.1140BU=−<
4000
/span>0.1684R−0.3316G+0.5000B+128
V=−0.5000R−0.4187G−0.0813B+128
具体算法及代码可参见第一次试验报告的内容。
二、实验流程分析
1.程序初始化(打开两个文件、定义变量和缓冲区等)2.
读取BMP文件,抽取或生成RGB数据写入缓冲区 |
---|
读位图文件头:判断是否可读出、判断是否是BMP文件 |
判断像素的实际点阵数 |
开辟缓冲区,读数据,倒序存放 |
根据每像素位数的不同,执行不同的操作: |
8bit以下 | 16bit | 24bit |
---|---|---|
构造调色板 | ||
位与移位取像素数据查调色板写RGB缓冲区 | 位与移位取像素数据转换8bit彩色分量写RGB缓冲区 | 直接取像素数据写RGB缓冲区 |
4.写YUV文件
5.程序收尾工作(关闭文件,释放缓冲区)
三、重要代码及分析
BITMAPFILEHEADER File_header; BITMAPINFOHEADER Info_header;
文件结构组成包含在windows.h中
/*用于打开bmp文件的代码,与第一次实验相同*/ bmpFile = fopen(bmpFileName, "rb"); if (bmpFile == NULL) { printf("cannot find rgb file\n"); exit(1); } else { printf("The input bmp file is %s\n", bmpFileName); }
/*读取bmp文件头和信息头*/ 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 %c%c\n", File_header.bfType); } if (fread(&Info_header, sizeof(BITMAPINFOHEADER), 1, bmpFile) != 1)//读取信息头 { printf("read info header error!"); exit(0); }
/*读取文件字节宽度和高度*/ if (((Info_header.biWidth / 8 * Info_header.biBitCount) % 4) == 0) width = Info_header.biWidth / 8 * Info_header.biBitCount; else width = (Info_header.biWidth*Info_header.biBitCount + 31) / 32 * 4; if ((Info_header.biHeight % 2) == 0) height = Info_header.biHeight; else height = Info_header.biHeight + 1; /*读取文件的宽和高*/ frameWidth = Info_header.biWidth; frameHeight = Info_header.biHeight;
BMP文件一行的字节数必须为4的倍数,如果不是4的倍数,就需要补充相应的位数使之成为4的位数,BMP的文件的列数也应该为偶数,如果不是偶数,则应该补一位使之成为偶数。
/*开空间,bmpBuf指向的空间大小为bmp文件的字节数,yuv空间的开辟在此不再赘述*/ rgbBuf = (unsigned char*)malloc(Info_header.biWidth * Info_header.biHeight * 3); bmpBuf = (unsigned char*)malloc(width * height );
根据位深度的不同进行不同的操作。
先判断是否有调色板
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(2, info_h.biBitCount)) { fseek(pFile, sizeof(BITMAPFILEHEADER) + info_h.biSize, 0); fread(pRGB_out, sizeof(RGBQUAD), (unsigned int)pow(2, info_h.biBitCount), pFile); return true; } else return false; }
RGBQUAD *pRGB = (RGBQUAD *)malloc(sizeof(RGBQUAD)*(unsigned int)pow(2, Info_header.biBitCount));
调色板的大小为2(biBitCount)。在这里要注意的是,当位深度为8bit时,28是256,所以要用unsigned int进行类型转换,不能用unsigned char,会超出0~255的范围,导致程序出错。
1. 24比特
24bit为真彩色,不会用到调色板,因此直接取像素数据写RGB缓冲区。
fread(rgbBuf, 1, frameWidth * frameHeight*3 , bmpFile);
2. 16比特
16bit也不用到调色板,但由于16bit保存方式rgb不是整字节值,因此采用如下代码进行写入。
xxx xxxxx xxxxxx xx
G1 B R G1
if (Info_header.biCompression == BI_RGB) { for (Loop = 0; Loop < height * width; Loop += 2) { *rgb = (bmpBuf[Loop] & 0x1F) << 3;//B *(rgb + 1) = ((bmpBuf[Loop] & 0xE0) >> 2) + ((bmpBuf[Loop + 1] & 0x03) << 6);//G *(rgb + 2) = (bmpBuf[Loop + 1] & 0x7C) << 1;//R rgb += 3; } }
先保存低位,再保存高位,高位字节的前六位保存红色R的内容,后两位及低位字节的前三位保存绿色G,后五位保存蓝色B。
3. 8bit及以下
for (Loop = 0; Loop< height * width; Loop++) { switch (Info_header.biBitCount) { case 1: mask = 0x80;//1000 0000 break; case 2: mask = 0xC0;//1100 0000 break; case 4: mask = 0xF0;//1111 0000 break; case 8: mask = 0xFF;//1111 1111 } /*对于1bit的图像,一个字节中的第一位为第一个像素的值。4bit图像,一个字节中的前四位为第一个像素的值。具体操作是将通过不需要的位与0,需要的位与1.2bit及8bit同理。*/ int shiftCnt = 1; while (mask) { unsigned char index = mask == 0xFF ? bmpBuf[Loop] : ((bmpBuf[Loop] & mask) >> (8 - shiftCnt * Info_header.biBitCount)); *rgb = pRGB[index].rgbBlue; *(rgb + 1) = pRGB[index].rgbGreen; *(rgb + 2) = pRGB[index].rgbRed; if (Info_header.biBitCount == 8) mask = 0; else mask >>= Info_header.biBitCount; rgb += 3; shiftCnt++; }
进行rgb2yuv操作
if (RGB2YUV(frameWidth, frameHeight, rgbBuf, yBuf, uBuf, vBuf, flip)) { printf("error"); return 0; } for (i = 0; i < bmpframe; i++) { fwrite(yBuf, 1, frameWidth * frameHeight, yuvFile); fwrite(uBuf, 1, (frameWidth * frameHeight) / 4, yuvFile); fwrite(vBuf, 1, (frameWidth * frameHeight) / 4, yuvFile); printf("\r...%d", ++videoFramesWritten); }
具体函数与第一次实验中的相同,在此不再赘述。
四、实验结果及分析
实验测试采用的是分辨率为256x256的位深度分别为1bit、4bit、8bit、16bit、24bit的五张图像。在命令行参数中写入读取的文件名,最终的yuv文件名及,每一幅图像出现的帧数。
最终结果
1bit | 4bit | 8bit |
---|---|---|
16bit | 24bit |
---|---|
五、结论
在这里写出调试程序是时出现的几个主要错误:出现错误1
*rgb = (bmpBuf[Loop] & 0x1F) << 3; *(rgb + 1) = ((bmpBuf[Loop] & 0xE0) >> 2) + ((bmpBuf[Loop + 1] & 0x03) << 6); *(rgb + 2) = (bmpBuf[Loop + 1] & 0x7C) << 1; rgb += 3;
在这段代码中一开始使用的是rgbbuf指针,在执行完这段循环后,rgbbuf已经指向了图像的最后,在接下来调用YUV2RGB函数是,仍要使用rgbbuf,因此此时的rgbbuf已经超出了开空间的范围,在运行时出现了错误。因此通过rgb=rgbbuf,将这段代码中的rgbbuf改为rgb,来使得rgbbuf指向的值改变的同时,rgbbuf指向的地址值不变。
出现错误2
RGBQUAD *pRGB = (RGBQUAD *)malloc(sizeof(RGBQUAD)*(unsigned char)pow(2, Info_header.biBitCount));
在这段程序中,unsigned char是1字节的,当位深度为1bit和4bit时不会出现问题,但当8bit时,28为256,超出了0~255的范围,因此在运行时到8比特时会出错。因此将次改为unsigned int,为2字节即可。
通过本次实验,可以掌握bmp文件格式和bmp2yuv文件转换流程,同时也可以学习复杂数据的组织。我也复习了结构体的相关知识,用函数处理结构中的数据有三种情况
1.把结构成员的值个别地传给函数处理
2.把整个结构作为值,通过参数传递给函数(不希望改变结构变量的值)
3.把结构的地址传给函数,即传递指向结构的指针(希望改变结构变量的值)
本次实验采取的是第二种。
相关文章推荐
- 数据压缩原理与应用 图像文件的读写和转换(BMP2YUV)实验报告
- 数据压缩实验二:bmp转yuv格式实验报告
- BMP转YUV实验报告
- 数据压缩第二次实验报告——用C语言实现bmp to yuv的图片格式转化
- 数据压缩实验二:bmp2yuv
- 实验二:BMP2YUV
- 数据压缩实验二:bmp转yuv格式实验报告
- 第九周实验报告4
- 20145236 《Java程序设计》实验三实验报告
- 第9周实验报告4
- 第二次C程序设计实验报告
- 算法上级报告(渗透问题(Percolation),几种排序算法的实验性能比较,地图路由(Map Routing))
- 实验报告 10 - 2
- 信息安全系统设计基础实验五报告
- 实验报告格式与写法
- 第十周实验报告2(派生类)
- 第十二周实验报告(三)
- 第十周实验报告3
- 实验二 20155307 20155335 20155338 实验报告 固件程序设计
- 第十周实验报告(3)