数据压缩原理实验2_BMP2YUV文件转换
2017-03-28 16:09
579 查看
一、实验原理
BMP(全称Bitmap)是Windows操作系统中的标准图像文件格式,可以分成两类:设备相关位图(DDB)和设备无关位图(DIB),使用非常广。它采用位映射存储格式,除了图像深度可选以外,在绝大多数应用中不采用其他任何压缩,因此,BMP文件所占用的空间很大。BMP文件的图像深度可选1bit,4bit,8bit,16bit及24bit。BMP文件存储数据时,图像的扫描方式是按从左到右、从下到上的顺序。由于BMP文件格式是Windows环境中交换与图有关的数据的一种标准,因此在Windows环境中运行的图形图像软件都支持BMP图像格式。
BMP文件格式主要由下面四部分组成:
位图文件头数据结构、位图信息头数据结构、调色板和位图数据四部分组成
(1)位图文件头数据结构,它包含BMP图像文件的类型、显示内容等信息
(2)位图信息头数据结构,它包含有BMP图像的宽、高、压缩方法,以及定义颜色等信息
(3)调色板,这个部分是可选的,调色板实际上是一个数组,它所包含的元素与位图所具有的颜色数相同,决定于biClrUsed和biBitCount字段。数组中每个元素的类型是一个RGBQUAD结构。有些位图需要调色板,有些位图,比如真彩色图(24位的BMP)就不需要调色板。
(4)位图数据,这部分的内容根据BMP位图使用的位数不同而不同。对于用到调色板的位图,图像数据就是该像素颜色在调色板中的索引值(逻辑色)。对于真彩色图,图像数据就是实际的R, G, B值。图像的每一扫描行由表示图像像素的连续的字节组成,每一行的字节数取决于图像的颜色数目和用像素表示的图像宽度。规定每一扫描行的字节数必须是4的整倍数,也就是DWORD对齐的。扫描行是由底向上存储的,这就是说,阵列中的第一个字节表示位图左下角的像素,而最后一个字节表示位图右上角的像素。
下面用一张1位的图像的二进制显示来具体解释上述BMP文件格式
图像说明:该图为1位图像,其分辨率为512*512,大小为32.0625KB=32832B=(8040)H
文件格式说明:
第1-2个字节42 4D表示图像类型为BMP
第3-6个字节 40 80 00 00 表示图像大小,可以看出在数据存放时采取低位在前高位在后的方式;
第11-14个字节3E 00 00 00表示图像的字节偏移量为(3E)H,为62个字节,即14(文件头)+40(信息头)+8(调色板)=62
第15-18个字节28 00 00 00表示信息头的结构体所需为(28)H,40个字节
第19-22个字节00 02 00 00表示图像的宽为(02 00)H,512个像素
第23-26个字节00 02 00 00表示图像的高为(02 00)H,512个像素
第29-30个字节01 00表示图像为1位图像
第37-40个字节 02 80 00 00 表示图像的大小为(00 00 80 20)H, 32800B其计算公式如下:
其中x,y分别是图像的宽和高,因为图像要求其中biSizeImage指的是实际图像数据的大小,以字节为单位。因为windows在进行行行扫描的时候,最小的单位是4个字节,所以当图片的宽度不是4的倍数的时候,需要在每行的后面补上缺少的字节,以0填充。因此在计算中宽必须是4的整数倍,如果不是整数倍,则取大于宽的离4的整数倍最近的数值,这样每行的像素可以整数次读取完成。
上图为将24位真彩图进行了转换成1位,4位,8位的图,可以看到颜色差别还是很明显的
二、实验流程及代码分析
具体的实验流程如下图所示
下面是main.cpp里的具体代码,代码分析加注在代码中
下面是bmp2yuv.cpp里的具体代码,代码分析加注在代码中
当每像素为16位的时候,位与移位取像素数据转换为8bit/彩色分量,写RGB缓冲区,情况与24bit不同,下面进行图示说明:
三、实验结果
以下为最终的视频截图,分别从1位,4位,8位,16位,24位,其分辨率都为512*512
BMP(全称Bitmap)是Windows操作系统中的标准图像文件格式,可以分成两类:设备相关位图(DDB)和设备无关位图(DIB),使用非常广。它采用位映射存储格式,除了图像深度可选以外,在绝大多数应用中不采用其他任何压缩,因此,BMP文件所占用的空间很大。BMP文件的图像深度可选1bit,4bit,8bit,16bit及24bit。BMP文件存储数据时,图像的扫描方式是按从左到右、从下到上的顺序。由于BMP文件格式是Windows环境中交换与图有关的数据的一种标准,因此在Windows环境中运行的图形图像软件都支持BMP图像格式。
BMP文件格式主要由下面四部分组成:
位图文件头数据结构、位图信息头数据结构、调色板和位图数据四部分组成
(1)位图文件头数据结构,它包含BMP图像文件的类型、显示内容等信息
typedef struct tagBITMAPFILEHEADER { WORD bfType; /* 说明文件的类型; 占2个字节 */ DWORD bfSize; /* 说明文件的大小,用字节为单位;占4个字节 */ WORD bfReserved1; /* 保留,设置为0; 占2个字节 */ WORD bfReserved2; /* 保留,设置为0; 占2个字节 */ DWORD bfOffBits; /* 说明从BITMAPFILEHEADER结构开始到实际的图像数据之间的字节偏移量;占4个字节 */ } BITMAPFILEHEADER;
(2)位图信息头数据结构,它包含有BMP图像的宽、高、压缩方法,以及定义颜色等信息
typedef struct tagBITMAPINFOHEADER { DWORD biSize; /* 说明结构体所需字节数 */ LONG biWidth; /* 以像素为单位说明图像的宽度 (需注意的是其数值低位在前高位在后)*/ LONG biHeight; /* 以像素为单位说明图像的高度 */ WORD biPlanes; /* 说明位面数,必须为1 */ WORD biBitCount; /* 说明位数/像素,1、2、4、8、24 */ DWORD biCompression; /* 说明图像是否压缩及压缩类型 其中压缩方式: 0表示不压缩,1表示RLE8压缩,2表示RLE4压缩,3表示每个像素值由指定的掩码决定 BI_RGB(不压缩),BI_RLE8(8位RLE压缩方式),BI_RLE4(4位RLE压缩方式),BI_BITFIELDS(位域存放方式)*/ DWORD biSizeImage; /* 以字节为单位说明图像大小 ,必须是4的整数倍,图像数据大小不是4的倍数时用0填充补足*/ LONG biXPelsPerMeter; /* 目标设备的水平分辨率,像素/米 */ LONG biYPelsPerMeter; /*目标设备的垂直分辨率,像素/米 */ DWORD biClrUsed; /* 说明图像实际用到的颜色数,如果为0,则颜色数为2的biBitCount次方 */ DWORD biClrImportant; /*说明对图像显示有重要影响的颜色索引的数目,如果是0,表示都重要。*/ /*以上按顺序依次所占字节数为:4/4/4/2/2/4/4/4/4/4/4 共40个字节*/ } BITMAPINFOHEADER;
(3)调色板,这个部分是可选的,调色板实际上是一个数组,它所包含的元素与位图所具有的颜色数相同,决定于biClrUsed和biBitCount字段。数组中每个元素的类型是一个RGBQUAD结构。有些位图需要调色板,有些位图,比如真彩色图(24位的BMP)就不需要调色板。
typedef struct tagRGBQUAD { BYTE rgbBlue; /*指定蓝色分量*/ BYTE rgbGreen; /*指定绿色分量*/ BYTE rgbRed; /*指定红色分量*/ BYTE rgbReserved; /*保留,指定为0,可能需要α*/ } RGBQUAD;
(4)位图数据,这部分的内容根据BMP位图使用的位数不同而不同。对于用到调色板的位图,图像数据就是该像素颜色在调色板中的索引值(逻辑色)。对于真彩色图,图像数据就是实际的R, G, B值。图像的每一扫描行由表示图像像素的连续的字节组成,每一行的字节数取决于图像的颜色数目和用像素表示的图像宽度。规定每一扫描行的字节数必须是4的整倍数,也就是DWORD对齐的。扫描行是由底向上存储的,这就是说,阵列中的第一个字节表示位图左下角的像素,而最后一个字节表示位图右上角的像素。
下面用一张1位的图像的二进制显示来具体解释上述BMP文件格式
图像说明:该图为1位图像,其分辨率为512*512,大小为32.0625KB=32832B=(8040)H
文件格式说明:
第1-2个字节42 4D表示图像类型为BMP
第3-6个字节 40 80 00 00 表示图像大小,可以看出在数据存放时采取低位在前高位在后的方式;
第11-14个字节3E 00 00 00表示图像的字节偏移量为(3E)H,为62个字节,即14(文件头)+40(信息头)+8(调色板)=62
第15-18个字节28 00 00 00表示信息头的结构体所需为(28)H,40个字节
第19-22个字节00 02 00 00表示图像的宽为(02 00)H,512个像素
第23-26个字节00 02 00 00表示图像的高为(02 00)H,512个像素
第29-30个字节01 00表示图像为1位图像
第37-40个字节 02 80 00 00 表示图像的大小为(00 00 80 20)H, 32800B其计算公式如下:
其中x,y分别是图像的宽和高,因为图像要求其中biSizeImage指的是实际图像数据的大小,以字节为单位。因为windows在进行行行扫描的时候,最小的单位是4个字节,所以当图片的宽度不是4的倍数的时候,需要在每行的后面补上缺少的字节,以0填充。因此在计算中宽必须是4的整数倍,如果不是整数倍,则取大于宽的离4的整数倍最近的数值,这样每行的像素可以整数次读取完成。
上图为将24位真彩图进行了转换成1位,4位,8位的图,可以看到颜色差别还是很明显的
二、实验流程及代码分析
具体的实验流程如下图所示
下面是main.cpp里的具体代码,代码分析加注在代码中
#include <stdio.h> #include <windows.h> //该头文件里包括了程序中对bmp文件的一些调用函数,如 BITMAPFILEHEADER等,所以必不可少 #include "bmp2yuv.h" void main(int argc, char *argv[]) { FILE *bmpFile = NULL, *yuvFile = NULL; BITMAPFILEHEADER File_header; //直接读取图像的文件头 BITMAPINFOHEADER Info_header; //读取图像的信息头 unsigned char * rgbBuf = NULL; unsigned char * yBuff = NULL; unsigned char * uBuff = NULL; unsigned char * vBuff = NULL; int flip = 0; // 因为bmp文件的存储方式是从左到右、从下到上,因此flip值为0来使rgb2yuv时从下往上读入数据 for (int k = 1; k < 6; k++) //这里采取了循环,每次读取一个bmp文件,k来控制一共需要转换的文件数 { // open bmp & yuv file if ((bmpFile = fopen(argv[k], "rb")) == NULL) //一共5个bmp文件 { printf("bmp file open failed!"); exit(0); } if ((yuvFile = fopen(argv[6], "ab+")) == NULL) //argv[6]中存放的是最后生成的视频文件 { //通过"ab+"来不断的往文件里写入 printf("yuv file failed!"); exit(0); } // end open bmp & yuv file // 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); } if (fread(&Info_header, sizeof(BITMAPINFOHEADER), 1, bmpFile) != 1) { printf("read info header error!"); exit(0); } // end read header int width, height; if ((Info_header.biWidth % 4) == 0) width = Info_header.biWidth; else width = (Info_header.biWidth*Info_header.biBitCount + 31) / 32 * 4; /*加31再除以32后下取整,就保证了计算结果是离这个数最近的而且是比它大的32的倍数, 也就保证了是4字节的整数倍。乘以4和行数,得到4字节整数倍的图像大小。 */ if ((Info_header.biHeight % 2) == 0) height = Info_header.biHeight; else height = Info_header.biHeight + 1; //开辟缓冲区 rgbBuf = (unsigned char *)malloc(height*width * 3); memset(rgbBuf, 0, height*width * 3); //memset函数是对较大的结构体或数组进行清零操作的快速操作 yBuff = (unsigned char *)malloc(height*width); uBuff = (unsigned char *)malloc((height*width) / 4); vBuff = (unsigned char *)malloc((height*width) / 4); //结束开辟缓冲区 printf("This is a %d bits image!\n", Info_header.biBitCount); printf("\nbmp size: \t%d X %d\n", Info_header.biWidth, Info_header.biHeight); ReadRGB(File_header, Info_header, bmpFile, rgbBuf); int i; if (RGB2YUV(width, height, rgbBuf, yBuff, uBuff, vBuff, flip)) { printf("rgb2yuv error"); exit(1); } for (i = 0; i < width*height; i++) { if (yBuff[i] < 16) yBuff[i] = 16; if (yBuff[i] > 235) yBuff[i] = 235; } for (i = 0; i < width*height / 4; i++) { if (uBuff[i] < 16) uBuff[i] = 16; if (uBuff[i] > 240) uBuff[i] = 240; if (vBuff[i] < 16) vBuff[i] = 16; if (vBuff[i] > 240) vBuff[i] = 240; } for (int m = 0; m < 40; m++) { fwrite(yBuff, 1, width * height, yuvFile); fwrite(uBuff, 1, (width * height) / 4, yuvFile); fwrite(vBuff, 1, (width * height) / 4, yuvFile); } } free(rgbBuf); free(yBuff); free(uBuff); free(vBuff); fclose(bmpFile); fclose(yuvFile); }
下面是bmp2yuv.cpp里的具体代码,代码分析加注在代码中
#include <stdio.h> #include <windows.h> #include <math.h> /*检测图像实际数据开始位置和信息头结束位置是否重合, 若中间还有2的biBitCount次方结构体RGBQUAQ的大小,那么说明中间有调色板 */ 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, (float)info_h.biBitCount)) { fseek(pFile, sizeof(BITMAPFILEHEADER) + info_h.biSize, 0); fread(pRGB_out, sizeof(RGBQUAD), (unsigned int)pow(2, (float)info_h.biBitCount), pFile); return true; } else return false; } void ReadRGB( BITMAPFILEHEADER & file_h, BITMAPINFOHEADER & info_h, FILE * pFile,unsigned char * rgbDataOut) { unsigned long Loop, i, j, width, height, w, h; unsigned char mask, *Index_Data, *Data; if ((info_h.biWidth % 4) == 0) w = info_h.biWidth; else w = (info_h.biWidth*info_h.biBitCount + 31) / 32 * 4; /*加31再除以32后下取整,就保证了计算结果是离这个数最近的而且是比它大的32的倍数, 也就保证了是4字节的整数倍。乘以4和行数,得到4字节整数倍的图像大小。 */ if ((info_h.biHeight % 2) == 0) h = info_h.biHeight; else h = info_h.biHeight + 1; width = w / 8 * info_h.biBitCount; //宽 height = h; //高 Index_Data = (unsigned char *)malloc(height*width); Data = (unsigned char *)malloc(height*width); fseek(pFile, file_h.bfOffBits, 0); if (fread(Index_Data, height*width, 1, pFile) != 1) { printf("read file error!"); exit(0); } for (i = 0; i < height; i++) for (j = 0; j < width; j++) { Data[i*width + j] = Index_Data[i*width + j]; } RGBQUAD *pRGB = (RGBQUAD *)malloc(sizeof(RGBQUAD)*(unsigned int)pow(2,(float)info_h.biBitCount)); //debug:把unsigned char 改为unsigned int if (!MakePalette(pFile, file_h, info_h, pRGB)) printf("No palette!"); //当每像素为24位时,直接取像素数据写RGB缓冲区 if (info_h.biBitCount == 24) { memcpy(rgbDataOut, Data, height*width); free(Index_Data); free(Data); return; } //当每像素为16位时,位与移位取像素数据转换为 8bit/彩色分量 写RGB缓冲区 if (info_h.biBitCount == 16) { for (Loop = 0; Loop < height * width ; Loop+=2) //debug:"Loop++"改为"Loop+=2"; //loop每次循环中应该要逐次增加2,因为loop表示一个字节,而它应该每次16位,即2个字节。 { *rgbDataOut = (Data[Loop] & 0x1F) << 3; *(rgbDataOut + 1) = ((Data[Loop] & 0xE0) >> 2) + ((Data[Loop + 1] & 0x03) << 6); *(rgbDataOut + 2) = (Data[Loop + 1] & 0x7C) << 1; rgbDataOut += 3; } //上述存储由BGR的顺序存储 } //当每像素为8位及以下时,构造调色板,位与移位取像素数据,查调色板,写RGB缓冲区 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) { /* 其中while 循环的次数:1bit 每字节循环8次;2bit 4次;4bit 2次;8bit 1次 */ unsigned char index = mask == 0xFF ? Data[Loop] : ((Data[Loop] & mask) >> (8 - shiftCnt * info_h.biBitCount)); *rgbDataOut = pRGB[index].rgbBlue; *(rgbDataOut + 1) = pRGB[index].rgbGreen; *(rgbDataOut + 2) = pRGB[index].rgbRed; if (info_h.biBitCount == 8) mask = 0; else mask >>= info_h.biBitCount; rgbDataOut += 3; shiftCnt++; } } free(Index_Data); free(Data); free(pRGB); } //下面为rgb2yuv的程序,因与上个实验相同就不贴了,详见报告一
当每像素为16位的时候,位与移位取像素数据转换为8bit/彩色分量,写RGB缓冲区,情况与24bit不同,下面进行图示说明:
三、实验结果
以下为最终的视频截图,分别从1位,4位,8位,16位,24位,其分辨率都为512*512
相关文章推荐
- 数据压缩原理 实验六 MPEG音频编码
- 数据压缩原理实验1_彩色空间转换实验YUVtoRGB
- 数据压缩原理 实验三 Huffman编解码算法实现与压缩效率分析
- 数据压缩原理与应用之彩色空间转换 实验报告1
- 数据压缩原理实验4_实验报告
- 数据压缩原理 实验四 DPCM压缩系统的实现和分析
- 数据压缩原理与应用 图像文件的读写和转换(BMP2YUV)实验报告
- 数据压缩原理实验3_实验报告
- 数据压缩原理实验6_MPEG音频编码
- 数据压缩原理实验5_实验报告
- 数据压缩原理 实验五 JPEG原理分析及JPEG解码器的调试
- 数据压缩原理实验2_实验报告
- 数据压缩原理实验3_Huffman编解码算法实现与压缩效率分析
- 数据压缩原理实验1_实验报告
- 数据压缩原理实验5_JPEG编解码原理及代码分析
- 数据压缩原理实验6_实验报告
- 数据压缩原理 实验二 图像文件的读写和转换
- 数据压缩实验二:BMP2YUV文件转换
- 数据压缩实验2
- 数据压缩实验三:用c语言实现Huffman编码和压缩效率分析