
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")⚠️ 注意事项:
- bitmap_format="bmp" 是 Windows ICO 的标准要求,必须显式指定;
- quality=100 和 subsampling=0 对 ICO 格式无效(仅 JPEG 生效),可安全移除;
- 若原图不含 Alpha 通道,填充时建议用 (255, 255, 255)(白底)避免透明边缘异常;
- Pillow ≥10.0 默认使用高质量 Lanczos 重采样,无需额外配置。
? 验证生成结果:使用命令行工具(如 ImageMagick)检查实际尺寸:
identify -format "%wx%h " output.ico # 输出应为:16x16 24x24 32x32 ... 256x256
总结:Pillow 的 sizes 参数并非“指令式尺寸列表”,而是“目标尺寸集合”——其实际渲染依赖于输入图像的几何属性。只要确保输入为正方形,即可 100% 精确生成所需尺寸的 ICO 图标,彻底摆脱 C# DLL 依赖,实现纯 Python 跨平台图标生成。










