Pillow 的保存方法忽略信息参数

Pillow's save method ignores info parameters

我想阅读、操作然后保存 png 图像,同时主要保持其原始 info 属性 gamma。我正在使用 Pillow 版本 9.0.1

SO 中的代码提到可以这样做:

from PIL import Image
img = Image.open("sample.png") # Sample image provided below code block
info = img.info
img.save("output.png", **info)

Sample image

但是info不会继续;由于 gamma 信息丢失,图像不再相似,使用测试进一步证明:

# Test
output_info = Image.open("output.png").info
print(info)                  # {'gamma': 2147.48365}
print(output_info)           # {}
print(output_info == info)   # False, should be True

问题:为什么PIL的Image.savegamma

它不是 gamma 特有的,没有其他块像 chromaticitytext 那样写。


sample.pngoutput.png 相比(使用支持 gAMA 的查看器查看,如 chromium)

这是一种无需使用任何外部工具即可完成您所要求的方法。它插入一个 gAMA 块,如下所示:

  • 它获取 PIL 将图像编码为 "in-memory" PNG
  • 它以二进制模式打开磁盘上的输出文件
  • 然后它采用 PIL 创建的 8 字节 PNG 签名并将其写入磁盘
  • 同样由 PIL
  • 创建的 25 字节 IHDR
  • 然后它用你的值创建一个 gAMA 块,在长度前加上一个 CRC 并写入磁盘
  • 然后它将 PIL 生成的图像的剩余部分复制到磁盘

注意:PNG规范规定签名必须在前,然后是IHDR块。它还规定 gAMA 必须在 IDAT 之前出现,因此我的代码没有理由创建无效的 PNG,除非:

  • PIL 突然开始生成 gAMA 块,这将导致多个这样的块并且是非法的 - 但是我们这样做只是因为 PIL 不写 gAMA 块,或者

  • 提供了不合理的 gamma 值,或者

  • 写入 on-disk PNG 的某些方面由于磁盘已满或其他 I/O 错误而失败。


#!/usr/bin/env python3

import zlib
import struct
from io import BytesIO
from PIL import Image

def savePNGwithGamma(im, filename, gamma):
   """Save the image as PNG with specified gamma"""

   # Encode as PNG into memory buffer
   buffer = BytesIO()
   im.save(buffer, 'PNG')

   # Seek back to start of in-memory PNG
   buffer.seek(0)
   # Open output file in binary mode
   with open(filename, 'wb') as fd:
      # Read 8-byte PNG signature from memory and write to on-disk PNG
      signature = buffer.read(8)
      fd.write(signature)
      # Read 25-byte PNG IHDR chunk from memory and write to on-disk PNG
      IHDR = buffer.read(25)
      fd.write(IHDR)

      # Create our lovely new gAMA chunk - https://www.w3.org/TR/2003/REC-PNG-20031110/#11gAMA
      # Network byte ordering, scale factor of 100000
      gAMA = b'gAMA' + struct.pack('!I',int(gamma*100000))
      # 4-byte length, gAMA chunk, 4-byte CRC
      gAMA = struct.pack('!I', 4) + gAMA + struct.pack('!I', zlib.crc32(gAMA))
      # Insert into on-disk PNG after IHDR and before anything else such as PLTE, IDAT, IEND
      fd.write(gAMA)

      # Write remainder of PNG from memory to disk
      fd.write(buffer.read())

################################################################################
# main
################################################################################

# Load sample image
im = Image.open('sample.png')

# Save with desired gamma
savePNGwithGamma(im, 'result.png', 2.2)

看来我的预期是错误的。 PIL 的 PNG 文件编写器不支持 gamma 关键字参数,因此它被默默地忽略了。与记录完全一致:

The save() method supports the following options:

  • Image.save 忽略无法识别的关键字参数:

Keyword options can be used to provide additional instructions to the writer. If a writer doesn’t recognise an option, it is silently ignored.