4. RAW域色彩处理:从传感器原始数据到真实色彩
各位好,欢迎来到第四章。前面我们聊了RAW域的黑电平校正和去噪,现在传感器总算输出了相对干净的RAW数据。但这时候的图像,颜色还是偏的——不信你拿一张没做白平衡的RAW图看看,要么偏蓝要么偏黄,看着就不对劲。
这一章我们要解决的就是:如何让RAW数据还原出真实的色彩。说白了,就是从传感器捕捉到的原始信号,经过白平衡、色彩校正、插值这三步,变成人眼看着舒服的颜色。
4.1 白平衡增益计算:让白色回归白色
白平衡,说白了就是让图像里本该是白色的物体,在任意光源下都呈现白色。你想想看,一张白纸在日光灯下偏蓝,在白炽灯下偏黄,但人眼会自动校正——相机可没这个本事,得靠算法来算。
白平衡的核心就是给R、G、B三个通道分别乘上一个增益系数,让它们达到平衡。公式很简单:
R_out = R_in * gain_R
G_out = G_in * gain_G
B_out = B_in * gain_B
关键问题是:这三个增益怎么算? 业界最常用的有两种方法。
4.1.1 灰度世界法(Gray World)
这个方法假设:一幅色彩丰富的图像,所有像素的R、G、B平均值应该趋于相等,也就是趋于灰色。基于这个假设,增益计算就变成了:
R_avg = mean(R_all)
G_avg = mean(G_all)
B_avg = mean(B_all)
gain_R = G_avg / R_avg
gain_G = 1.0 // 通常以G通道为基准
gain_B = G_avg / B_avg
我在项目中遇到过一个问题:如果场景里大面积都是同一种颜色,比如一片蓝天或者一片绿草地,灰度世界法就会翻车。天空的蓝色会让B_avg偏高,gain_B被压得很低,结果整个画面偏黄。嗯,这就是灰度世界法的局限性——它依赖场景的"色彩丰富度"。
4.1.2 完美反射法(Perfect Reflector)
这个方法基于另一个假设:图像中最亮的像素点,应该是纯白色的——也就是它反射了所有波长的光。所以,找到这些"最亮点",用它们的RGB值来计算增益。
具体做法:
- 统计每个像素的亮度值(比如取R+G+B或者max(R,G,B))
- 选出亮度最高的前1%~5%的像素
- 计算这些像素的R、G、B平均值
- 用这些平均值计算增益,让它们趋于相等
// 伪代码示例
bright_pixels = select_top_percent(image, 0.02) // 取最亮2%
R_highlight = mean(bright_pixels.R)
G_highlight = mean(bright_pixels.G)
B_highlight = mean(bright_pixels.B)
gain_R = G_highlight / R_highlight
gain_G = 1.0
gain_B = G_highlight / B_highlight
完美反射法对高光区域比较敏感。我曾经调试一个室内场景,天花板上有盏灯,灯罩本身是暖黄色的——结果完美反射法把整个画面都往冷色调拉,因为算法以为那个暖黄色就是"白色"。后来我加了一个色温范围约束,才把这个问题解决。
4.2 色彩校正矩阵(CCM)原理
白平衡做完之后,颜色基本不偏了,但还不够准。为什么?因为传感器的光谱响应和人眼不一样。传感器对红绿蓝的敏感曲线,跟人眼视锥细胞的响应曲线有偏差。所以我们需要一个矩阵,把传感器空间的RGB映射到标准色彩空间(比如sRGB或BT.709)。
这个矩阵就是CCM,一个3x3的矩阵:
| R_out | | a11 a12 a13 | | R_in |
| G_out | = | a21 a22 a23 | * | G_in |
| B_out | | a31 a32 a33 | | B_in |
展开来看,每个输出通道都包含了输入通道的线性组合。比如R_out = a11*R_in + a12*G_in + a13*B_in。这意味着红色通道会混入一些绿色和蓝色的信息——这其实是好事,因为人眼的红色视锥细胞对绿光也有微弱响应。
4.2.1 CCM的标定过程
CCM不是拍脑袋定的,得通过标定得到。标准流程是这样的:
- 拍摄一张标准色卡(比如X-Rite ColorChecker,24色块)
- 记录每个色块在传感器RAW域的RGB值
- 查表得到每个色块在目标色彩空间的标准值
- 用最小二乘法求解3x3矩阵,让误差最小
我记得第一次做CCM标定时,拍完色卡直接算矩阵,结果出来的颜色偏得离谱。后来才发现——CCM必须在白平衡之后做。如果先做CCM再做白平衡,矩阵系数就全乱了。这个顺序问题,我踩过坑,希望大家别重蹈覆辙。
- 对角线元素(a11, a22, a33):通常接近1.0,代表主通道的贡献
- 非对角线元素:通常为负值或小正值,代表通道间的串扰校正
- 行和约束:每行系数之和通常为1.0,保证亮度不变
4.2.2 不同色温下的CCM
一个CCM不能打天下。不同色温下,传感器的光谱响应特性会变化。我一般会标定3~5组CCM,对应不同的色温区间:
| 色温区间 | 典型光源 | CCM特点 |
|---|---|---|
| 2500K - 3500K | 白炽灯、烛光 | 红色增益较大,蓝色串扰校正较强 |
| 4000K - 5000K | 荧光灯、暖白LED | 各通道均衡,串扰适中 |
| 5500K - 6500K | 日光、闪光灯 | 标准CCM,参考设计基准 |
| 7000K - 10000K | 阴天、蓝天阴影 | 蓝色增益较大,红色串扰校正较强 |
实际应用中,会根据白平衡估算出的色温,在几组CCM之间做线性插值。这样就能平滑过渡,不会出现色温切换时的颜色跳变。
4.3 色彩插值(Demosaic)算法
好了,白平衡和CCM都做完了,但这时候的数据还是Bayer格式——每个像素只有一个颜色通道。要得到全彩图像,就得把缺失的两个通道猜出来。这个过程就叫Demosaic,也叫色彩插值。
Bayer模式最常见的排列是RGGB,也就是一个像素是R,两个是G,一个是B。G通道占了一半,因为人眼对绿色最敏感。
4.3.1 双线性插值(Bilinear)
这是最基础的方法,简单粗暴:每个像素缺失的颜色,用周围同色像素的平均值来填充。
比如一个R像素,它缺G和B。G值就用上下左右四个G像素的平均,B值就用对角线上四个B像素的平均。
// 以R像素为例,计算缺失的G值
G_at_R = (G_left + G_right + G_up + G_down) / 4
// 计算缺失的B值
B_at_R = (B_top_left + B_top_right + B_bottom_left + B_bottom_right) / 4
这个方法计算量小,但缺点很明显——边缘会模糊。你想想看,如果R像素刚好在一条黑白边界上,周围G像素的平均值会把边界抹掉,出现彩色伪影(也叫"拉链效应")。
4.3.2 边缘导向插值(Edge-directed)
这个方法比双线性聪明一点。它先判断当前像素附近有没有边缘,然后沿着边缘方向插值,而不是无脑平均。
具体做法:
- 计算水平和垂直方向的梯度
- 如果水平梯度小,说明边缘是垂直方向的,就沿着水平方向插值
- 如果垂直梯度小,说明边缘是水平方向的,就沿着垂直方向插值
- 如果两个方向梯度差不多,就取平均
// 计算梯度
grad_h = abs(G_left - G_right)
grad_v = abs(G_up - G_down)
// 边缘导向插值
if (grad_h < grad_v) {
G_at_R = (G_left + G_right) / 2 // 水平方向
} else if (grad_v < grad_h) {
G_at_R = (G_up + G_down) / 2 // 垂直方向
} else {
G_at_R = (G_left + G_right + G_up + G_down) / 4
}
边缘导向插值能大幅减少拉链效应,但也不是万能的。我在调试一个带细条纹的测试图时,发现斜向边缘还是会有伪影——因为算法只考虑了水平和垂直两个方向。
4.3.3 自适应插值(Adaptive)
这是目前主流方案,也是我项目中用得最多的。自适应插值会综合考虑多个因素:梯度、色差、纹理复杂度,甚至会用上迭代优化。
一个经典的自适应算法流程:
- 先插G通道:因为G通道采样率最高,信息最完整。用边缘导向+色差约束来插值
- 再插R和B通道:利用G通道的细节信息,通过色差恒定性假设来恢复R和B
- 后处理:检测并修正彩色伪影,做边缘增强
色差恒定性假设是什么意思?说白了,就是认为在局部区域内,R-G和B-G的差值变化很缓慢。所以我们可以用已知像素的色差来推断未知像素的色差。
// 自适应插值核心思路
// 1. 用边缘导向插值得到完整的G通道
G_full = edge_directed_interpolate(raw_bayer)
// 2. 计算已知位置的色差
delta_RG = R_known - G_full_at_R
delta_BG = B_known - G_full_at_B
// 3. 用色差插值得到R和B
R_missing = G_full_at_R + interpolate(delta_RG)
B_missing = G_full_at_B + interpolate(delta_BG)
这个方法的效果明显好于前两种。我做过对比测试:同样的RAW图,双线性插值的PSNR大概在35dB左右,边缘导向能到38dB,自适应算法可以做到40dB以上。而且主观上看,自适应算法的伪影最少,边缘最锐利。
- 低端平台:边缘导向插值 + 简单后处理
- 中端平台:自适应插值(色差法)
- 高端平台:自适应插值 + 迭代优化 + 深度学习去伪影
本章小结
RAW域的色彩处理,说白了就是三步走:白平衡把色偏拉回来,CCM把传感器颜色映射到标准空间,Demosaic把单通道猜成全彩。每一步都有坑,每一步都有取舍。
我个人觉得,这三步里最容易被忽视的是顺序。白平衡必须在CCM之前,Demosaic必须在CCM之后——这个顺序错了,颜色怎么调都调不对。另外,白平衡的两种方法各有适用场景,别死磕一种;CCM要多标定几组,覆盖不同色温;Demosaic要根据平台性能选合适的算法,别一味追求最高质量。