上次介绍了如何用python生成bmp图片,今天我们继续bmp的话题:用python读取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位的数。
struct.unpack的返回值是一个元组(只读型的列表)。在这个例子中,bmp_header_s[0]就是b'BM',bmp_header_s[6]和bmp_header_s[7]就是分辨率的宽和高。可见struct.unpack很适合用来做bytes型数据流的解析。
class bmp:
"data structure"
def __init__(self, tp):
self.tag = tp[0]
self.fileSize = tp[1]
#reserved tp[2]
#reserved tp[3]
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]
#reserved tp[12]
#reserved tp[13]
#reserved tp[14]
#reserved tp[15]
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等来调用,比较方便。
# bytearray
bmp_rgb_data_b = f.read()
# bytearray -> list
list_b = array.array('B', bmp_rgb_data_b).tolist()
# reshape -> 3d list
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。
上面的代码打印第一个和最后一个像素,结果如下:
# first & last pixels
print(rgb_data_3d_list[0][0])
print(rgb_data_3d_list[image.height-1][image.width-1])
# [0, 255, 0]
# [0, 255, 0]
这篇文章简要介绍了读取bmp图片的方法、bytearray与list的转换、list的维度转换等。经过解析后把rgb数据读取到了一个3维列表里,方便后续处理。
注意: