pil.image.save 支持 webp 但默认不支持 avif,需手动安装带 avif 后端的 pillow 或改用 pyavif;avif 依赖系统 libavif,保存更慢且线程不安全,推荐进程池处理,quality 不宜低于 25,注意 alpha 通道合成与浏览器兼容性。

WebP 压缩用 PIL.Image.save 就够,但默认不支持 AVIF
Python 标准库的 PIL(Pillow)开箱支持 WebP,调用 .save(..., format="WEBP") 即可;AVIF 则不行——它依赖系统级编解码器(如 libavif),Pillow 3.4.0+ 虽宣称支持,但实际需手动编译或安装带 AVIF 后端的 wheel。
常见错误现象:OSError: cannot write mode RGBA as AVIF 或直接报 KeyError: 'AVIF',本质是 Pillow 没加载 AVIF 插件。
- 确认是否支持:运行
PIL.Image.registered_extensions().get("avif"),返回None就没装好 - Linux/macOS 推荐用
pip install "Pillow[avif]" --force-reinstall(需先装libavif-dev/libavif) - Windows 用户更稳妥的方式是改用
pyavif库,它封装了静态链接的libavif,无需系统依赖 - WebP 的
quality参数范围是 1–100,但低于 30 时压缩伪影明显;AVIF 的quality同样有效,但对 8-bit 图像建议不低于 25,否则色带风险高
io.BytesIO 是避免磁盘 I/O 的关键中间层
WebP/AVIF 转换常用于 Web 服务(如 Flask/FastAPI 图片上传处理),直接写文件再读取既慢又占磁盘。必须用内存流中转,否则吞吐量掉一半以上。
使用场景:接收 request.files["image"] 后立即压缩并返回响应流,全程不落地。
立即学习“Python免费学习笔记(深入)”;
- 别用
open("temp.jpg", "rb")→PIL.Image.open(),而是PIL.Image.open(io.BytesIO(raw_bytes)) - 保存时也走
BytesIO:buf = io.BytesIO(); img.save(buf, format="WEBP", quality=75); buf.seek(0) - 注意:AVIF 保存比 WebP 慢 3–5 倍,若并发高,务必加超时和尺寸限制(如 >4096px 宽度直接拒收)
- 某些旧版 Pillow 对
BytesIO中 AVIF 写入有 bug,表现为ValueError: buffer is too small,升级到 Pillow 10.2.0+ 可修复
透明通道(Alpha)处理不当会导致 WebP/AVIF 白边或黑边
源图含透明背景(如 PNG)时,WebP 和 AVIF 默认保留 Alpha,但浏览器渲染时若未显式设置 background-color,可能显示为灰/黑/白边——这不是压缩问题,是合成逻辑缺失。
常见错误现象:原图圆角透明 PNG 压缩后边缘发虚、出现细白线,或整图变暗。
- 明确需求:要透明就保留
mode="RGBA";要白底就先img.convert("RGB").save(...);要指定底色则用PIL.Image.alpha_composite合成 - WebP 支持
lossless=True,但仅对无 Alpha 的 RGB 有效;带 Alpha 的 lossless WebP 实际仍是半有损,慎用 - AVIF 的 Alpha 支持更稳定,但 Safari 16.4+ 才完全支持带 Alpha 的 AVIF,老版本会 fallback 到黑底
- 别依赖
img.save(..., optimize=True)自动去 Alpha——它不工作,optimize只影响熵编码,不影响通道
批量转换时注意 libavif 的线程安全与内存泄漏
用 pyavif 或编译版 Pillow 多线程跑 AVIF 转换,容易触发段错误或内存持续增长,根源是底层 libavif 的编码器实例非线程安全,且部分版本未正确释放 YUV 缓冲区。
性能影响:单线程吞吐约 12 FPS(1080p),4 线程可能只到 18 FPS,甚至因锁竞争反降速。
- PyAVIF 推荐用进程池(
multiprocessing.Pool)而非线程池,每个子进程独占 libavif 实例 - Pillow 用户可在每次转换后手动触发 GC:
import gc; gc.collect(),尤其在循环中处理上百张图时 - AVIF 的
speed参数(0–10)影响极大:speed=6 是质量/速度平衡点;speed=0(最慢)比 speed=6 多耗 3 倍时间,但 PSNR 仅高 0.8dB,通常不值得 - WebP 没这问题,
thread_count参数可放心设为 CPU 核数,多线程加速比接近线性
AVIF 的真正门槛不在 Python 层,而在编解码器兼容性和资源管控——哪怕代码写对了,容器环境没配好 libavif 版本,照样崩。这点比 WebP 严苛得多。










