4. 传统ISP Pipeline:去马赛克、白平衡、色彩校正矩阵
各位同学,今天我们聊聊ISP Pipeline里最核心的三个模块——去马赛克、白平衡、色彩校正矩阵。这三个模块,说白了就是让Raw图变成人眼看着舒服的彩色图像的关键步骤。
我刚开始做ISP时,总觉得这些模块都是现成的算法,调调参数就行了。后来踩了不少坑才明白——每个模块背后都有物理原理和数学逻辑,不懂原理,调参就是瞎蒙。
4.1 去马赛克(Demosaic)
先说说去马赛克。为什么需要这步?因为CMOS传感器上每个像素只能感知一种颜色——要么红、要么绿、要么蓝。最常见的Bayer模式,就是RGGB排列。
你想想看,每个像素只有一种颜色信息,但我们要输出三通道的彩色图。怎么办?就得靠周围像素猜出缺失的颜色。这就是去马赛克。
4.1.1 最基础的插值方法
最简单的做法是双线性插值。比如G通道,直接取周围四个G像素的平均值。R和B通道也类似。但这种方法有个大问题——边缘处会出现伪彩色和锯齿。
我记得第一次用双线性插值做测试,拍了一张黑白条纹的测试卡,结果边缘全是彩色条纹。当时我还以为是传感器坏了,后来才知道这就是经典的“拉链效应”。
4.1.2 边缘导向插值
为了解决边缘伪色,业界提出了边缘导向插值。核心思想是:先判断当前像素是水平边缘还是垂直边缘,然后沿着边缘方向插值,不跨边缘。
// 边缘导向插值伪代码
// 判断水平梯度和垂直梯度
grad_h = |G(i,j-1) - G(i,j+1)|
grad_v = |G(i-1,j) - G(i+1,j)|
if (grad_h < grad_v) {
// 水平边缘,沿水平方向插值
G(i,j) = (G(i,j-1) + G(i,j+1)) / 2
} else if (grad_v < grad_h) {
// 垂直边缘,沿垂直方向插值
G(i,j) = (G(i-1,j) + G(i+1,j)) / 2
} else {
// 平坦区域,用四个方向平均
G(i,j) = (G(i,j-1) + G(i,j+1) + G(i-1,j) + G(i+1,j)) / 4
}
这种方法比双线性好很多,但在纹理复杂区域还是会有瑕疵。后来出现了自适应插值、基于残差插值等方法,效果越来越好,但计算量也上去了。
4.2 白平衡(AWB)
白平衡,说白了就是让白色物体在任何光源下都呈现白色。人眼有色彩恒常性,但传感器没有。同一个白纸,在日光灯下发蓝,在白炽灯下发黄,传感器拍出来就是偏色的。
白平衡的核心就是估计光源色温,然后做通道增益补偿。
4.2.1 灰度世界假设
最经典的算法是灰度世界假设——认为一幅彩色图像中,R、G、B三个通道的平均值应该相等。如果不等,就说明有偏色,需要调整。
// 灰度世界算法
avg_r = mean(R_channel)
avg_g = mean(G_channel)
avg_b = mean(B_channel)
// 计算增益
gain_r = avg_g / avg_r
gain_b = avg_g / avg_b
// 应用增益
R_out = R_in * gain_r
B_out = B_in * gain_b
这个方法简单,但有个前提——场景中必须有足够多的颜色。如果场景本身就是单一色调(比如一片蓝天),灰度世界假设就会失效。
4.2.2 白点检测法
更靠谱的方法是白点检测。先找出图像中可能是白色的区域,然后用这些区域来估计光源色温。
怎么判断白点?一般用色温曲线。在色度图上,不同光源下的白色点会落在一条曲线上。我们只需要找到落在曲线附近的像素,认为它们是白点,然后计算增益。
4.2.3 色温估计与增益计算
实际工程中,我们通常用查表法。预先标定好不同色温下的R/G和B/G比值,然后根据检测到的白点,找到最接近的色温,再查表得到增益。
| 色温(K) | R/G比值 | B/G比值 | 应用场景 |
|---|---|---|---|
| 2500 | 2.10 | 1.20 | 白炽灯 |
| 4000 | 1.50 | 1.40 | 荧光灯 |
| 5500 | 1.00 | 1.00 | 日光 |
| 6500 | 0.85 | 1.15 | 阴天 |
嗯,这里要注意——查表法需要针对每个传感器模组单独标定。不同传感器、不同镜头、甚至不同批次,色温曲线都会有差异。
4.3 色彩校正矩阵(CCM)
白平衡做完后,颜色基本不偏了,但还不够准。为什么?因为传感器的色彩响应和人眼不一样。传感器滤色片的光谱响应和人眼LMS锥细胞的响应曲线不同,拍出来的颜色饱和度、色调都有偏差。
色彩校正矩阵,就是用一个3x3的矩阵,把传感器颜色空间映射到标准颜色空间(比如sRGB)。
4.3.1 CCM的数学形式
说白了就是线性变换:
[R_out] [a11 a12 a13] [R_in]
[G_out] = [a21 a22 a23] * [G_in]
[B_out] [a31 a32 a33] [B_in]
矩阵系数怎么来的?通过标定。拍一张标准色卡(比如Macbeth色卡),记录传感器输出的RGB值,再和标准值做最小二乘拟合,就能算出矩阵系数。
4.3.2 矩阵系数的约束
CCM不是随便算的。有几个约束必须满足:
- 中性轴约束: 灰色输入必须输出灰色。也就是说,R=G=B时,输出也必须是R=G=B。
- 行和约束: 每行系数之和必须为1,否则会改变亮度。
- 非负约束: 系数最好非负,否则可能出现负值,导致颜色溢出。
4.3.3 多色温CCM
不同色温下,传感器的色彩响应也会变化。所以实际产品中,我们通常准备多组CCM,对应不同色温区间。
比如低色温一组、中色温一组、高色温一组。AWB估计出色温后,再选择对应的CCM。有些方案还会做线性插值,让色温过渡更平滑。
我记得有一次,客户反馈说某个场景下颜色突然跳变。查了半天,发现是色温刚好在两个CCM的切换点附近,导致颜色突变。后来改成线性插值,问题就解决了。
4.4 三者的流水线关系
这三个模块在Pipeline中的顺序是固定的:先做去马赛克,再做白平衡,最后做色彩校正矩阵。
为什么是这个顺序?因为去马赛克需要在Raw域做,白平衡也需要在Raw域或线性域做,而CCM必须在白平衡之后做——否则白平衡的增益会破坏CCM的标定结果。
4.5 工程实践中的注意事项
最后,分享几个我在项目中踩过的坑:
- 去马赛克和降噪的顺序: 先降噪再去马赛克,还是先去马赛克再降噪?我建议先做轻量降噪,再去马赛克,最后做精细降噪。因为去马赛克会放大噪声,先降噪可以减少伪色。
- 白平衡的收敛速度: AWB不能跳变太快,否则视频会闪烁。一般会用低通滤波,让色温平滑过渡。
- CCM的精度与噪声: 矩阵系数越大,颜色越饱和,但噪声也越明显。需要根据场景动态调整——低照度下用弱CCM,高照度下用强CCM。
好了,这一章的内容就到这里。去马赛克、白平衡、CCM这三个模块,是传统ISP Pipeline的基石。理解透了,后面学AI ISP时才能知道哪些地方可以改进、哪些地方必须保留。