pillow 的 `save(…, format=’ico’, sizes=…)` 并非直接按指定尺寸缩放,而是基于源图像宽高比进行等比缩放并截断,导致实际生成的 ico 尺寸与预期不符(如 192×192 变为 192×183)。根本解决方法是确保输入图像为正方形,并在必要时主动缩放/填充。
在使用 Pillow 生成多尺寸 .ico 文件时,开发者常误以为传入 sizes=[(16,16), (32,32), …, (256,256)] 即可强制生成完全匹配这些宽高的图标图像。但实际情况是:Pillow 的 ICO 保存器会对原始图像执行等比缩放(aspect-preserving resize),然后裁剪至目标尺寸的中心区域——这正是你观察到 (192, 192) 变成 (192, 183)、(96, 96) 变成 (96, 92) 等“尺寸偏移”的根本原因:源图非正方形(例如 256×244),缩放后无法完美填满所有目标尺寸的正方形画布。
✅ 正确做法是:在调用 img.save(…, format=’ICO’) 前,确保 img 是严格正方形(width == height)。可通过以下任一方式实现:
-
方案 1:缩放至最大目标尺寸(推荐)
将原图等比缩放到最大所需尺寸(如 256×256),再填充为正方形(若需保持比例);或直接拉伸(不推荐,失真)。 -
方案 2:填充(Pad)为正方形
使用 ImageOps.pad() 在短边添加透明/纯色背景,生成无失真的正方形源图:
from PIL import Image, ImageOps
def make_square(img: Image.Image, fill_color=(255, 255, 255, 0)) -> Image.Image:
"""将图像填充为正方形,保持原始比例,透明背景(支持 RGBA)"""
size = max(img.size)
return ImageOps.pad(img, (size, size), method=Image.LANCZOS, color=fill_color)
# 使用示例
with Image.open("input.png") as img:
if img.mode != "RGBA":
img = img.convert("RGBA") # 确保支持透明度
square_img = make_square(img)
# 现在可安全传入 sizes 参数
square_img.save("output.ico", format="ICO", sizes=[
(16, 16), (24, 24), (32, 32), (48, 48),
(64, 64), (96, 96), (128, 128), (256, 256)
], bitmap_format="bmp")
⚠️ 注意事项: