仅用 PSNR 和少量的图片样本评价编解码和视觉表现是相当局限的,结论需要更多的测试以验证。

输出格式

长期以来,在摄影软件里能选的输出格式为 JPEG,TIFF 等。我最常使用的输出格式便是软件中能选到的最高质量的 JPEG,仅在需要打印等情况下用 16bit TIFF。

这样方案的缺点是:最高质量的 JPEG 也存在一定的有损压缩(与 TIFF 对比,PSNR 在 48dB 左右),文件体积比较大,位深度也只有 8bit,如果要作为一种“存档”目的的数字母版,似乎规格有点太低。而选择 16bit TIFF 的话,虽然可以做到无损和高位深,但动辄百兆的体积有些不可接受。

现在有这么多的先进编码格式,是否可以用 JPEG-XL 或 AVIF 来改变这一情况?经过一些简单的图像质量评价和文件体积测试,我决定在未来一段时间采取如下的图片存档方案:

高效图片压缩

流程图中的文件体积和 PSNR 使用一张 3840 长边的照片测试得到的,相机得到的照片体积一般会更大些。

母版的选择:JPEG-XL

图中,从左到右是从图片编辑软件出发,逐渐降低质量和文件体积,到存档和分享的过程。

图片编辑软件最常见的高规格输出是 16bit TIFF,而 JPEG-XL 支持 16bit 位深,虽然无损压缩模式的压缩率不够理想(大约 1.5:1),但只要能接受一点点有损压缩,就能实现非常高的压缩率。

在 JPEG-XL 中,使用类似锥响应的 XYB 空间中的距离(distance)来衡量图像质量,distance 为 1 时,表示一个 JND(恰可察觉差)单位,在 JPEG-XL 中被描述为“视觉无损”。尽管 distance 为 1 时的压缩率非常诱人,有时甚至能达到 40:1,但此时的图像质量不如以往最大质量的 JPEG,PSNR 在 42dB 左右,我们的新时代数字母版在图像质量上总不能比之前还菜吧。

为了平衡压缩率和图像质量,调试出的 distance 大约会在 0.1-0.3 之间,这样能够实现高位深下,在图像质量上超越之前最大质量的 JPEG,文件体积缩小到原本的 50% 以下。例如一张图片,JPEG 和 JXL 的对比如下表:

格式distance文件体积PSNR
JPEG-26.0 MB48.6 dB
JPEG-XL0.122.9 MB52.9 dB
JPEG-XL0.215.2 MB49.7 dB
JPEG-XL0.311.7 MB47.9 dB

因此,新流程的母版格式是:一个高质量的 JPEG-XL,用于代替原本的 TIFF,在无损和视觉无损之间的质量时,文件体积仅为 TIFF 的不到 1/10,还比原本的 JPEG 小 50%。

为什么不 AVIF

如果单纯衡量 PSNR,AVIF 在相同质量下比 JPEG-XL 有更小的文件体积。在存档这一步时不选择 AVIF 的原因是位深。AVIF 本身支持 8,10,12 三种位深度,可以通过编码 12 和 8bit 两帧来实现 16bit 位深度。虽然编辑后的图像量化成 16 还是 12bit,甚至是 10bit,观看起来都没有区别,包括未来一段时间内显示终端也都只会停留在 8bit,但从存档和“洁癖”的角度,我还是希望有一个 16bit 位深的母版,至少听起来很爽。

作为对比,也使用一张图来测试:

  • AVIF 在 10bit 下实现 56.78dB PSNR 时,文件体积为 10.02MB。JPEG-XL 实现基本相同的 PSNR 时,文件体积略大,为 10.79MB。
  • AVIF 在 10bit 下实现 51.506dB PSNR 时,文件体积为 5.51MB。JPEG-XL 实现略低的 51.427dB PSNR 时,文件体积略大,为 7.19MB。

需要注意的是,这个结果仅有一张图测试,也许是 AVIF 更适合这张图的特性,且 PSNR 也不是一个特别好的评价指标。不过 AVIF 在稍低码率下的优势是得到了广泛共识的,在非常接近无损的情况下,AVIF 的性能则接近或略逊于 JPEG-XL。

不过得益于 AVIF 优秀的浏览器兼容性和强大的低码率性能,它将成为分享时的首选格式。

关于 AVIF 的 16bit 模式,编解码用法如下:

avifenc --depth 12,8 --lossless input.png output.avif
avifdec --sato -d 16 output.avif decode.png

AVIF 的无损模式非常糟糕,这个 16bit 模式也很一般,有损压缩时,相同 PSNR 下,JPEG-XL 体积是 AVIF 的 1/4 到 1/9。

二次压缩

数字母版听起来很美好,又小又厉害,但毕竟存在压缩,不希望影响到下一步的分享和使用。

实际使用中,还需要从母版导出用来分享的 JPEG 等格式,意味着我们需要对已经被压缩过的 JPEG-XL 再做一次压缩。这样的二重压缩会带来额外的损失,由于第一步时非常接近无损,还有从高位深到低位深带来的额外冗余。经过测试,二次压缩得到的 JPEG 或 AVIF 能够比较接近从 TIFF 直接压缩得到的 JPEG 或 AVIF。使用相同的压缩质量,相比 TIFF 的 PSNR 的降低在 1-2 dB,且随着压缩幅度的增加,差距会逐渐缩小,例如二次压缩到 AVIF 时,质量设置为 80,TIFF 获得的 AVIF PSNR 为 40.67dB,而从 JPEG-XL 二次压缩获得的 AVIF PSNR 为 40.59dB。

遗憾的是,如果原本的流程是直接输出最高质量的 JPEG,现在的流程是先输出 JPEG-XL,再用最高质量输出到 JPEG,新的 JPEG 一定比之前的 JPEG 质量略差。

但如果以前的输出流程不是最高质量的 JPEG(我有时候为了省些体积,会选择 90 或 95 的质量),那么现在的流程中,在二次压缩到 JPEG 时选择 95 或 100 的质量,得到的 JPEG 质量就会比原本的 JPEG 高。

目前,我采用的两种二次压缩格式分别是 JPEG 和 AVIF,JPEG 为了最大的兼容性(比如安卓手机),AVIF 则用在 iPhone 以及网络用途。AVIF 的质量设置通常在 70-85 之间,4K 照片的体积在 1-2 MB,比如博客中的大多数照片都是这个规格。

如何用起来?

ACR 和 Lightroom

如果你常用 ACR 或 Lightroom,在导出时就已经可选这些先进格式了。ACR 把质量选项重新映射了一遍,给用户展示的是 0-12 个等级,13 为无损。

经过少量的测试,等级 12 的 JPEG-XL 大约对应 distance 0.1,等级 10 大约是 0.6。可以尝试等级 11 或 12 的 JPEG-XL 来生成母版文件。

AVIF 方面,等级 12 的 Quality 非常高,PSNR 比等级 12 的 JPEG-XL 还要高不少,大致相当于 distance 0.035 的 JPEG-XL,等级 11 则比较接近等级 12 的 JPEG-XL。更低质量的 AVIF 则可做分享用途。

ACR 中的 JPEG 即使选到最高等级,也不如 Capture One 或其他编码器设置成 100 的质量,PSNR 略低 3dB,文件体积也会小一些。

Capture One

像 Capture One 这种格式支持比较少的(甚至不支持 16bit PNG),则要通过 TIFF 中转一下。

先保存无损的 TIFF,然后转码为 PNM 格式,这是一种约等于裸码值的无压缩格式,也可以是 PNG,这一步的目的是让 libjxl 的例程读取,然后运行:

cjxl input.pnm output.jxl -d 0.2

即可完成 JPEG-XL 的压缩,如果需要 EXIF 的话,要额外加一步从 TIFF 搬运到 JPEG-XL 的过程。

从 JPEG-XL 到 AVIF 的转换也类似,推荐使用 PNG 作为中间格式。

djxl input.jxl output.png
avifenc -d 10 -q 80 output.png output.avif

当然也可以直接修改和编译 libjxl 和 libavif 实现更高效的转换。

短暂的拥抱未来

这个方案已发现不少问题,我希望给他一些时间,先真实的用起来,逐步发现改进之处,至少能短暂的拥抱一下未来。

以下是遇到的一些问题和思路:

  1. JPEG-XL 的编码速度:libjxl 纯编码速度在 M1 Pro 上大约为 10 MP/s,也就是一张 30 MP 的照片大约需要 3 秒,再加上一些额外的 IO 开销,有优化的空间。
  2. 在 macOS 上,打开一个有很多 JPEG-XL 文件的文件夹时,需要一些时间来生成预览图。尝试通过嵌入一个很小的 JPEG 预览图来解决。
  3. JPEG-XL 的兼容性差,作为母版格式,兼容性差点也可以理解,但还是希望随着时间的推移,JPEG-XL 能够被更多的应用和系统支持。
  4. HDR 支持,涉及这么多不同的图像格式,要在它们之间实现转换,还保留 HDR 信息,估计会比较麻烦。设想一下,如果真的需要输出 HDR,16 bit 搭配 PQ 应该也足够用。
  5. 广色域支持,有想过在母版这一层换成 P3 甚至 2020 色域,但需要在转码为分享格式的步骤引入色彩管理和色域变换。