2、色彩校正算法基础:白平衡、Gamma与色彩矩阵

色彩校正,说白了就是让摄像头拍出来的颜色,跟人眼看到的颜色尽量一致。我做了这么多年图像处理IP,发现很多刚入行的同学容易忽略算法层面的理解,一上来就撸RTL代码,结果调出来的颜色怎么看怎么别扭。这一章,咱们先把算法底子打牢。

色彩校正的核心,主要围绕三个问题:白平衡(解决色偏)、Gamma校正(解决亮度非线性)、色彩矩阵变换(解决色彩串扰)。下面我一个一个讲。

2.1 白平衡算法

白平衡要解决什么问题?你想想看,同一张白纸,在日光灯下偏蓝,在白炽灯下偏黄。人眼会自动适应,但传感器不会。所以我们需要算法把“白”给找回来。

业界常用的白平衡算法,主要有两种:灰度世界法和完美反射法。我个人的习惯是,在低端Sensor上用灰度世界法,在中高端Sensor上用完美反射法或者两者的融合。

2.1.1 灰度世界法

这个算法的假设很简单:一张色彩丰富的图片,所有像素的R、G、B平均值应该趋于相等(即灰色)。如果某个通道的平均值偏高,说明存在色偏,需要压低它。

具体步骤就三步:

  1. 计算整帧图像R、G、B三个通道的平均值:R_avg、G_avg、B_avg
  2. 计算三个平均值的总平均:Gray = (R_avg + G_avg + B_avg) / 3
  3. 计算每个通道的增益:Gain_R = Gray / R_avg,Gain_G = Gray / G_avg,Gain_B = Gray / B_avg

然后,每个像素乘以对应的增益,就完成了白平衡校正。

关键点:灰度世界法对场景有要求。如果画面里大面积是单一颜色(比如一片蓝天、一片绿草地),这个假设就不成立了。我曾经在一个项目中遇到过,用户拍了一整片蓝色的海,结果灰度世界法把蓝色通道增益压得很低,整张图变成了偏黄色。后来我加了一个场景检测逻辑,才解决了这个问题。

2.1.2 完美反射法

完美反射法的假设是:图像中最亮的点(R+G+B之和最大)应该是纯白色的。所以,我们找到这些“疑似白色”的点,用它们的RGB值来计算增益。

算法流程:

  1. 遍历所有像素,计算每个像素的亮度值:L = R + G + B
  2. 找出亮度最高的前1%~5%的像素(这个比例可以配置)
  3. 计算这些像素的R、G、B平均值:R_max、G_max、B_max
  4. 计算增益:Gain_R = 255 / R_max,以此类推

我的经验:完美反射法对高光区域敏感。如果场景中有强光源(比如太阳、灯泡),这些区域会被误判为白色参考点,导致整体偏暗。我建议在硬件实现时,加一个亮度阈值上限,排除掉过曝的像素。

两种算法的对比,我整理了一个表格:

特性 灰度世界法 完美反射法
假设前提 图像平均色为灰色 最亮点为白色
计算复杂度 低(只需累加) 中(需要排序或阈值比较)
适用场景 色彩丰富的自然场景 有明确白色参考的场景
硬件资源 少量加法器和除法器 需要比较器和FIFO
典型缺陷 大面积单色会失效 强光源会导致过暗

2.2 Gamma校正原理

Gamma校正,说白了就是“亮度映射”。人眼对暗部细节的敏感度远高于亮部,而传感器是线性响应的。如果不做Gamma校正,暗部细节会丢失,画面看起来灰蒙蒙的。

标准Gamma校正公式:

V_out = V_in ^ (1 / Gamma)

其中Gamma值通常取2.2。为什么是2.2?这是历史原因,CRT显示器的物理特性决定的。现在虽然不用CRT了,但这个标准保留了下来。

在硬件实现中,直接用乘方运算是不现实的。我们通常用分段线性逼近或者查找表(LUT)来实现。

避坑指南:我曾经在FPGA上实现Gamma校正时,直接用浮点运算,结果资源消耗巨大,时序还跑不过。后来改用512条目的LUT,用8位输入查10位输出,效果几乎一样,资源却省了90%。记住,硬件里能用查表解决的问题,就别用计算。

Gamma校正的典型曲线(输入8bit,输出10bit):

输入值 线性输出 Gamma校正后 效果
0 0 0 纯黑不变
64(暗部) 256 ~512 暗部被提亮
128(中间) 512 ~724 适度提升
192(亮部) 768 ~887 轻微压缩
255 1023 1023 纯白不变

你看,暗部(输入64)被映射到了更高的值,细节就出来了。亮部被轻微压缩,但人眼对亮部不敏感,所以感知上没问题。

2.3 色彩矩阵变换

色彩矩阵变换,解决的是传感器色彩空间到标准色彩空间的转换问题。传感器的RGB跟标准sRGB的色域不一样,直接显示会偏色。

标准形式是一个3x3矩阵:

[ R_out ]   [ a11  a12  a13 ]   [ R_in ]
[ G_out ] = [ a21  a22  a23 ] * [ G_in ]
[ B_out ]   [ a31  a32  a33 ]   [ B_in ]

这个矩阵的系数,通常由Sensor厂商提供,或者通过拍摄标准色卡(比如X-Rite ColorChecker)标定得到。

在RTL实现时,我一般用流水线结构:

  1. 第一级:并行计算三个乘法(比如R_out的a11*R_in、a12*G_in、a13*B_in)
  2. 第二级:加法器累加三个乘积
  3. 第三级:饱和截位(防止溢出)

我的建议:矩阵系数用定点数表示,比如Q8.8格式(8位整数,8位小数)。这样乘法结果用16位,加法用18位,最后截位到8位输出。精度够用,资源也省。

下面这张图,展示了本章三个算法的整体流程关系:

色彩校正算法流程 RAW 图像输入 白平衡 灰度世界法 / 完美反射法 色彩矩阵变换 3x3 矩阵乘法 + 流水线 Gamma 校正 查找表 (LUT) / 分段线性 算法要点 1. 白平衡:消除色偏 2. 色彩矩阵:色域转换 3. Gamma:亮度非线性 4. 顺序:先WB,再CCM 最后Gamma 5. 硬件:流水线+查表 6. 精度:定点Q8.8

嗯,到这里,色彩校正的三大算法基础就讲完了。白平衡负责把颜色拉回中性,Gamma校正负责把亮度映射到人眼敏感区,色彩矩阵负责把传感器颜色转换到标准色域。这三个模块在硬件流水线中是串联的,顺序不能乱。