前情提要
需求
使用 Python 处理和生成的图片,通常以 NumPy 数组的形式存在,把这个数组输出到文件是处理链路的最后一环。以往,使用最多的是 PIL 编码成 JPEG 格式。而随着 HDR,高位深,高压缩率的需求增加,我开始寻找能将数字直接编码成 AVIF 图像的方法。
libavif 和 libheif
几乎所有相关的库都是 libavif 或 libheif 的 Python 绑定,实际上你完全可以先导出一个 JPEG 或者高位深的 TIFF,然后直接调用编译好的 libavif 进行编码,所有参数(质量、速度、颜色配置)都可以传进去。如果你的需求只是压缩图片,那没有必要进入 Python 再绕一圈。
所以核心需求其实就是找一个好用的 libavif 的 Python 绑定,需要能够支持各种参数传入,如果 API 设计的优雅就更好了
为数不多的选择
Python 中的 AVIF 编码已经有了一些发展,PIL 在 11.3.0 版本中加入了对 AVIF 的官方支持,但读和写都仅支持 8 bit,也没有传参数的接口,下面的两个插件则可以读写高位深图片和传参数。
在 PIL 的 11.3.0 版本之前,要使用 PIL 编码 AVIF 的选择是 pillow-avif-plugin 插件,高级参数可以用变长关键字塞进去。
PIL 的另一个选择是 pillow-heif,它是 libheif 的绑定,在 1.0.0 版本之后,去除了对 AVIF 的支持,开发者给出的理由是 PIL 已经原生支持了 AVIF,最后一个可用 AVIF 的版本是 0.22.0,高级参数同样需要使用变长关键字。
ImageIO 和 OpenImageIO,这两个库都提供了各种图像格式的读写功能,但似乎也不支持高位深和高级参数,(其实是这俩 API 的用法比较复杂,我还没研究明白,但应该是没有或者比较麻烦)。
新选择:ImageCodecs
ImageCodecs 也是一个提供了各种图像格式读写功能的库,AVIF 用的后端是 libavif。
它的优点是 API 设计的非常简洁,曾经不支持色彩空间参数的传入,在 2025.11.11 版本中加入了支持。(有需求时,请大胆又合理地在 Issue 里“伸手”)
注意:请手动指定编码速度和线程数量,速度可以参考 libavif 的例程 avifenc 的默认值 6。否则默认参数可能会以单线程和最低速度进行编码,一张 4K 分辨率的图片将会消耗数分钟。
库中包装了一些参数,也可以使用 int 数字直接传入,基色和传递函数的定义可以在 H.273 中找到。
另外,使用 10 或 12 bit 时,需要使用 uint16 类型,但不要拉伸。例如一个 10 bit 图像的最大值应为 1023,12 bit 则为 4095。
from imagecodecs import avif_version, avif_encode, AVIF
import numpy as np
print(AVIF.available)
print(avif_version())
array = np.ones((100, 100), dtype=np.uint16) * 1023
# 10-bit, BT.2020 with BT.2100 PQ transfer function
encoded: bytes = avif_encode(
array,
level=AVIF.QUALITY.DEFAULT, # 0-100
speed=AVIF.SPEED.FASTEST, # 0-10
bitspersample=10,
primaries=AVIF.COLOR_PRIMARIES.BT2020,
transfer=AVIF.TRANSFER_CHARACTERISTICS.PQ,
numthreads=8,
)
with open("output.avif", "wb") as f:
f.write(encoded)
它的所有图像格式都采用类似的函数命名,你只需要检查是否可用,版本号,然后 encode 或 decode 即可。
小心闪光弹(不过可能晚了)

BT.2020 基色,BT.2100 PQ 传递函数的最大 RGB 及白色,10 bit,由 ImageCodecs 编码。