用c、cpp实现位图(BMP)读入写出的思路及细节

部分资料来自于:

https://blog.csdn.net/mvtechnology/article/details/9008499

https://blog.csdn.net/zhangquan2015/article/details/80160864

https://blog.csdn.net/Ink_cherry/article/details/73610709

https://blog.csdn.net/weixin_42164528/article/details/80594667

BMP介绍

BMP(全称Bitmap)是Windows操作系统中的标准图像文件格式,可以分成两类:设备相关位图(DDB)和设备无关位图(DIB)

BMP文件所占用的空间很大

图像深度可选1bit、4bit、8bit及24bit

注意,BMP文件存储数据时,

图像的扫描方式是按 从左到右、从下到上 的顺序。

由于BMP文件格式是Windows环境中交换与图有关的数据的一种标准,

因此在Windows环境中运行的图形图像软件都支持BMP图像格式

通道的概念

图像的通道指的是什么?

基本上,描述一个像素点,如果是灰度,那么只需要一个数值来描述它,就是单通道

如果一个像素点,有RGB三种颜色(红绿蓝)来描述它,就是三通道

当然也有四通道的说法,R、G、B加上一个A通道,表示透明度

图像深度

深度,基于通道数,表示存储每个像素所用的位数

最常见的就是24位图,所谓的24位图,就是说一个像素的颜色信息用24位来表示,也就是说,对于三原色BRG每一个颜色都用以字节(8)位来表示

除了24位图,还有1位(单色),2位(4色,CGA),4位(16色,VGA),8位(256色),16位(增强色),24位(真彩色)和32位等

而32位正是上面所说的四通道,即增加了Alpha值(透明度)的

BMP中数据存储方式

先解释下数据存贮方式:

(1)在windows中,颜色顺序是:B G R

(2)BMP的内存行顺序和图像显示的行顺序是上下颠倒的。

(这就是前面为什么要强调扫描顺序是从下到上。这句话通俗地讲,BMP内存的第0行,是真实图像下面的最后一行)

举个栗子,假如图像为 2*2 大小,像素三颜色按照RGB的顺序, 我们看到的图像为:

1 2 3, 11 22 33

4 5 6, 44 55 66

但是在内存中,表示如下:

6 5 4, 66 55 44 (0 0) -- 第0行

3 2 1, 33 22 11 (0 0) -- 第1行

注意,通常内存是需要内存对齐的,所以每行后面可能会有对齐所产生的0.

BMP文件的数据

BMP文件的数据按照从文件头开始按内存的先后顺序分为以下四个部分:

文件头、信息头、调色板和数据域

我们读入BMP的时候,类的格式按照这四块写好即可

(1)**header 文件头 共14字节 **

提供文件的格式、大小等信息

(2)infoheader 信息头 共40字节

提供图像数据的尺寸、位平面数、压缩方式、颜色索引等信息

(3)optional palette 可选调色板(24位的不需要,可选)

使用索引来表示图像,调色板就是索引与其对应的颜色的映射表

(4)**image data 数据域 **

图像像素信息的数据,包括:高度,宽度,图像通道数,像素信息


(1)文件头 header 14字节

文件头包含如下信息:

bfType:2字节,文件类型;

bfSize:4字节,文件大小;

bfReserved1:2字节,保留,必须设置为0;

bfReserved2:2字节,保留,必须设置为0;

bfOffBits:4字节,从头到位图数据的偏移;

下面同理

(2)位图信息头 bitmap information 40字节

biSize:4字节,信息头的大小,即40;

biWidth:4字节,以像素为单位说明图像的宽度

biHeight:4字节,以像素为单位说明图像的高度,(它可能是负数

biPlanes:2字节,为目标设备说明颜色平面数,总被设置为1

biBitCount:2字节,说明比特数/像素数,值有1、2、4、8、16、24、32;

注意这个biBitCount就是指他的每个像素用8多少位表示,即 图像深度

biCompression:4字节,说明图像的压缩类型,最常用的就是0(BI_RGB),表示不压缩

biSizeImages:4字节,说明位图数据的大小,当用BI_RGB格式时,可以设置为0;

biXPelsPerMeter:表示水平分辨率,单位是像素/米,有符号整数;

biYPelsPerMeter:表示垂直分辨率,单位是像素/米,有符号整数;

biClrUsed:说明位图使用的调色板中的颜色索引数为0说明使用所有

biClrImportant:说明对图像显示有重要影响的颜色索引数,为0说明都重要;

(3)调色板 optional palette

注意:有的图像没有调色板,比如24、32位色图

调色板是可选的,不过这里讲的是8位色图有调色板

调色板就是一个颜色的索引,8位色图,一共有256种颜色,由于每个颜色都有RGB三原色,也就是要3个字节表示,这样的话256个颜色就不能表示所有的颜色。

所以就需要一个索引,用一个字节的索引指向4个字节表示的颜色(RGB加上Alpha值)。如果把这4个字节表示为一个Color类型,那么调色板就是Color的数组

由于Color类型也是一个数组,调色板就像一个二维数组palette[N][4]

其中N是颜色的数量,这里就是256。因此,这个例子中的调色板的大小就是256x4=1024字节,


(4)位图数据域 image data

最为重要的一块

注意区分情况

1)8位色图

由于是8位色图,所以每个像素用1个字节表示,取出每个字节,显示到相应的设备上就可以了。

注意,这里的biHeight为正数,说明图像倒立,从左下角开始到右上角,以行为主序排列。

2)24位色图

按照BGR的顺序排列,即三色

3)32位色图

按照BGRAlpha排列,即三色+透明度

一般就是这上面三种情况,下面要介绍对齐规则


注意点

内存对齐

如果不懂什么是内存对齐,请看本人的另外的一篇BLOG:

https://www.cnblogs.com/et3-tsy/p/12918932.html

这基于这一点,我们本来应该是14字节的结构体很容易就成了16字节

对此,我们定义结构体的时候有两种处理方式:

1)#pragma pack(1)

取消编译器的字节对齐,从而让结构体的实际内存成为理想中的14字节

2)单独读文件类型

把文件类型单独读入,这样做即可以巧妙回避内存对齐这一问题,又可以方便校验文件,从而防止异常读入

BMP生成规则

Windows默认的扫描的最小单位是4字节BMP图像顺应了这个要求

也就是说,每行的数据内存必须要是4字节的倍数,BMP才能正常显示

所以,

读入时-->我们要忽视它用于填充的0

写出时-->我们要补上它用于填充的0,使得每行的内存是4字节的倍数

posted @ 2020-05-23 22:27  et3_tsy  阅读(1347)  评论(0编辑  收藏  举报