BMP格式详解

介绍

  数字图像在外存储器设备中的存储形式是图像文件,图像必须按照某个已知的、公认的数据存储顺序和结构进行存储,才能使不同的程序对图像文件顺利进行打开或存盘操作,实现数据共享。图像数据在文件中的存储顺序和结构称为图像文件格式。目前广为流传的图像文件格式有许多种,常见的格式包括BMP、 GIF、JPEG、TIFF、PSD、DICOM、MPEG等。在各种图像文件格式中,一部分是由某个软硬件厂商提出并被广泛接受和采用的格式,例如 BMP、GIF和PSD格式;另一部分是由各种国际标准组织提出的格式,例如JPEG、TIFF和DICOM,其中JPEG是国际静止图像压缩标准组织提出的格式,TIFF是由部分厂商组织提出的格式,DICOM是医学图像国际标准组织提出的医学图像专用格式。

  BMP文件是Windows操作系统所推荐和支持的图像文件格式,是一种将内存或显示器的图像数据不经过压缩而直接按位存盘的文件格式,所以称为位图(bitmap)文件,因其文件扩展名为BMP,故称为BMP文件格式,简称BMP文件。

  BMP文件总体上由4部分组成,分别是位图文件头、位图信息头、调色板和图像数据

  • 位图文件头(bitmap-file header)
  • 位图信息头(bitmap-information header)
  • 彩色表/调色板(color table)
  • 位图数据(bitmap-data)

数据结构

//位图文件头
typedef __packed struct
{
    uint16_t  bfType ;     //文件标志.固定为'BM',用来识别BMP位图类型
    uint32_t  bfSize ;	   //文件大小,占四个字节
    uint16_t  bfReserved1 ;//保留,总为0
    uint16_t  bfReserved2 ;//保留,总为0
    uint32_t  bfOffBits ;  //从文件开始到位图数据(bitmap data)开始之间的的偏移量
}BITMAPFILEHEADER ;

//位图信息头
typedef __packed struct
{
    uint32_t biSize ;		   	//BITMAPINFOHEADER结构所需要的字数。
    long  biWidth ;		   	    //图象的宽度,以象素为单位 
    long  biHeight ;	   	    //图象的高度,以象素为单位 
    uint16_t  biPlanes ;	    //为目标设备说明位面数,其值将总是被设为1 
    uint16_t  biBitCount ;	   	//比特数/象素,其值为1、4、8、16、24、或32
    uint32_t biCompression ;  	//图象数据压缩的类型。其值可以是下述值之一:
	                              //BI_RGB:没有压缩;
	                              //BI_RLE8:每个象素8比特的RLE压缩编码,压缩格式由2字节组成(重复象素计数和颜色索引);  
                                  //BI_RLE4:每个象素4比特的RLE压缩编码,压缩格式由2字节组成
  	                              //BI_BITFIELDS:每个象素的比特由指定的掩码决定。
    uint32_t biSizeImage ;		//图象的大小,以字节为单位。当用BI_RGB格式时,可设置为0  
    long  biXPelsPerMeter ;	    //水平分辨率,用象素/米表示
    long  biYPelsPerMeter ;	    //垂直分辨率,用象素/米表示
    uint32_t biClrUsed ;	  	//位图实际使用的彩色表中的颜色索引数
    uint32_t biClrImportant ; 	//对图象显示有重要影响的颜色索引的数目,如果是0,表示都重要。 
}BITMAPINFOHEADER ;

//调色板
typedef __packed struct 
{
    uint8_t rgbBlue ;    //指定蓝色强度
    uint8_t rgbGreen ;	 //指定绿色强度 
    uint8_t rgbRed ;	 //指定红色强度 
    uint8_t rgbReserved ;//保留,设置为0 
}RGBQUAD ;

typedef __packed struct
{ 
	BITMAPFILEHEADER bmfHeader;     //位图文件头
	BITMAPINFOHEADER bmiHeader;     //位图信息头 
	uint32_t RGB_MASK[3];			//调色板用于存放RGB掩码.
	//RGBQUAD bmiColors[256];  
}BITMAPINFO; 

8位色图片实例讲解

下面举个实例来讲解:
在Winhex中打开cat2.bmp图像文件((8位色BMP图像cat2.bmp,分辨率为 200×153,文件大小为31 680字节))

位图文件头(bitmap-file header)

  位图文件头(bitmap-file header)包含了图像类型、图像大小、图像数据存放地址和两个保留未使用的字段。
  打开WINGDI.h文件,搜索"BITMAPFILEHEADER"就可以定位到BMP文件的位图文件头的数据结构定义。

typedef __packed struct
{
    uint16_t  bfType ;     //文件标志.固定为'BM',用来识别BMP位图类型
    uint32_t  bfSize ;	   //文件大小,占四个字节
    uint16_t  bfReserved1 ;//保留,总为0
    uint16_t  bfReserved2 ;//保留,总为0
    uint32_t  bfOffBits ;  //从文件开始到位图数据(bitmap data)开始之间的的偏移量
}BITMAPFILEHEADER ;

对应图片开始的14个字节数据:
BMP格式详解_第1张图片

十六进制值 描 述
42 4D BM的ASCII值,在Windows中的BMP文件标识符
C0 7B 00 00 7B C0h=31680,是cat2文件的大小
00 00 00 00 保留值,总为0
36 04 00 436h=1078,是图像数据的地址,即文件头+信息头+调色板的长度

位图信息头(bitmap-information header)

  位图信息头(bitmap-information header)包含了位图信息头的大小、图像的宽高、图像的色深、压缩说明图像数据的大小和其他一些参数。
  打开WINGDI.h文件,搜索"tagBITMAPINFOHEADER"就可以定位到BMP文件的位图信息头的数据结构定义。

typedef __packed struct
{
    uint32_t biSize ;		   	//BITMAPINFOHEADER结构所需要的字数。
    long  biWidth ;		   	    //图象的宽度,以象素为单位 
    long  biHeight ;	   	    //图象的高度,以象素为单位 
    uint16_t  biPlanes ;	    //为目标设备说明位面数,其值将总是被设为1 
    uint16_t  biBitCount ;	   	//比特数/象素,其值为1、4、8、16、24、或32
    uint32_t biCompression ;  	//图象数据压缩的类型。其值可以是下述值之一:
	                              //BI_RGB:没有压缩;
	                              //BI_RLE8:每个象素8比特的RLE压缩编码,压缩格式由2字节组成(重复象素计数和颜色索引);  
                                  //BI_RLE4:每个象素4比特的RLE压缩编码,压缩格式由2字节组成
  	                              //BI_BITFIELDS:每个象素的比特由指定的掩码决定。
    uint32_t biSizeImage ;		//图象的大小,以字节为单位。当用BI_RGB格式时,可设置为0  
    long  biXPelsPerMeter ;	    //水平分辨率,用象素/米表示
    long  biYPelsPerMeter ;	    //垂直分辨率,用象素/米表示
    uint32_t biClrUsed ;	  	//位图实际使用的彩色表中的颜色索引数
    uint32_t biClrImportant ; 	//对图象显示有重要影响的颜色索引的数目,如果是0,表示都重要。 
}BITMAPINFOHEADER ;

BMP格式详解_第2张图片

十六进制值 描 述
28 00 00 00 cat2.bmp图像的位图信息头大小,40字节
C8 00 00 00 00 00 00 C8 = 200,是cat2图像的宽度,单位像素
99 00 00 00 00 00 00 99 = 153,是cat2图像的高度,单位像素
01 00 总是1
08 00 00 08 = 8,cat2图像的色深,即2的8次幂等于256色
00 00 00 00 压缩方式,0表示不压缩
8A 77 00 00 00 00 77 8A = 30602,是cat2图像的图像数据大小,单位字节
12 0B 00 00 00 00 0B 12 = 2834,cat2图像的水平分辨率,单位像素/m
12 0B 00 00 00 00 0B 12 = 2834,cat2图像的垂直分辨率,单位像素/m
00 00 00 00 cat2图像使用的颜色数,0表示使用全部颜色
00 00 00 00 cat2图像中重要的颜色数,0表示所有颜色都重要

彩色表/调色板(color table)

接下来是调色板数据,根据BMP文件结构的定义,因为cat2.bmp图像是256色的位图,所以应该有256个调色板,每个调色板占4字节,整 个调色板一共1024字节大小。 cat2.bmp图像文件的调色板数据如下图:
BMP格式详解_第3张图片
cat2.bmp图像的调色板地址从00000036h开始存储
BMP格式详解_第4张图片
cat2.bmp图像的调色板数据结束地址是00000435h

cat2.bmp图像的调色板地址从00000036h开始到00000435h结束,即00000435h - 00000036h + 1 =400h = 1024 bytes。
如果想查看cat2图像的调色板对应的实际显示颜色,可以使用Adobe Photoshop CS打开cat2.bmp,在Adobe Photoshop CS的菜单栏中选择"图像"→"模式"→"颜色表",即可观看cat2的调色板
BMP格式详解_第5张图片
  cat2.bmp的调色板颜色和十六进制数据是一一对应的。在Adobe Photoshop CS的调色板上单击任何一个像素的颜色即可弹出一个拾色器对话框显示该像素颜色的详细组成信息。cat2.bmp调色板和cat2.bmp的十六进制数据 的对应关系如图。
BMP格式详解_第6张图片

位图数据(bitmap-data)

  接下来就是位图数据了,根据BMP文件结构的定义,如果一个图像有调色板,那么紧跟在调色板后面的是图像的数据,这些数据不是实际的颜色值,而是指向调色板数组的索引,根据索引来获取调色板中的颜色,如图
BMP格式详解_第7张图片
  因为cat2.bmp是256色的位图,即采用了8位色深作为指向调色板数组的索引,所以根据上图显示的数据可以得知:49 49 49 B1 49 49 49 49 49 99。表示cat2.bmp位图左下角第1个像素的颜色等于调色板[49];第2个像素的颜色等于调色板[49] ;第3个像素的颜色等于调色板[49] ;第4个像素的颜色等于调色板[B1];第5个像素的颜色等于调色板[49] ……依此类推。

24位色图片实例讲解

  cat1.bmp图像是24位色图像,根据BMP文件结构定义得知,cat1.bmp图像没有调色板,图像数据存储的是实际的颜色数据,每个像素用 3字节表示,分别是红绿蓝。由于cat1.bmp和cat2.bmp的位图文件头和位图信息头结构一样,所以cat1.bmp的位图文件头和位图信息头可以参考上面对cat2.bmp的分析,下面从cat1.bmp的位图信息头结束的位置开始分析,如图
BMP格式详解_第8张图片
  上图可以看到表示每个像素的红绿蓝三色的值,实际存放的时候是倒过来存放的,在分析BMP图像格式时需要注意这点。

省略文件头重复数据

  通过上面对BMP文件存储结构的分析发现,BMP文件的位图文件头和位图信息头存在着大量的重复数据。如果存储大量同一色深的BMP位图,必然会浪费大量存储空间,所以很多时候游戏编程人员都会去掉BMP文件头和信息头,只保留几个必要的信息和图像数据,那么BMP文件头和信息头中哪几个字段是必须 保留的呢?

  使用Winhex的文件比较功能比较两个24位色深的BMP图像文件,观察两个文件的文件头和信息头有什么不同的地方,如图。
BMP格式详解_第9张图片
  从上图可以看出,两个色深相同的BMP图像的文件头和信息头一共有4处不同的地方,分别是文件头的文件大小、信息头的图像宽度、图像高度和图像数据大小。

  所以很多时候,游戏编程人员只保留图像文件的文件大小、图像宽度、图像高度和图像数据大小信息,甚至有时不需要保留文件大小这个数值,使用图像数据大小数值即可。

  在分析未知文件存储格式时,如果遇到去掉了文件头的文件时,如上面所说的BMP文件,会给分析未知文件格式带来一定的困难。这时需要使用十六进制编 辑器的文件比较功能,观察两个同类的未知文件格式寻找某些潜在的规律,如果实在观察不出规律的,那只能使用白盒分析方法,对调用此未知文件格式的程序进行反汇编跟踪调试了。当然,有时灵感和运气也很重要。

位图文件大小的精准计算方法

8位色

  8位(bit)位图:图片中有28=256种颜色,具体哪256种颜色可由调色板灵活规定,因此每个像素点最多有256种情况(颜色),用1字节来表示就可以了。
  一幅512×512的8位位图大小计算方法:
位图文件头(14字节00000000h开始到0000000Dh)+
位图信息头(40字节0000000Eh开始到00000035h)+
调色板(256色,每个颜色4字节,256*4=1024, 00000036h开始到00000435h)+
实际像素点占内存(512×512×1字节)=263 222字节(Byte)。

24位色

  24位位图:又名RGB真彩色图,含224=16 777 216=16M色,没有彩色表,原因上文已说明。每个像素点由3个字节(十六进制码6位)表示,每个字节负责控制一种颜色,分别为蓝(Blue)、绿(Green)、红(Red)。
  一幅256×256的24位位图大小计算方法:
位图文件头(14字节00000000h开始到0000000Dh)+
位图信息头(40字节0000000Eh开始到00000035h)+
实际像素点占内存(256×256×3字节)=196 662字节(Byte)。

补零

  需要注意的是,Windows有“补零”的习惯!即要求位图的每一行像素所占字节数必须被4整除。若不能倍4整除,则在该位图每一行的十六进制码末尾“补”1至3个字节的“00”。

  例如:一幅宽253×高256的24位位图,微软在生成该图为实际文件时,计算每一行像素所占字节=宽253×3字节=759字节,检验其被4除余1,则在每行的十六进制码末尾加1个字节,补“00”,变为760字节。因此我们计算该图大小时应先判断是否“补零”,再得出算法:该图大小=位图文件头(14字节)+位图信息头(40字节)+实际像素点占内存(高256×每行760字节)=194614字节(Byte)。
   有趣的是,“补零”只针对位图的宽进行检验,一幅宽256×高253的24位位图,其大小=位图文件头(14字节)+位图信息头(40字节)+实际像素点占内存(高253×每行768字节)=194358字节(Byte)< 196 662字节(Byte)。这样,只是把此图像的宽和高颠倒,图像所占内存竟然变小了。

你可能感兴趣的:(MCU,算法,BMP)