Ultra HDR 与 Gainmap增益图
Ultra HDR 与 Gainmap增益图
1. 核心思想:SDR + 增益图 = HDR
传统 JPEG/PNG 每个像素亮度值只有 0-255,对应 SDR(标准动态范围,约 200 nits 上限)。
HDR 显示需要更高亮度(可达 1000+ nits)。
Gainmap 方案的核心是:
HDR 像素 = SDR 像素 × 增益系数
增益系数由一张独立的灰度图(Gainmap)编码。这样同一个文件:
- 在 SDR 屏幕上直接显示 SDR 基图
- 在 HDR 屏幕上将 SDR 基图与 Gainmap 合成,还原 HDR 图像
2. 直观理解:两张图叠加
原始 JPEG(SDR 基图) Gainmap(增益图)
┌─────────────────┐ ┌─────────────────┐
│ 天空:灰色 │ │ 天空:白色 │ ← 白 = 需要大幅提亮
│ 人脸:肤色 │ × │ 人脸:深灰 │ ← 深灰 = 轻微提亮
│ 阴影:黑色 │ │ 阴影:黑色 │ ← 黑 = 不提亮
└─────────────────┘ └─────────────────┘
↓ HDR 屏幕上叠加
┌─────────────────┐
│ 天空:刺眼亮白 │ ← 2000 nits
│ 人脸:自然肤色 │ ← 300 nits
│ 阴影:纯黑 │ ← 0 nits
└─────────────────┘
Gainmap 像素越白(值越大)→ 叠加后越亮
Gainmap 像素越黑(值越小)→ 几乎不改变亮度
3. 增益倍数的计算:对数空间插值
为什么用对数空间?
增益倍数的映射(Gainmap 像素值 → 实际增益倍数)使用对数空间线性插值,而不是线性插值。
因为人眼对亮度的感知是对数的——"增亮一倍"在任何亮度区间感觉差不多:
- 10 → 20 nits,感觉"亮了一倍"
- 1000 → 2000 nits,同样感觉"亮了一倍"
使用对数映射后,Gainmap 的每一个像素级别的差异对人眼代表均匀的感知变化,精度分配更合理。如果用线性映射,低增益区间会被大量像素值浪费在人眼几乎感觉不到的细微差异上。
映射公式
设 Gainmap 像素归一化值为 t∈[0,1]t \in [0, 1]t∈[0,1](原始 0-255 除以 255):
对数空间线性插值:
log(gain)=log(ratioMin)+t⋅[log(ratioMax)−log(ratioMin)]\log(\text{gain}) = \log(\text{ratioMin}) + t \cdot [\log(\text{ratioMax}) - \log(\text{ratioMin})]log(gain)=log(ratioMin)+t⋅[log(ratioMax)−log(ratioMin)]
等价的幂次形式(底数无关):
gain=ratioMin1−t⋅ratioMaxt\text{gain} = \text{ratioMin}^{1-t} \cdot \text{ratioMax}^{t}gain=ratioMin1−t⋅ratioMaxt
注意:底数(eee、2、10)不影响结果,因为换底后系数在分子分母同时约掉。实际代码使用幂次形式,完全不含对数底数。
对比线性插值(ratioMin=1, ratioMax=8, t=0.5)
| 插值方式 | t=0.5 时的增益 |
|---|---|
| 线性插值 | (1+8)/2=4.5(1+8)/2 = 4.5(1+8)/2=4.5 |
| 对数插值 | 8≈2.83\sqrt{8} \approx 2.838≈2.83(几何平均数) |
gain
8 | ● 对数
| ●
| ●
4.5| ○ 线性(t=0.5)
| ●
| ●
2.83| ● 对数(t=0.5)
| ●
1 |●
+----+----+----+----+----+----+----+----+--→ t
0 0.5 1
4. gamma:Gainmap 像素的编码校正
Gainmap 图像存储时经过了 gamma 压缩(类似 sRGB),读取时需要做 gamma 还原(线性化),指数取倒数:
t=pixel1/γt = \text{pixel}^{1/\gamma}t=pixel1/γ
注意方向:
gamma是编码时用的指数,解码时取倒数 1/γ1/\gamma1/γ。pixel^γ是压缩(变暗),pixel^(1/γ)是还原(变亮)。
完整映射:
gain=ratioMin1−pixel1/γ⋅ratioMaxpixel1/γ\text{gain} = \text{ratioMin}^{1 - \text{pixel}^{1/\gamma}} \cdot \text{ratioMax}^{\text{pixel}^{1/\gamma}}gain=ratioMin1−pixel1/γ⋅ratioMaxpixel1/γ
| gamma 值 | 含义 |
|---|---|
1.0 |
像素值直接作为 ttt,不做处理 |
2.2 |
像素值已做 gamma 压缩(偏暗),解码后 ttt 被拉高(亮部细节更多) |
< 1.0 |
像素值已做反向压缩(偏亮),解码后 ttt 被压低(暗部细节更多) |
5. 设备自适应:minDisplayRatioForHdrTransition
HDR 效果需要在不同能力的设备上平滑过渡,而不是非 HDR 即 SDR 的二元切换。
定义两个阈值:
| 字段 | 含义 |
|---|---|
minDisplayRatioForHdrTransition(dmind_{\min}dmin) |
低于此值:完全显示 SDR,不应用 Gainmap |
displayRatioForFullHdr(dmaxd_{\max}dmax) |
高于此值:完全应用 Gainmap,显示完整 HDR |
设当前设备亮度比为 ddd,计算混合权重 www:
w=clamp(log(d)−log(dmin)log(dmax)−log(dmin), 0, 1)w = \text{clamp}\left(\frac{\log(d) - \log(d_{\min})}{\log(d_{\max}) - \log(d_{\min})},\ 0,\ 1\right)w=clamp(log(dmax)−log(dmin)log(d)−log(dmin), 0, 1)
| www 值 | 场景 | 效果 |
|---|---|---|
| w=0w = 0w=0 | SDR 屏幕 | gain0=1\text{gain}^0 = 1gain0=1,完全不增亮 |
| w=1w = 1w=1 | 全 HDR 屏幕 | gain1=gain\text{gain}^1 = \text{gain}gain1=gain,完全增亮 |
| w=0.5w = 0.5w=0.5 | 中等能力设备 | gain0.5=gain\text{gain}^{0.5} = \sqrt{\text{gain}}gain0.5=gain,增亮一半 |
6. 完整公式
官方公式(ISO 21496-1 / Ultra HDR 规范,Skia 实现)
对每个颜色通道 c∈{R,G,B}c \in \{R, G, B\}c∈{R,G,B} 独立计算:
Step 1:Gainmap 线性化
tc=Gc1/γct_c = G_c^{1/\gamma_c}tc=Gc1/γc
其中 GcG_cGc 为 Gainmap 图像归一化像素值(0-1)。
Step 2:对数空间插值,得到最大可用增益
log(gainc)=tc⋅[log(ratioMaxc)−log(ratioMinc)]+log(ratioMinc)\log(\text{gain}_c) = t_c \cdot [\log(\text{ratioMax}_c) - \log(\text{ratioMin}_c)] + \log(\text{ratioMin}_c)log(gainc)=tc⋅[log(ratioMaxc)−log(ratioMinc)]+log(ratioMinc)
Step 3:按设备显示能力加权
w=clamp (log(d)−log(dmin)log(dmax)−log(dmin), 0, 1)w = \text{clamp}\!\left(\frac{\log(d) - \log(d_{\min})}{\log(d_{\max}) - \log(d_{\min})},\ 0,\ 1\right)w=clamp(log(dmax)−log(dmin)log(d)−log(dmin), 0, 1)
gain_finalc=gainc w\text{gain\_final}_c = \text{gain}_c^{\ w}gain_finalc=gainc w
Step 4:合成 HDR 像素
HDRc=(SDRc+εsdr,c)⋅gain_finalc−εhdr,c\text{HDR}_c = (\text{SDR}_c + \varepsilon_{\text{sdr},c}) \cdot \text{gain\_final}_c - \varepsilon_{\text{hdr},c}HDRc=(SDRc+εsdr,c)⋅gain_finalc−εhdr,c
三阶段串联示意
G(Gainmap 像素 0-1)
↓ Step 1:gamma 线性化
t = G^(1/γ) ← 还原编码时的 gamma 压缩
↓ Step 2:插值到增益空间
gain = ratioMin^(1-t) × ratioMax^t ← 该像素点"最多能亮多少倍"
↓ Step 3:按设备能力缩放
gain_final = gain^w ← 当前设备实际应用多少
↓ Step 4:作用到 SDR 像素
HDR = (SDR + εSdr) × gain_final − εHdr
合并 Step 1-3 为单一表达式:
gain_finalc=(ratioMinc1−Gc1/γc⋅ratioMaxcGc1/γc)w\text{gain\_final}_c = \left(\text{ratioMin}_c^{1 - G_c^{1/\gamma_c}} \cdot \text{ratioMax}_c^{G_c^{1/\gamma_c}}\right)^{w}gain_finalc=(ratioMinc1−Gc1/γc⋅ratioMaxcGc1/γc)w
epsilon 是防止 log(0)\log(0)log(0) 的极小偏移量(通常为 0 或 1/255),对视觉无影响。
7. Android Gainmap API(API 34+)
字段对应关系
android.graphics.Gainmap 字段 |
QGainmapInfo 字段 |
含义 |
|---|---|---|
gainmapContents |
mGainmapContent |
增益图像素数据 |
displayRatioForFullHdr |
mfDisplayRatioForFullHdr |
触发完整 HDR 的显示亮度比 |
minDisplayRatioForHdrTransition |
mfMinDisplayRatioForHdrTransition |
开始 HDR 过渡的最低亮度比 |
ratioMax[R,G,B] |
mRatioMax |
Gainmap 最大增益 |
ratioMin[R,G,B] |
mRatioMin |
Gainmap 最小增益 |
gamma[R,G,B] |
mGamma |
Gainmap 编码 gamma |
epsilonHdr[R,G,B] |
mEpsilonHdr |
HDR 侧防黑场偏移 |
epsilonSdr[R,G,B] |
mEpsilonSdr |
SDR 侧防黑场偏移 |
ratioMax/ratioMin/gamma/epsilon 均为 R/G/B 三通道独立值,各通道独立计算增益。
基本用法
// 读取 HDR 图片的 Gainmap
Bitmap hdrBitmap = BitmapFactory.decodeFile(path);
Gainmap gainmap = hdrBitmap.getGainmap(); // 从 JPEG XMP 或 HEIF 解析
// 重新组装 HDR Bitmap
Bitmap sdrBitmap = ...;
Gainmap gainmap = new Gainmap(gainmapPixels);
gainmap.setDisplayRatioForFullHdr(4.0f);
gainmap.setRatioMax(2.0f, 2.0f, 2.0f);
// setters 均为 3 个独立 float 参数(R, G, B),不是 float[]
sdrBitmap.setGainmap(gainmap);
// 此时 sdrBitmap 成为 HDR Bitmap,可在 HDR 显示器上正确渲染
8. 一句话总结
SDR 基图是"骨架",Gainmap 是"哪里需要发光的地图",metadata(ratioMax/ratioMin/gamma/epsilon)是"解读这张地图的说明书"。三者缺一不可才能还原 HDR 图像。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)