LV01-图像-03-图片格式-02-BMP

本文主要是图片格式——BMP的相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。

点击查看使用工具及版本
PC端开发环境 Windows Windows11
Ubuntu Ubuntu20.04.6的64位版本
VMware® Workstation 17 Pro 17.0.0 build-20800274
终端软件 MobaXterm(Professional Edition v23.0 Build 5042 (license))
Win32DiskImager Win32DiskImager v1.0
Linux开发板环境 Linux开发板 正点原子 i.MX6ULL Linux 阿尔法开发板
uboot NXP官方提供的uboot,NXP提供的版本为uboot-imx-rel_imx_4.1.15_2.1.0_ga(使用的uboot版本为U-Boot 2016.03)
linux内核 linux-4.15(NXP官方提供)
点击查看本文参考资料
  • 通用
参考资料 相关链接
A Bitmap File AnalyzerBitmap File Analyzer (flounder.com)
点击查看相关文件下载
------

一、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位$2^n$​种颜色的包含调色板的位图近似字节数可以用下面的公式计算:
$$
SIZE=54+4*2^n+\frac{width*height*n}{8}
$$
其中高度(height)和宽度(width)都以像素为单位。需要注意的是上面公式中的54是位图文件的文件头(英语:Header (information technology)),$4*2^n$​是彩色调色板的大小。 如果位图文件不包含调色板,如24位,32位位图,则位图的近似字节数可以用下面的公式计算:
$$
SIZE=54+\frac{width*height*n}{8}
$$
另外需要注意的是这是一个近似值,对于n位的位图图像来说,尽管可能有最多$2^n$种颜色,一个特定的图像可能并不会使用这些所有的颜色。由于彩色调色板仅仅定义了图像所用的颜色,所以实际的彩色调色板将小于$4*2^n$​。

由于存储算法本身决定的因素,根据几个图像参数的不同计算出的大小与实际的文件大小将会有一些细小的差别。

二、文件格式

接下来我们来了解一下这种文件的格式。

1. 获取一张BMP图片

1.1 软件生成

首先我们需要有一张BMP格式的文件,我们可以自己做一张小的,便于查看,windows系统中自带的画图软件就可以生成BMP格式文件:

image-20240708075318473

这样我们就可以得到一张24位BMP格式的图片:

format-200x100

我们用notepad++打开这个文件,或者其他能查看十六进制的软件都行:

image-20240708075739775

上面的图片数据感觉还有点多,搞个小一点的:

image-20240709070406156

这图片是30x13个像素的:

format-30x13

我们看一下它的十六进制:

image-20240709070751891

1.2 FFmpeg转化

1
2
ffmpeg -i fileName.jpg fileName.bmp # jpg-->bmp
ffmpeg -i fileName.bmp fileName.jpg # bmp-->jpg

2. 格式说明

位图图像文件由若干大小固定(文件头)和大小可变的结构体按一定的顺序构成。由于该文件格式几经演进,这些结构体的版本也很多。

结构体名称 可选 大小 用途 备注
位图文件头 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 文件分析

如下图所示:

image-20240709071932907

(1)BMP文件头的大小是 14 字节,第 1-2 字节 是 魔数,在这里里面是 BM 字符串。

(2)第 3-6 个字节是BMP文件的大小,在这里是 00 00 04 e2,注意,数据类型存储是 小端存储的。换算一下就是1250字节,这个跟前面文件大小是对得上的:

image-20240709072312104

(3)第 7-10 个字节 是保留字节,以后扩展用的,这4个字节目前永远是 0 。

(4)第 11-14 个字节 是实际的像素数据的偏移值,在这里是 0x36 ,没有调色板通常都是 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时表示所有颜色都是重要的;通常不使用本项

注意:BI_RGB图像的图像大小字段可以为0。

压缩方法字段(字节#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版本,下面来看一下:

image-20240709074735943

(1)0Eh开始的第 15 - 18 字节,是 BMP信息头 的大小,本文的 format-30x13.bmp 的 第 15 - 18 字节 是 0x00000028 ,所以是 40 字节大小(包含自身)。

(2)12h开始的第 19 - 22 字节 是 图像的宽度,单位是像素,在本文是 0x0000001e , 十进制是 30,也就是 一行里面有 30 个像素。

(3)16h开始的第 23 - 26 字节 是 图像的高度,单位是像素,在本文是 0x0000000d , 十进制是 13,也就是 一列里面有 13个像素。高度是有符号的,可能是负数,负数的情况这里没遇到过,遇到了再说。

(4)1Ah开始的第 27 - 28 字节是 color planes 的数量 ,通常是 00 01 。

(5)1Ch开始的第 29 - 30 字节是 指每个像素 需要多少 位来存储,本文是 0x0018 ,十进制是 24 ,所以本文的 format-30x13.bmp p 是 RGB24 模式的,一个像素占 24 位。

(6)1Eh开始的第 31 - 34 字节是 压缩类型,本文没有压缩,所以是 0 。

(7)22h开始的第 35 - 38 字节是 图像大小,不包含 BMP文件头 跟 BMP信息头,其实 BMP文件头就有 总文件大小,0x00 00 04 ac,转成十进制就是1196,加上 54 字节头部数据,就是1250字节,跟 最开始的这 4 字节内容是一致的。如下图:

image-20240709075537604

(8)26h开始的第 39 - 42 字节是 Width resolution in pixels per meter,水平分辨率,本文是 00 ,所以不管。

(9)2Ah开始的第 43 - 45 字节是 Height resolution in pixels per meter,垂直分辨率,本文是 00 ,所以不管。

(10)2Eh开始的第 46 - 49 字节是 颜色表中的颜色索引数,本文没有索引表,是真彩色,所以是 00 。

(11)32h开始的第 50 - 53字节表示对图像显示有重要影响的颜色索引数码,本文是 00 ,代表全都重要。本文没用到索引表,不用管这个字段。

3.3 调色板

这部分定义了图像中所用的颜色。如上所述,位图图像一个像素接着一个像素储存,每个像素使用一个或者多个字节的值表示,所以调色板的目的就是要告诉应用程序这些值所对应的实际颜色。

典型的位图文件使用RGB彩色模型。在这种模型中,每种颜色都是由不同强度(从0到最大强度)的红色(R)、绿色(G)和蓝色(B)组成的,也就是说,每种颜色都可以使用红色、绿色和蓝色的值所定义。在位图文件的实现中,调色板可以包含很多条目,条目个数就是图像中所使用的颜色的个数。

1
2
3
4
5
6
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,多个经过填充实现对齐的行就形成了像素数组。完整存储的一行像素所需的字节数可以通过这个公式计算:
$$
RowSize=\frac{BitsPerPixel*ImageWidth+31}{32}*4
$$

其中ImageWidth以像素为单位。

像素数组这部分逐个像素表示图像。每个像素使用一个或者多个字节表示。通常,像素是从下到上、从左到右保存的。但如果使用的不是BITMAPCOREHEADER,那么未压缩的Windows位图还可以从上到下存储,此时图像高度为负值。

每一行的末尾通过填充若干个字节的数据(并不一定为0)使该行的长度为4字节的倍数。像素数组读入内存后,每一行的起始地址必须为4的倍数。这个限制仅针对内存中的像素数组,针对存储时,仅要求每一行的大小为4字节的倍数,对文件的偏移没有限制。

例如:对于24位色的位图,如果它的宽度为1像素,那么除了每一行的数据(蓝、绿、红)需要占3字节外,还会填充1字节;而如果宽为2像素,则需要2字节的填充;宽为3像素时,需要3字节填充;宽为4像素时则不需要填充。

图像相同的条件下,位图图像文件通常比使用其它压缩算法的图像文件大很多。

3.4.2 文件分析

上面已经学习完了BMP文件的文件头和信息头,剩下的字节内容就是 真正的 RGB 值。请看下图中第一个像素的值:

image-20240710074738522

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

image-20240710075032616

可以看到已经有两个像素点变成红色了。RGB24 真彩色占用的存储空间是很大的,上面的BMP图片有1.2M大小,从十六进制数据可以看到有很多重复的像素点是一样的,这些都是可以压缩的。