12  高级增强

到目前为止,我们对图像做的大多是”修”:去掉噪声(章节 6)、补平照明(章节 4)、把灰度拉到合适的区间。这些操作的共同点是恢复——图像本来应该是什么样,我们就把它修回什么样。本章换一个方向:强化。有些信息确实在图像里,却弱到后续算法无法直接利用——边缘因镜头与离焦的软化而变得平缓,暗部细节淹没在读出噪声里,亮部细节又被传感器的满阱与量化天花板压成一片白。本章的两件工具正是为此而设:锐化(sharpening)把变缓的边缘重新立起来,补偿成像链路的 MTF 损失;高动态范围(high dynamic range, HDR)成像用多次曝光把超出单帧动态范围的明暗两端信息一并收编。图 12.1 先给出第一件工具的直观效果:被离焦软化的边缘,经钝化掩模增强后重新立起。至于照明不均引起的阴影问题,章节 4 已经详细讨论过阴影补正,本章不再重复。

(a) 软化场景(\(\sigma=1.5\) 高斯模拟离焦)
(b) 钝化掩模增强(\(\lambda=1.0\)
图 12.1: 钝化掩模锐化。(a) 锐利场景经 \(\sigma=1.5\) 高斯软化,阶跃边缘被拖出过渡带;(b) \(\lambda=1.0\) 的适度增强让边缘重新立起,且未引入可见伪影。

12.1 锐化与 unsharp mask

锐化最经典、也最常用的形式是钝化掩模(unsharp masking)。这个看似矛盾的名字来自暗房时代:摄影师把底片故意印出一张模糊(“钝化”)的拷贝作为掩模,与原片叠加曝光,结果反而让照片更锐利。它的数字版本只需三步:把图像低通模糊一次,从原图中减去模糊版得到细节层,再把细节层按比例加回去:

\[ g = f + \lambda\,\big(f - G_{\sigma} * f\big), \]

其中 \(G_{\sigma} * f\)\(f\) 的高斯模糊(章节 6 中我们用它压噪声,这里用它分离频率成分),\(\lambda\) 是增强强度。这套操作的逻辑非常直白:高斯模糊保留低频、滤掉高频,因此 \(f - G_{\sigma}*f\) 恰好就是被滤掉的那部分高频——边缘、细线、纹理;把它乘上 \(\lambda\) 加回原图,等价于把图像的高频成分放大 \(1+\lambda\) 倍。\(\lambda\) 就是”高频增益旋钮”:\(\lambda=0\) 原样输出,\(\lambda\) 越大边缘对比越强。模糊核的 \(\sigma\) 则决定”多细才算细节”——它应当与待补偿的模糊尺度相当。

锐化补偿的是镜头像差、轻度离焦等造成的 MTF 衰减(章节 3),但它不能创造信息:它只能放大尚存的高频残余,连同其中的噪声一起放大。若某个空间频率的 MTF 已衰减到零,细节层里就没有它的任何痕迹,乘多大的 \(\lambda\) 也变不出来。锐化是”放大器”,不是”复原器”。

我们用一个可控的实验来演示。取 章节 6 同款的合成场景(亮背景 180、暗矩形 60、一条 2 px 细亮线 250),先用 SciVision 的高斯滤波(\(9\times 9\)\(\sigma=1.5\))把它软化,模拟轻度离焦的镜头:得到 图 12.1 (a),边缘已经明显发虚。再以同样的高斯(\(9\times 9\)\(\sigma=1.5\))作为钝化掩模的模糊项,取 \(\lambda=1.0\) 增强,得到 图 12.1 (b):阶跃边缘重新变得干脆,细亮线的对比也恢复了不少。

12.2 过冲与振铃

既然 \(\lambda\) 是增益旋钮,拧大一点是不是更锐利?图 12.2 (a) 给出了 \(\lambda=4.0\) 的结果:边缘确实”更锐”了,但暗矩形四周浮起一圈刺眼的亮晕(halo),矩形内侧则镶了一道过深的暗边。这就是过冲(overshoot)下冲(undershoot)——锐化在阶跃两侧分别向上、向下”用力过猛”,灰度冲过了本来的平台值。当增强核带有陡峭的频率截止时,过冲还会以衰减振荡的形式重复出现,称为振铃(ringing)

数字最有说服力。我们沿第 180 行提取列 90–150 的灰度剖线(恰好横穿暗矩形左缘,名义灰度亮侧 180、暗侧 60),三条曲线绘于 图 12.2 (b),统计如下:

表 12.1: 行 180 剖线(列 90–150)的过冲统计,名义灰度为 180/60
剖线 min max 过冲 / 下冲
软化原图 58 178 +0 / −2
\(\lambda=1.0\) 56 185 +5 / −4
\(\lambda=4.0\) 36 213 +33 / −24

\(\lambda=1.0\) 时过冲仅 +5/−4 个灰度级,与图像噪声同量级,肉眼不可察;\(\lambda=4.0\) 时冲到 +33/−24,足足把边缘两侧各推出 30 个灰度级上下的”假平台”。

(a) 过度增强(\(\lambda=4.0\)
(b) 行 180 灰度剖线对比
图 12.2: 过冲实验。(a) \(\lambda=4.0\) 在暗矩形外侧产生明显亮晕;(b) 列 90–150 的剖线(浅灰=软化原图,中灰=\(\lambda=1.0\),黑=\(\lambda=4.0\)),边缘位于列 120 附近:\(\lambda=4.0\) 的曲线在阶跃两侧分别冲到 213 与 36,远超 180/60 的名义平台。

过冲绝不只是审美问题,它对测量与检测都是实打实的危害。对测量章节 20 的卡尺工具靠灰度剖线的导数极值定边,过冲改变了剖线在边缘两侧的形状,导数曲线随之畸变,找到的边缘位置会系统性偏移——你以为锐化让边”更好找”,实际上把边”挪了窝”。对检测:暗工件四周的亮晕在 blob 分析或阈值分割看来就是一圈真实存在的高亮区域,足以触发假缺陷报警;反过来,亮区内侧的暗边也可能被误判为裂纹。由此得出 \(\lambda\) 的选择法则:在代表性样本上提取边缘剖线,把 \(\lambda\) 加大到过冲幅度逼近图像噪声幅度为止,不再多给。过冲淹没在噪声里,就不会对下游算法产生系统性影响;本例中 \(\lambda=1.0\)(过冲 +5,噪声同级)合格,\(\lambda=4.0\) 远远越界。另要留意饱和:过冲一旦顶到 0 或 255 被裁剪,原有灰度信息就永久丢失了。

12.3 高动态范围与曝光融合

第二类”弱信息”问题出在动态范围(dynamic range):场景最亮与最暗处的辐照度之比,超过了单帧图像所能记录的范围。上限来自传感器满阱容量与 8 位量化的天花板(章节 3章节 1),下限来自读出噪声的地板。结果是一个两难:曝光迁就亮部,暗部细节沉入噪声;曝光迁就暗部,亮部一片死白。焊接熔池旁的工件轮廓、抛光金属上的刻印字符,都是典型受害者。

多曝光融合(exposure fusion)的思路是:动态范围不够,就多拍几帧凑。同一静止场景拍摄多档曝光——短曝光留住亮部,长曝光捞起暗部——再逐像素把各帧信息合并成一幅辐照度(irradiance)估计图。在线性响应假设下(像素值 \(v_i \approx E \cdot t_i\)\(t_i\) 为相对曝光量),每一帧都给出辐照度的一个观测 \(v_i/t_i\),加权平均即得估计:

\[ \hat E(p) = \frac{\sum_i w(v_i)\, v_i / t_i}{\sum_i w(v_i)}, \qquad w(v) = \min(v,\, 255-v) + \varepsilon . \]

这是 Debevec 式辐照度恢复的简化形式,\(w\)三角权重:像素值居中时权重最大,趋近 0 或 255 时权重趋零。

三角权重的直觉是一场”选票审查”:每帧曝光都对该像素的辐照度投一票,但太暗的票不算数(信号淹在噪声里,\(v_i/t_i\) 极不可靠),太亮的票也不算数(已截断饱和,\(v_i\) 根本不等于 \(E\cdot t_i\))。只有曝光适中的帧才有发言权——而对场景里的任何一个点,总有某档曝光恰好适中,这正是多曝光的全部意义。

恢复出的 \(\hat E\) 范围超出 8 位,最后还需色调映射(tone mapping)压回可显示区间。本章用最简单的全局 \(\gamma\) 映射:\(\text{out} = 255\,(\hat E/\hat E_{\max})^{1/2.2}\)\(\gamma=1/2.2\) 抬升暗部、压缩亮部,与人眼感受大致匹配。

实验场景的辐照度场这样构造:中灰背景 110,左上亮区藏着 252/272 的竖条纹细节(对比 20,但峰值超过 255 的 8 位天花板),右下暗区藏着 14/24 的竖条纹(对比 10,紧贴噪声地板)。以 ×0.25 / ×1.0 / ×4.0 三档增益曝光,各自截断到 [0,255] 并叠加 \(\sigma=2\) 的读出噪声(固定种子 11/22/33,结果可复现),得到 图 12.3 的三帧;融合并色调映射后得到 图 12.4

(a) 短曝光(×0.25)
(b) 中曝光(×1.0)
(c) 长曝光(×4.0)
图 12.3: 三档曝光。(a) ×0.25:亮区条纹尚存,暗区沉入噪声;(b) ×1.0:亮区条纹被 255 截断殆尽,暗区细节勉强可辨;(c) ×4.0:暗区条纹清晰,亮区彻底饱和成一片白。
图 12.4: 三帧融合并经 \(\gamma=1/2.2\) 色调映射后的 HDR 结果:亮区条纹与暗区条纹同时清晰可见。

肉眼判断之外,我们对两个条纹细节块统计均值与标准差(标准差衡量条纹对比的可见性):

表 12.2: 细节块的均值与标准差对比
图像 暗细节块 mean / std 亮细节块 mean / std
短曝光(×0.25) 4.8 / 2.35 65.5 / 3.22
中曝光(×1.0) 19.0 / 5.40 253.5 / 2.03
长曝光(×4.0) 76.0 / 20.13 255.0 / 0.00
HDR 融合 72.5 / 8.76 239.8 / 5.35

逐行读这张表:短曝光的暗块 std 2.35 与读出噪声 \(\sigma=2\) 几乎相等——条纹完全淹没;中曝光的亮块均值 253.5、std 2.03,是截断后的残迹;长曝光的亮块 std 精确为 0.00——彻底饱和,信息归零。四幅图中,只有融合图在两个细节块上同时保有明确的条纹对比(std 8.76 与 5.35,均数倍于噪声),这正是 HDR 的价值所在。

实验还附赠了一个教学点:程序打印的恢复辐照度峰值 \(\hat E_{\max}=300.0\),而场景真值是 272。误差来自短曝光帧——它的 \(\sigma=2\) 读出噪声在 \(v/t\) 还原时被 \(1/0.25=4\) 倍放大;在中、长曝光都已饱和的最亮像素处,只剩短曝光一帧”有效选票”,被放大 4 倍的噪声便直接顶到了估计值上。这提醒我们:短曝光帧对辐照度估计贡献的是被放大的噪声,更精细的实现会在三角权重之外再乘以曝光时间相关的信噪比权重。

12.4 SciVision 实现

本章的 SDK 使用情况需要如实交代,因为它本身就是一堂工程课。我们原计划用 SciSvEdgeEnhance::EnhanceEdges 做锐化、用 SciSvHDR 系列做曝光融合,但开发本章示例时的系统化测试(v3.1)得到如下结论:

  • SciSvEdgeEnhance::EnhanceEdgestype/offset/factor/brightness 做全参数扫描,所有组合都输出 0/255 的二值边缘图,而非”原图 + 边缘增强”的锐化图像——它实际上是一个边缘图生成器,名字会误导人;
  • SciSvHDR::FusedPicture 灰度输入返回码为 0,但把整个暗区压成均值 0.1 的死黑(细节全失);3 通道输入直接触发 OpenCV cv::Mat 断言崩溃;
  • SciSvHDR::Compose 对 480×360 单通道输入,返回被裁剪成 449×340 的 3 通道图;
  • SciSvHDR::ToneMapping(method=1) 触发 OpenCV cv::scaleAdd 断言,进程直接终止(退出码 0xC0000409)。

因此本章的锐化与 HDR 融合均为手写实现,SDK 只承担它确实可靠的两件事:高斯滤波与图像读写。高斯部分与 章节 6 完全一致:

SCIMV::SciSvFilter flt;
SciImage soft, blur;
flt.Gaussian(imgSharp, roi, &soft, 9, 9, 1.5, 1.5);  // 模拟轻度离焦
flt.Gaussian(soft, roi, &blur, 9, 9, 1.5, 1.5);      // 钝化掩模的模糊项

钝化掩模核心只有一行算术,逐像素执行 \(\text{out} = \text{in} + \lambda(\text{in} - \text{blur})\) 并钳位到 [0,255]:

double v = in[i] + lambda * (in[i] - blur[i]);
out[i] = (unsigned char)(v < 0 ? 0 : (v > 255 ? 255 : (int)(v + 0.5)));

实现中距图像边界 5 px 以内不处理:SDK 高斯在边界处的外推值与原图相减会产生虚假差值,直接增强会在四周框出黑白边框伪影。曝光融合的核心同样紧凑——逐像素对三帧累加三角权重投票:

for (int k = 0; k < 3; ++k) {
    double v = (double)exps[k][i];
    double w = (v < 255.0 - v ? v : 255.0 - v) + 1e-3;  // 三角权重
    sw += w;
    se += w * v / t[k];     // t[] = {0.25, 1.0, 4.0}
}
E[i] = se / sw;             // 随后做 gamma=1/2.2 色调映射

完整工程在 code/advanced_enhancement/,文件头保留了上述 SDK 缺陷的完整测试记录。这段经历的工程启示值得加粗:商用视觉库的每个 API,在进产线之前都必须用已知真值的黄金实验验证其实际行为——构造一个你能手算结果的合成输入,喂给 API,核对输出。这与 章节 5 用已知靶标检验标定结果的方法论一脉相承:文档描述的是承诺,实验验证的才是行为。

工业案例:电池壳激光刻码的 HDR 读码

某电池壳产线在抛光铝壳表面激光刻印批次码,读码相机正对高反金属面:单帧曝光要么字符笔画饱和成一片白,要么背景压成死黑,字符与背景的对比始终凑不齐,读码率徘徊在 70% 左右。改造方案是同一工位连拍两档曝光(短曝光保字符、长曝光保背景),按本章的加权融合方式合成一帧再送读码器,读码率升至 99% 以上。代价同样明确:每件产品多拍一帧,采集节拍翻倍,产线被迫降速。后续评估的替代路线是换用 12 bit 高动态范围相机,一帧覆盖全部灰度跨度——硬件更贵,但节拍不变。这个案例的通用结论是:动态范围问题先想硬件(更深位深、更大满阱),再想算法(多曝光融合)——算法路线省钱,却用时间来支付。

12.5 小结

  • 增强是放大,不是复原。钝化掩模 \(g = f + \lambda(f - G_\sigma * f)\) 把高频成分放大 \(1+\lambda\) 倍,可补偿镜头与离焦造成的 MTF 衰减,但 MTF 已归零的细节谁也救不回来,噪声还会被一并放大。
  • \(\lambda\) 以过冲为界。在代表性边缘上提剖线,过冲幅度不超过噪声幅度即止(本章 \(\lambda=1.0\) 过冲 +5 合格,\(\lambda=4.0\) 过冲 +33 越界);过冲会让卡尺找边系统性偏移,亮晕会制造假缺陷。
  • 动态范围不够,多曝光来凑。辐照度恢复 \(\hat E = \sum w(v_i)v_i/t_i \,/\, \sum w(v_i)\) 配三角权重——太暗太亮的票不算数;实验中只有融合图让亮、暗两处细节同时可见。
  • 短曝光帧的噪声会被 \(1/t\) 放大(本章 \(\hat E_{\max}=300\) 对真值 272),精细实现应叠加信噪比权重;融合的工程代价是节拍翻倍,动态范围问题应先评估高位深相机的硬件路线。
  • 商用库的 API 行为必须用黄金实验验证。本章 SDK 的锐化与 HDR 接口全部名不符实或直接崩溃,最终方案是 SDK 只做高斯与 IO、核心算法手写——文档是承诺,实验才是行为。

钝化掩模等锐化方法的标准论述见 Gonzalez 与 Woods 的教材 (Gonzalez 和 Woods 2018);本章多曝光合成的方法学源头,是 Debevec 与 Malik 在 SIGGRAPH 1997 上恢复相机响应函数、融合高动态范围辐照图的工作 (Debevec 和 Malik 1997),而高动态范围成像从采集、显示到色调映射的完整体系,可参阅 Reinhard 等人的专著 (Reinhard 等 2010)。锐化滤波器的频域分析与更系统的灰度变换方法,可进一步阅读 Steger 等人的著作 (Steger, Ulrich, 和 Wiedemann 2018)