互联网每天传输数十亿张图片,但承载这些图片的编码格式,二十多年来几乎没变过。JPEG 在 1992 年成为标准后,统治了整个 Web 图像生态——直到带宽与画质的矛盾越来越不可调和:要么压缩到糊成一团,要么体积大到拖慢页面。
JPEG XL 就是为终结这个矛盾而生的。它不是某天突然冒出来的标准提案,而是 Google 一个研究团队用十年时间、通过一系列看似分散的开源项目,一步步逼近最优解的结果。这个格式被预期能用三十年,背后的技术路线值得每一个做图像、视频或基础设施的工程师理解。
从理解旧格式开始:2011-2017 的"拆解"阶段
JPEG XL 的起点不是"我们要做一个新标准",而是"先把现有格式搞透"。
2011 年前后,Google 的图像研究团队开始系统性地拆解 JPEG 和 WebP 的压缩机制。他们发现几个关键瓶颈:
- JPEG 的 8×8 DCT 块结构是画质损失的核心原因。块效应在低码率下尤其明显,但提高码率又违背压缩初衷。
- 色度下采样(4:2:0)在节省空间的同时,让边缘和细节区域的色彩严重失真,而人眼对这些区域恰恰最敏感。
- 熵编码效率低——JPEG 用的 Huffman 编码在概率分布不均匀时浪费大量比特。
这些发现不是纸上谈兵。团队把结论直接写进了开源项目:2017 年发布的 Guetzli,就是一个针对 JPEG 率失真优化的编码器。它不改变格式本身,只在 JPEG 的约束内寻找更优的量化策略,平均能比 libjpeg 减少 20-30% 文件体积而保持同等视觉质量。
Guetzli 的代价是编码速度极慢(一张图几分钟),但它验证了一个关键假设:JPEG 格式内部的冗余远比人们以为的大,只是传统编码器没找到最优的参数组合。
突破格式限制:从修补到重建
理解了旧格式的天花板后,下一步自然就是突破格式本身。
2018 年,Google 发布了 Pik——一个实验性的新图像编码器。Pik 的核心思路:
- 更大的变换块:不再受限于 8×8,使用自适应块大小,让平滑区域用大块、细节区域用小块。
- 更精细的色度处理:根据图像内容局部决定色度分辨率,而不是全局一刀切的 4:2:0。
- ANS(Asymmetric Numeral Systems)熵编码:比 Huffman 更接近理论极限,编码效率提升显著。
Pik 是 JPEG XL 的直接前身。但团队很快发现,Pik 的某些设计虽然效果好,却过于依赖特定实现,难以标准化。于是他们转向了更系统的方法。
JPEG XL 的核心技术栈
2019 年,团队联合 Cloudinary 等业界伙伴,正式向 JPEG 委员会提交提案,代号 JPEG XL。核心设计可以拆成三层:
变换层:VarDCT + Modular
JPEG XL 不是单一变换,而是双模式:
- VarDCT 模式:继承自 Pik 的思路,用可变大小的 DCT 块处理自然图像。块大小从 4×4 到 256×256 动态选择,配合自适应量化,在低码率下大幅减少块效应。
- Modular 模式:用预测编码(类似 FLIF/FUIF 的思路)处理计算机生成图像、图标、截图等非自然图像。这类图像的像素分布和自然照片完全不同,DCT 反而是低效的。
两种模式在编码器端自动选择或混合,不需要用户干预。
熵编码:ANS + 上下文建模
JPEG XL 使用 ANS 作为核心熵编码器,配合高度精细的上下文模型。每个符号的编码概率不是全局统计,而是根据其空间邻域、变换系数位置、已解码信息等动态预测。这意味着同一张图里,平坦天空和复杂纹理区域用的是完全不同的概率模型。
工具层:JPEG 无损重压缩
这是 JPEG XL 最实用的功能之一:可以把现有 JPEG 文件无损压缩到更小体积,同时保留完整的像素级还原能力。 原始 JPEG 的所有数据都被保留,只是用更高效的熵编码重新打包。这意味着迁移成本几乎为零——你不需要重新编码,不需要担心画质变化,只需要把文件体积缩小 20% 左右。
实践:用 JPEG XL 编码、解码和对比
下面是一套可以直接跑的命令,让你在自己的机器上体验 JPEG XL 的效果。
安装 libjxl 工具链
# macOS
brew install jpeg-xl
# Ubuntu/Debian(从源码编译,确保拿到最新版)
sudo apt-get install cmake g++ libbrotli-dev
git clone https://github.com/libjxl/libjxl.git --recursive --depth 1
cd libjxl
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j$(nproc)
sudo cmake --install build
# 验证安装
cjxl --version
djxl --version
编码与解码
# 将 PNG 编码为 JPEG XL(指定目标质量,1-100,类似 JPEG 的质量参数)
cjxl input.png output.jxl --quality 85
# 更精细的控制:指定目标码率而非质量
cjxl input.png output.jxl --distance 1.0 # distance 越小画质越高,0 为无损
# 无损编码
cjxl input.png lossless.jxl --distance 0
# JPEG 无损重压缩(不改变像素,只缩小文件)
cjxl original.jpg recompressed.jxl --jpeg_quality_transcoding # 保持原 JPEG 质量
# 解码回 PNG
djxl output.jxl decoded.png
批量对比脚本
下面这个 Python 脚本可以批量对比 JPEG、WebP 和 JPEG XL 在相同视觉质量下的文件体积:
#!/usr/bin/env python3
"""对比 JPEG / WebP / JPEG XL 在近似视觉质量下的文件体积。
依赖:cjxl、cwebp、libjpeg(cjpeg)已安装并在 PATH 中。
用法:python3 compare_formats.py <输入PNG或JPEG> <JPEG质量参数 1-100>
"""
import subprocess, sys, os, pathlib
def run(cmd):
print(f" → {cmd}")
subprocess.run(cmd, shell=True, check=True)
def size_kb(path):
return os.path.getsize(path) / 1024
def main():
if len(sys.argv) < 3:
print("用法: python3 compare_formats.py <输入文件> <质量 1-100>")
sys.exit(1)
src = sys.argv[1]
q = sys.argv[2]
stem = pathlib.Path(src).stem
out_dir = pathlib.Path("compare_out")
out_dir.mkdir(exist_ok=True)
# JPEG
jpeg_path = out_dir / f"{stem}_q{q}.jpg"
run(f"cjpeg -quality {q} {src} > {jpeg_path}")
# WebP
webp_path = out_dir / f"{stem}_q{q}.webp"
run(f"cwebp -q {q} {src} -o {webp_path}")
# JPEG XL(用 distance 映射质量:distance ≈ (100-q)/100 的简化近似)
distance = round((100 - int(q)) / 100, 2)
jxl_path = out_dir / f"{stem}_d{distance}.jxl"
run(f"cjxl {src} {jxl_path} --distance {distance}")
# JPEG XL 无损重压缩(仅当输入是 JPEG 时)
if src.lower().endswith((".jpg", ".jpeg")):
jxl_trans_path = out_dir / f"{stem}_transcode.jxl"
run(f"cjxl {src} {jxl_trans_path}")
print(f"\nJPEG 无损重压缩: {size_kb(jxl_trans_path):.1f} KB(原始 JPEG: {size_kb(src):.1f} KB)")
print(f"\n{'格式':<12} {'体积(KB)':<10}")
print("-" * 22)
print(f"{'JPEG':<12} {size_kb(jpeg_path):<10.1f}")
print(f"{'WebP':<12} {size_kb(webp_path):<10.1f}")
print(f"{'JPEG XL':<12} {size_kb(jxl_path):<10.1f}")
if __name__ == "__main__":
main()
运行示例:
python3 compare_formats.py photo.png 80
典型结果:在同等视觉质量下,JPEG XL 的体积约为 JPEG 的 50-60%,比 WebP 再小 10-20%。无损模式下,JPEG XL 的体积通常比 PNG 小 40-60%。
在浏览器中启用 JPEG XL
Chrome 曾在 2023 年短暂支持 JPEG XL 后又移除,目前需要通过 flag 或扩展启用。Firefox 的支持仍在实验阶段。如果你想在 Web 页面中提前使用,可以用服务端检测 + fallback 策略:
# Nginx 配置:优先发送 JXL,不支持时 fallback 到 JPEG
location /images/ {
# 检查 Accept 头是否包含 image/jxl
if ($http_accept ~* "image/jxl") {
rewrite ^(.*)\.(jpg|jpeg|png)$ $1.jxl last;
}
# fallback:原文件照常返回
try_files $uri =404;
}
三十年标准的底气与现实的阻力
JPEG XL 被称为"能用三十年的格式",底气来自几个设计决策:
- 前向兼容:比特流设计预留了扩展空间,未来可以加入新工具而不破坏旧解码器。
- 全场景覆盖:自然照片、计算机图形、无损、有损、动画、Alpha 通道、JPEG 重压缩——一个格式全搞定,不需要在不同场景切换不同格式。
- 编码效率天花板高:VarDCT + Modular + ANS 的组合,在理论上已经逼近当前数学工具的极限,短期内很难有格式在同等复杂度下大幅超越。
但现实阻力同样明显:
- 浏览器支持未落地。Chrome 移除支持的官方理由是"生态不够成熟",背后是既有格式(WebP、AVIF)的利益格局和专利顾虑。
- 编码速度仍需优化。JPEG XL 的编码复杂度高于 AVIF,实时场景(如用户上传缩略图生成)需要硬件加速或进一步算法优化。
- 迁移成本不只是技术。CDN、图片处理服务、设计工具链的全面支持需要时间,而 AVIF 已经在抢同一块地盘。
工程师的行动清单
如果你在做图片相关的基础设施,现在可以做的:
- 立即试用 JPEG 无损重压缩。这是零风险、零画质损失的收益——把现有 JPEG 资源池跑一遍
cjxl --jpeg_quality_transcoding,平均缩小 20%,回退只需djxl还原为原 JPEG。 - 在新项目中做 A/B 测试。用上面的对比脚本跑自己的图片集,看 JPEG XL 在你的典型内容(产品图、截图、摄影)上实际能省多少带宽。
- 关注浏览器进展。Chrome 的
--enable-jxlflag 仍在更新,Firefox 的 Nightly 也有实验支持。在服务端做好 Accept 头检测,一旦主流浏览器正式支持,可以一天内切换。 - 不要把 AVIF 和 JPEG XL 当成二选一。两者各有优势——AVIF 在视频序列和硬件解码上更强,JPEG XL 在静态图像的画质/体积比和 JPEG 兼容性上更强。根据场景选工具,而不是选阵营。
JPEG XL 的十年故事说明一件事:好的标准不是靠委员会投票出来的,而是靠十年拆解、十年实验、十年迭代,把每一个比特的效率都逼到极限后,自然成为标准。剩下的,只是生态跟上技术的时间差。