上次介绍了如何用python生成bmp图片,今天我们继续bmp的话题:用python读取bmp图片。
读取bmp文件头
从上一篇文章,我们知道bmp文件头就是开始的54字节。这54字节中包含了分辨率、rgb数据的偏移量、rgb数据的大小、文件大小等信息。
我们先用二进制方式读取文件的前54字节,再用struct.unpack()
来解析数据。如下:
f = open("save1.bmp", 'rb')
bmp_header_b = f.read(0x36)
print(bmp_header_b)
print('')
bmp_header_s = struct.unpack('<2sI2H4I2H6I', bmp_header_b)
print(bmp_header_s)
print('')
以上篇文章生成的500x500的图片作为测试,输出结果如下:
struct.unpack的参数'<2sI2H4I2H6I'中的<表示低字节在前,s表示按字符解析,I按无符号整数(4bytes),H按无符号的短型(2bytes)。s、I、H前的数字表示重复次数,4I就表示4个32位的数。
详见官方文档:
https://docs.python.org/3/library/struct.html
struct.unpack的返回值是一个元组(只读型的列表)。在这个例子中,bmp_header_s[0]就是b'BM',bmp_header_s[6]和bmp_header_s[7]就是分辨率的宽和高。可见struct.unpack很适合用来做bytes型数据流的解析。
把元组转换成class对象
class bmp:
"data structure"
def __init__(self, tp):
self.tag = tp[0]
self.fileSize = tp[1]
self.rgbOffset = tp[4]
self.infoSize = tp[5]
self.width = tp[6]
self.height = tp[7]
self.pane = tp[8]
self.color = tp[9]
self.compress = tp[10]
self.rgbSize = tp[11]
def print_bmp_header(self):
print("tag :{}".format(self.tag))
print("fileSize :{}".format(self.fileSize))
print("rgbOffset:{}".format(self.rgbOffset))
print("infoSize :{}".format(self.infoSize))
print("width :{}".format(self.width))
print("height :{}".format(self.height))
print("pane :{}".format(self.pane))
print("color :{}".format(self.color))
print("compress :{}".format(self.color))
print("rgbSize :{}".format(self.rgbSize))
if __name__ == '__main__':
image = bmp(bmp_header_s)
image.print_bmp_header()
print('')
我们把bmp图片文件头的基本信息定义到class bmp里,用__init__函数里进行转换。在后面的操作中可以用image.width、image.height等来调用,比较方便。
读取rgb数据
bmp_rgb_data_b = f.read()
list_b = array.array('B', bmp_rgb_data_b).tolist()
rgb_data_3d_list = numpy.reshape(list_b, (image.height, image.width, 3)).tolist()
第一步,接前面读取54字节bmp文件头的操作,f.read()
不带参数即可读取剩余的所有字节。返回值是bytearray的类型。二进制文件、网络协议等操作函数都是bytearray类型。
第二步,把bytearray转换成list,再用numpy.reshape
转换成多维列表。这里,我们转换成了(height, width, 3)
的三维数据。第一和第二维是高和宽,第三个维度就是一个像素的blue、green、red。
上面的代码打印第一个和最后一个像素,结果如下:
print(rgb_data_3d_list[0][0])
print(rgb_data_3d_list[image.height-1][image.width-1])
总结
这篇文章简要介绍了读取bmp图片的方法、bytearray与list的转换、list的维度转换等。经过解析后把rgb数据读取到了一个3维列表里,方便后续处理。
注意:
需要import struct、numpy、array等package。
在读取rgb数据时没有考虑一行字节数非4整倍数的情形。
python读取图片有更好用的库。本文仅用来学习,实际项目最好不要重复造轮子。