BMP文件扫盲——基础知识

 

网上有很多关于BMP和DIB的资料,这对我搞清楚位图的结构有很大的帮助,但是网上也有些资料存在一定错误,有的讲得不够清楚,或者没有把我想知道的问题说彻底。我花了一天的时间学习这些资料,然后结合具体的例子,基本上是把位图的结构搞清楚了。下面以一个初学者的角度将内容整理如下:

 

(1)什么是BMP,它与DIB有什么关系?

BMP是英文Bitmap(位图)的简写,它是Windows操作系统中的标准图像文件格式,能够被多种Windows应用程序所支持,在Windows环境下运行的所有图象处理软件都支持BMP图象文件格式。Windows系统内部各图像绘制操作都是以BMP为基础的。Windows 3.0以前的BMP图文件格式与显示设备有关,因此把这种BMP图象文件格式称为设备相关位图DDB(device-dependent bitmap)文件格式。Windows 3.0以后的BMP图象文件与显示设备无关,因此把这种BMP图象文件格式称为设备无关位图DIB(device-independent bitmap)格式(注:Windows 3.0以后,在系统中仍然存在DDB位图,象BitBlt()这种函数就是基于DDB位图的,只不过如果你想将图像以BMP格式保存到磁盘文件中时,微软极力推荐你以DIB格式保存),目的是为了让Windows能够在任何类型的显示设备上显示所存储的图象。BMP位图文件默认的文件扩展名是BMP或者bmp(有时它也会以.DIB或.RLE作扩展名)。

(2)BMP文件的本质?

BMP文件是一种图像文件,它的本质就是一堆包含图像信息的数据。用Ultra Edit打开如下图所示的一幅BMP位图,可以看到它其实就是一堆数据,就是二进制0和1的组合(这里只截取了一部分数据,后面还有很多)。

BMP文件扫盲——基础知识1 - 冷 - 冷的博客BMP文件扫盲——基础知识1 - 冷 - 冷的博客 

(3)图像颜色的存储,这些数据是如何反应图像信息的?

首先,按照图像的色彩可以讲BMP图像分为黑白图像(仅有黑白两种颜色)、灰度图像(有黑色、白色及各种不同深浅的灰色)、彩色图像。这几种BMP图像的存储方式是不一样的,彩色位图存放的是每一个像素点的色彩,例如24位真彩色图像是用24位也就是3个字节来存放一个像素点的颜色。这3个字节分别表示红色(R)、绿色(G)和蓝色(B)分量。而灰度图像存放的是一个颜色表,颜色表里是整个图像可能会出现的所有颜色,图像中每一点的颜色信息用该颜色在颜色表里的索引来表示。这样做的好处是可以大大减少数据量,节省存储空间。以一幅300*400像素的256色图像为例,如果直接存储每一像素点的颜色,则需要300*400*3=360000bytes(字节)≈352KB;而如果用颜色表的方式来存储,每一个像素点只需要一个字节就可以表示该点是256种颜色种的第几个(相当于给256种颜色编个号,用编号来表示该点的颜色),那么只需要300*400=120000bytes≈118KB,而存储256色的颜色表所需要的空间为256*3=768bytes=0.75KB,还不到1KB。可见用颜色表存储的方式可以大大减少存储空间,节约了近三分之二的空间。

到这里自然而言就会产生一个疑问,既然颜色表存储的方式这么有效,为什么彩色图像不采用这个方式来存储呢?其实原因很简单,因为彩色图像的色彩太多了,8位已经不够用来表示一个颜色索引了(8位最多只能表示256个不同的索引),需要16位、24位、32位甚至更多位才能表示一个颜色索引。以16位真彩色图像来说,用16位(2个字节)来表示一种颜色,那么所有可能出现的颜色一共有216=65536≈6.5万种颜色,也就是说这个颜色表里有6.5万多种颜色,如果编上号的话要编到6.5万多,这么大的编号至少需要16位也就是2个字节才能表示,那么一幅300*400的图像所需要的空间为300*400*2=240000bytes≈234.4KB,再加上一个相当长的颜色表所需要的空间:65536*2=131072bytes=128KB,加起来一共426.4KB;而如果直接存储每点的色彩,则只需要300*400*2=240000bytes≈234.4KB。用颜色表存储的方式非但没节省空间,反而还多用了192KB(颜色表的大小),事情还不止如此,随着颜色的增多,浪费的空间将变得更多,例如前面说的24位色的图像如果颜色表方式存储,则将浪费高达224*3=49152KB的容量,还不如直接存储每一点的颜色呢。

再稍微深入一点,我们知道灰色的特点是它的RGB三个分量的大小始终是一样的,我们只需要知道其中一个的数值就可以推知另外两个分量的值了,没有必要同时存储三个分量的值。换句话说,数据存在冗余,从信息论的角度来说,也真是因为冗余才有压缩的空间。而彩色图像则不存在灰度图像的这种冗余,所以不能用颜色表的方式来减少存储空间。不过彩色图像也存在其他的冗余,例如连续的像素点之间色彩的连续等,这些冗余使得图像能够进一步的压缩,像JPG格式就是一种压缩图像格式。另外,灰度图像的这种存储方式在减少了存储空间的同时没有损失任何颜色信息,而很多压缩方式是会损失一定图像细节或者图像色彩信息的。

(4)BMP文件结构

BMP文件本质是一些表示图像颜色信息的数据,不同的位图图像文件存储颜色信息的方式还不一样,那么应用程序是怎么知道某个BMP文件是多少种颜色的,又是怎么存储的呢?这就要涉及到BMP文件的结构了。

首先,整个bmp文件的内容可以分为3到4块。有4块内容的就是有颜色表的,直接存储颜色的彩色图像的话就只有3块内容。第一块是bmp的文件头用于描述整个bmp文件的情况,如文件类型、文件大小、数据偏移量等,一共14个字节。第二块是位图信息头,用于描述整个位图文件的情况,如位图的宽度、高度,每个像素点用多少位表示等。第三块就是颜色表信息。(更完整和确切的说法是第三块是调色板信息或者掩码部分,如果是8位位图 则存放调色板 ;16 与32位 位图则存放RGB颜色的掩码,这些掩码以DWORD大小来存放。关于这一点,在后面的《BMP入门》中会有进一步的说明)。最后一块就是位图的数据实体。

5)在MFC中对BMP文件的封装

Borland C++下的框架类库OWL不同,MFC未提供现成的类来封装BMP位图。尽管Microsoft列出了一些理由,但没有位图类确实给MFC用户带来很多不便。不过MFC提供了和位图文件头、位图信息头相对应的结构体BITMAPFILEHEADERBITMAPINFOHEADER。从MSDN里可以查到这两个结构体的定义如下:

typedef struct tagBITMAPFILEHEADER {

  WORD    bfType;

  DWORD   bfSize;

  WORD    bfReserved1;

  WORD    bfReserved2;

  DWORD   bfOffBits;

} BITMAPFILEHEADER, *PBITMAPFILEHEADER;

typedef struct tagBITMAPINFOHEADER{
  DWORD  biSize; 
  LONG   biWidth; 
  LONG   biHeight; 
  WORD   biPlanes; 
  WORD   biBitCount; 
  DWORD  biCompression; 
  DWORD  biSizeImage; 
  LONG   biXPelsPerMeter; 
  LONG   biYPelsPerMeter; 
  DWORD  biClrUsed; 
  DWORD  biClrImportant; 
} BITMAPINFOHEADER, *PBITMAPINFOHEADER; 

BITMAPFILEHEADER一共14个字节,BITMAPINFOHEADER一般为40个字节,这个与位图文件头和信息头的大小是完全一致的,结构体成员的大小以及顺序与位图文件头和信息头里存储的内容是一致的。

查看MSDN,可知BITMAPFILEHEADER各个成员的含义如下:

bfType

Specifies the file type, must be BM.

bfSize

Specifies the size, in bytes, of the bitmap file.

bfReserved1

Reserved; must be zero.

bfReserved2

Reserved; must be zero.

bfOffBits

Specifies the offset, in bytes, from the BITMAPFILEHEADER structure to the bitmap bits.

 

BITMAPINFOHEADER各个成员的含义是:

biSize

Specifies the number of bytes required by the structure.

biWidth

Specifies the width of the bitmap, in pixels.

biHeight

Specifies the height of the bitmap, in pixels. If biHeight is positive, the bitmap is a bottom-up DIB and its origin is the lower-left corner. If biHeightis negative, the bitmap is a top-down DIB and its origin is the upper-left corner.

biPlanes

Specifies the number of planes for the target device. This value must be set to 1.

biBitCount

Specifies the number of bits-per-pixel. The biBitCount member of theBITMAPINFOHEADER structure determines the number of bits that define each pixel and the maximum number of colors in the bitmap.

biCompression

Specifies the type of compression for a compressed bottom-up bitmap (top-down DIBs cannot be compressed).

biSizeImage

Specifies the size, in bytes, of the image. This may be set to zero for BI_RGB bitmaps.

biXPelsPerMeter

Specifies the horizontal resolution, in pixels-per-meter, of the target device for the bitmap. An application can use this value to select a bitmap from a resource group that best matches the characteristics of the current device.

biYPelsPerMeter

Specifies the vertical resolution, in pixels-per-meter, of the target device for the bitmap.

biClrUsed

Specifies the number of color indexes in the color table that are actually used by the bitmap. If this value is zero, the bitmap uses the maximum number of colors corresponding to the value of the biBitCount member for the compression mode specified by biCompression.

biClrImportant

Specifies the number of color indexes that are required for displaying the bitmap. If this value is zero, all colors are required.

关于各参数更为详细的说明可以参看MSDN,这里就不一一赘述了。要注意的是这里biHeight这个变量是LONG型而不是DWORD类型的,这两种类型都是4个字节的,它们的区别在于LONG型是有符号的,而DWORD是无符号的。要用区分符号的LONG型的原因,从它的含义里也可以看出来:如果biHeight是正的,那么就从图像的左下角开始存储数据,按照自下而上的顺序存储整个图像的信息;如果biHeight是负的,则从图像的左上角开始存储数据,按照自上而下的顺序存储整个图像的信息。

另外MFC还提供了一个RGBQUARD来描述颜色,它的定义如下:

typedef struct tagRGBQUAD {
  BYTE    rgbBlue; 
  BYTE    rgbGreen; 
  BYTE    rgbRed; 
  BYTE    rgbReserved; 
} RGBQUAD; 

     实际上,在位图文件里也是用4个字节来表示颜色表中的一种颜色信息的,其中3个字节分别表示红、绿、蓝三个颜色分量,还有一个是保留字节。其中需要注意的问题是,RGBQUAD结构中的颜色顺序是BGR,而不是平常的RGB

6)实例详解

下面以一幅具体的图片为例来看看BMP图像里的数据。

BMP文件扫盲——基础知识2 - 冷 - 冷的博客

这是一幅340*275BMP灰度图像,大小是92.3KB,内容肝脏的一个切面图,用Ultra Edit打开,可以看到里面的数据(实际是以二进制存放的,但是为了看起来方便,用了十六进制来显示)。

BMP文件扫盲——基础知识2 - 冷 - 冷的博客 
 
BMP文件扫盲——基础知识2 - 冷 - 冷的博客

     可以看到,数据从 00000000h 一直到 00017171h ,一共是 1+7*16+162+7*163+164+1=94578 字节( bytes ),约为 92.36KB 。最前面的两个字节是 424Dh ,就是字母 B M ASCII 码,也就是文件头里面的 bfType BM 就代笔是 BMP 位图。接下来 4 个字节 , 00 01 71 72h ,注意在存储时是低地址存放低字节,而书写时一般是高字节在前面,所以正好反过来。这四个字节是bfSize,也就是位图文件的大小,以字节为单位。可以算一下,十六进制的00 01 71 72就是94578,也就是92.36KB。再接下来是两个保留字,各2个字节,均为零。然后是4个字节的偏移量,这里是00 00 04 36h,也就是1078Bytes。所谓偏移量就是存储的像素阵列相对于文件头的偏移量,知道了这个量,在对BMP文件进行操作的时候就可以跳过文件头和信息头,直接读取像素数据。为什么这里的偏移量是1078bytes呢?其实也很简单,因为文件头和信息头一共14+40=54字节,这是一幅256色的灰度图像,还有一个包含256种颜色的颜色表,而每种颜色都是用前面提到的4个字节的RGBQUARD结构体来表示的,所以颜色表的大小是256*4=1024字节,加上文件头和信息头一共就是1024+54=1078字节。
文件头的部分整理如下表:

类型

名字

大小/字节

内容/十六进制

含义

WORD

bfType

2

4D 42h

BM

文件的类型

DWORD

bfSize

4

00 01 71 72h

94578Bytes

文件的大小

WORD

bfReserved1

2

00 00h

0

保留字,总为0

WORD

bfReserved2

2

00 00h

0

保留字,总为0

DWORD

bfOffBits

4

00 00 04 36h

1078Bytes

存储的像素阵列相对于文件头的偏移量


接下来就是信息头的部分,按照同样的方式整理到表格中,如下:

类型

名字

大小/字节

内容/十六进制

十进制数值

含义

DWORD

biSize

4

00 00 00 28h

40

本结构的大小,根据不同的操作系统而不同,在Windows中,此字段的值总为28h字节=40字节

LONG

biWidth

4

00 00 01 54h

+340

BMP图像的宽度,单位像素

LONG

biHeight

4

00 00 01 13h

+275

BMP图像的高度,单位像素

WORD

biPlanes

2

00 01h

1

目标设备的plane数,必须设为1

WORD

biBitCount

2

00 08h

256

BMP图像的色深,即一个像素用多少位表示,常见有1、4、8、16、24和32,分别对应单色、16色、256色、16位高彩色、24位真彩色和32位增强型真彩色

DWORD

biCompression

4

00 00 00 00h

0

压缩方式,0表示不压缩,1表示RLE8压缩,2表示RLE4压缩,3表示每个像素值由指定的掩码决定

DWORD

biSizeImage

4

00 01 6D 3Ch

93500

BMP图像数据大小,必须是4的倍数,图像数据大小不是4的倍数时用0填充补足

LONG

biXPelsPerMeter

4

00 00 00 00h

0

水平分辨率,单位像素/m

LONG

biYPelsPerMeter

4

00 00 00 00h

0

垂直分辨率,单位像素/m

DWORD

biClrUsed

4

00 00 01 00h

256

BMP图像使用的颜色,0表示使用全部颜色,对于256色位图来说,此值为100h=256

DWORD

biClrImportant

4

00 00 00 00h

0

重要的颜色数,此值为0时所有颜色都重要,对于使用调色板的BMP图像来说,当显卡不能够显示所有颜色时,此值将辅助驱动程序显示颜色

 

表中biSizeImage的值是93500字节,这个是图像数据的大小,加上54字节的文件头和信息头,再加上1024字节的颜色表,一共就是94578字节,也就是整个位图文件的总大小。

从第55个字节开始就是颜色表了,每4个字节为一个颜色,从00 00 00 00h一直到FF FF FF 00h。从第1079个字节开始就是图像中每个像素的信息,对于灰度图像,它是该点颜色在颜色表中的索引。由于biHeight的值是正的,所以该图是从左下角开始存储数据的,图像的左下角是黑色,对应于颜色表中的第一个颜色,所以索引值就是00,如下图所示:

因为这个图像只有三种颜色,所以像素数据里头只有三个索引值,一个是00,然后是96h(浅灰色),还有一个32h(深灰色),如下图所示。









 

  

至此,BMP文件的内容就很清晰得展示在眼前了,比较透彻的认识了BMP文件格式后,处理起来就会得心应手很多了。


 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值