色彩校正矩阵(Color Correction Matrix, CCM)是图像信号处理(ISP)流水线中的关键组件,用于校正图像传感器的颜色响应,使其输出色彩与人眼感知或标准色彩空间相匹配。
CCM基础原理
1. CCM的作用
-
校正传感器色彩滤镜的不完美
-
匹配人眼视觉感知特性
-
补偿光源色温变化影响
-
使不同设备的色彩表现一致
2. 数学表示
色彩校正通过3x3矩阵乘法实现:
[R'] [m11 m12 m13] [R] [G'] = [m21 m22 m23] x [G] [B'] [m31 m32 m33] [B]
其中:
-
R,G,B为传感器原始RGB值
-
R',G',B'为校正后RGB值
-
mij为校正矩阵系数
CCM计算流程
1. 基于色卡的CCM计算
import numpy as np
import cv2
def compute_ccm(src_colors, target_colors):
"""
计算色彩校正矩阵
:param src_colors: 传感器捕获的色块颜色(Nx3矩阵)
:param target_colors: 标准色块颜色(Nx3矩阵)
:return: 3x3色彩校正矩阵
"""
# 添加齐次坐标
src = np.hstack([src_colors, np.ones((len(src_colors), 1))])
# 计算每个通道的变换矩阵
ccm = np.zeros((3, 4))
for i in range(3): # 对R,G,B通道分别计算
ccm[i] = np.linalg.lstsq(src, target_colors[:, i], rcond=None)[0]
# 提取3x3矩阵
return ccm[:, :3]
# 示例:使用ColorChecker 24色卡
# 假设测得传感器值和标准值(实际应通过拍摄色卡获取)
src_rgb = np.array([
[115, 82, 68], # 深肤色
[194, 150, 130], # 浅肤色
[90, 60, 102], # 蓝色
[85, 128, 115], # 绿色
[180, 130, 70], # 橙色
[70, 148, 73] # 紫色
], dtype='float32')
target_rgb = np.array([
[115, 82, 68], # 标准值
[194, 150, 130],
[90, 60, 102],
[85, 128, 115],
[180, 130, 70],
[70, 148, 73]
], dtype='float32')
ccm = compute_ccm(src_rgb, target_rgb)
print("计算得到的CCM:\n", ccm)
2. 优化CCM计算(带约束)
from scipy.optimize import minimize
def constrained_ccm(src, target):
"""带约束的CCM优化计算"""
def loss_function(params):
ccm = params.reshape(3,3)
predicted = np.dot(src, ccm.T)
return np.sum((predicted - target)**2)
# 初始猜测(单位矩阵)
init_guess = np.eye(3).flatten()
# 约束条件(保持灰度平衡)
def gray_balance_constraint(params):
ccm = params.reshape(3,3)
gray_response = np.sum(ccm, axis=1)
return gray_response - 1 # 应等于1
constraints = {
'type': 'eq',
'fun': gray_balance_constraint
}
result = minimize(loss_function, init_guess,
constraints=constraints, method='SLSQP')
return result.x.reshape(3,3)
optimized_ccm = constrained_ccm(src_rgb, target_rgb)
print("优化后的CCM:\n", optimized_ccm)
CCM在ISP中的实现
1. 基本CCM应用
def apply_ccm(image, ccm):
"""
应用色彩校正矩阵
:param image: 输入RGB图像(0-255)
:param ccm: 3x3色彩校正矩阵
:return: 校正后的RGB图像
"""
# 转换为浮点并归一化
img_float = image.astype('float32') / 255.0
# 重塑为(像素数, 3)并应用矩阵
pixels = img_float.reshape(-1, 3)
corrected = np.dot(pixels, ccm.T)
# 裁剪并转换回原格式
corrected = np.clip(corrected, 0, 1).reshape(image.shape)
return (corrected * 255).astype('uint8')
2. 带白平衡的CCM应用
def apply_ccm_with_wb(image, ccm, wb_gains):
"""
应用CCM并整合白平衡
:param wb_gains: 白平衡增益[R_gain, G_gain, B_gain]
"""
# 应用白平衡
wb_image = image.astype('float32')
wb_image[..., 0] *= wb_gains[0] # R
wb_image[..., 1] *= wb_gains[1] # G
wb_image[..., 2] *= wb_gains[2] # B
# 应用CCM
return apply_ccm(wb_image, ccm)
# 示例白平衡增益(通常从AWB算法获取)
wb_gains = [1.2, 1.0, 1.4] # R, G, B
corrected_image = apply_ccm_with_wb(raw_image, ccm, wb_gains)
CCM优化
1. 色适应变换(CAT)
def apply_cat(ccm, src_white, target_white):
"""
应用色适应变换(考虑光源变化)
:param src_white: 源白点(XYZ坐标)
:param target_white: 目标白点(XYZ坐标)
"""
# Bradford变换矩阵
bradford = np.array([
[0.8951, 0.2664, -0.1614],
[-0.7502, 1.7135, 0.0367],
[0.0389, -0.0685, 1.0296]
])
# 计算适应矩阵
src_cone = np.dot(bradford, src_white)
target_cone = np.dot(bradford, target_white)
diag = np.diag(target_cone / src_cone)
adapt_matrix = np.dot(np.dot(np.linalg.inv(bradford), diag), bradford)
# 调整CCM
return np.dot(ccm, adapt_matrix)
# 示例白点(D65和A光源)
d65_white = np.array([0.95047, 1.0, 1.08883])
a_white = np.array([1.0985, 1.0, 0.3558])
adapted_ccm = apply_cat(ccm, a_white, d65_white)
2. 多光照CCM融合
def interpolate_ccm(ccm_daylight, ccm_tungsten, ct, ct_day=6500, ct_tung=2850): """ 根据色温插值CCM :param ct: 当前色温 :param ct_day: 日光色温(默认6500K) :param ct_tung: 钨丝灯色温(默认2850K) """ if ct >= ct_day: return ccm_daylight elif ct <= ct_tung: return ccm_tungsten else: # 线性插值 ratio = (ct - ct_tung) / (ct_day - ct_tung) return ccm_daylight * ratio + ccm_tungsten * (1 - ratio)
CCM性能优化
1. 定点数实现
def ccm_fixed_point(image, ccm, fraction_bits=8): """ 定点数CCM实现(适合硬件) :param fraction_bits: 小数部分位数 """ scale = 1 << fraction_bits ccm_scaled = (ccm * scale).astype('int32') # 处理每个像素 corrected = np.zeros_like(image) for i in range(3): # R,G,B通道 for j in range(3): # CCM行 corrected[...,i] += image[...,j] * ccm_scaled[i,j] corrected[...,i] = np.right_shift(corrected[...,i], fraction_bits) return np.clip(corrected, 0, 255).astype('uint8')
2. 查表法(LUT)优化
def build_ccm_lut(ccm, size=33):
"""
构建CCM的3D LUT
:param size: LUT每维大小
"""
lut = np.zeros((size, size, size, 3), dtype='float32')
axis = np.linspace(0, 1, size)
for ri, r in enumerate(axis):
for gi, g in enumerate(axis):
for bi, b in enumerate(axis):
rgb = np.array([r, g, b])
lut[ri, gi, bi] = np.dot(ccm, rgb)
return lut
def apply_ccm_lut(image, lut):
"""应用CCM LUT"""
# 归一化图像到LUT尺寸
scaled = (image * (lut.shape[0]-1) / 255.0
indices = scaled.astype('int32')
# 三线性插值(简化版)
return lut[indices[...,0], indices[...,1], indices[...,2]]
CCM验证与评估
1. 色差计算(ΔE)
def delta_e(rgb1, rgb2):
"""计算CIEDE2000色差"""
# 转换为Lab色彩空间
lab1 = cv2.cvtColor(rgb1[np.newaxis, np.newaxis], cv2.COLOR_RGB2LAB)[0,0]
lab2 = cv2.cvtColor(rgb2[np.newaxis, np.newaxis], cv2.COLOR_RGB2LAB)[0,0]
# 简化的ΔE计算
return np.sqrt(np.sum((lab1 - lab2)**2))
def evaluate_ccm(ccm, test_colors, target_colors):
"""评估CCM性能"""
errors = []
for src, tgt in zip(test_colors, target_colors):
corrected = np.dot(ccm, src)
errors.append(delta_e(corrected, tgt))
avg_error = np.mean(errors)
max_error = np.max(errors)
print(f"平均ΔE: {avg_error:.2f}, 最大ΔE: {max_error:.2f}")
return avg_error, max_error
# 使用色卡测试集评估
test_colors = np.array([...]) # 测试色块
target_colors = np.array([...]) # 标准值
evaluate_ccm(ccm, test_colors, target_colors)
2. 灰度平衡检查
def check_gray_balance(ccm): """检查CCM的灰度平衡""" gray_input = np.array([[0.5, 0.5, 0.5]]) # 中性灰 gray_output = np.dot(gray_input, ccm.T) imbalance = np.abs(gray_output - gray_input).mean() print(f"灰度不平衡度: {imbalance:.4f}") return imbalance < 0.01 # 阈值 is_balanced = check_gray_balance(ccm)
实际应用建议
-
校准流程:
-
使用标准色卡(如X-Rite ColorChecker)拍摄
-
在不同光照条件下采集数据
-
使用优化算法计算最佳CCM
-
-
动态调整:
-
根据场景色温调整CCM
-
结合AWB算法实时优化
-
-
硬件考虑:
-
定点数精度选择(通常8-12位小数)
-
矩阵乘法并行化处理
-
考虑与相邻ISP模块(如去马赛克、降噪)的协同优化
-
CCM是保证图像色彩准确性的关键环节,需要根据具体传感器特性和应用场景进行精细调校。现代ISP通常采用动态CCM策略,能够根据光照条件和场景内容自动调整矩阵参数,以获得最佳色彩表现。