4. 高通色差校正算法原理:ACD 机制深度解析
色差校正,说白了就是去掉照片边缘那些紫边、绿边。你拍过逆光的树枝吧?放大一看,树干边缘一圈紫,那就是色差。高通平台处理这个问题的核心算法叫 ACD——Achromatic Color Difference,翻译过来就是「消色差色差校正」。名字有点绕,但机制其实很巧妙。
我刚开始接触高通 ISP 时,以为色差校正就是简单做个颜色滤波。后来在调试一款 48M 传感器时才发现,ACD 的底层逻辑跟我想的完全不一样。它不是直接改颜色,而是先判断「这个地方该不该有色差」,再决定怎么修。嗯,这个判断机制才是精髓。
4.1 色差是怎么产生的?
先简单回顾一下物理原因。镜头本质上是凸透镜,不同波长的光折射率不同——蓝光弯折多,红光弯折少。所以当光线经过镜头边缘时,红绿蓝三个通道的成像位置会错开,形成横向色差(Lateral Chromatic Aberration)。
在 Bayer 域看,就是某个像素本该是纯白,结果 R 通道和 B 通道的响应值出现了偏移。偏移量跟像素到图像中心的距离成正比,离中心越远,色差越明显。
关键指标:色差强度通常用 R/G 和 B/G 的比值偏离 1.0 的程度来衡量。偏离超过 5% 时,人眼就能明显感知到紫边或绿边。
4.2 ACD 算法的核心思想
高通 ACD 跟传统方法最大的区别在于:它不直接做通道减法,而是先建立一个「消色差参考面」。什么意思呢?
传统方法通常是:检测到边缘 → 计算 R-B 差值 → 如果差值过大就削平。这种做法的问题是容易把真实颜色细节也削掉,比如蓝色天空和绿色树叶的交界处,本来就有颜色差异,你一刀切下去,细节就糊了。
ACD 的做法是:
- 对每个像素,计算一个「无彩色期望值」——也就是如果这里没有色差,R、G、B 应该是什么比例。
- 比较实际值和期望值的偏差。
- 只修正超出阈值的偏差部分。
说白了,它知道「什么颜色是镜头带来的假象」,只修假象,不动真颜色。
我的经验:我在调试一款 13M 广角镜头时发现,ACD 对镜头边缘的紫边抑制效果非常好,但前提是镜头标定数据要准确。如果标定时的色差映射表不准,ACD 反而会把边缘搞出伪色。所以,先标定,再调参,顺序不能乱。
4.3 ACD 的算法流程
ACD 的处理流程大致分四步。我画了一张流程图,方便你理解整体脉络。
4.4 梯度检测与边缘方向
ACD 的第一步是判断当前像素是否在边缘上,以及边缘的方向。为什么方向重要?因为色差只在垂直于边缘的方向上才明显。沿着边缘方向,色差几乎为零。
具体做法是计算水平和垂直两个方向的梯度:
// 简化版梯度计算(以 G 通道为例)
Grad_H = |G(x-1,y) - G(x+1,y)|
Grad_V = |G(x,y-1) - G(x,y+1)|
if (Grad_H > Grad_V * 1.5) {
// 垂直边缘,色差主要出现在水平方向
edge_direction = VERTICAL;
} else if (Grad_V > Grad_H * 1.5) {
// 水平边缘,色差主要出现在垂直方向
edge_direction = HORIZONTAL;
} else {
// 纹理区域或平坦区域,色差校正强度降低
edge_direction = FLAT;
}
注意:梯度阈值 1.5 不是固定值。我在项目中遇到过,某些低照度场景下噪声会干扰梯度判断,导致边缘方向误判。建议根据 sensor 的噪声水平动态调整这个阈值,或者在做梯度检测前先做一层轻量级降噪。
4.5 局部色差估计与参考面构建
这一步是 ACD 的核心。算法会在边缘两侧各取一个局部窗口,分别计算 R/G 和 B/G 的比值。然后根据边缘方向,插值出一个「无色差参考面」。
举个例子:假设检测到一条垂直边缘,左边是亮区,右边是暗区。ACD 会在亮区取一个 5x5 窗口,计算平均 R/G 和 B/G;在暗区也取一个 5x5 窗口,计算同样的比值。然后,对边缘上的每个像素,根据它到亮区和暗区的距离,线性插值出它应该有的 R/G 和 B/G 比值。
这个插值出来的比值,就是「消色差参考面」。
| 区域 | R/G 比值 | B/G 比值 | 说明 |
|---|---|---|---|
| 亮区参考 | 0.98 | 1.02 | 无偏色,接近 1.0 |
| 暗区参考 | 0.95 | 1.05 | 轻微偏蓝(常见于镜头边缘) |
| 边缘像素实际值 | 0.88 | 1.12 | 明显色差,R 偏低,B 偏高 |
| 边缘像素参考值 | 0.96 | 1.04 | 插值得到的期望值 |
你看,实际值和参考值之间的偏差,就是需要校正的部分。偏差越大,校正强度越高。
4.6 阈值滤波与校正输出
最后一步,算法会设置一个阈值。只有偏差超过阈值的像素才做校正。为什么要加阈值?因为传感器本身有噪声,R/G 和 B/G 的比值不可能精确到 1.0,总会有 ±1%~2% 的波动。如果没有阈值,ACD 会把噪声也当成色差来修,结果就是平坦区域出现彩色噪点。
阈值通常设置为 3%~5%,具体值取决于镜头质量和 sensor 噪声水平。
// 校正逻辑伪代码
float threshold = 0.04; // 4% 阈值
float dev_R = actual_RG_ratio - ref_RG_ratio;
float dev_B = actual_BG_ratio - ref_BG_ratio;
if (abs(dev_R) > threshold) {
correction_R = dev_R * gain; // gain 通常 0.5~1.0
} else {
correction_R = 0;
}
// 对 B 通道做同样处理
// 最终输出:R_out = G * (ref_RG_ratio + correction_R)
调参建议:我习惯先把 gain 设到 1.0,threshold 设到 0.03,然后对着色卡拍一张。如果边缘还有紫边,就降低 threshold;如果平坦区域出现伪色,就提高 threshold。gain 一般不动,除非你想做「过校正」来补偿后续的锐化带来的色差放大。
4.7 ACD 的局限性
ACD 不是万能的。我在项目中遇到过两个典型问题:
- 大光比场景:当边缘两侧亮度差超过 10 档时,暗区的信噪比太低,R/G 和 B/G 的比值波动很大,参考面构建不准确。这时候 ACD 容易把噪声当成色差来修,反而引入伪色。
- 细纹理区域:比如远处的铁丝网,每个像素都在边缘上,ACD 的窗口统计会失效。这种情况下,我建议降低 ACD 的强度,或者配合边缘增强模块做联合优化。
我曾经在一个项目中,客户反馈说「拍树叶时边缘有绿色溢出」。排查了半天,发现是 ACD 的窗口大小设置得太小(3x3),导致参考面统计不稳定。改成 7x7 后问题就解决了。嗯,窗口大小这个参数,真的得根据实际场景来调。
4.8 小结
ACD 的核心思路就是:先知道「应该是什么颜色」,再决定「要不要修」。它不直接操作颜色值,而是操作比值,这样对光照变化不敏感。理解了这个机制,后面调参和 debug 就会顺手很多。