(一)实验目的
1.理解图像文件的基本组成。
2.掌握结构体作为复杂数据对象的用法。进一步熟悉由问题到程序的解决方案,并掌握编程细节:如内存分配、倒序读写、字节序、文件读写过程等。
(二)实验内容
- 在图像处理软件中自行生成多个BMP文件,至少含5个不同的场景画面,要求带含有班级、学号后四位和本人姓名(缩写或昵称均可)的logo。(基本要求为24bit的BMP,进阶要求为支持小于24bit的BMP。)
- 编写将第一步所生成的多个BMP文件转化为YUV文件,要求可在命令行中设置每个画面出现的帧数。最后形成的YUV文件应至少包含200帧。重点掌握函数定义、缓冲区分配、倒序读写、结构体的操作。
- 对整个程序进行调试,并将生成的YUV文件用播放软件观看,验证是否正确。
(三)实验步骤
思路方面理解起来很简单,理清BMP文件数据中的四层结构,分别是位图文件头、位图信息头、调色板、实际的位图数据。从位图信息头中提取图像宽高、实际的位图数据所在数据,然后从BMP中读取实际的位图数据,进而索引调色板数组得到每一数据的实际物理意义,即查找得到各位图数据代表了什么颜色(顺序是BGR),进而采用下列转化公式,将RGB转化为YUV
Y=0.30R+0.59G+0.11B, U=0.493(B-Y), V=0.877(R-Y)
- BMP文件的组成结构
1)BMP(全称Bitmap)是Window操作系统中的标准图像文件格式,可以分成两类:设备相关位图(DDB)和设备无关位图(DIB),使用非常广。它采用位映射存储格式,除了图像深度可选以外,不采用其他任何压缩,因此,BMP文件所占用的空间很大。
2)BMP文件的图像深度可选1bit、4bit、8bit及24bit。BMP文件存储数据时,图像的扫描方式是按从左到右、从下到上的顺序。 由于BMP文件格式是Windows环境中交换与图有关的数据的一种标准,因此在Windows环境中运行的图形图像软件都支持BMP图像格式。
结构 | 说明 |
---|---|
位图头文件数据结构 | 包含BMP图像文件的类型、显示内容等信息 |
位图信息数据结构 | 包含有BMP图像的宽、高、压缩方法,以及定义颜色等信息 |
调色板 | 这个部分是可选的,有些位图需要调色板,有些位图,比如真彩色图(24位的BMP)就不需要调色板 |
位图数据 | 这部分的内容根据BMP位图使用的位数不同而不同,在24位图中直接使用RGB,而其他的小于24位的使用调色板中颜色索引值 |
(1)位图头文件结构体
typedef struct tagBITMAPFILEHEADER {
WORD bfType; //说明文件的类型
DWORD bfSize; // 说明文件的大小,用字节为单位
WORD bfReserved1; //保留,设置为0
WORD bfReserved2; // 保留,设置为0
DWORD bfOffBits; //说明从BITMAPFILEHEADER结构开始到实际的图像数据之间的字节偏移量
} BITMAPFILEHEADER;
(2)位图信息头
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;
(3)调色板
typedef struct tagRGBQUAD {
BYTE rgbBlue; /*指定蓝色分量*/
BYTE rgbGreen; /*指定绿色分量*/
BYTE rgbRed; /*指定红色分量*/
BYTE rgbReserved; /*保留,指定为0*/
} RGBQUAD;
(四)关键代码
用结构体组织复杂数据;
结构成员的值个别地传给函数处理。
- 前序工作
这里以生成多帧YUV播放视频为例,首先设置命令行参数
argv[1]=potter argv[2]=5,第一个参数代表文件名的共同部分,第二个代表图片张数(也就是视频的帧数),目的是方便读取图一所示的文件
至于YUV文件,文件名直接使用argv[1]:potter.yuv
- 主函数分析
主函数的思路就是整个实验的思路:
1)读取BMP文件;
2) 抽取BMP文件头和信息头信息,用结构体File_header盛放文件头数据,用结构体Info_header盛放信息头数据;
3)调用ReadRGB 函数,从bmp文件中读取RGB信息;
4)最后调用RGB2YUV函数,将rgb数据转为yuv;
5)写YUV文件
int main(int argc,char**argv)
{
BITMAPFILEHEADERFile_header;
BITMAPINFOHEADERInfo_header;
FILE*bmpFile;
FILE*yuvFile;
unsignedchar*rgbBuffer, *yBuffer, *uBuffer, *vBuffer;
charname__[20];
//连接成字符串:potter.yuv,方便文件写入磁盘
strcpy_s(name__, argv[1]);
strcat_s(name__, 20, ".yuv");
fopen_s(&yuvFile,name__ , "wb");
if(!yuvFile)
{
printf("yuvfile open error!\n");
exit(0);
}
for (inti = 1; i <= atoi(argv[2]); ++i){
charstr[20];
charname_[20];
//将“potter”“序号”“.bmp”连接成文件名,再用fopen函数打开文件
strcpy_s(name_, argv[1]);
_itoa_s(i, str, 20, 10);
strcat_s(name_, 20, str);
strcat_s(name_, 20,".bmp");
fopen_s(&bmpFile,name_ , "rb");
if(!bmpFile)
{
printf("bmpfile open error!\n");
exit(0);
}
//读取位图文件头
if(fread(&File_header, sizeof(BITMAPFILEHEADER), 1, bmpFile) != 1)
{
printf("readfile header error!\n");
exit(0);
}
//判断文件类型
if(File_header.bfType != 0x4D42)
{
printf("Notbmp file!\n");