3、局部 Tone Mapping 算子:基于 Retinex 理论、双边滤波分解、局部自适应映射、光晕伪影问题

全局映射搞定了整体亮度,但细节呢?

我刚开始做 HDR 显示时,发现全局曲线一拉,暗部细节是亮了,可亮部纹理却糊成一片。说白了,全局映射是「一刀切」——它不管像素周围是什么情况。于是我们得请出局部算子。

3.1 Retinex 理论:把光照和反射拆开

局部映射的核心思想,其实来自一个很老的视觉理论——Retinex。Land 在 70 年代提出:人眼看到的图像 = 光照 × 反射。

  • 光照分量(Illumination):场景中的光源分布,变化缓慢,低频。
  • 反射分量(Reflectance):物体本身的纹理和颜色,高频细节。

我们做 Tone Mapping,本质上就是压缩光照,保留反射。我在项目中遇到过一种情况:直接用高斯模糊提取光照,结果边缘全糊了,光晕严重。为什么?因为高斯滤波不分边界,把边缘也当成了光照。

核心公式:

I(x, y) = L(x, y) × R(x, y)

取对数后:log(I) = log(L) + log(R)

我们只需要压缩 log(L),保留 log(R)。

3.2 双边滤波分解:边缘保留的关键

要避免光晕,就得用边缘保留滤波。双边滤波(Bilateral Filter)就是最经典的选择。

它比高斯滤波多了一个「值域权重」——不仅看空间距离,还看像素值差异。边缘两侧的像素值差异大,权重就低,所以边缘不会被模糊。

滤波方式 空间权重 值域权重 边缘保留 光晕风险
高斯滤波 ✓ ✗ ✗ 高
双边滤波 ✓ ✓ ✓ 低
引导滤波 ✓ ✓(线性) ✓ 极低

我个人习惯用双边滤波做基础层提取。参数有两个:σs(空间标准差)和 σr(值域标准差)。σs 控制模糊范围,σr 控制边缘敏感度。

调参经验:

σr 设得太小,滤波效果弱,光照分量不干净;设得太大,边缘保留失效,光晕又回来了。我一般从 0.1 开始调(归一化后),配合图像内容微调。

3.3 局部自适应映射:细节层单独处理

分解完成后,我们得到:

  • 基础层(Base Layer):光照分量,低频,需要压缩。
  • 细节层(Detail Layer):反射分量,高频,需要增强或保留。

映射策略很简单:

  1. 对基础层做全局压缩(比如用前面的 Reinhard 或对数映射)。
  2. 细节层乘以一个增益系数(通常 > 1),补偿压缩后的细节损失。
  3. 合成:输出 = 压缩后的基础层 × 细节层。
// 伪代码示例
float sigma_s = 16.0f;   // 空间半径
float sigma_r = 0.1f;    // 值域标准差

Mat base, detail;
bilateralFilter(logImage, base, -1, sigma_r, sigma_s);
detail = logImage - base;

// 压缩基础层
Mat compressedBase = reinhardToneMap(base);

// 增强细节
float detailGain = 1.5f;
Mat output = exp(compressedBase + detailGain * detail);

你想想看,细节增益设到 2.0 以上会怎样?嗯,纹理是清晰了,但噪声也被放大了。我在做夜景 HDR 时吃过这个亏——暗部噪声全出来了,画面脏得不行。

避坑指南:

我曾经把细节增益调到 3.0,结果天空出现明显的颗粒感。后来我加了一个噪声掩膜:只在纹理丰富区域增强,平滑区域保持原样。效果好了很多。

3.4 光晕伪影问题:成因与对策

光晕(Halo)是局部映射最常见的 artifacts。表现为高对比边缘附近出现亮边或暗边。

成因:

  • 滤波不够边缘保留:高斯滤波必然导致光晕。
  • σr 设置过大:双边滤波退化为高斯滤波。
  • 基础层压缩过强:边缘两侧的亮度差被放大。

对策:

  • 改用引导滤波(Guided Filter)或加权最小二乘滤波(WLS)。
  • 多尺度分解:把图像分成多个频带,分别处理,减少单层压缩压力。
  • 边缘感知权重:在映射时检测边缘,降低边缘处的压缩强度。

我的经验:

如果双边滤波已经调得很好了还有轻微光晕,试试在合成前对基础层做一次「边缘遮罩」——用 Sobel 检测边缘,在边缘附近降低细节增益。这招我用了好几年,效果稳定。

3.5 知识体系图:局部映射流程

下面这张图总结了局部 Tone Mapping 的完整流程,从输入到输出,每一步都绕不开。

局部 Tone Mapping 流程图 局部 Tone Mapping 流程 HDR 输入图像 对数变换 log(I) 双边滤波分解 σs、σr 参数控制 基础层(光照) 细节层(反射) 全局压缩映射 细节增益(×Gain) 合成 + 指数还原 LDR 输出图像 ⚠ 常见问题 光晕伪影 噪声放大 边缘模糊

3.6 实战建议:参数调试顺序

如果你刚开始调局部映射,我建议按这个顺序来:

  1. 先固定 σs:设为图像宽度的 1/50 左右,比如 1920 宽的图用 38。
  2. 再调 σr:从 0.05 开始,观察边缘保留情况。如果光晕明显,降低 σr。
  3. 最后调细节增益:从 1.2 起步,每次加 0.1,直到细节满意但噪声不显。
  4. 检查高对比边缘:找天空与树枝、窗户与墙壁的交界处,看有没有亮边。

小技巧:

如果双边滤波太慢(尤其大图),可以用引导滤波替代。效果接近,但速度快一个数量级。我在移动端项目里全换成了引导滤波。