Pillow contain 缩小细长图片,报 height and width must be > 0
Pillow 缩小极细图片时,短边取整可能为零。12.3.0 会抛异常,13.0.0 可返回零尺寸;需要可见缩略图时应另定最小像素规则。
将一张 100×1 图片缩进 10×10 范围,ImageOps.contain() 在 Pillow 12.3.0 中可能报 ValueError: height and width must be > 0。输入图片并不为空;按比例算出的高度只有 0.1 像素,取整后变成了 0。
from PIL import Image, ImageOps
source = Image.new("RGB", (100, 1), "white")
result = ImageOps.contain(source, (10, 10))
这段例子用于说明尺寸条件。相同风险也可能发生在极窄的竖图,不能只对高度写一个特殊分支。
先决定零像素结果在业务中代表什么
contain() 的目标是保持原比例并落在给定范围内。离散像素无法精确表示所有比例;这个例子的等比高度小于半个像素,四舍五入后就是零。
Pillow 13.0.0 已能在这条路径返回零尺寸图像,而不是继续把零尺寸交给 resize() 报错。但结果可以是 10×0,并不是自动得到一张 10×1 图片。后续编码器、图片上传服务或展示组件仍可能拒绝零尺寸。13.0.0 还要求 Python 3.11 或更新版本,升级前应核对运行环境。
如果业务允许无可见像素的结果,应明确处理这个分支,例如保留原件、跳过缩略图输出并记录原因。不要把“调用没有异常”直接当作缩略图可用。
需要至少一个像素时,显式采用自己的取整规则
对必须生成可见缩略图的应用,可以把两个目标维度的下限设为 1。下面只处理原图和边界均为正整数的普通图像;它会在极端比例下牺牲精确比例,换取至少一个像素:
from PIL import Image
def visible_thumbnail(image, bounds):
max_width, max_height = bounds
if image.width <= 0 or image.height <= 0:
raise ValueError("source dimensions must be positive")
if max_width <= 0 or max_height <= 0:
raise ValueError("bounds must be positive")
scale = min(max_width / image.width, max_height / image.height)
size = (
max(1, min(max_width, round(image.width * scale))),
max(1, min(max_height, round(image.height * scale))),
)
return image.resize(size, Image.Resampling.LANCZOS)
调用方负责传入整数边界。这是应用自己的尺寸政策,不能称为 contain() 原有行为;若不允许放大原图,还应将缩放比例限制在 1 以内。
ImageOps.pad() 也不能保证极细内容仍可见。新版本可以创建完整的背景画布,但缩放后的内容若已是零像素,画布可能只剩背景。若需求是保留原比例且看见全部细线,应考虑更大的目标尺寸或在源图上先增加边距,而不是仅换一个函数名。
验收保留横向细图、纵向细图和普通图片,检查输出尺寸、方向、是否落在边界内,以及细线是否符合展示要求。保存到新位置后重新打开,确认实际编码格式接受这份输出;失败时保留原图,避免覆盖唯一原件。对于多帧图片,这个单图示例也没有处理逐帧内容、时长和动画信息,不能直接充当动画缩略图流程。


