恰巧作业里有一道要对BMP进行卷积的题,所以干脆就写一篇文章来记录一下

BMP的基本常识

BMP取自位图Bitmap的缩写,也称为DIB(与设备无关的位图),是一种独立于显示器的位图数字图像文件格式。常见于微软视窗和OS/2操作系统,Windows GDI API内部使用的DIB数据结构与 BMP 文件格式几乎相同。
来自于此 BMP - Wikipedia


BMP的主要存储方式

此处主要用C中基本的文件指针操作来读写BMP文件,所以需要先了解BMP的文件构成(此处只列一些需要用的基本信息)。


偏移量 大小 用途
0x0000 2 Byte 用于标识BMP和DIB文件的前缀,一般为0x42 0x4D,即ASCII的BM(在Windows下)
0x000E 4 Byte BMP Header的大小,Header中存储着此BMP文件的基本信息,此处读取的是Header末尾距离文件头的偏移量
0x0012 4 Byte 位图的宽度,单位为像素(有符号整数)
0x0016 4 Byte 位图高度,单位为像素(有符号整数)
0x001C 2 Byte 每个像素所占位数,即图像的色深。典型值为1、4、8、16、24和32

所以接下来先创建文件指针

1
2
3
4
5
fpbmp = fopen(inputFileName, "rb");
if (fpbmp == NULL){
printf("Bad bmpPic!\n");
return 1;
}

读取Header

读取Header末尾距离文件头的偏移量放置在offset变量中

1
2
3
4
//get header size
unsigned int offset = 0;
fseek(fpbmp, 10L, SEEK_SET);
fread(&offset, sizeof(char), 4, fpbmp);

读取图片宽高

根据之前的偏移量,可以读取图片的宽高

1
2
3
4
5
//get width and height
fseek(fpbmp, 18L, SEEK_SET);
fread(&width, sizeof(char), 4, fpbmp);
fseek(fpbmp, 22L, SEEK_SET);
fread(&height, sizeof(char), 4, fpbmp);

读取图片位深

位深可以计算出图片的一行的宽度

1
2
3
//get bit depth
fseek(fpbmp, 28L, SEEK_SET);
fread(&bitdep, sizeof(char), 2, fpbmp);

拷贝Header内容 直接输出

1
2
3
4
5
6
unsigned char *fp_temp = NULL;
fseek(fpbmp, 0L, SEEK_SET);
fseek(fpout, 0L, SEEK_SET);
fp_temp = (unsigned char *)malloc(offset);
fread(fp_temp, 1, offset, fpbmp);
fwrite(fp_temp, 1, offset, fpout);

将指针移动到Header末尾 计算rowSize

1
2
3
4
fseek(fpbmp, offset, SEEK_SET);
//Read BMP into array
rowSize = (bitdep * width + 31) / 32;
rowSize *= 4;

读取一行的pixel

这里需要注意的是,他的存储方式非常神奇

RGB1 RGB2 RGB3

RGB4 RGB5 RGB6

RGB7 RGB8 RGB9

假设这里有一张3x3的点阵BMP

BMP中的存储方式是这样的
像素是从下到上、从左到右保存的 而且为GBR(我也不知道为什么怎么诡异)
读取前一定要移动指针到header的末尾才能进行读取

BGR7 BGR8 BGR9

BGR4 BGR5 BGR6

BGR1 BGR2 BGR3

1
2
3
4
5
6
7
8
9
10
11
pix = (unsigned char *)malloc(rowSize);
for (int j = 0; j < height; j++)
{
fread(pix, 1, rowSize, fpbmp);
for (int i = 0; i < width; i++)
{
R[height - j][i + 1] = pix[i * 3 + 2];
G[height - j][i + 1] = pix[i * 3 + 1];
B[height - j][i + 1] = pix[i * 3];
}
}

然后就可以对R G B 三个数组里面的数据进行处理,然后输出。
输出方式十分类似不再赘述。

完成

这篇文章参考了某位大佬的博客,时间比较久远所以忘记保存来源了,鉴于网上别的博客都比较复杂,不适合刚学文件指针的小朋友们,所以记录下之前自己写的简易版