首先如何使用 read() 读取文件内容:
def raw_loader(path):"""A loader for loading raw file content.Args:@param path: str, the file path.Return:the content of the file."""try:content = b''with open(path, 'rb') as f:while True:tmp = f.read(4096)if not tmp: breakcontent = content + tmpreturn contentexcept IOError as e:return None
一般博客文章会建议使用一次 read() 直接读取文件全部内容,这里为了避免出现文件内容不能读全的可能,采用循环读取的方式每次从文件读取 4k 个字节的数据,最终返回 bytes 类型的文件内容。
下面说几个直接从文件读取图片数据会遇到的问题和困惑:(我们假设待读取的图片为 tmp_batch.png , RGBA , 大小 84 x 84*3 。
1. 字节数少于预期字节数
对于一张 84 x 84*3 的 RGBA 图片,其数据量应为 84*84*3*4=197568 个字节数,但我们使用 raw_loader 函数直接读取的到的数据流字节数却少于预期。
im_raw = raw_loader('tmp_batch.png')print(len(im_raw), type(im_raw))# output# 109409 <class 'bytes'>
可以看到读取出来的字节流只包含 109409 个字节,明显少于 197568.
但如果使用PIL读取图片文件的话,可以得到预期长度的字节流。
from PIL import Image as PILImageim = PILImage.open('tmp_batch.png')print(im.size)imbytes = im.tobytes()print(len(imbytes), type(imbytes))# output# (84, 588)# 197568 <class 'bytes'>
这种字节数量上的差异可能是由数据编码格式导致的,数据格式会对数据进行一定程度的压缩。通过 ls -lh 可以看到读取的raw文件内容在字节数量上和 ls 显示的文件实际占有的存储空间一致。
2. 不能使用 PIL.Image.frombytes 从 raw_loader 读取的内容重建图片
PILImage.frombytes('RGBA', (84, 588), im_raw)# 会报错
3. 正确从raw内容重建图片的方法
from io import BytesIOim = PILImage.open(BytesIO(im_raw))print(type(im), im.size)# output# <class 'PIL.PngImagePlugin.PngImageFile'> (84, 588)
