TIFF 按文件名打开后图像错乱,文件对象却正常
带方向 5–8 的非方形 TIFF 可能在 Pillow 内存映射中用错尺寸。通过显式二进制文件对象绕过路径 mmap,检查 load 前后尺寸与方向。
一张 TIFF 通过文件名打开后,load() 前后尺寸互换,像素也被打乱;同一文件用二进制文件对象读取却正常。若 TIFF 带有方向 5、6、7 或 8,应检查内存映射使用的存储尺寸与显示尺寸。
这条路径有 Pillow 11.0.0、12.3.0 和开发版本的记录,正式 13.0.0 相关实现仍可涉及。触发条件较窄:非正方形、未压缩、单 strip 或 tile、可被 Pillow 映射的模式,并且按路径打开。L、P、RGBA、CMYK、I;16 等模式可进入相应路径;不能将它扩大成所有带方向标签的 TIFF 都会坏。
同一个文件比较两种打开方式
路径读取可能让 ImageFile.load() 直接映射文件字节。TIFF 的方向 5–8 会使显示宽高与存储宽高互换;若映射按显示宽度解释存储行,后续再自动处理方向,得到的就不是简单旋转,而是已错排的像素。
先保留原文件,不要覆盖。记录格式、模式、压缩、条带或 tile 数量、方向标签,以及 load() 前后的尺寸。最后一行错误也可能表现为 box can't exceed original image size,特别是在显式加载前就做 resize() 或 thumbnail() 时。
用显式文件对象避开路径映射
对受影响的读取路径,可以让 Pillow 从已打开的二进制文件对象解码:
from PIL import Image
with open("input.tiff", "rb") as stream:
with Image.open(stream) as image:
image.load()
pixels = image.copy()
input.tiff 换成原文件。先加载再复制,离开上下文后使用 pixels,避免数据尚未读取就关闭底层流。该片段针对当前选中的一帧;多页 TIFF 需要按原流程逐帧选择、加载和保存,不能将单帧副本当成整份文件。
这条 TIFF 解码路径会自动处理方向,不要再按同一个方向标签盲目旋转一次。副本也不保证带上所有 TIFF 专属标签、分辨率、ICC、EXIF 或多页信息;应用需要这些元数据时,应单独保留、传递并验收。
已经错排的像素不能靠旋转复原
如果之前生成了坏 PNG 或其他转码结果,重新旋转那张坏图不会恢复按错行宽读取的信息。应从保留的 TIFF 原件重新解码,并输出到新位置。也不要为了读取成功将 Orientation 清成 1,或先压成有损格式,这会改变图像解释和质量。
改用 BytesIO 也可以改变读取路径,但会把文件内容读进内存;对于大 TIFF,显式文件对象通常更容易保持原有内存预算。无论走哪条路径,都不能关闭解压缩炸弹检查或截断文件检查来掩盖其他错误。
验收用非方形、各方向及实际模式的样本,核对最终尺寸、可辨认的方向标记、关键像素和未受影响对照。压缩、多条带或正方形文件可能恰好不暴露这个问题,因此只拿它们验证不足以覆盖原触发条件。
后续更新正式版本时,检查映射尺寸与长度校验是否共同按存储结构处理,再复查原文件。不能仅因问题关闭就移除绕行,也不能把同样按路径读取失败的 PSD、MIC 等其他格式全归成 TIFF 方向问题。


