首先如何使用 read() 读取文件内容:

  1. def raw_loader(path):
  2. """A loader for loading raw file content.
  3. Args:
  4. @param path: str, the file path.
  5. Return:
  6. the content of the file.
  7. """
  8. try:
  9. content = b''
  10. with open(path, 'rb') as f:
  11. while True:
  12. tmp = f.read(4096)
  13. if not tmp: break
  14. content = content + tmp
  15. return content
  16. except IOError as e:
  17. return None

一般博客文章会建议使用一次 read() 直接读取文件全部内容,这里为了避免出现文件内容不能读全的可能,采用循环读取的方式每次从文件读取 4k 个字节的数据,最终返回 bytes 类型的文件内容。

下面说几个直接从文件读取图片数据会遇到的问题和困惑:(我们假设待读取的图片为 tmp_batch.png , RGBA , 大小 84 x 84*3

1. 字节数少于预期字节数

对于一张 84 x 84*3RGBA 图片,其数据量应为 84*84*3*4=197568 个字节数,但我们使用 raw_loader 函数直接读取的到的数据流字节数却少于预期。

  1. im_raw = raw_loader('tmp_batch.png')
  2. print(len(im_raw), type(im_raw))
  3. # output
  4. # 109409 <class 'bytes'>

可以看到读取出来的字节流只包含 109409 个字节,明显少于 197568.

但如果使用PIL读取图片文件的话,可以得到预期长度的字节流。

  1. from PIL import Image as PILImage
  2. im = PILImage.open('tmp_batch.png')
  3. print(im.size)
  4. imbytes = im.tobytes()
  5. print(len(imbytes), type(imbytes))
  6. # output
  7. # (84, 588)
  8. # 197568 <class 'bytes'>

这种字节数量上的差异可能是由数据编码格式导致的,数据格式会对数据进行一定程度的压缩。通过 ls -lh 可以看到读取的raw文件内容在字节数量上和 ls 显示的文件实际占有的存储空间一致。

2. 不能使用 PIL.Image.frombytesraw_loader 读取的内容重建图片

  1. PILImage.frombytes('RGBA', (84, 588), im_raw)
  2. # 会报错

3. 正确从raw内容重建图片的方法

  1. from io import BytesIO
  2. im = PILImage.open(BytesIO(im_raw))
  3. print(type(im), im.size)
  4. # output
  5. # <class 'PIL.PngImagePlugin.PngImageFile'> (84, 588)