简介:个人学习分享,如有错误,欢迎批评指正。
一、相关概念
1. 色调映射
影像系统的一个核心功能是图像显示,好的显示效果能够真实地再现原始场景,给人的知觉效果就像直接观察原始场景一样。色调映射是影像再现系统的一个重要组成部分,它将原始场景的光照映射成显示设备的发光强度。
一个好的影像再现系统需要考虑到人类视觉系统(Human Vision System, HVS)是如何处理光线的。场景辐射的光线被人类视网膜上的杆细胞和锥细胞捕捉产生电信号,并传递到视觉神经通道(visual pathway)中做进一步处理。这些信号会被若干层神经网络进行非线性处理,形成的图像被称为知觉(percept),此时的知觉图像已经与原始的物理辐射图像相去甚远。
自然场景中可以存在非常广泛的光照条件,从昏暗的夜间场景到明媚的室外场景。下图列举了一些典型场景的光照度等级。


人类为了能够在上述各种场景下都能够正常观察物体并分辨足够的细节,人的视觉系统进化出了多种机制以适应不同的光照条件。首先是瞳孔,它可以改变直径从而控制进入眼睛的光线多少,这是对全局光照强度的一种适应性机制。其次是视锥细胞可以根据视场(Field of View, FOV)上的平均光亮度(即视网膜照度)调节敏感度。最后,当目光注视某一小区域时,视觉系统会自动调节局部对比度。因此,尽管人类生活环境中的光照度变化可以达到12个数量级,从极弱星光的10^-6nits 到阳光直射的 10^6 nits,而人眼感光细胞的实际动态范围只有4个数量级就能够适应各种环境场景。

视频捕捉设备生成的图像一般是正比于场景的光辐射强度的,即响应符合线性关系。尤其是当前主流的CMOS Image Sensor (CIS),线性度已经好到可以对入射光子的数量进行准确的计数。CIS的线性特点使人们可以很方便地对摄像机捕捉的原始图像进行处理,从而模拟人类视觉的行为,使设备输出的图像在人眼看来更加自然。这个过程叫做色调映射,如下图所示。
![[图片]
图片\]](https://i-blog.csdnimg.cn/direct/4e258471b21e4d249698b3da36e548ab.png)

如果不做色调映射处理,摄像机直接输出最原始的线性图像,在人眼看来图像比正常的要暗,如下图所示。
![\[图片\]](https://i-blog.csdnimg.cn/direct/98625beec12f4a8386fe8486fa522fca.png)
(a)摄像机捕捉的原始线性图像 (b)人眼知觉的正常效果
这显然是因为人的视觉系统对视野中的暗区信号做了提升以形成更强烈的知觉,这个特性可以帮助人类识别隐藏在黑暗中的危险,是人类对生存环境的一种适应(adaptation)。
### 2. Tone Mapping 到底是什么
最简洁的定义是:
**Tone Mapping 是把高动态范围亮度映射到低动态范围输出范围中的过程,同时尽量保留人眼感知上的亮度关系、层次和观感。**
这里有两个关键词:
* **高动态范围**
* **映射到可显示范围**
现实世界的亮度范围非常大。
例如:
* 阴影角落可能很暗
* 窗外阳光可能非常亮
* 夜景里灯牌和暗部差异也很大
但显示设备、JPEG、8bit/10bit 编码都只能表示有限范围。
因此,必须把"场景中的大亮度跨度"压缩进"显示系统能表达的小范围"里。
这个压缩过程就是 Tone Mapping。
### 3. 为什么需要 Tone Mapping
#### 3.1 场景动态范围远大于显示动态范围
现实世界的场景亮度跨度可能非常大。
一个典型逆光场景里:
* 室内人物脸部很暗
* 窗外天空极亮
传感器可能通过 HDR、多帧融合等手段记录了更多范围,但最终你仍然要显示在:
* 手机屏幕
* SDR 显示器
* JPEG 文件
* 视频编码系统
这些都不可能原样显示真实场景亮度。
所以必须进行压缩。
#### 3.2 直接线性压缩效果很差
如果只是把 HDR 亮度线性压缩:
* 暗部容易发黑
* 高亮容易发灰
* 中间调缺乏层次
* 画面整体"平"
因为人眼并不是线性感知亮度的。
单纯线性缩放无法兼顾:
* 高亮细节
* 暗部可见性
* 整体对比感
Tone Mapping 的价值就在于:
**不是机械压缩,而是按视觉需求进行有策略的压缩。**
#### 3.3 需要保留"看起来合理"的观感
Tone Mapping 不追求把物理亮度原封不动搬过来,因为做不到。
它追求的是:
* 看起来自然
* 明暗关系合理
* 高光不刺眼
* 暗部有细节
* 画面不死、不灰、不假
所以 Tone Mapping 的核心不是"物理忠实",而是:
**在有限显示范围中重建一个视觉上合理的亮度世界。**
### 4. Tone Mapping 的核心目标
Tone Mapping 通常要同时满足这几件事:
#### 4.1 压缩动态范围
把大范围亮度压缩到小范围输出内:
L o u t = f ( L i n ) L_{out} = f(L_{in}) Lout=f(Lin)
其中:
* L i n L_{in} Lin:输入 HDR 或线性亮度
* L o u t L_{out} Lout:输出显示亮度或编码亮度
* f ( ⋅ ) f(\\cdot) f(⋅):非线性压缩函数
#### 4.2 保留高光细节
高亮区域不能一片死白。
比如:
* 云层纹理
* 灯牌细节
* 反光物体层次
Tone Mapping 需要压高亮,但不能把高亮全压平。
#### 4.3 提升暗部可见性
暗部不能死黑。
例如:
* 室内阴影
* 夜景暗巷
* 背光人脸
Tone Mapping 往往要在压缩整体动态范围的同时,把暗部适度抬起来。
#### 4.4 保持中间调自然
中间调往往决定"观感"。
如果中间调处理不好,会出现:
* 发灰
* 发闷
* 平
* 假 HDR 感
* 过度修图感
所以 Tone Mapping 很多时候真正难调的不是极亮极暗,而是中间调的走向。
#### 4.5 尽量保持局部对比和层次
压缩动态范围会天然损失对比。
Tone Mapping 的难点在于:**既要压,又不能压得没层次。**
所以优秀的 Tone Mapping 不只是让亮度落进范围,更重要的是:
* 保细节
* 保层次
* 保局部对比
* 保自然感
### 5. Tone Mapping 的数学本质
从数学上看,Tone Mapping 就是在设计一个非线性函数:
L o u t = f ( L i n ) L_{out} = f(L_{in}) Lout=f(Lin)
这个函数通常具有这些特征:
**1. 单调递增**
更亮的输入不能映射成更暗的输出。
**2. 低亮区域相对展开**
暗部保留更多可见性。
**3. 高亮区域逐渐压缩**
避免高光溢出和死白。
**4. 中间调控制整体观感**
决定画面是通透、厚重、平淡还是刺激。
你可以把 Tone Mapping 曲线理解成一条 S 曲线、对数曲线或 shoulder/toe 曲线,而不是一条简单直线。
### 6. Tone Mapping 最直观的图像理解
如果横轴是输入亮度,纵轴是输出亮度,那么一条典型 Tone Mapping 曲线通常会有:
* toe(脚部):处理暗部
* mid-tone region(中间调区域):决定主体观感
* shoulder(肩部):压缩高亮
也就是说:
* 暗部不是死压,而是柔和抬起
* 中间调保持较好的斜率,保留主体对比
* 高亮逐渐变平,防止爆掉
这和简单 Gamma 不一样。
Gamma 更多是`感知编码`;
Tone Mapping 更强调"`动态范围重分配`"。
## 二、全局 Tone Mapping 与局部 Tone Mapping
这是 Tone Mapping 最重要的分类。
![\[图片\]](https://i-blog.csdnimg.cn/direct/ca71624538ee4f50aef7ee4fda219aa9.png)
(a)全局色调映射 (b)全局加局部色调映射
### 1. 全局 Tone Mapping(Global Tone Mapping)
全局 Tone Mapping 的意思是:
**整张图所有像素,只按同一条亮度映射曲线处理。**
也就是:
L o u t ( x , y ) = f ( L i n ( x , y ) ) L_{out}(x,y)=f(L_{in}(x,y)) Lout(x,y)=f(Lin(x,y))
同样的输入亮度,无论在哪个位置,都会映射成同样输出亮度。
**优点**
* 实现简单
* 稳定
* 不容易出 halo
* 计算成本低
* 视频时序一致性好
**缺点**
* 对复杂场景适应能力有限
* 很难同时兼顾高光和暗部
* 容易出现:
* 高亮保住了,暗部太黑
* 暗部抬起来了,画面发灰
**典型应用**
* 基础 ISP tone curve
* 简单 HDR 输出
* 显示端标准映射
### 2. 局部 Tone Mapping(Local Tone Mapping)
局部 Tone Mapping 的意思是:**不同区域采用不同程度的亮度压缩和对比调节。**
即某个像素的输出不仅取决于它自己的亮度,还取决于周围区域的亮度分布:
L o u t ( x , y ) = f ( L i n ( x , y ) , N ( x , y ) ) L_{out}(x,y)=f(L_{in}(x,y),\\mathcal{N}(x,y)) Lout(x,y)=f(Lin(x,y),N(x,y))
其中 N ( x , y ) \\mathcal{N}(x,y) N(x,y) 表示邻域信息。
**优点**
* 更能兼顾高亮和暗部
* 局部细节保留更好
* 更容易做出"人眼觉得清楚"的效果
**缺点**
* 容易过度处理
* 容易出 halo(光晕)
* 容易出现局部不自然
* 视频里容易闪烁
* 算法复杂度更高
**典型应用**
* 手机 HDR
* LTM(Local Tone Mapping)
* 高级显示增强
* 计算摄影后处理
## 三、常见 Tone Mapping 算法思想
### 1. 算法定义
从算法角度看,Tone Mapping 的任务就是构造一个映射函数:
L o u t ( x , y ) = f ( L i n ( x , y ) ) L_{out}(x,y)=f\\big(L_{in}(x,y)\\big) Lout(x,y)=f(Lin(x,y))
或者在局部算法中写成:
L o u t ( x , y ) = f ( L i n ( x , y ) , N ( x , y ) ) L_{out}(x,y)=f\\big(L_{in}(x,y),\\mathcal{N}(x,y)\\big) Lout(x,y)=f(Lin(x,y),N(x,y))
其中:
* L i n L_{in} Lin:输入亮度,通常是 HDR 亮度、线性 RGB 转出的亮度、或融合后的 luminance
* L o u t L_{out} Lout:输出亮度,通常是 SDR 或显示范围内的亮度
* N ( x , y ) \\mathcal{N}(x,y) N(x,y):像素邻域信息
所以所有 Tone Mapping 算法本质上都在回答一个问题:
**输入亮度太大了,怎么压到输出范围里,同时尽量保住细节和观感?**
### 2. Tone Mapping 的基本处理对象是什么
在讲算法前,要先明确一个问题:
**大多数 Tone Mapping 主要处理"亮度",不是直接处理 RGB**
因为如果对 RGB 三通道分别随便做非线性压缩,很容易导致:
* 色相漂移
* 饱和度变化异常
* 彩色高光不自然
所以很多算法会先从 RGB 中提取亮度,例如:
L = 0.2126 R + 0.7152 G + 0.0722 B L = 0.2126R + 0.7152G + 0.0722B L=0.2126R+0.7152G+0.0722B
或者在某些空间里用 Y、log-luminance 等。
然后对亮度 L L L做 Tone Mapping,再把颜色重新恢复:
R ′ = L ′ L R , G ′ = L ′ L G , B ′ = L ′ L B R' = \\frac{L'}{L}R,\\quad G' = \\frac{L'}{L}G,\\quad B' = \\frac{L'}{L}B R′=LL′R,G′=LL′G,B′=LL′B
这里:
* L L L:原亮度
* L ′ L' L′:Tone Mapping 后亮度
这是一种常见"亮度-色彩分离"的思想。
### 3. 第一类:线性 / 分段线性 Tone Mapping
这类方法最简单,先从它讲起,因为很多 ISP 里最基础的 tone curve 本质上还是这一类。
#### 3.1 纯线性压缩
最简单的办法是:
L o u t = a L i n L_{out} = aL_{in} Lout=aLin
如果最大输入亮度是 L m a x L_{max} Lmax,目标输出范围是 \[0,1\],那么可以取:
a = 1 L m a x a = \\frac{1}{L_{max}} a=Lmax1
于是:
L o u t = L i n L m a x L_{out} = \\frac{L_{in}}{L_{max}} Lout=LmaxLin
**优点**
* 极其简单
* 稳定
* 没有局部伪影
* 硬件实现容易
**缺点**
* 画面通常很平
* 暗部不够亮
* 高亮压缩不灵活
* 中间调缺乏视觉优化
**结论**
这几乎只能算最原始基线,不适合高质量 HDR。
#### 3.2 分段线性曲线
比纯线性更常见的是分段线性:
```bash
暗部一段斜率
中间调一段斜率
高亮一段斜率
```
数学上可以写成:
L o u t = { a 1 L i n + b 1 , L i n \< T 1 a 2 L i n + b 2 , T 1 ≤ L i n \< T 2 a 3 L i n + b 3 , L i n ≥ T 2 L_{out} = \\begin{cases} a_1L_{in}+b_1, \& L_{in}\