BMP文件格式解析

梦里梦外; 2023-05-30 08:57 282阅读 0赞

BMP文件格式解析

作者:水木子

文章目录

    • 一、图像概述
      • 1.1 位图
      • 1.2 矢量图
      • 1.3 位图与矢量图的区别
      • 1.4 如何表示像素点的颜色
    • 二、BMP文件格式
      • 2.1 BMP简介
      • 2.2 BMP文件格式组成
        • 2.2.1 位图文件头
        • 2.2.2 位图信息头
        • 2.2.3 颜色表
        • 2.2.4 像素阵列
    • 三、实例解析BMP文件格式
        • 3.1 位图文件头
        • 3.2 位图信息头
        • 3.3 颜色表
        • 3.4 像素阵列
    • 三、代码实验
        • 3.1 实验环境
        • 3.2 实验内容
        • 3.3 其他信息
        • 3.4 关键代码
        • 3.5 注意点!!!
        • 3.6 结果
        • 3.7 完整代码
    • 四、扩展实验
    • 五、参考资料

一、图像概述

1.1 位图

位图Bitmap),又称栅格图(英语:Raster graphics)或点阵图,是使用像素阵列来表示的图像。 ——维基百科

简单地说,位图就是由一个一个像素点构成的图像。常见的图像格式有jpg(jpeg)、png、bmp都是位图。

1.2 矢量图

矢量图形是计算机图形学中用点、直线或者多边形等基于数学方程的几何图元表示图像。

​ ——维基百科

矢量图与位图不同,它不是由一个一个像素点构成的,它的实质是数学表达式。svg格式文件是矢量图。

1.3 位图与矢量图的区别

位图与矢量图最明显的区别是:位图放大会出现马赛克,画质变差;矢量图可以在不降低画质的条件下无限放大。

在这里插入图片描述

图像来源:维基百科

图中a表示原图像。若a为矢量图,则放大红框中图像时,效果如b,可以看见图像质量并没有下降;如果a是位图,则放大红框中图像时,效果如c,可以明显看见有一个一个小方格,图像质量明显下降。

1.4 如何表示像素点的颜色

选择一张位图,在PS中放大到3200%,如下所见:

\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MESatFy9-1573204554252)(img/image-20191102143341677.png)\]

可以很明显得看见一个一个的小方块,这就是像素点。

一个像素点有特定的位置和颜色值。每个像素的颜色由RGB组合或灰度值表示。

本小节重点介绍如何表示颜色。

根据位深度,可以将位图分为1、4、8、16、24及32位图像等。这里的位深度是指用于表示像素点颜色的比特位个数。如果一个像素点由一个比特位表示颜色,那么其位深度为1,如果一个像素点由四个比特位表示颜色,则其位深度为4,依次类推。

  • 如果一张图片的像素点由1个比特位来表示颜色,则这个比特位为0或1,则可以表示21种颜色,即黑与白,则这张照片是纯粹的黑白照片。

  • 如果一张图片的像素点由8个比特位来表示颜色,则这八个比特位共可以表示28种颜色,即256。这种图像通常(有例外,下面会讲)称为灰度图,因为这256种颜色是黑白灰(这里的灰是指244种不同程度的灰)。如下为灰度图:

    \[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XND0Cdem-1573204554252)(img/grey8.bmp)\]

  • 如果一张图片的像素点由24个比特位来表示颜色,则这24个比特位共可以表示224种颜色,即有1600万个以上的颜色。这种图像称为真彩色图。这24个比特位以8位分为三个通道,分别表示红、绿、蓝。这就是RGB颜色编码方式,用红、绿、蓝三原色的光学强度来表示一种颜色。这是最常见的位图编码方法,可以直接用于屏幕显示。

二、BMP文件格式

2.1 BMP简介

BMP取自位图Bitmap的缩写,也称为DIB(与设备无关的位图),是一种独立于显示器的位图数字图像文件格式。常见于微软视窗和OS/2操作系统。 ——维基百科

BMP格式就是表示位图的格式。

BMP格式图像中的像素点,其位深度可以是1,4,8,24,32,但常见的BMP位深度还是8和24。

选择一张BMP图像,右键打开属性 —> 详细信息,可以查看其位深度。

当BMP文件位深度为8时,并不一定代表这张图片为灰度图,如下:

\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mo8accMD-1573204554254)(img/swift8.bmp)\]

这张图片的位深度为8,但其并不是灰度图,我们称之为伪彩色图

下面这张图是位深度为24的真彩色图,可以作为对比:

\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wwYgzFde-1573204554258)(img/swift24.bmp)\]

可以看出真彩色图的质量明显高于伪彩色图。

2.2 BMP文件格式组成

BMP文件由以下四部分组成:

  • 位图文件头(BITMAPFILEHEADER)
  • 位图信息头(BITMAPINFOHEADER)
  • 颜色表*(RGBQUAD[])
  • 像素阵列(Pixels[][])

由于颜色表并不一定存在,故加*说明。

下面先将各部分的信息简单说明一下:

2.2.1 位图文件头

用于描述整个bmp文件的情况,具体包括BMP文件的类型、文件大小和位图起始位置等信息。

  1. typedef struct tagBITMAPFILEHEADER{
  2. WORD bfType; // 位图文件的类型,必须为BMP (2个字节)
  3. DWORD bfSize; // 位图文件的大小,以字节为单位 (4个字节)
  4. WORD bfReserved1; // 位图文件保留字,必须为0 (2个字节)
  5. WORD bfReserved2; // 位图文件保留字,必须为0 (2个字节)
  6. DWORD bfOffBits; // 位图数据的起始位置,以相对于位图 (4个字节)
  7. } BITMAPFILEHEADER;

位图文件头总共有14个字节

2.2.2 位图信息头

用于描述位图的尺寸等信息。

  1. typedef struct tagBITMAPINFOHEADER{
  2. DWORD biSize; // 本结构所占用字节数 (4个字节)
  3. LONG biWidth; // 位图的宽度,以像素为单位(4个字节)
  4. LONG biHeight; // 位图的高度,以像素为单位(4个字节)
  5. WORD biPlanes; // 目标设备的级别,必须为1(2个字节)
  6. WORD biBitCount; // 每个像素所需的位数,必须是1(双色)、// 4(16色)、8(256色)、
  7. //24(真彩色)或32(增强真彩色)之一 (2个字节)
  8. DWORD biCompression; // 位图压缩类型,必须是 0(不压缩)、 1(BI_RLE8
  9. // 压缩类型)或2(BI_RLE4压缩类型)之一 ) (4个字节)
  10. DWORD biSizeImage; // 位图的大小,以字节为单位(4个字节)
  11. LONG biXPelsPerMeter; // 位图水平分辨率,每米像素数(4个字节)
  12. LONG biYPelsPerMeter; // 位图垂直分辨率,每米像素数(4个字节)
  13. DWORD biClrUsed; // 位图实际使用的颜色表中的颜色数(4个字节)
  14. DWORD biClrImportant; // 位图显示过程中重要的颜色数(4个字节)
  15. } BITMAPINFOHEADER;

位图信息头一共有40个字节

2.2.3 颜色表

用于说明位图中的颜色,它有若干个表项,每一个表项是一个RGBQUAD类型的结构,定义一种颜色。

  1. typedef struct tagRGBQUAD
  2. {
  3.   BYTE rgbBlue; // 蓝色的亮度(值范围为0-255)
  4.   BYTE rgbGreen; // 绿色的亮度(值范围为0-255)
  5.   BYTE rgbRed; // 红色的亮度(值范围为0-255)
  6.   BYTE rgbReserved; // 保留,必须为0
  7. } RGBQUAD;

可以看到一个RGB表项为4个字节

颜色表中RGBQUAD结构数据的个数由位图信息头中的biBitCount来确定:

  • 当biBitCount=1, 4, 8时,分别有2, 16,256个表项
  • 当biBitCount=24时,没有颜色表项

2.2.4 像素阵列

l记录了位图的每一个像素值,记录顺序是在扫描行内是从左到右,扫描行之间是从下到上。位图的一个像素值所占的字节数如下:

  • 当biBitCount=1时,8个像素占1个字节;
  • 当biBitCount=4时,2个像素占1个字节;
  • 当biBitCount=8时,1个像素占1个字节;
  • 当biBitCount=24时,1个像素占3个字节,分别是R、G、B;

Windows规定一个扫描行所占的字节数必须是4的倍数(即以long为单位),不足的以0填充。

三、实例解析BMP文件格式

用notepad++打开grey8.bmp文件,选择插件 —> HEX-Editor —> View in HEX,如果没有可自行选择插件管理进行安装,最终界面如下:

\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MYsNmSvS-1573204554260)(img/image-20191102154654330.png)\]

这是以十六进制的形式显示图像信息,一个十六进制数字占4个比特位,故一行表示十六个字节。

在对BMP文件进行具体解析前,我们还要先了解一下数据的存放顺序:

在BMP文件中,如果一个数据需要用几个字节来表示的话,那么该数据的存放字节顺序为“低地址存放低位数据,高地址存放高位数据”。如数据0x1756在内存中的存储顺序为:

\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NSD9dSVY-1573204554262)(img/bmp\_5.png)\]

这种存储方式称为小端方式(little endian) , 与之相反的是大端方式(big endian)。

3.1 位图文件头

下图红框中为位图文件头:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxMjYxMjUx_size_16_color_FFFFFF_t_70 1

  • 头两个字节(0,1)表示位图文件的类型,即0x4d42表示类型,为BMP,这与DUMP中的头两个字节表示的符号BM一致
  • 后面的四个字节(2,3,4,5)表示位图文件的大小,即0x0000c436表示位图文件大小,换算为十进制为50230,我们打开grey8.bmp的属性,发现其大小确为50230字节:

    20191108171947989.png

    位图文件的大小,即bfSize的值需要如何计算呢?
    b f S i z e = 位 图 文 件 头 的 大 小 ( 14 字 节 ) + 位 图 信 息 头 的 大 小 ( 40 字 节 ) + 颜 色 表 项 ∗ 4 字 节 + 图 像 的 宽 度 ∗ 高 度 ∗ 位 深 度 / 8 ( 字 节 ) bfSize=位图文件头的大小(14字节)+位图信息头的大小(40字节)\\+颜色表项*4字节+图像的宽度*高度*位深度/8(字节) bfSize=位图文件头的大小(14字节)+位图信息头的大小(40字节)+颜色表项∗4字节+图像的宽度∗高度∗位深度/8(字节)
    我们打开grey8.bmp的属性,点击详细信息,可以看到grey8.bmp的大小为256x192像素,位深度为8,又因为其位深度为8,故其颜色表项有256项(每项占据4字节),故grey8.bmp的大小为:
    b f S i z e = 14 + 40 + 256 ∗ 4 + 256 ∗ 192 ∗ 8 / 8 = 50230 ( 字 节 ) bfSize=14+40+256*4+256*192*8/8=50230(字节) bfSize=14+40+256∗4+256∗192∗8/8=50230(字节)

  • 再后面的两个字节(6,7)是位图文件保留字1,必须为0,即为0x0000。
  • 再后面的两个字节(8,9)是位图文件保留字2,必须为0,即为0x0000。
  • 最后的四个字节(a,b,c,d)是位图数据的起始位置,其值为0x00000436,换算为十进制为1078。这个表示的是从文件开始到像素阵列之间的字节数,即其大小为:位图文件头(14字节)+位图信息头(40字节)+[256个表项*4字节],因为颜色表项并不一定存在,故用[]括起来。在grey8.bmp图像中存在颜色表,故位图数据的起始值为1078。

3.2 位图信息头

\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7pzsYKPp-1573204554264)(img/BITMAPINFOHEADER .png)\]

  • 头四个字节(第一行的e,f,第二行的0,1)表示位图信息头所占用的字节数,即为0x00000028,换算为十进制是40;
  • 后四个字节(第二行的2,3,4,5)为位图的以像素为单位的宽度,即0x00000100,换算为十进制为256,与实际相符。
  • 后四个字节(第二行的6,7,8,9)为位图的以像素为单位的高度,即0x000000c0,换算为十进制为192,与实际相符。
  • 后两个字节(第二行的a,b)为目标设备的级别,必须为1,其值为0x0001,相符。
  • 后两个字节(第二行的c,d)为每个像素所需的位数,其值为0x0008,与实际的位深度相一致。
  • 后四个字节(第二行的e,f,第三行的0,1)表示位图的压缩类型,其值为0x00000000,即不压缩。
  • 后四个字节(第三行的2,3,4,5)为位图的大小,以字节为单位,其值为0x0000c000,换算为十进制为49152,其实就是计算像素阵列的大小,计算方式为:
    b i S i z e I m a g e = 图 像 的 宽 度 ∗ 高 度 ∗ 位 深 度 / 8 biSizeImage=图像的宽度*高度*位深度/8 biSizeImage=图像的宽度∗高度∗位深度/8
    在grey8.bmp图像中
    b i S i z e I m a g e = 256 ∗ 192 ∗ 8 / 8 = 49152 biSizeImage=256*192*8/8=49152 biSizeImage=256∗192∗8/8=49152
  • 后四个字节(第三行的6,7,8,9)表示位图的水平分辨率,其值为0x00002e23。
  • 后四个字节(第三行的a,b,c,d)表示位图的垂直分辨率,其值为0x00002e23。
  • 后四个字节(第三行的e,f,第四行的0,1)表示位图实际使用的颜色表中的颜色数,其值为0x00000000,一般置为0。
  • 最后的四个字节(第四行的2,3,4,5)表示位图显示过程中重要的颜色数,其值为0x00000000,一般置为0。

3.3 颜色表

当位深度为24时,没有颜色表,位图信息头紧接着就是像素阵列;

当位深度不为24时,有颜色表,并且有2位深度个颜色表项,每个表项占据4个字节。

在如下图的黄框中,共有256个颜色表项,共由256*4个字节(只显示了一部分):

在这里插入图片描述

由于每个颜色表项为4个字节,因此我们以4个字节为单位划分为一个个的颜色表项,即一个黑框。

由于grey8.bmp是灰度图,因此其颜色表项是由规律可循的。

在颜色表项内部,RGB三个分量相等,第4个分量为0;在整个颜色表中,颜色表项的前三个分量数值由0到255依次递增1。其实,rgb(0,0,0)代表黑色,rgb(255,255,255)代表白色,rgb(x,x,x)(x不等于0或255,x为0到255之间的整数)代表不同程度的灰色。

当BMP文件为伪彩色图时,其位深度是8位,但并没有规律可循。比如第一个颜色表项是rgb(1,22,3,0),第二个颜色表项是rgb(10,89,90,0),依次类推。这些颜色并不是黑白灰,而是会显示出其他颜色,但是,最多只能显示256种颜色,远远不如真彩色图的1600万多种颜色,因此这类图像叫做伪彩色图。

3.4 像素阵列

颜色表(或位图信息头)后就是像素阵列,在本例中,位深度为8,故一个字节就代表一个像素点。那如何确定这个像素点的颜色呢?一个字节表示的范围为[0,255],这下你该明白了吧!根据这个字节的值找到相应的颜色表项,这个颜色表项所对应的颜色就是这个像素的颜色。对于有颜色表的BMP,其运作方式就是如此。

对于真彩色图,其位深度为24,一个像素就自然而然地对应着R、G、B三个颜色分量,故不需要颜色表了,并且,对于真彩色图,如果有颜色表,那么其有1600万多个颜色表项,整个文件将会非常庞大。

三、代码实验

3.1 实验环境

  • 操作系统:Windows 10
  • 编译器:Dev-cpp,Visual Studio 2017

3.2 实验内容

将以下grey8_test.bmp灰度图的颜色表项修改为随机值,将原灰度图变为伪彩色图。

\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fPGLoDqK-1573204554267)(img/grey8\_test-1572943023710.bmp "grey8\_test.bmp")\]

3.3 其他信息

BITMAPFILEHEADER、BITMAPINFOHEADER、RGBQUAD 这三个结构体在windows.h中都有定义

3.4 关键代码

因为代码都有详细注释,故在此处不再详细说明。

  • 读取相关信息:

    BITMAPFILEHEADER *fileHeader;

    1. BITMAPINFOHEADER *infoHeader;
    2. //为位图文件头、位图信息头指针分配空间
    3. fileHeader = (BITMAPFILEHEADER *)malloc(sizeof(BITMAPFILEHEADER));
    4. infoHeader = (BITMAPINFOHEADER *)malloc(sizeof(BITMAPINFOHEADER));
    5. //读取灰度图src的位图文件头、位图信息头信息
    6. fread(fileHeader, sizeof(BITMAPFILEHEADER), 1, src);
    7. fread(infoHeader, sizeof(BITMAPINFOHEADER), 1, src);
    8. //为颜色表指针分配空间
    9. RGBQUAD *rgb = (RGBQUAD *)malloc(sizeof(RGBQUAD) * 256);
    10. //读取颜色表项
    11. fread(rgb, sizeof(RGBQUAD), 256, src);
  • 利用随机数函数修改颜色表项

    //修改颜色表项的RGB三分量的值

    1. srand((unsigned)time(NULL));
    2. for (int i = 0; i < 256; i++) {
    3. rgb[i].rgbBlue = rand() % 255;
    4. rgb[i].rgbGreen = rand() % 255;
    5. rgb[i].rgbRed = rand() % 255;
    6. }
  • 将读取到的信息写入目标文件

    //将位图文件头、位图信息头、颜色表写入目标文件

    1. fwrite(fileHeader, sizeof(BITMAPFILEHEADER), 1, target);
    2. fwrite(infoHeader, sizeof(BITMAPINFOHEADER), 1, target);
    3. fwrite(rgb, sizeof(RGBQUAD), 256, target);
  • 读取像素信息并写入,注意四字节对齐

    //读取灰度图像的像素

    1. unsigned char *ch;
    2. ch = (unsigned char*)malloc(sizeof(unsigned char));
    3. for (int i = 0; i < height; i++)
    4. {
    5. for (int j = 0; j < ((width + 3) / 4) * 4; j++)
    6. {
    7. fread(ch, sizeof(unsigned char), 1, src);
    8. fwrite(ch, sizeof(unsigned char), 1, target);
    9. }
    10. }

3.5 注意点!!!

  • 以二进制形式打开文件!!!!!!!
  • 注意一行的像素所占的字节数是4的倍数。读文件时,要多读取后面补充的0字节;写文件时,也要多写需要填充的0字节。否则可能会出现图像错乱的现象。

3.6 结果

利用我们的程序,生成了以下图片,还挺漂亮的!

\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CPtn37fu-1573204554269)(img/1.bmp)\]\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eTnc5MNE-1573204554271)(img/2.bmp)\]\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7PEO2f0U-1573204554273)(img/3.bmp)\]

\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PMaqaxgH-1573204554275)(img/4.bmp)\]\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7bQn5VRs-1573204554277)(img/5.bmp)\]\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oMHLZ8xG-1573204554281)(img/pseudo.bmp)\]

3.7 完整代码

参考code文件中的源.cpp和question3文件

四、扩展实验

  • 编写C/C++程序,读取一个24位真彩色BMP文件,然后转化为灰色图像,最后存储为8位伪彩色BMP文件。
  • 编写C/C++程序,读取一个8位伪彩色BMP文件,转化为24位真彩色BMP文件,最后存储。

备注:从RGB到灰度图的转换公式:Gray = R*0.299 + G*0.587 + B*0.114

五、参考资料

[1] https://www.cnblogs.com/Matrix_Yao/archive/2009/12/02/1615295.html

[2] 多媒体基础课程相关资料

[3] 维基百科关于“位图”、“矢量图”、“BMP格式”的介绍

发表评论

表情:
评论列表 (有 0 条评论,282人围观)

还没有评论,来说两句吧...

相关阅读

    相关 BMP文件格式

    6.1 BMP文件格式 6.1.1 简介 位图文件(Bitmap\-File,BMP)格式是Windows采用的图像文件存储格式,在Windows环境下运行的所有图像处理软