Ultra HDR 与 ISO 21496-1 增益图(Gain-Map JPEG)处理边界
现代智能手机(Android 14+ Ultra HDR、iPhone 自适应 HDR JPEG)广泛采用 ISO 21496-1(或 Google Ultra HDR)增益图(Gain Map) 标准。这类 .jpg 文件在普通显示器上就是一张完全向后兼容的 8-bit SDR JPEG,但在支持 HDR 的浏览器与移动设备上,高光区域可以额外提升数档动态范围。
为什么这类手机实拍 HDR 照片一旦经过传统图片网关缩放或重压缩,在手机或 Chrome 上看就会瞬间变得灰暗平淡?本页拆解其底层机制与四个入口的真实支持状态。
1. Gain-Map JPEG 的内部结构与丢失原因
Section titled “1. Gain-Map JPEG 的内部结构与丢失原因”一张 ISO 21496-1 / Ultra HDR JPEG 文件并不是单张 10-bit 图像,而是由三部分封装在同一个 JPEG 容器中:
- 主图(Base SDR Image):标准的 8-bit sRGB / Display P3 JPEG,供所有传统软件直接解码显示。
- MPF(Multi-Picture Format)副图(Gain Map Image):紧跟在主图 EOI(End of Image)标记之后的第二张小型灰度或三通道 JPEG(通常分辨率为主图的
1/4),记录每个像素从 SDR 提升到 HDR 所需的对数亮度增益。 - XMP / ISO 21496-1 元数据:描述增益图的最小/最大增益倍数(
hdrCapacityMin/hdrCapacityMax)、Gamma 与偏移量。
当传统的图像处理管线执行 Load -> Resize -> ExportJpeg 时:
- JPEG 解码器默认只读取第 1 张主图(忽略 MPF 尾部副图);
- 即使开启了保留 EXIF/XMP 元数据,由于副图像素没有随主图一起缩放并重新写入 MPF 容器,导出的 JPEG 会彻底丢失增益图副图,退化为普通 SDR 照片。
2. 四入口对 Gain Map 的支持矩阵(截至 2026 年 10 月)
Section titled “2. 四入口对 Gain Map 的支持矩阵(截至 2026 年 10 月)”底层对 gainmap 元数据字段与 uhdr(libuhdr)容器的原生支持是在 libvips 8.18 周期中引入的,因此不同绑定因所链接的 libvips 版本不同而存在代差:
| 绑定与核实版本 | 底层 libvips | 读取增益图 | 保留或附加增益图导出 | 状态与边界说明 |
|---|---|---|---|---|
sharp 0.35.5 |
预编译内置 libvips 8.18.7 |
metadata.gainMap(返回副图 Buffer 与解析信息) |
.keepGainMap() / .withGainMap(options) |
实验性支持(Experimental):转码无几何变换时可用 keepGainMap();若执行了缩放/裁剪,需单独对增益图做对应变换后用 withGainMap() 合成 |
pyvips 3.2.0 |
取决于系统安装的 libvips |
image.get_gainmap()(需系统 libvips >= 8.18) |
通过 set_type 更新 "gainmap" 字段 |
在链接了 libvips 8.17.3 的系统上,get_gainmap() 会返回 None |
govips v2.19.0 |
测试环境为 libvips 8.17.3 |
无公开 API | 无公开 API | 重编码 JPEG/WebP/AVIF 时会丢弃 MPF 增益图副图,退化为 SDR |
vips CLI 8.17.3 |
libvips 8.17.3 |
不支持(需 libvips 8.18+) |
不支持(需 libvips 8.18+) |
8.17.3 尚无内置 gainmap 读写算子 |
3. 代码示例:检测、保留与缩放增益图
Section titled “3. 代码示例:检测、保留与缩放增益图”// govips v2.19.0 尚未封装 ISO 21496-1 Gain Map 读写接口。// 注意:对包含 Ultra HDR 增益图的 JPEG 执行 ExportJpeg / ExportWebp 时,// 输出将仅保留 SDR 主图(若原图嵌入了 Display P3 ICC,请保留 ICC 以避免色域同时退化)。params := vips.NewJpegExportParams()params.Quality = 85params.StripMetadata = false // 保留 Display P3 ICC 配置文件import sharp from 'sharp';
// 1. 探测输入 JPEG 是否包含 ISO 21496-1 / Ultra HDR 增益图(sharp 0.35.5 实验性特性)const meta = await sharp('ultra-hdr-input.jpg').metadata();
if (meta.gainMap) { // 场景 A:不改变几何尺寸,仅重编码主图并原样保留增益图 await sharp('ultra-hdr-input.jpg') .keepGainMap() .jpeg({ quality: 85 }) .toFile('ultra-hdr-reencoded.jpg');}import pyvips
image = pyvips.Image.new_from_file("ultra-hdr-input.jpg")
# pyvips 3.2.0 提供 get_gainmap()(需要底层链接 libvips >= 8.18 且编译了 libuhdr 支持)gainmap = image.get_gainmap()if gainmap is not None: print(f"Detected gain map: {gainmap.width}x{gainmap.height}, bands={gainmap.bands}")else: print("No gain map found (or linked libvips is earlier than 8.18)")# 在 libvips 8.17.3 下,vipsheader 不会列出 gainmap 副图字段;# 升级至编译了 libuhdr 的 libvips 8.18+ 后,可通过 vipsheader -a 检查 gainmap 元数据。vipsheader -a ultra-hdr-input.jpg