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)
但是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.save
不写gamma
?
它不是 gamma
特有的,没有其他块像 chromaticity
和 text
那样写。
sample.png
与 output.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
关键字参数,因此它被默默地忽略了。与记录完全一致:
- PIL 的 PNG file format documentation:
下引用行后未提及 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.
我想阅读、操作然后保存 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)
但是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.save
不写gamma
?
它不是 gamma
特有的,没有其他块像 chromaticity
和 text
那样写。
sample.png
与 output.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
关键字参数,因此它被默默地忽略了。与记录完全一致:
- PIL 的 PNG file format documentation: 下引用行后未提及
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.