色彩校正矩阵(CCM)是ISP流水线中解决"相机看到的颜色和人眼看到的颜色不一致"问题的核心算法模块。简单来说,当你在日光灯下拍出的照片总是偏蓝,或者在夕阳下人脸显得过分红润,这些问题往往需要通过调整CCM矩阵来解决。
CCM本质上是一个3x3的矩阵,通过矩阵乘法对RGB三个通道进行线性变换。举个例子,假设原始像素值是[R, G, B],经过CCM变换后会得到新的像素值[R', G', B'],其中:
code复制R' = a00*R + a01*G + a02*B
G' = a10*R + a11*G + a12*B
B' = a20*R + a21*G + a22*B
我在调试车载摄像头时遇到过典型场景:拍摄交通标志时,红色停止牌总是偏橙色。通过分析发现是CCM中R通道的G分量系数(a01)过高,导致绿色成分混入过多。调整这个参数后,红色立即变得纯正。
注意:CCM调整必须在线性RGB空间进行,这意味着需要先对图像做degamma处理,调整后再重新应用gamma校正。很多新手会忽略这一点,直接处理sRGB图像导致效果异常。
标定CCM需要标准24色卡(推荐X-Rite ColorChecker Classic)和稳定光源环境。我建议搭建一个简易灯箱,保证照度在600Lux左右且均匀度>90%。实测中发现,使用D65光源(色温6500K)能获得最稳定的标定结果。
关键设备清单:
采集RAW数据时,要确保最亮灰阶块(第19块)的G分量在饱和值的80%左右。这个经验值来自多次测试——过高会导致高光细节丢失,过低则信噪比不足。我通常用以下Python代码自动检测合适曝光:
python复制def find_optimal_exposure(camera):
for ev in range(5, 20):
img = camera.capture(ev_comp=ev)
gray = 0.299*img[:,:,0] + 0.587*img[:,:,1] + 0.114*img[:,:,2]
if np.max(gray[18]) > 0.75*255: # 第19块是0-based索引的18
return ev-1
return 15
标定的数学本质是最小二乘法求解。设色卡实际测得值为X(18x3矩阵),标准值为Y(18x3矩阵),则CCM矩阵A(3x3)满足:
code复制min ||XA - Y||^2
但直接这样计算可能会破坏白平衡。我的改进方法是增加约束条件,保证[1,1,1]*A≈[1,1,1]。具体实现时,可以把这个约束作为正则项加入损失函数:
python复制def calculate_ccm(X, Y, lambda_wb=0.1):
# 添加白平衡约束
X_aug = np.vstack([X, lambda_wb*np.ones((1,3))])
Y_aug = np.vstack([Y, lambda_wb*np.ones((1,3))])
# 最小二乘求解
A = np.linalg.lstsq(X_aug, Y_aug, rcond=None)[0]
return A.T
当出现色彩偏差时,首先要确定问题是来自AWB还是CCM。我常用的诊断流程是:
最近调试安防摄像头时,发现夜间模式人脸偏绿。通过上述方法确认是AWB在低照度下失效,而不是CCM问题,最终通过调整AWB参数解决。
对于特定颜色校正(如交通标志、肤色),可以针对性调整CCM参数。记住这个对应关系:
比如要让天空更蓝,可以适当增加a22并减小a20。但要注意调整幅度不宜超过±0.15,否则会导致其他颜色异常。
WDR(宽动态范围)场景是CCM调试的难点。我的经验是:
这个组合方案在车载环视系统中效果显著。具体参数需要根据实际场景微调,建议保存不同配置快速切换对比。
智能相机通常需要存储多组CCM(如D50/D65/A光源)。关键在于平滑切换策略:
python复制def interpolate_ccm(ccm1, ccm2, ratio):
"""线性插值两组CCM"""
return ccm1 * (1-ratio) + ccm2 * ratio
实际应用中,我会根据色温估计值动态混合相邻光源的CCM。比如检测到色温在4000K时,按比例混合D50和A光源的CCM。
嵌入式设备往往需要定点数计算。海思平台的典型实现:
python复制def ccm_fixed_point(rgb, ccm):
# 输入rgb是8bit,ccm是Q4.12格式
r = (rgb[0]*ccm[0] + rgb[1]*ccm[1] + rgb[2]*ccm[2]) >> 12
g = (rgb[0]*ccm[3] + rgb[1]*ccm[4] + rgb[2]*ccm[5]) >> 12
b = (rgb[0]*ccm[6] + rgb[1]*ccm[7] + rgb[2]*ccm[8]) >> 12
return np.clip([r,g,b], 0, 255)
测试发现Q4.12格式在精度和性能间取得了良好平衡,误差<0.5%。
CCM需要与多个ISP模块协同工作:
在调试无人机相机时,通过联调CCM和CA模块,成功解决了雪景中蓝色溢出的问题。关键是把CA的饱和度增益从1.2降到0.9,同时微调CCM的蓝色系数。