文章目录
【内容概述】
- 图像压缩
- 在ubuntu系统下读出BMP图像的十六进制数据
- 分析24位真彩BMP文件的文件头、图像头、调色板、位图数据
- 截取图像中心32×32区域,并灰度化
- 分析灰度化后带来的改变
- 位图数据存储方式与像素位置的关系
接下来我们就开始叭 ~ (* ^ ω ^ *)
【图像压缩】
这是一个非必要步骤,如果你的原图很小就不用啦。但是如果原图很大的话,建议压缩一下,不然数据量会hin大的。可以用格式工厂做(有点大材小用嘞但是我首先就想到这个方法).具体步骤可参考这个说明。我通过压缩把原本4032×3016的图像压缩成128×95的了。看看这个变化叭:
【压缩前】
【压缩后】
【获取16进制数据】
这一步我是在Ubuntu系统下进行的,我把原图命名为【ballon.bmp】(因为是2019跨年时候在珠海长隆拍的小丑气球)在图片所在文件夹打开终端,输入$ xxd -i ballon.bmp ballon.h
并回车,具体操作可以参考这个说明。然后可以看到文件夹下新生成了一个.h文件,打开之后就可以看到十六进制的数组啦。
【文件头】
0x42, 0x4d, 0xb6, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00,
0x00, 0x00
BMP图像的文件头由14个字节组成。其中:
1-2:42 4D分别表示B和M,说明这是一个BMP格式的图像。
3-6:b6 8e 00 00,从低位到高位的寻址方式,十进制是36534,表示图片的大小,即所占的字节数。
7-10:00 00 00 00,特定应用程序使用,这里不用,默认为0.
11-14:36 00 00 00,十进制是54,实际位图数据开始的偏移地址。
【位图头】
0x28, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x5f, 0x00,
0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xc4, 0x0e, 0x00, 0x00, 0xc4, 0x0e, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
位图头由40个字节组成。其中:
1-4:28 00 00 00,十进制是40,表示图像头信息占用的字节数。
5-8:80 00 00 00,十进制是128,表示图像的宽度。
9-12:5f 00 00 00,十进制是95,表示图像的高度。
13-14:01 00,使用的彩色平面数,都为1.
15-16:18 00,十进制24,表示图像的位深度,24为真彩色。
17-20:00 00 00 00,规定像素位的掩码。
21-24:00 00 00 00,位图全部像素占用的字节数,24位为0.
25-28:c4 0e 00 00,十进制为3780,表示水平分辨率3780像素/米。
29-32:c4 0e 00 00,十进制为3780,表示垂直分辨率3780像素/米。
33-36:00 00 00 00,位图使用的颜色数,为0表示颜色数为2的位深度次幂。
37-40:00 00 00 00,重要的颜色数,0代表所有颜色都重要。
【调色板】
因为这个是24位的BMP图像,是没有调色板的,所以从第55个字节开始就是位图数据。这一点和前面的【文件头】里【第11-14字节】显示的【实际位图数据开始的偏移地址】为【54】相符。
顺带一提,如果是8位的图像,就会存在调色板,并且位图数据是从第1079位开始的。
【24位真彩图的位图数据】
即使是128×95大小的图像,数据还是很多的,我只截取一行(128×3=384个字节)的数据放出来看看。为什么要乘个3呢,因为每个像素是24位,所以一个像素需要三个字节来表示,三个字节依次表示的是B(blue)、G(green)、R(red)的值。
0x33, 0x38, 0x39, 0x31, 0x37, 0x32,
0x3f, 0x49, 0x49, 0x36, 0x40, 0x40, 0x2e, 0x37, 0x3a, 0x48, 0x46, 0x45,
0x37, 0x3e, 0x41, 0x37, 0x43, 0x43, 0x3a, 0x43, 0x47, 0x2e, 0x36, 0x36,
0x33, 0x39, 0x34, 0x37, 0x38, 0x3c, 0x41, 0x40, 0x42, 0x36, 0x3a, 0x3f,
0x3b, 0x47, 0x47, 0x37, 0x3e, 0x41, 0x39, 0x3c, 0x41, 0x35, 0x3b, 0x42,
0x3b, 0x44, 0x47, 0x38, 0x47, 0x4a, 0x51, 0x6e, 0x77, 0x52, 0x7f, 0x8a,
0x44, 0x62, 0x67, 0x33, 0x3a, 0x3d, 0x30, 0x3b, 0x3f, 0x45, 0x67, 0x66,
0x3e, 0x67, 0x70, 0x3b, 0x65, 0x6a, 0x33, 0x39, 0x44, 0x37, 0x3d, 0x42,
0x37, 0x42, 0x46, 0x37, 0x43, 0x47, 0x3d, 0x49, 0x4d, 0x47, 0x51, 0x58,
0x41, 0x4e, 0x56, 0x49, 0x54, 0x58, 0x3d, 0x4d, 0x54, 0x33, 0x3c, 0x45,
0x3d, 0x42, 0x45, 0x30, 0x27, 0x24, 0x35, 0x28, 0x2a, 0x36, 0x30, 0x31,
0x30, 0x27, 0x24, 0x31, 0x25, 0x23, 0x2f, 0x26, 0x23, 0x33, 0x46, 0x4e,
0x39, 0x4f, 0x61, 0x33, 0x48, 0x5e, 0x31, 0x47, 0x52, 0x40, 0x5d, 0x6c,
0x2b, 0x47, 0x7d, 0x30, 0x4e, 0xad, 0x2e, 0x50, 0xa9, 0x31, 0x60, 0xb1,
0x2b, 0x5f, 0xb2, 0x37, 0x5e, 0xb3, 0x36, 0x66, 0xb4, 0x5c, 0x8e, 0xca,
0x67, 0x9b, 0xd0, 0x70, 0x9c, 0xd7, 0x7c, 0xa4, 0xd4, 0x84, 0xa9, 0xdb,
0x82, 0xac, 0xdb, 0x93, 0xb6, 0xe2, 0x9f, 0xbe, 0xe5, 0x9f, 0xbd, 0xe6,
0xa0, 0xbd, 0xe2, 0x92, 0xb9, 0xdf, 0xa1, 0xbe, 0xe5, 0x9f, 0xb5, 0xdf,
0x96, 0xae, 0xdc, 0x83, 0xa3, 0xd4, 0x71, 0x88, 0xc0, 0x64, 0x7f, 0xb8,
0x35, 0x77, 0xac, 0x24, 0x79, 0xa6, 0x24, 0x98, 0xb7, 0x23, 0xa0, 0xbc,
0x1f, 0x9c, 0xb8, 0x23, 0xa4, 0xbd, 0x23, 0xae, 0xc1, 0x1f, 0xb2, 0xc2,
0x1f, 0xad, 0xbe, 0x21, 0xad, 0xbe, 0x20, 0xab, 0xbe, 0x20, 0xaa, 0xbc,
0x20, 0xa7, 0xbb, 0x21, 0xaa, 0xba, 0x21, 0xa4, 0xb2, 0x27, 0xa4, 0xb2,
0x2d, 0x6e, 0xa5, 0x33, 0x74, 0xab, 0x47, 0x80, 0xb7, 0x3d, 0x82, 0xbb,
0x32, 0x42, 0xa2, 0x21, 0x9e, 0xad, 0x1e, 0xa5, 0xb3, 0x1f, 0xad, 0xb8,
0x1c, 0xb6, 0xc1, 0x22, 0xbd, 0xc6, 0x23, 0xb6, 0xc0, 0x22, 0xb5, 0xbf,
0x20, 0xb2, 0xbe, 0x21, 0xae, 0xbb, 0x21, 0xb8, 0xc6, 0x23, 0xb2, 0xc1,
0x24, 0x9c, 0xb2, 0x21, 0x9d, 0xb5, 0x22, 0x99, 0xb2, 0x21, 0x9b, 0xb3,
0x20, 0x9c, 0xb4, 0x26, 0xa6, 0xbd, 0x22, 0xa0, 0xb9, 0x21, 0xa8, 0xbc,
0x21, 0xaa, 0xba, 0x26, 0x39, 0x40, 0x34, 0x33, 0x3c, 0x34, 0x38, 0x43,
0x39, 0x42, 0x46, 0x3d, 0x40, 0x45, 0x41, 0x43, 0x4e, 0x3b, 0x3d, 0x45,
0x40, 0x34, 0x32, 0x3b, 0x2d, 0x2e, 0x42, 0x33, 0x31, 0x37, 0x35, 0x34,
0x40, 0x34, 0x32, 0x3e, 0x37, 0x3a,
【图像灰度化】
要分析亮度和数据值的关系,就需要把图像灰度化。为了减少数据量(实际就是偷懒),我们在灰度化之前,先裁剪一下图片。
- 图片裁剪
一般来说,图片中间的颜色变化会比图片边缘的明显一些。我们在matlab里用imcrop函数对原图像裁剪,得到中间32×32的区域。
%将ballon.bmp裁剪成32×32大小并保存为cutballon.bmp
x1 = imread('ballon.bmp');
x2=imcrop(x1,[48,29,31,31]);
figure
imshow(x2);
imwrite(x2,'cutballon.bmp');
运行代码之后我们就可以在目录下看到新生成了”cutballon.bmp".
我们继续在matlab里,把彩色图像变成灰度图像(就是常说的“黑白图像”)。
%把裁剪后的图像灰度化
x1 = imread('cutballon.bmp');
x2=rgb2gray(x1);
imwrite(x2,'greyballon.bmp');
运行代码之后我们就可以在目录下看到新生成了”greyballon.bmp".
- 读取greyballon.bmp的十六进制数据
像前面读取ballon.bmp一样,在Ubuntu系统里获取到greyballon.h,打开之后长这样:
【灰度化后数据格式的变化】
- 我们注意到,这个新的数据中,文件头第11-14位(红色框部分)的数据变成了:36 04 00 00,十进制为1078,而前面分析时候得到的是54,说明经过matlab的灰度化之后,这个图象的位图数据起始位置变了。
- 再看位图头的第15-16位(绿色框部分)数据变成了:08 00,十进制为8,说明图片的位深度变成了8。那么在后面的位图数据中,一个字节就表示一个像素。
- 结合分析,不难猜出,因为位深度的变化,让图片的信息数据中多了【调色板】(文件头和位图头都是固定不变的长度),24位图无调色板,而8位图则存在调色板,且调色板占用的字节数为1024.
【读32×32灰度图片的位图数据】
从第1079个字节开始,就是我们的位图数据了。Ubuntu是默认一行显示12个字节吗?。。。我也不知道能不能调整,这样数下去在91行63列的位置,往后就是位图数据了。
位图数据有多少个字节呢,我们算一算,一个像素占一个字节的话,应该是32×32=1024个。而1024=2+85×12+2,所以从91行最后两个字节一直到177行头两个字节,都是位图数据。
从上面的图片也可以看到,第177行只有两个字节,之后这整个数据就结束了。
所以BMP图像的十六进制数据就是【文件头】+【位图头】+【调色板】+【位图数据】。
【位图数据的存储和像素位置的关系】
【手动调整一行显示的数据】
一行12个字节的话,我们很难直观地和原图对比(因为原图一行32个像素),所以就调整一下,以一行32个字节的方式呈现。顺便把每个字节前面的“0x”去掉,只剩下十六进制的值。(害,这个调整过程超级烦的,所以就不具体说明,大家需要的话评论一下我再加叭)
这样看就清爽多了:
默认一个知识:数值越大,对应像素的亮度越大。我们把数据做以下处理:
- 把亮度值大于176(十六进制大于B0)的用”–”代替。
分析:
从数据图像上看到,亮度大于176的区域主要集中在图片中间,分为偏右上的一个圆形和中间偏下的一个类似椭圆形。而直观从原始图像上看(下图经过放大处理),并没有找到合适的绝对对应关系。
但是换个角度,把图片做“上下颠倒”,会发现对应亮度的位置大致是吻合的。可以初步断定,位图数据的存储,是从图片的左下方开始的,并一直到右上方结束。
- 把亮度值小于48(十六进制小于30)的用”–”代替。
分析:
再从亮度小的区域佐证一下上面的判断。直观上看,最暗的区域在左下方,即红色圈出来的区域。但在位图数据上,看到的亮度值最小的区域却是在左上方,如下图所示。不难想象把图片上下颠倒后,就符合预设,这就验证了先前的猜想。
【总结】
BMP图像的数据可以解析成四部分,分别是文件头(14字节)、图像头(40字节)、调色板和位图数据。其中特别需要注意的是,位图数据是从最后一行最左边的像素开始,往右记录到尽头,然后回到倒数第二行的最左像素,从左往右、从下往上,直至记录完第一行的最右一个像素。
来源:CSDN
作者:泼夫
链接:https://blog.csdn.net/Clover_pofu/article/details/104768442