1. 简介

BMP 格式是 Windows 下最简单的图像存储格式,它支持图像以每像素 1,4,8,16,24,321,4,8,16,24,32 比特表示。BMP 格式也支持 4,84,8 比特每像素的游程算法压缩图像。

2. 格式

BMP 文件基本结构如下:

其详细结构如下:

2.1 File Header

BMP 文件中的 File Header 包含一个 BITMAPFILEHEADER 结构,主要用来识别文件格式,其包含的字段如下表所示:

字段名 大小(Bytes) 说明
bfType 22 存储两个字符「BM」用于验证文件类型
bfSize 44 存储该文件大小
bfReserved1 22 未使用(值必须为 00
bfReserved2 22 未使用(值必须为 00
bfOffBits 44 存储像素数据开始处的偏移大小

2.2 Image Header

File Header 后紧接着为 Image Header。Image Header 可以为两种不同结构的其中一种:BITMAPINFOHEADERBITMAPCOREHEADER。Windows 下 BMP 文件一般使用的是 BITMAPINFOHEADER 结构。BMP 中没有专门的子段来指明 Image Header 使用的结构,因此只能通过比较 IMage Header 的大小来判断其使用的结构。BITMAPINFOHEADER 结构至少为 4040 Byters 大小,而 BITMAPCOREHEADER 结构只有 1212 Bytes 大小。

BITMAPINFOHEADER 结构包含的字段如下表所示:

字段名 大小(Bytes) 说明
biSize 44 Image Header 大小(至少 4040
biWidth 44 图像宽度
biHeight 44 图像高度
biPlanes 22 值必须为 11
biBitCount 22 每像素所用比特数——1,4,8,16,24,321,4,8,16,24,32
biCompression 44 压缩类型——BI_RGB=0,BI_RLE8=1,BI_RLE4=2,BI_BITFIELDS=3
biSizeImage 44 图像大小——如果没有压缩则该值为 00
biXPelsPerMeter 44 X 轴首选分辨率(每米)
biYPelsPerMeter 44 Y 轴首选分辨率(每米)
biClrUsed 44 Color Table 中实际使用的色彩项数目
biClrImportant 44 重要的色彩数目

BITMAPCOREHEADER 结构包含的字段如下表所示:

字段名 大小(Bytes) 说明
bcSize 44 Image Header 大小(值必须为 1212
bcWidth 22 图像宽度
bcHeight 22 图像高度
bcPlanes 22 值必须为 11
bcBitCount 22 每像素所用比特数——1,4,8,241,4,8,24

【注】如果 BMP 文件的 Image Header 使用 BITMAPCOREHEADER 结构,像素数据则不能被压缩。

2.3 Color Table

Image Header 后紧接着为 Color Table。Color Table 也称为 Color Palette(调色板)可以为三种格式中的一种:前两种主要用于将像素值映射到 RGB 颜色值,适用于每像素比特数为 1,4,81,4,8 的情况(由 Image Header 中的 biBitCountbcBitCount 字段指定)。

  • 对于 Windows 下的 BMP 文件,Color Table 为由 2bitcount2^{bitcount}RGBQUAD 项构成的数组,每个 RGBQUAD 项包含的字段如下表所示:
字段名 大小(Bytes) 说明
rgbBlue 11 蓝色分量值
rgbGreen 11 绿色分量值
rgbRed 11 红色分量值
rgbReserved 11 未使用(值必须为 00
  • 对于 OS/2 下的 BMP 文件,Color Table 为由 2bitcount2^{bitcount}RGBTRIPLE 项构成的数组,每个 RGBTRIPLE 项包含的字段如下表所示:
字段名 大小(Bytes) 说明
rgbtBlue 11 蓝色分量值
rgbtGreen 11 绿色分量值
rgbtRed 11 红色分量值

最后一种格式是当 BITMAPINFOHEADER 结构中的 BiCompression 字段取值为 BI_BITFIELDS(3) 时,在 RGBQUAD 数组前多出了一个掩码结构。比如以 3232 位的 BMP 图像为例,三个掩码均为 3232 位,即 44 Bytes。

掩码字段 掩码值
Red 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 120\ 0\ 0\ 0\ 0\ 0\ 0\ 0\ 0\ 0\ 0\ 0\ 0\ 0\ 0\ 0\ 0\ 0\ 0\ 0\ 0\ 0\ 1\ 1\ 1\ 1\ 1\ 1\ 1\ 1\ 1\ 1_2
Green 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 020\ 0\ 0\ 0\ 0\ 0\ 0\ 0\ 0\ 0\ 0\ 0\ 1\ 1\ 1\ 1\ 1\ 1\ 1\ 1\ 1\ 1\ 0\ 0\ 0\ 0\ 0\ 0\ 0\ 0\ 0\ 0_2
Blue 0 0 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 020\ 0\ 1\ 1\ 1\ 1\ 1\ 1\ 1\ 1\ 1\ 1\ 0\ 0\ 0\ 0\ 0\ 0\ 0\ 0\ 0\ 0\ 0\ 0\ 0\ 0\ 0\ 0\ 0\ 0\ 0\ 0_2

掩码字段主要用于明确哪些比特位分别用来表示 Red、Green、Blue 分量。对于 3232 位的 BMP 图像来说,每个掩码字段中连续的 1010 比特(必须是连续且相互无交叉的)用来表明每个分量使用到的比特位置。

【注】对于 biCompression 字段没有设定为值 BI_BITFIELDS16,24,3216,24,32 位的 BMP 图像,没有 Color Table。

2.4 Pixel Data

Pixel Data 通常直接接在 Color Table 或 Image Header 后面,但有时为了保证对齐也会先填充一些字节再接上 Pixel Data。所以必须使用 bfOffBits 字段来明确 Pixel Data 的起始偏移。像素行是由底向上排列在 Pixel Data 中,即 Pixel Data 第一行为图像的最底行,以此类推。行数由 biHeightbcHeight 决定,行大小则由 biBitCountbiWidthbcBitCountbcWidth 共同决定。每行大小都按字节对齐到 44 的倍数,具体计算公式如下:

bytes per row=18(width×bitcount+7)+34\begin{array}{c} bytes\ per\ row = \frac{\frac{1}{8}(width \times bitcount + 7) + 3}{4} \end{array}

像素数据格式取决于 biBitCountbcBitCount

  • 1144 比特每像素:每个数据字节被分成 8822 部分,每部分表示 Color Table 中的索引值。

  • 88 比特每像素:每个数据字节表示 Color Table 中的索引值。

  • 1616 比特每像素:

  1. 如果 BITMAPINFOHEADER 中的 biCompressionBI_RGB(0),则每个颜色分量使用 55 比特表示,最高比特位保留。
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
Red Blue Green
  1. 如果 BITMAPINFOHEADER 中的 biCompressionBI_BITMAP,三个 44 Bytes 的掩码指明了每个颜色分量使用到的比特。三个掩码依次为 Red、Blue、Green。
  • 对于 2424 比特每像素:每个像素使用三个连续的字节依次表示 Blue、Green、Red 分量的颜色值。在其他大多数图像格式中,这三个分量的顺序往往是反过来的,即 Red、Green、Blue。

  • 对于 3232 比特每像素:

  1. 如果 BITMAPINFOHEADER 中的 biCompressionBI_RGB(0),则三个低位的字节依次表示 Blue、Green、Red,最高位字节未使用(相当于是在 2424 比特每像素基础上再浪费一个字节)。
  2. 如果 BITMAPINFOHEADER 中的 biCompressionBI_BITMAP,三个 44 Bytes 的掩码指明了每个颜色分量使用到的比特。三个掩码依次为 Red、Blue、Green。

3. 算法

对于 4,84,8 比特每像素,BMP 格式支持游程编码压缩。以下情况表示 BMP 格式使用了游程编码:

  • 44 比特每像素:如果 BITMAPINFOHEADER 中的 biCompressionBI_RLE4(2)
  • 88 比特每像素:如果 BITMAPINFOHEADER 中的 biCompressionBI_RLE8(1)

3.1 RLE4

在 RLE4 压缩算法中,压缩数据被切分称 22 Bytes 的数值对,第二个字节为像素值对,第一个字节给出了像素值出现的数目。对于第二个字节的像素值对,高四位表示第奇数个像素的像素值,低四位表示第偶数个像素的像素值。比如,在 44 比特每像素的 BMP 图像中,<0516,005605_{16},00_{56}> 数值对解码后为:

516 616 516 616 5165_{16}\ 6_{16}\ 5_{16}\ 6_{16}\ 5_{16}

第一个字节如果为 001600_{16} 则表示转义码。

  • <0016,001600_{16},00_{16}>:表示新的图像行开始。
  • <0016,011600_{16},01_{16}>:表示图像像素数据结束。
  • <0016,021600_{16},02_{16}>:表示改变当前图像位置,接下来两个无符号字节分别表示跳过的列数和行数。这个转义码主要用于跳过大片矩形的 00 块。
  • <0016,xy16(>0216)00_{16},xy_{16}(>02_{16})>:表示未压缩的像素字节数,接下来 xy16xy_{16} 个字节都是未压缩的像素对字节,高四位 x16x_{16} 为第一个像素值,低四位 y16y_{16} 为第二个像素值。这个转义码主要用于存储未压缩的数据。(注意:xy16xy_{16} 必须为 22 的倍数,否则会在最后填充字节)

3.2 RLE8

在 RLE8 压缩算法中,压缩数据被切分称 22 Bytes 的数值对,第二个字节为像素值,第一个字节则给出了像素值连续出现的数目。比如,在 88 比特每像素的 BMP 图像中,<0816,001608_{16},00_{16}> 数值对解码后为:

0016 0016 0016 0016 0016 0016 0016 001600_{16}\ 00_{16}\ 00_{16}\ 00_{16}\ 00_{16}\ 00_{16}\ 00_{16}\ 00_{16}

第一个字节如果为 001600_{16} 则表示转义码。

  • <0016,001600_{16},00_{16}>:表示新的图像行开始。
  • <0016,011600_{16},01_{16}>:表示图像像素数据结束。
  • <0016,021600_{16},02_{16}>:表示改变当前图像位置,接下来两个无符号字节分别表示跳过的列数和行数。这个转义码主要用于跳过大片矩形的 00 块。
  • <0016,xy16(>0216)00_{16},xy_{16}(>02_{16})>:表示未压缩的像素字节数,接下来 xy16xy_{16} 个字节都是未压缩的像素字节,无需解压。这个转义码主要用于存储未压缩的数据。

附录

  • BMP file format
  • 《Compressed Image File Formats JPEG, PNG, GIF, XBM, BMP》 by John Miano