BMP图像读取

0. BMP格式介绍

根据百度百科对BMP的定义,我们知道BMP是Bitmap(位图)的简称,是Windows操作系统中的标准图像文件格式。其特点是由于几乎不进行压缩,所以包含的图像信息较丰富,但同时也到之占用的磁盘空间较大。

1 文件格式

位图文件由4个部分组成:

  1. 位图头文件(bitmap-file header)
  2. 位图信息头(bitmap-information header)
  3. 颜色表(color table):使用索引来表示图像,此时颜色表就是索引与其对应的颜色之间的映射表(即通过索引值,结合颜色表,找到对应的像素信息)
  4. 位图数据(data bits)

上述不同的部分,有不同的数据结构,下面分别介绍前面3个部分的数据结构。

数据段名称    对应的Windows结构体定义大小(Byte)
bmp文件头   BITMAPFILEHEADER       14
bmp信息头        BITMAPINFOHEADER        40
颜色表由颜色索引数决定
bmp数据由图像尺寸决定

2 位图头文件

 本文,以上图来分析BMP图像的文件格式。在分析图像数据之前,需要强调的是BMP文件中,数据的存储方式为小端方式(little endian),即假设一个数据需要多个字节来表示,那么数据的存放字节的顺序为“低地址存放地位数据,高地址存放高位数据”。在十六进制中,一个数字占4位,因此每个字节可以存储2个十六进制数字

因此,对于数据0x1756在内存中的存储顺序为:

 位图头文件的结构体定义如下:

typedef struct tagBITMAPFILEHEADER 
{  
    UINT16 bfType;    // 19778,必须是BM字符串,对应的十六进制为0x4d42,十进制为19778,否则不是bmp格式文件
    DWORD bfSize; // 文件大小 以字节为单位(2-5字节)
    UINT16 bfReserved1; // 保留,必须设置为0 (6-7字节)
    UINT16 bfReserved2; // 保留,必须设置为0 (8-9字节)
    DWORD bfOffBits;    // 从文件头到像素数据的偏移  (10-13字节)
} BITMAPFILEHEADER;

 在VScode中,可以通过安装hexdump插件来以二进制的形式查看BMP图像数据。

 对照结构体和文件数据可以看到:

0-1 : 0x4d42='BM',表示这是Windows支持的位图格式

2-5 : 0x000088EA表示文件大小

6-9 : 保留段,为0

A-D : 0x00000436=1078,表示从文件头到位图数据需要偏移1078个字节

【注意】利用fopen打开文件,得到文件句柄读取相应的数据时,存在一个问题就是结构体内存对齐。这个会使得后续数据的读取出现错误。

何为内存对齐具体可以参考文章:结构体成员在内存中的对齐方式

上述问题的具体表现可以通过如下的代码进行实现:

#include <stdio.h>

typedef struct tagBITMAPFILEHEADER 
{  
    unsigned short bfType;    // 19778,必须是BM字符串,对应的十六进制为0x4d42,十进制为19778,否则不是bmp格式文件(0-4)
    int bfSize; // 文件大小 以字节为单位(2-5字节)                                                               (4-8)
    unsigned short bfReserved1; // 保留,必须设置为0 (6-7字节)                                                          (8-10)
    unsigned short bfReserved2; // 保留,必须设置为0 (8-9字节)                                                          (10-12)
    int bfOffBits;    // 从文件头到像素数据的偏移  (10-13字节)                                                (12-16)
} BITMAPFILEHEADER;

int main(int argc, char* argv[])
{
    printf("sizeof(BITMAPFILEHEADER) = %lu\n", sizeof(BITMAPFILEHEADER));
    return 0;
}

// sizeof(BITMAPFILEHEADER) = 16

那么如何关闭结构体的内存对齐 ,有两种方式:添加预处理指令 #pragma pack(1) 或者 __attribute__ ((packed))。这两种方式的区别在于:

  • #pragma pack(1)预处理指令,是关闭整个文件的内存对齐模式
  • __attribute__ ((packed))指令,则是针对指定的结构体
#include <stdio.h>

// #pragma pack(1)

typedef struct tagBITMAPFILEHEADER 
{  
    unsigned short bfType;    // 19778,必须是BM字符串,对应的十六进制为0x4d42,十进制为19778,否则不是bmp格式文件(0-4)
    int bfSize; // 文件大小 以字节为单位(2-5字节)                                                               (4-8)
    unsigned short bfReserved1; // 保留,必须设置为0 (6-7字节)                                                          (8-10)
    unsigned short bfReserved2; // 保留,必须设置为0 (8-9字节)                                                          (10-12)
    int bfOffBits;    // 从文件头到像素数据的偏移  (10-13字节)                                                (12-16)
}__attribute__ ((packed)) BITMAPFILEHEADER;

int main(int argc, char* argv[])
{
    printf("sizeof(BITMAPFILEHEADER) = %lu\n", sizeof(BITMAPFILEHEADER));
    return 0;
}

// sizeof(BITMAPFILEHEADER) = 14

3 位图信息头

位图信息头的结构体定义如下:

typedef struct tagBITMAPINFOHEADER
{
    unsigned int    biSize;          // 此结构体的大小 (14-17字节)
    long            biWidth;         // 图像的宽  (18-21字节)
    long            biHeight;        // 图像的高  (22-25字节)
    unsigned short  biPlanes;        // 表示bmp图片的平面属,显然显示器只有一个平面,所以恒等于1 (26-27字节)
    unsigned short  biBitCount;      // 一像素所占的位数,一般为24   (28-29字节)
    unsigned int    biCompression;   // 说明图象数据压缩的类型,0为不压缩。 (30-33字节)
    unsigned int    biSizeImage;     // 像素数据所占大小, 这个值应该等于上面文件头结构中bfSize-bfOffBits (34-37字节)
    long            biXPelsPerMeter; // 说明水平分辨率,用象素/米表示。一般为0 (38-41字节)
    long            biYPelsPerMeter; // 说明垂直分辨率,用象素/米表示。一般为0 (42-45字节)
    unsigned int    biClrUsed;       // 说明位图实际使用的彩色表中的颜色索引数(设为0的话,则说明使用所有调色板项)。 (46-49字节)
    unsigned int    biClrImportant;  // 说明对图象显示有重要影响的颜色索引的数目,如果是0,表示都重要。(50-53字节)
} BITMAPINFOHEADER;

 4 颜色表

颜色表本质上是一个查询表,通过颜色序号来查询对应的颜色。在文件中的布局类似一个二维数组palette[N][4]. 其中4个元素分别表示对应的B,G,R和Alpha的值,每个分量占一个字节。N一般等于256。

一共有256种颜色,每个颜色占用4个字节,就是一共1024个字节,再加上前面的文件信息头和位图信息头的54个字节加起来一共是1078个字节。

5. 位图数据

位图数据中,每个像素占一个字节,以该字节的数作为颜色索引去颜色表查询相应的颜色,并将其显示出来即可。

6. 参考链接

C实现BMP转JPG 附源码_等jzy的博客-CSDN博客_c语言bmp转jpg

用c、cpp实现位图(BMP)读入写出的思路及细节 - et3_tsy - 博客园 (cnblogs.com)

C语言实现BMP图像的读写功能_C 语言_脚本之家 (jb51.net)

利用C语言读取BMP文件 - 知乎 (zhihu.com)

BMP文件格式详解(BMP file format) - 姚伟峰 - 博客园 (cnblogs.com)

结构体成员在内存中的对齐方式 – My Code

  • 4
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值