LV015-BMP文件格式
一、BMP文件简介
1. BMP简介
BMP取自位图Bitmap的缩写,也称为DIB(与设备无关的位图),是一种独立于显示器的位图数字图像文件格式。常见于微软视窗和OS/2操作系统,Windows GDI API内部使用的DIB数据结构与 BMP 文件格式几乎相同。
图像通常保存的颜色深度有2(1位)、16(4位)、256(8位)、65536(16位)和约1677万(24位)种颜色(其中位是表示每点所用的数据位)。8位图像可以是索引彩色图像外,也可以是灰阶图像。表示透明(英语:transparency (graphic))的alpha通道也可以保存在一个类似于灰阶图像的独立文件中。带有集成的alpha通道的32位版本已经随着Windows XP出现,它在Windows系统的登录界面和系统主题中都有使用。
2. 存储算法
BMP文件通常是不压缩的,所以它们通常比同一幅图像的压缩图像文件格式要大很多。例如,一个800×600的24位BMP格式图片几乎占据1.4MB空间(800x600x3=1440000B≈1.37M)。因此它们通常不适合在因特网或者其他低速或者有容量限制的媒介上进行传输。
根据颜色深度的不同,图像上的一个像素可以用一个或者多个字节表示,它由n/8所确定(n是位深度,1字节包含8个数据位)。图片浏览器等基于字节的ASCII值计算像素的颜色,然后从调色板中读出相应的值。更为详细的信息请参阅下面关于位图文件的部分。
n位
其中高度(height)和宽度(width)都以像素为单位。需要注意的是上面公式中的54是位图文件的文件头(英语:Header (information technology)),
另外需要注意的是这是一个近似值,对于n位的位图图像来说,尽管可能有最多
由于存储算法本身决定的因素,根据几个图像参数的不同计算出的大小与实际的文件大小将会有一些细小的差别。
二、文件格式
接下来我们来了解一下这种文件的格式。
1. 获取一张BMP图片
1.1 软件生成
首先我们需要有一张BMP格式的文件,我们可以自己做一张小的,便于查看,windows系统中自带的画图软件就可以生成BMP格式文件,我们打开画图新建一个画布,然后点击重新调整大小: 
这里创建一个8x8像素的图像,然后将画布放大到最大方便后面填充颜色,设置好画布后,我们编辑自己的画笔,因为默认的颜色不是纯色,方法如下。

然后我们就可以在编辑颜色这里选择一些纯色了,可以在【查看】这里打开网格线方便为每个像素上色:

这样我们就可以得到一张24位BMP然后格式的图片(像素太少了,下面通过html语法放大了1000倍):
我们用notepad++打开这个文件,或者其他能查看十六进制的软件都行:

1.2 FFmpeg转化
可以通过下面的命令吧jpg转成bmp图像:
ffmpeg -i fileName.jpg fileName.bmp # jpg-->bmp
ffmpeg -i fileName.bmp fileName.jpg # bmp-->jpg2. 格式说明
这里有张图:BMP File Format - 维基百科

位图图像文件由若干大小固定(文件头)和大小可变的结构体按一定的顺序构成。由于该文件格式几经演进,这些结构体的版本也很多。
| 结构体名称 | 可选 | 大小 | 用途 | 备注 |
|---|---|---|---|---|
| 位图文件头 | 否 | 14字节 | 存储位图文件通用信息 | 仅在读取文件时有用 |
| DIB头 | 否 | 固定(存在7种不同版本) | 存储位图详细信息及像素格式 | 紧接在位图文件头后 |
| 附加位掩码 | 是 | 3或4 DWORD(12或16字节) | 定义像素格式 | 仅在DIB头是BITMAPINFOHEADER时存在 |
| 调色板 | 见备注 | 可变 | 定义图像数据(像素数组)所用颜色 | 色深≤ 8时不能省略 |
| 填充区A | 是 | 可变 | 结构体对齐 | 位图文件头中像素数组偏移量的产物 |
| 像素数组 | 否 | 可变 | 定义实际的像素数值 | 像素数据在DIB头和附加位掩码中定义。像素数组中每行均以4字节对齐 |
| 填充区B | 是 | 可变 | 结构体对齐 | DIB头中ICC色彩特性数据偏移量的产物 |
| ICC色彩特性数据 | 是 | 可变 | 定义色彩特性 | 可以包含外部文件路径,由该文件来定义色彩特性 |
下面的部分将会详细地学习位图文件中保存的数据。需要注意的是这是标准位图的文件格式,其他一些位图图像可能根据生成文件的应用程序不同所使用格式可能会有细微的区别。
3. 文件数据分析
3.1 位图文件头
3.1.1 数据格式
这部分数据块位于文件开头,存放一些文件信息,用于进行文件的识别。典型的应用程序会首先普通读取这部分数据以确保的确是位图文件并且没有损坏。所有的整数值都以小端序存放(即最低有效位前置)。
| 偏移量 | 大小 | 用途 |
|---|---|---|
| 0000h | 2字节 | 用于标识BMP和DIB文件的魔数,一般为0x42 0x4D,即ASCII的BM。以下为可能的取值: BM – Windows 3.1x, 95, NT, ... etc. BA – OS/2 struct Bitmap Array CI – OS/2 struct Color Icon CP – OS/2 const Color Pointer IC – OS/2 struct Icon PT – OS/2 Pointer |
| 0002h | 4字节 | BMP文件的大小(单位为字节) |
| 0006h | 2字节 | 保留;实际值因创建程序而异 |
| 0008h | 2字节 | 保留;实际值因创建程序而异 |
| 000Ah | 4字节 | 位图数据(像素数组)的地址偏移,也就是起始地址。 |
3.1.2 文件分析
如下图所示:

(1)BMP文件头的大小是 14 字节,第 1 ~ 2 字节 是 魔数,在这里是4d 42 ,表示是 BM 字符串。
(2)第 3 ~ 6 个字节是BMP文件的大小,在这里是 00 00 00 f6,注意,数据类型存储是 小端存储的。换算一下就是246字节,这个跟前面文件大小是对得上的:

(3)和(4)是第 7 ~ 10个字节:00 00 00 00,是保留字节,以后扩展用的,这4个字节目前永远是 0 。
(5)第 11 ~ 14 个字节 是实际的像素数据的偏移值,在这里是 00 00 00 36 ,没有调色板通常都是 0x36,所以从 54 字节开始,就是真正的 RGB 数据。
3.2 DIB头
3.2.1 数据格式
DIB头也就是BMP信息头,这部分告诉应用程序图像的详细信息,在屏幕上显示图像将会使用这些信息,它从文件的第15个字节开始。
这部分数据块对应了Windows和OS/2中的内部使用的头结构以及其它一些版本的变体。但所有版本均以一个DWORD位(32位)开始,用以说明该数据块的大小,使得应用程序能够根据这个大小来区分该图像实际使用了哪种版本的DIB头结构。存在多种版本的头结构的原因是微软对DIB格式进行过多次扩充。下表即为所有不同版本的DIB头:
| 大小 | 结构名称 | 作业系统支持 | 所加特性 |
|---|---|---|---|
| 12 | BITMAPCOREHEADER OS21XBITMAPHEADER | OS/2和3.0版本以来的Windows | |
| 64 | BITMAPCOREHEADER2 OS22XBITMAPHEADER | OS/2 | 添加半调网屏;添加RLE及霍夫曼1D压缩。 |
| 40 | BITMAPINFOHEADER | Windows 3.0及更高版本 | 删除RLE-24及霍夫曼1D压缩;添加16/32位像素格式;添加可选的RGB位掩码。 |
| 52 | BITMAPV2INFOHEADER | Undocumented | 删除可选的RGB位掩码;添加必选的RGB位掩码。Adobe Photoshop |
| 56 | BITMAPV3INFOHEADER | Not officially documented | 添加必选的Alpha通道位掩码。Adobe Photoshop |
| 108 | BITMAPV4HEADER | Windows 95/NT4及更高版本 | 添加色彩空间类型和伽玛校正。 |
| 124 | BITMAPV5HEADER | Windows 98/2000及更高版本 | 添加ICC色彩特性。 |
BITMAPCOREHEADER之后的版本都只是在前一版本结构末尾追加字段。出于兼容性的考量,大多数应用程序使用较旧版本的DIB头保存文件。在不考虑OS/2的情况下,目前通用的格式为BITMAPINFOHEADER版本,内容在下表中列出。除非有特殊说明,其中所有值均为无符号整数。
| 偏移量 | 大小(位元組) | 用途 |
|---|---|---|
| 0Eh | 4 | 该头结构的大小(40字节) |
| 12h | 4 | 位图宽度,单位为像素(有符号整数) |
| 16h | 4 | 位图高度,单位为像素(有符号整数) |
| 1Ah | 2 | 色彩平面数;只有1为有效值 |
| 1Ch | 2 | 每个像素所占位数,即图像的色深。典型值为1、4、8、16、24和32 |
| 1Eh | 4 | 所使用的压缩方法,可取值见下表。 |
| 22h | 4 | 图像大小。指原始位图数据的大小(详见后文),与文件大小不是同一个概念。 |
| 26h | 4 | 图像的横向分辨率,单位为像素每米(有符号整数) |
| 2Ah | 4 | 图像的纵向分辨率,单位为像素每米(有符号整数) |
| 2Eh | 4 | 调色板的颜色数,为0时表示颜色数为默认的2色深个 |
| 32h | 4 | 重要颜色数,为0时表示所有颜色都是重要的;通常不使用本项 |
注意:
(1)BI_RGB图像的图像大小字段可以为0。
(2)除 图像宽度(biWidth)、图像高度(biHeight)、水平分辨率(biXPelsPerMeter)、垂直分辨率(biYPelsPerMeter) 外,其他变量均为无符号整数,而以上四个则是 整型数(int),即可以为负的:
图像宽度(biWidth):如果 biWidth 值为 正数,即图像是 从左向右排列 的,那么每行第一个像素就位于最左侧。如果 biWidth 值为 负数,即图像是 从右向左排列 的,那么每行第一个像素就位于最右侧。
图像高度(biHeight):如果 biHeight 值为 正数,即图像是 从下到上排布 的,那么第一个像素就位于文件的左下角。如果 biHeight 值为 负数,即图像是 从上到下排布 的,那么第一个像素就位于文件的左上角。
水平分辨率(biXPelsPerMeter):当 biXPelsPerMeter 为 正数 时,表示 水平分辨率为每英寸的像素数。当 biXPelsPerMeter 为 负数 时,表示 水平分辨率为每米的像素数。
垂直分辨率(biYPelsPerMeter):当 biYPelsPerMeter 为 正数 时,表示 垂直分辨率为每英寸的像素数。当 biYPelsPerMeter 为 负数 时,表示 垂直分辨率为每米的像素数。
压缩方法字段(字节#30-33 从1Eh开始)的有效值如下表:
| 值 | 标识 | 压缩方法 | 备注 |
|---|---|---|---|
| 0 | BI_RGB | 无 | 最常见 |
| 1 | BI_RLE8 | RLE 8位/像素 | 只能用於格式为8位/像素的位图 |
| 2 | BI_RLE4 | RLE 4位/像素 | 只能用於格式为4位/像素的位图 |
| 3 | BI_BITFIELDS | 位字段或者霍夫曼1D压缩(BITMAPCOREHEADER2) | 像素格式由位掩码指定,或位图经过霍夫曼1D压缩(BITMAPCOREHEADER2) |
| 4 | BI_JPEG | JPEG或RLE-24压缩(BITMAPCOREHEADER2) | 位图包含JPEG图像或经过RLE-24压缩(BITMAPCOREHEADER2) |
| 5 | BI_PNG | PNG | 位图包含PNG图像 |
| 6 | BI_ALPHABITFIELDS | 位字段 | 针对Windows CE .NET 4.0及之后版本 |
注意:BI_JPEG和BI_PNG仅对打印机驱动有效,不支持屏幕渲染。
除此之外,OS/2的BITMAPCOREHEADER头也不鲜见:
| 偏移量 | 大小(位元组) | 用途 |
|---|---|---|
| 0Eh | 4 | 该头结构的大小(12字节) |
| 12h | 2 | 位图宽度,单位为像素 |
| 14h | 2 | 位图高度,单位为像素 |
| 16h | 2 | 色彩平面数;只有1为有效值 |
| 18h | 2 | 每个像素所占位数。典型值为1、4、8和24 |
注意:OS/2 BITMAPCOREHEADER的位图都是未压缩的,而且不能是16或32位/像素。OS/2 BITMAPCOREHEADER中的所有值均为无符号整数。
3.2.2 文件分析
之前准备的文件使用的DIB头应该是BITMAPINFOHEADER版本,下面来看一下:

(1)0Eh开始的第 15 - 18 字节,是 BMP信息头 的大小,这里是 00 00 00 28 ,所以是 40 字节大小(包含自身)。
(2)12h开始的第 19 - 22 字节 是 图像的宽度,单位是像素,在本文是 0x00000008 , 十进制是 8,也就是 一行里面有 8 个像素。
(3)16h开始的第 23 - 26 字节 是 图像的高度,单位是像素,在本文是 0x00000008 , 十进制是 8,也就是 一列里面有 8个像素。高度是有符号的,可能是负数,负数的情况这里没遇到过,遇到了再说。
(4)1Ah开始的第 27 - 28 字节是 color planes 的数量 ,通常是 00 01 。
(5)1Ch开始的第 29 - 30 字节是 指每个像素 需要多少 位来存储,本文是 0x0018 ,十进制是 24 ,所以这个bmp文件 是 RGB24 模式的,一个像素占 24 位。
(6)1Eh开始的第 31 - 34 字节是 压缩类型,这个文件没有压缩,所以是 0 。
(7)22h开始的第 35 - 38 字节是 图像大小,不包含 BMP文件头 跟 BMP信息头,其实 BMP文件头就有 总文件大小,这里的0x00 00 00 c0,转成十进制就是192,加上 54 字节头部数据,就是246字节,跟 最开始的这 4 字节(00 00 00 f6)内容是一致的。
(8)26h开始的第 39 - 42 字节是 Width resolution in pixels per meter,水平分辨率,本图是 00 00 00 00 ,表示未知或不适用。
(9)2Ah开始的第 43 - 45 字节是 Height resolution in pixels per meter,垂直分辨率,本图是 00 00 00 00 ,表示未知或不适用。
(10)2Eh开始的第 46 - 49 字节是 颜色表中的颜色索引数,本文没有索引表,是真彩色,所以是 00 00 00 00。
(11)32h开始的第 50 - 53字节表示对图像显示有重要影响的颜色索引数码,这里是 00 00 00 00,代表全都重要。这张图没用到索引表,不用管这个字段。
3.3 调色板
这部分定义了图像中所用的颜色。如上所述,位图图像一个像素接着一个像素储存,每个像素使用一个或者多个字节的值表示,所以调色板的目的就是要告诉应用程序这些值所对应的实际颜色。
典型的位图文件使用RGB彩色模型。在这种模型中,每种颜色都是由不同强度(从0到最大强度)的红色(R)、绿色(G)和蓝色(B)组成的,也就是说,每种颜色都可以使用红色、绿色和蓝色的值所定义。在位图文件的实现中,调色板可以包含很多条目,条目个数就是图像中所使用的颜色的个数。
typedef struct tagRGBQUAD {
BYTE rgbBlue;
BYTE rgbGreen;
BYTE rgbRed;
BYTE rgbReserved;
} RGBQUAD;每个条目用来描述一种颜色,包含4个字节,其中三个表示蓝色、绿色和红色,第四个字节没有使用(大多数应用程序将它设为0);对于每个字节,数值0表示该颜色分量在当前的颜色中没有使用,而数值255表示这种颜色分量使用最大的强度。
3.4 像素数组
3.4.1 基本概念
表示位图中像素的位元是以行为单位对齐存储的,每一行的大小都向上取整为4字节(32位DWORD)的倍数。如果图像的高度大于1,多个经过填充实现对齐的行就形成了像素数组。完整存储的一行像素所需的字节数可以通过这个公式计算:
其中ImageWidth以像素为单位。
像素数组这部分逐个像素表示图像。每个像素使用一个或者多个字节表示。通常,像素是从下到上、从左到右保存的。但如果使用的不是BITMAPCOREHEADER,那么未压缩的Windows位图还可以从上到下存储,此时图像高度为负值。
每一行的末尾通过填充若干个字节的数据(并不一定为0)使该行的长度为4字节的倍数。像素数组读入内存后,每一行的起始地址必须为4的倍数。这个限制仅针对内存中的像素数组,针对存储时,仅要求每一行的大小为4字节的倍数,对文件的偏移没有限制。
例如:对于24位色的位图,如果它的宽度为1像素,那么除了每一行的数据(蓝、绿、红)需要占3字节外,还会填充1字节;而如果宽为2像素,则需要2字节的填充;宽为3像素时,需要3字节填充;宽为4像素时则不需要填充。
图像相同的条件下,位图图像文件通常比使用其它压缩算法的图像文件大很多。
3.4.2 文件分析
上面已经学习完了BMP文件的文件头和信息头,剩下的字节内容就是 真正的 RGB 值。请看下图中第一个像素的值:

上图中圈出来的像素 是 R等于FF,B等于FF,G等于FF ,所以是一个纯白色的像素,注意,这个像素是位于 左下角的第一个像素,不是 左上角。文件中的像素排列顺序是从左下角到右上角,这个排列顺序是由 BMP信息头 里面那个 高度是正数还是负数决定的。下面我们手动修改一下 这些颜色,把黑色白色改成红色,注意,RGB24 模式中的顺序是 BGR,第一个字节是 B,第二个字节是 G, 第三个字节才是 R。如下图:

可以看到已经有两个像素点变成红色了。RGB24 真彩色占用的存储空间是很大的,上面的BMP图片有246字节大小,从十六进制数据可以看到有很多重复的像素点是一样的,这些其实都是可以压缩的。
参考资料:
[Bitmap File Analyzer](http://www.flounder.com/bitmapinfo.htm#Displaying the bitmap)