数据压缩实验二——图像文件的读写和转换

一、           实验基本原理

1.   BMP文件的组成结构

         BMP(全称 Bitmap)是 Windows 操作系统中的标准图像文件格式,可以分成两类:设备相关位图(DDB)和设备无关位图(DIB),使用非常广。它采用位映射存储格式,除了图像深度可选以外,在绝大多数应用中不采用其他任何压缩,因此,BMP文件所占用的空间很大。BMP文件的图像深度可选1bit4bit8bit16bit 24bitBMP文件存储数据时,图像的扫描方式是按从左到右、从下到上的顺序。由于 BMP文件格式是 Windows环境中交换与图有关的数据的一种标准,因此在 Windows环境中运行的图形图像软件都支持 BMP图像格式。典型的 BMP图像文件由四部分组成:

a)        位图头文件数据结构,它包含 BMP图像文件的类型、显示内容等信息。

b)        位图信息数据结构,它包含有 BMP图像的宽、高、压缩方法,以及定义颜色等信息。

c)        调色板,这个部分是可选的,有些位图需要调色板,有些位图,比如真彩色图(24位的 BMP)就不需要调色板。

d)        位图数据,这部分的内容根据 BMP位图使用的位数不同而不同,在 24位图中直接使用 RGB,而其他的小于 24位的使用调色板中颜色索引值。

2.   字节序

         不同的计算机系统采用不同的字节序存储数据,同样一个 4字节的 32位整数,在内存中存储的方式不同。字节序分为小尾字节序(Little Endian)和大尾字节序(Big Endian)Intel处理器大多数使用小尾字节序,Motorola处理器大多数使用大尾(Big Endian)字节序。

         小尾就是低位字节排放在内存的低端,高位字节排放在内存的高端,即所谓的“低位在前,高位在后”。大尾就是高位字节排放在内存的低端,低位字节排放在内存的高端,即所谓的“高位在前,低位在后”。 TCP/IP各层协议将字节序定义为大尾,因此 TCP/IP协议中使用的字节序通常称之为网络字节序。

         在实现 BMP文件头信息的写入时,需要注意整数保存时的字节序。例如:文件大小是以Intel序保存的。在编程前先用二进制打开方式观察 BMP文件各个部分的数据存储格式。

         BMP文件为16比特/像素色彩深度格式,前14字节为位图文件头结构体内容,后40字节为位图信息头结构体内容,之后是自下而上、从左向右的位图数据,不包含调色盘。

相应的数据结构可表示如下:

a)        位图文件头主要包括:

typedefstruct tagBITMAPFILEHEADER {

WORD bfType;         /* 4D 42       说明文件的类型,ASCII码译为为BM */

DWORD bfSize;        /* 00 1C 20 38 说明文件的大小,用字节为单位 */

WORD bfReserved1;    /* 00 00       保留,设置为 0 */ 

WORD bfReserved2;    /* 00 00       保留,设置为 0 */ 

DWORD bfOffBits;     /* 00 00 00 36 说明从 BITMAPFILEHEADER结构开始到实际的图像数据之间的字节偏移量 */ 

} BITMAPFILEHEADER;

b)        位图信息头主要包括:

typedefstruct tagBITMAPINFOHEADER {

DWORD biSize;         /* 00 00 00 28说明结构体所需字节数 */ 

LONG biWidth;         /* 00 00 05 00以像素为单位说明图像的宽度为1280 */ 

LONG biHeight;        /* 00 00 02 D0以像素为单位说明图像的高速为720 */ 

WORD biPlanes;        /* 00 01       说明位面数,必须为 1 */ 

WORD biBitCount;      /* 00 10       说明位数/像素,1、2、4、8、24,此图为16位/像素 */ 

DWORDbiCompression;  /* 00 00 00 00说明图像是否压缩及压缩类型 BI_RGB,BI_RLE8,BI_RLE4, BI_BITFIELDS */

DWORD biSizeImage;    /* 00 1C 20 02以字节为单位说明图像大小,必须是 4 的整数倍*/ 

LONGbiXPelsPerMeter;/* 00 00 0B 12 目标设备的水平分辨率,像素/米 */

LONGbiYPelsPerMeter;/* 00 00 0B 12 目标设备的垂直分辨率,像素/米 */

DWORD biClrUsed;      /* 00 00 00 00说明图像实际用到的颜色数,如果为 0,则颜色数为 2的 biBitCount 次方 */

DWORDbiClrImportant;/* 00 00 00 00 说明对图像显示有重要影响的颜色索引的数目,如果是 0,表示都重要。*/

} BITMAPINFOHEADER;

c)        调色板实际上是一个数组,它所包含的元素与位图所具有的颜色数相同,决定于biClrUsed biBitCount字段。数组中每个元素的类型是一个 RGBQUAD结构。真彩色无调色板部分。

对于小于等于8比特/像素色彩深度的BMP文件,当biClrUsed不为零时,它的值代表颜色数,当biClrUsed为零时,颜色数为2^biBitCount个。以biClrUsed0biBitCount4为例,则调色板结构体内容为:

00 00 00 00      0000 80 00      00 80 00 00      00 80 80 00

80 00 00 00      8000 80 00      80 80 00 00      80 80 80 00

C0 C0 C0 00     0000 FF 00       00 FF 00 00       00 FF FF 00

FF 00 00 00       FF00 FF 00        FF FF 00 00        FF FF FF 00

即有16种颜色,每字节位图数据按色彩深度比特数分割,如biBitCount4时每字节位图数据分两部分,每部分查调色盘分配RGB分量。

typedefstruct tagRGBQUAD {

BYTE rgbBlue;      /*指定蓝色分量*/

BYTE rgbGreen;     /*指定绿色分量*/

BYTE rgbRed;       /*指定红色分量*/

BYTE rgbReserved;/*保留,指定为 0*/ 

} RGBQUAD;

d)        紧跟在调色板之后的是图像数据字节阵列。对于用到调色板的位图,图像数据就是该像素颜色在调色板中的索引值(逻辑色)。对于真彩色图,图像数据就是实际的 RGB值。图像的每一扫描行由表示图像像素的连续的字节组成,每一行的字节数取决于图像的颜色数目和用像素表示的图像宽度。规定每一扫描行的字节数必须是 4 的整倍数,也就是DWORD对齐的。扫描行是由底向上存储的,这就是说,阵列中的第一个字节表示位图左下角的像素,而最后一个字节表示位图右上角的像素。

 

二、           实验流程分析

  1. 程序初始化(打开两个文件、定义变量和缓冲区等)
  2. 读取BMP文件,抽取或生成RGB数据写入缓冲区

            a.      读位图文件头(判断是否可读出,判断是否是BMP文件)

          b.      读位图信息头(判断是否读出)

          c.      判断像素的实际点阵数

          d.      开辟缓冲区,读数据,倒序存放

          e.      根据每像素位数的不同,执行不同的操作:

              8bit以下:构造调色板、位与移位取像素数据、查调色板、写RGB缓冲区

              16bit:位与移位取像素数据转换为8bit/彩色分量、写RGB缓冲区

              24/32bit:直接取像素数据、写RGB缓冲区

  1. 调用RGB2YUV的函数实现RGBYUV数据的转换
  2. YUV文件
  3. 程序收尾工作(关闭文件,释放缓冲区)

 

三、           关键代码及分析

1.   主函数:

         多个BMP文件转化为YUV文件框架,可在命令行中设置每个画面出现的帧数。分别用循环生成多帧的yuvFile,读入多个bmpFile

int main(int argc,char** argv)

{

    bool flip=false;     //sequence of rgbFile in funciton rgb2yuv: reverse

    long bmpCount;         //bmpFile count

    long frameCount=NULL;//frame count for each bmpFile

    

    //read outputting yuvFile

    yuvFileName= argv[argc-1];

    if((yuvFile= fopen(yuvFileName,"wb"))==NULL)

    printf("failed open file\n");

    else

    printf("%s file opened\n",yuvFileName);

    //read & process inputting bmpFile in a for cycle

    for(bmpCount=1;bmpCount

         结构体读取操作。位图文件头、位图信息头和调色板结构体包含在”windows.h”头文件中。分别读出文件头、信息头。用文件头结构体中表示文件类型字段判断读到的是否为BMP文件。读取信息头结构体中表示图像像素宽度、高度的字段,将宽高数据用于给缓冲区开辟空间,注意为保证图像像素数为4字节的整数倍,即图像宽度为4字节、32位整数倍,对行像素数进行判断及处理。

#include

//.....................................

BITMAPFILEHEADERFile_header;

BITMAPINFOHEADERInfo_header;

// read file & info header

if(fread(&File_header,sizeof(BITMAPFILEHEADER),1,bmpFile)!=1)

{

   printf("readfile header error!");

   exit(0);

}

if(File_header.bfType!=0x4D42)

{

   printf("Notbmp file!");

   exit(0);

}

else

   printf("thisis a bmp file\n");

if(fread(&Info_header,sizeof(BITMAPINFOHEADER),1,bmpFile)!=1)

{

   printf("readinfo header error!");

   exit(0);

}

// end read header

// ensure frameWidth a multiple of 4

if(Info_header.biWidth%4==0)

   frameWidth=Info_header.biWidth;

else

   frameWidth=(Info_header.biWidth*Info_header.biBitCount+31)/32*4;

   frameHeight=Info_header.biHeight;

2.   BMP2RGB函数:

         同样需读取信息头中的宽高数据,保证行像素数为4字节整数倍,并将行像素数根据色彩深度转为实际字节数/行。变量定义,缓冲区设置代码省略。

//  ensure frameWidth a multiple of4

if(info_h.biWidth%4==0)

    Width= info_h.biWidth;

else

    Width=(info_h.biWidth* info_h.biBitCount+31)/ 32 *4;

if(info_h.biHeight%2==0)

    Height= info_h.biHeight;

else

    Height= info_h.biHeight+1;

//  transfer pixel/row to Byte/row

w = Width* info_h.biBitCount/8;

h = Height;

         使用fseek函数将文件指针pFile移动至指向位图数据的首地址,读一帧数据。

// move pFile to bit map data

fseek(pFile, file_h.bfOffBits,0);

if(fread(Data, w * h,1, pFile)!=1)

{

   printf("failreading file");

   exit(0);

}

         接下来根据bmp文件不同的颜色深度位数,分类进行处理。

         颜色深度为24位时为真彩色图,一个像素分别用一字节RGB分量表示,可直接将数据拷给输出的RGB指针。

if(info_h.biBitCount==24)

{

   memcpy(rgbDataOut, Data, h* w);

   return;

}

         颜色深度为16位时也不用调色盘,两个字节表示一个像素,生成图像时选用RGB分别占5位,最高位置空的方案。提取两字节中的三个5位颜色分量,赋给输出的RGB指针。需要注意BMP文件以Intel序(小尾字节序)保存,低位字节排放在内存的低端,高位字节排放在内存的高端。

数据压缩实验二——图像文件的读写和转换_第1张图片

if(info_h.biBitCount==16)

{

   for(i=0; i< w* h; i+=2)

   {

       *rgbDataOut=(Data[i]&0x1F)<<3;

       *(rgbDataOut+1)=((Data[i]&0xE0)>>2)+((Data[i+1]&0x03)<<6);

       *(rgbDataOut+2)=(Data[i+1]&0x7C)<<1;

       rgbDataOut+=3;

   }

   return;

}


         颜色深度为8位及以下时,需要用到调色盘,供位图数据查询RGB分量。首先为调色盘指针开辟缓冲区,大小为一种颜色的大小乘以颜色个数。注意pow函数默认两个参量的数据类型为double,或重载函数要求至少一个为double/double/long double,另一个为整型,所以在info_h.biBitCount前加强制类型转换。之后调用函数读取文件中的调色盘数据到调色盘指。函数中判断语句用来检验调色盘数据长度是否等于预测值,调色盘数据长度为bfOffBits BITMAPFILEHEADER 结构开始到实际的图像数据之间的字节偏移量)减去文件头和信息头的长度。之后用fseek函数使文件指针从起始位置偏移文件头加信息头的长度,使其指向调色板的首地址。最后读数据到调色盘缓冲区。

RGBQUAD*pRGB=(RGBQUAD*)malloc(sizeof(RGBQUAD)*(unsignedint)pow(2,(float)info_h.biBitCount));

if(!MakePalette(pFile,file_h,info_h,pRGB))printf("Nopalette!");

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),(unsignedint)pow(2,(float)info_h.biBitCount),pFile);

       returntrue;

   }

   else

       returnfalse;

}

         以下代码为根据不同颜色深度用位图数据查询调色盘RGB分量并写入输出的RGB指针。Loop为位图字节计数。首先给深度为1/2/4/8的位图设计遮罩mask,数据类型为无符号字符,用于提取每字节位图数据不同位数的信息。当mask0xff时,每次取一字节数据,当mask不为0xff时,用遮罩提取1/2/4位位图数据,后将数据移位至最低位。用这个数据index对调色盘进行寻址,将BGR分量分别传给输出的RGB指针。对遮罩进行右移颜色深度位数操作,知道遮罩为全零后,开始下一字节的读取。

数据压缩实验二——图像文件的读写和转换_第2张图片

for(Loop=0;Loop>(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++;

   }

}


RGB2YUV函数见上一次实验报告。

 

四、           实验结果

 

使用同一个图片,不同颜色深度(1bit/4bit/8bit/16bit/24bit)的BMP文件进行转换结果:

数据压缩实验二——图像文件的读写和转换_第3张图片

使用不同图片转换结果:

你可能感兴趣的:(数据压缩课程实验)