ISP-CCM(Color Correction Matrix)

简介:个人学习分享,如有错误,欢迎批评指正。

一、什么是颜色校正矩阵(Color Correction Matrix)?

颜色校正矩阵(CCM) 是一个 3×3矩阵,其作用是将图像中每个像素的RGB通道值通过矩阵运算调整为目标颜色空间中的RGB值。简单来说,CCM将图像从传感器输出的原始颜色空间转换为标准色彩空间(如sRGB、XYZ或Rec.2020),或者对颜色进行校正,以达到色彩还原的目标。

CCM 的基本形式

假设图像的每个像素的RGB值为:
(RinGinBin)\begin{pmatrix} R_{in} \\ G_{in} \\ B_{in} \end{pmatrix} RinGinBin

经过颜色校正矩阵 C\mathbf{C}C 变换后,输出的RGB值为:
(RoutGoutBout)=C×(RinGinBin)\begin{pmatrix} R_{out} \\ G_{out} \\ B_{out} \end{pmatrix} = \mathbf{C} \times \begin{pmatrix} R_{in} \\ G_{in} \\ B_{in} \end{pmatrix} RoutGoutBout =C× RinGinBin

其中, C\mathbf{C}C 就是 颜色校正矩阵(CCM),它定义了如何将原始的RGB通道映射到目标颜色空间。CCM矩阵的系数决定了颜色的转换、色调调整、色彩饱和度等因素。

二、为什么需要颜色校正矩阵(CCM)?

CCM之前和之后对比图

1. 传感器和显示设备之间的颜色差异

不同的图像传感器和显示设备(如LCD、OLED显示屏)使用不同的颜色空间。这意味着即使传感器捕捉到了图像,颜色也可能和实际场景有所偏差。CCM的作用就是校正这些偏差,确保输出图像的颜色符合标准的视觉要求。

2. 去除色偏和调整白平衡

色偏(color cast)是指图像在某种光照条件下表现出不自然的颜色。CCM通过调整矩阵系数,帮助去除色偏,恢复真实色彩。尤其是在低光照或高感光条件下,传感器可能会出现明显的色偏,CCM能够有效修正这些问题。此外,白平衡校正也是CCM的一个重要应用,帮助图像中的白色物体恢复自然的白色,避免偏蓝或偏红的现象。

3. 多摄像头一致性

现代智能手机通常配备多个摄像头(如广角、长焦、微距摄像头),它们可能使用不同厂商的传感器,导致颜色表现的差异。CCM的应用能够统一各摄像头的色彩表现,确保同一场景拍摄的多张图片色彩一致,避免多摄像头间的颜色不匹配问题。

三、颜色校正矩阵的计算方法

关于如何计算CCM矩阵的三种常见方法:通过标定板数据计算、通过算法优化计算、以及基于机器学习的自动化计算方法。

1. 通过标定板数据计算

通过使用已知的标准色卡(例如X-Rite ColorChecker),我们可以得到传感器的实际输出值,并将其与色卡上的真实颜色进行对比。这种方法是获取CCM矩阵最直接和最常见的方式。具体步骤如下:

1.1 标定板和色卡

在标定过程中,我们使用的标准色卡,如X-Rite ColorChecker,包含了已知的标准颜色。这些颜色的RGB值是预先定义和标准化的,通常是通过严格的色彩校准设备测量得到的。标定过程依赖于已知的这些标准颜色值。

1.2 拍摄标定图像

首先,我们通过图像传感器(例如相机)拍摄一张标准色卡的图像。传感器捕捉到的每个像素的RGB值(称为原始RGB值)通常会有所偏差。为了校正这个偏差,我们需要计算一个映射矩阵,即颜色校正矩阵,将传感器输出的RGB值转化为真实的标准颜色。

1.3 计算颜色误差

通过对比标准色卡的RGB值和传感器拍摄得到的RGB值,我们可以计算出图像传感器的误差。例如,如果标准色卡上的某个颜色的RGB值是 (Rs,Gs,Bs)(R_s, G_s, B_s)(Rs,Gs,Bs),而传感器捕捉到的颜色是 (Ri,Gi,Bi)(R_i, G_i, B_i)(Ri,Gi,Bi),则误差为:
ΔC=(Ri Gi Bi)−(Rs Gs Bs)\Delta C = \begin{pmatrix} R_i \ G_i \ B_i \end{pmatrix} - \begin{pmatrix} R_s \ G_s \ B_s \end{pmatrix} ΔC=(Ri Gi Bi)−(Rs Gs Bs)

1.4 使用最小二乘法进行矩阵优化

计算误差后,下一步是通过最小二乘法 来优化颜色校正矩阵,使得误差最小。最小二乘法的目标是最小化所有颜色误差的平方和,并计算出使得该误差最小的矩阵系数。

为了实现这一点,我们可以将颜色校正问题转化为一个线性系统的求解问题。假设传感器捕捉到的原始RGB值为 (Ri Gi Bi)\begin{pmatrix} R_i \ G_i \ B_i \end{pmatrix}(Ri Gi Bi),而标准色卡中的真实RGB值为 (Rs Gs Bs)\begin{pmatrix} R_s \ G_s \ B_s \end{pmatrix}(Rs Gs Bs)。我们希望找到一个矩阵 C\mathbf{C}C,使得:
C×(Ri Gi Bi)=(Rs Gs Bs)\mathbf{C} \times \begin{pmatrix} R_i \ G_i \ B_i \end{pmatrix} = \begin{pmatrix} R_s \ G_s \ B_s \end{pmatrix}C×(Ri Gi Bi)=(Rs Gs Bs)

通过最小二乘法来优化该矩阵,使得误差最小化,得到一个最优的颜色校正矩阵 C\mathbf{C}C。

2. 通过算法优化计算

除了使用标定板数据来直接计算CCM矩阵外,还可以通过一些算法优化方法来求解最优矩阵,尤其是通过图像数据进行自动调整。这种方法依赖于在不同的图像条件下,通过计算传感器输出和目标颜色之间的差异,自动优化CCM矩阵。

2.1 基于误差最小化的优化方法

通过误差最小化,我们可以利用不同图像数据集,求解一个最优的CCM矩阵。这种方法通常使用最小二乘法或梯度下降法等优化算法,利用传感器的实际输出数据和目标标准数据之间的差异来调整CCM矩阵。

步骤:

  1. 选择训练数据集:通过选择多组具有已知颜色目标的图像数据集来进行训练。通常需要涵盖多种光照条件、环境变化和传感器特性。
  2. 计算误差:在每组数据中,计算实际传感器输出的RGB与目标标准RGB之间的误差。常见的误差度量方式是计算均方误差(MSE)或者色差(如CIEDE2000)。
  3. 优化矩阵:通过最小化所有误差的平方和,使用优化算法(如最小二乘法或梯度下降法)来优化矩阵系数,得到最优的CCM。

2.2 使用目标函数优化

通过定义一个目标函数来优化CCM矩阵的系数,使得最终计算出的RGB图像与标准色卡颜色尽可能匹配。常见的目标函数可以是误差函数:
E=∑i=1N(∣C×Ri−Rs∣2)E = \sum_{i=1}^{N} \left( | \mathbf{C} \times \mathbf{R_i} - \mathbf{R_s} |^2 \right)E=i=1∑N(∣C×Ri−Rs∣2)

其中, NNN 是训练样本的数量, Ri\mathbf{R_i}Ri 是传感器的RGB值, Rs\mathbf{R_s}Rs 是标准RGB值, C\mathbf{C}C 是待求解的颜色校正矩阵。

3. 基于机器学习的自动化计算

随着机器学习技术的进步,神经网络逐渐被应用于自动计算CCM矩阵,尤其是在多传感器、多场景条件下。通过大量的训练数据,神经网络可以学习到如何从传感器输出的RAW图像生成标准色彩空间的RGB图像,并自动估算出最优的CCM矩阵。

3.1 神经网络训练方法

使用卷积神经网络(CNN)或其他深度学习模型来学习从传感器图像到目标色彩空间的映射。训练过程中,网络会根据输入的RAW图像和目标RGB图像之间的差异,自动优化矩阵中的权重系数。

训练步骤:

  1. 准备训练数据集:收集大量有标定图像的训练数据集,包括各种光照、场景、传感器的颜色输出。每一组数据包括传感器拍摄的RAW图像和其对应的标准RGB图像。
  2. 网络模型设计:设计一个卷积神经网络或其他深度学习模型,通过模型学习RGB与标准颜色之间的映射关系。
  3. 训练网络:使用梯度下降法或其他优化算法,训练神经网络,使得其输出与标准RGB图像尽可能接近,从而自动推导出最优的CCM矩阵。

3.2 自动化和实时调节

神经网络还可以应用于实时调节CCM矩阵,尤其是在动态环境中。当光照条件或传感器参数发生变化时,神经网络可以根据新的输入图像自动调整CCM矩阵,使得图像颜色始终保持真实和一致。

通过上述三种方法,我们可以获得 Color Correction Matrix (CCM)

  1. 通过标定板数据计算:通过拍摄标准色卡图像,比较传感器输出和标准色卡之间的误差,通过最小二乘法等优化算法计算得到CCM矩阵。
  2. 通过算法优化计算:通过误差最小化和算法优化(如梯度下降法),自动调整矩阵系数,使得传感器输出的颜色接近标准色卡。
  3. 基于机器学习的自动化计算:使用深度学习技术,通过训练神经网络自动计算CCM矩阵,适应不同传感器和场景条件。

通过这些方法,我们可以为图像信号处理(ISP)链路中的颜色校正提供高效且精确的解决方案,确保图像输出的色彩准确性和一致性。

四、颜色校正矩阵(CCM)值的含义

颜色校正矩阵(CCM)本质上是一个 3×3 的线性变换矩阵。

它里面的 9 个值,每一个都表示:

  • 输入的某个颜色通道,对输出的某个颜色通道贡献多少。

也就是说,CCM 不是简单地"R 调 R、G 调 G、B 调 B",而是允许 三个输入通道互相混合,从而把传感器原始颜色空间映射到目标颜色空间。

1. 先写出 CCM 的标准形式

设输入颜色是:
RinGinBin\begin{bmatrix} R_{in}\\ G_{in}\\ B_{in} \end{bmatrix} RinGinBin

经过 CCM 之后输出为:
RoutGoutBout=m11m12m13m21m22m23m31m32m33RinGinBin\begin{bmatrix} R_{out}\\ G_{out}\\ B_{out} \end{bmatrix}= \begin{bmatrix} m_{11} & m_{12} & m_{13}\\ m_{21} & m_{22} & m_{23}\\ m_{31} & m_{32} & m_{33} \end{bmatrix} \begin{bmatrix} R_{in}\\ G_{in}\\ B_{in} \end{bmatrix} RoutGoutBout = m11m21m31m12m22m32m13m23m33 RinGinBin

展开后就是:

Rout=m11Rin+m12Gin+m13BinGout=m21Rin+m22Gin+m23BinBout=m31Rin+m32Gin+m33BinR_{out}=m_{11}R_{in}+m_{12}G_{in}+m_{13}B_{in}\\ G_{out}=m_{21}R_{in}+m_{22}G_{in}+m_{23}B_{in}\\ B_{out}=m_{31}R_{in}+m_{32}G_{in}+m_{33}B_{in}Rout=m11Rin+m12Gin+m13BinGout=m21Rin+m22Gin+m23BinBout=m31Rin+m32Gin+m33Bin

2. 矩阵里每个值到底表示什么

下面逐个解释。

第一行:决定输出红色 RoutR_{out}Rout
Rout=m11Rin+m12Gin+m13BinR_{out}=m_{11}R_{in}+m_{12}G_{in}+m_{13}B_{in}Rout=m11Rin+m12Gin+m13Bin

这三个系数的含义是:

  • m11m_{11}m11:输入红色 RinR_{in}Rin 对输出红色 RoutR_{out}Rout 的贡献
  • m12m_{12}m12:输入绿色 GinG_{in}Gin 对输出红色 RoutR_{out}Rout 的贡献
  • m13m_{13}m13:输入蓝色 BinB_{in}Bin 对输出红色 RoutR_{out}Rout 的贡献

也就是说,输出红色不一定只来自输入红色,还可能吸收一部分绿色和蓝色的信息。

第二行:决定输出绿色 GoutG_{out}Gout
Gout=m21Rin+m22Gin+m23BinG_{out}=m_{21}R_{in}+m_{22}G_{in}+m_{23}B_{in}Gout=m21Rin+m22Gin+m23Bin

  • m21m_{21}m21:输入红色对输出绿色的贡献
  • m22m_{22}m22:输入绿色对输出绿色的贡献
  • m23m_{23}m23:输入蓝色对输出绿色的贡献

第三行:决定输出蓝色 BoutB_{out}Bout
Bout=m31Rin+m32Gin+m33BinB_{out}=m_{31}R_{in}+m_{32}G_{in}+m_{33}B_{in}Bout=m31Rin+m32Gin+m33Bin

  • m31m_{31}m31:输入红色对输出蓝色的贡献
  • m32m_{32}m32:输入绿色对输出蓝色的贡献
  • m33m_{33}m33:输入蓝色对输出蓝色的贡献

3. 从"位置"上怎么理解这 9 个值

你可以记一个规律:

第 i 行第 j 列的元素 mijm_{ij}mij,表示"输入第 j 个通道,对输出第 i 个通道的贡献系数"。

也就是:

  • 行:决定"输出谁"
  • 列:表示"来自谁"
    所以:
  • 第一行:输出 R 怎么组成
  • 第二行:输出 G 怎么组成
  • 第三行:输出 B 怎么组成
    而:
  • 第一列:输入 R 分别对输出 R/G/B 的影响
  • 第二列:输入 G 分别对输出 R/G/B 的影响
  • 第三列:输入 B 分别对输出 R/G/B 的影响

4. 对角线元素和非对角线元素分别代表什么

这是理解 CCM 最关键的一点。

4.1 对角线元素

对角线是:
m11,m22,m33m_{11}, m_{22}, m_{33}m11,m22,m33

它们表示:

  • 红到红
  • 绿到绿
  • 蓝到蓝

也就是"本通道对本通道"的保留或缩放。

通常它们是最主要的项,往往数值较大。

例如:

  • m11m_{11}m11 大,表示输出红色主要由输入红色决定
  • m22m_{22}m22 大,表示绿色主要保留原来的绿色
  • m33m_{33}m33 大,表示蓝色主要保留原来的蓝色

4.2 非对角线元素

非对角线元素表示通道之间的交叉混合。

例如:

  • m12m_{12}m12:绿色混到红色里多少
  • m13m_{13}m13:蓝色混到红色里多少
  • m21m_{21}m21:红色混到绿色里多少
  • m23m_{23}m23:蓝色混到绿色里多少
  • m31m_{31}m31:红色混到蓝色里多少
  • m32m_{32}m32:绿色混到蓝色里多少

这些项的存在非常重要,因为真实相机传感器的 RGB 光谱响应并不理想,三个通道之间常常"串色",所以必须通过交叉混合来修正。

5. 为什么会出现负数

CCM 里很多元素并不是全是正数,出现负数是非常正常的

比如一个矩阵可能是:
1.6−0.4−0.2−0.21.4−0.20.0−0.51.5\begin{bmatrix} 1.6 & -0.4 & -0.2\\ -0.2 & 1.4 & -0.2\\ 0.0 & -0.5 & 1.5 \end{bmatrix} 1.6−0.20.0−0.41.4−0.5−0.2−0.21.5

这里的负数表示:

  • 某个输入通道会被"减掉一部分",用来抵消串色或色偏。

例如:

  • m12=−0.4m_{12}=-0.4m12=−0.4
    表示在生成输出红色时,要减去一部分输入绿色
    这通常意味着传感器红通道里混入了过多"绿成分",所以要扣掉一些
  • m23=−0.2m_{23}=-0.2m23=−0.2
    表示输出绿色时减去一些输入蓝色
    这说明蓝色对绿色有串扰,需要纠正
    所以负数不是异常,而是 颜色解耦和纠偏 的体现。

6. 举一个具体例子来理解

假设 CCM 为:
M=1.5−0.3−0.2−0.11.3−0.20.0−0.41.4\mathbf{M}= \begin{bmatrix} 1.5 & -0.3 & -0.2\\ -0.1 & 1.3 & -0.2\\ 0.0 & -0.4 & 1.4 \end{bmatrix}M= 1.5−0.10.0−0.31.3−0.4−0.2−0.21.4

输入一个像素:
RinGinBin=10012080 \begin{bmatrix} R_{in}\\ G_{in}\\ B_{in} \end{bmatrix}= \begin{bmatrix} 100\\ 120\\ 80 \end{bmatrix} RinGinBin = 10012080

则:

输出红色
Rout=1.5×100+(−0.3)×120+(−0.2)×80R_{out}=1.5\times100+(-0.3)\times120+(-0.2)\times80Rout=1.5×100+(−0.3)×120+(−0.2)×80
Rout=150−36−16=98R_{out}=150-36-16=98Rout=150−36−16=98

意思是:

  • 先保留较强的红色
  • 再减去部分绿色
  • 再减去部分蓝色

说明原始红通道可能被 G/B 污染了,所以要净化。

输出绿色
Gout=−0.1×100+1.3×120+(−0.2)×80G_{out}=-0.1\times100+1.3\times120+(-0.2)\times80Gout=−0.1×100+1.3×120+(−0.2)×80
Gout=−10+156−16=130G_{out}=-10+156-16=130Gout=−10+156−16=130

输出蓝色
Bout=0×100+(−0.4)×120+1.4×80B_{out}=0\times100+(-0.4)\times120+1.4\times80Bout=0×100+(−0.4)×120+1.4×80
Bout=0−48+112=64B_{out}=0-48+112=64Bout=0−48+112=64

这个过程本质上就是把"传感器看到的颜色"变成"更接近标准颜色空间的颜色"。

7. 这些值在视觉上分别会影响什么

虽然 9 个值是整体联动的,但从直觉上可以这样理解:
1. 对角线项过大或过小
m11m_{11}m11 变大

输出红色更强,画面会更偏暖、偏红。
m22m_{22}m22 变大

绿色更强,画面可能发绿,亮度结构也可能更明显。
m33m_{33}m33 变大

蓝色更强,画面会更冷、偏蓝。

2. 非对角项变化
m12m_{12}m12 变化

改变绿色向红色的混合,常影响黄、橙、肤色区域。
m13m_{13}m13 变化

改变蓝色向红色的混合,常影响紫、洋红、冷暖平衡。
m21、m23m_{21}、m_{23}m21、m23

影响绿色纯度,常影响草地、植物、肤色过渡。
m31、m32m_{31}、m_{32}m31、m32

影响蓝色纯度,常影响天空、水面、阴影区颜色。

8. 能不能把 CCM 看成"颜色旋转+拉伸"

从线性代数角度看,CCM 这个 3×3 矩阵,本质上对 RGB 颜色空间做了:

  • 缩放
  • 旋转
  • 剪切
  • 通道混合

也就是说,它不是简单地把每个通道单独拉亮,而是在三维颜色空间中重新调整颜色坐标的位置。

所以 CCM 的作用不是"调亮一点红色"这么简单,而是:
把传感器原始颜色空间整体变换到目标颜色空间。

CCM 中每个元素,表示某个输入颜色通道对某个输出颜色通道的线性贡献系数。
行看输出,列看输入。对角线是本通道保留,非对角线是通道间混合,正值表示增加,负值表示抵消。

五、CCM 的 9 个值是怎么通过色卡拟合出来

1. 为什么可以用色卡拟合 CCM

CCM 的任务是:把相机传感器测到的颜色,变换到目标标准颜色空间。

也就是说,相机看到的颜色和"真实标准颜色"之间,存在一个映射关系。

如果我们假设这个映射在某个工作条件下可以近似成线性变换 ,那么就有:
y=Mx\mathbf{y} = \mathbf{M}\mathbf{x}y=Mx

其中:

  • x\mathbf{x}x:传感器测到的 RGB
  • y\mathbf{y}y:目标标准 RGB(或者 XYZ)
  • M\mathbf{M}M:待求的 3×3 CCM

而色卡的作用就是:

给你很多组"已知输入"和"已知正确输出"的配对样本。

比如一张 ColorChecker 有 24 个色块,那么你就有 24 组:

  • 输入:相机拍到的某个色块 RGB
  • 输出:这个色块标准的参考值

这样, M\mathbf{M}M 就不是拍脑袋设的,而是可以从这些样本里"拟合"出来。

2. CCM 的数学形式

设 CCM 为:
M=m11m12m13m21m22m23m31m32m33\mathbf{M}= \begin{bmatrix} m_{11} & m_{12} & m_{13}\\ m_{21} & m_{22} & m_{23}\\ m_{31} & m_{32} & m_{33} \end{bmatrix}M= m11m21m31m12m22m32m13m23m33

对于某一个色块,假设传感器测到的 RGB 为:
x=RsGsBs\mathbf{x}= \begin{bmatrix} R_s\\ G_s\\ B_s \end{bmatrix}x= RsGsBs

它对应的标准目标值为:
y=RtGtBt\mathbf{y}= \begin{bmatrix} R_t\\ G_t\\ B_t \end{bmatrix}y= RtGtBt

则有:
RtGtBt=m11m12m13m21m22m23m31m32m33RsGsBs \begin{bmatrix} R_t\\ G_t\\ B_t \end{bmatrix}= \begin{bmatrix} m_{11} & m_{12} & m_{13}\\ m_{21} & m_{22} & m_{23}\\ m_{31} & m_{32} & m_{33} \end{bmatrix} \begin{bmatrix} R_s\\ G_s\\ B_s \end{bmatrix} RtGtBt = m11m21m31m12m22m32m13m23m33 RsGsBs

展开就是:

Rt=m11Rs+m12Gs+m13BsGt=m21Rs+m22Gs+m23BsBt=m31Rs+m32Gs+m33BsR_t = m_{11}R_s + m_{12}G_s + m_{13}B_s\\ G_t = m_{21}R_s + m_{22}G_s + m_{23}B_s\\ B_t = m_{31}R_s + m_{32}G_s + m_{33}B_sRt=m11Rs+m12Gs+m13BsGt=m21Rs+m22Gs+m23BsBt=m31Rs+m32Gs+m33Bs

这就是一组三元线性方程。

3. 为什么需要很多色块

因为 CCM 一共有 9 个未知数:
m11,m12,m13,m21,m22,m23,m31,m32,m33m_{11},m_{12},m_{13},m_{21},m_{22},m_{23},m_{31},m_{32},m_{33}m11,m12,m13,m21,m22,m23,m31,m32,m33

理论上,3 个色块就可以提供 9 个方程:

  • 每个色块提供 3 个方程
  • 3 个色块一共 9 个方程

似乎可以解 9 个未知数。

但是工程上几乎不会只用 3 个点,原因有三个:
1. 噪声

相机拍出来的 RGB 有噪声。

2. 色卡测量误差

色块均值不可能完全精确。

3. 真实颜色映射并非完全线性

CCM 只是线性近似,不可能对所有颜色都绝对精确。

所以工程上会用很多色块,比如 24 色卡、96 色卡,甚至更多采样点,然后做:

最小二乘拟合

即:求一个最优矩阵,让总体误差最小。

4. 如何把很多色块写成矩阵方程

假设我们有 N 个色块。

把相机测得的 RGB 组成一个矩阵:
X=Rs1Gs1Bs1Rs2Gs2Bs2⋮⋮⋮RsNGsNBsN(N×3)X= \begin{bmatrix} R_{s1} & G_{s1} & B_{s1}\\ R_{s2} & G_{s2} & B_{s2}\\ \vdots & \vdots & \vdots\\ R_{sN} & G_{sN} & B_{sN} \end{bmatrix} \quad (N\times 3)X= Rs1Rs2⋮RsNGs1Gs2⋮GsNBs1Bs2⋮BsN (N×3)

把目标标准 RGB 组成矩阵:
Y=Rt1Gt1Bt1Gt2Gt2Bt2⋮⋮⋮RtNGtNBtN(N×3)Y= \begin{bmatrix} R_{t1} & G_{t1} & B_{t1}\\ G_{t2} & G_{t2} & B_{t2}\\ \vdots & \vdots & \vdots\\ R_{tN} & G_{tN} & B_{tN} \end{bmatrix} \quad (N\times 3)Y= Rt1Gt2⋮RtNGt1Gt2⋮GtNBt1Bt2⋮BtN (N×3)

注意这里更标准地写应该是:

Y=Rt1Gt1Bt1Rt2Gt2Bt2⋮⋮⋮RtNGtNBtNY= \begin{bmatrix} R_{t1} & G_{t1} & B_{t1}\\ R_{t2} & G_{t2} & B_{t2}\\ \vdots & \vdots & \vdots\\ R_{tN} & G_{tN} & B_{tN} \end{bmatrix}Y= Rt1Rt2⋮RtNGt1Gt2⋮GtNBt1Bt2⋮BtN

那么拟合关系可写成:

Y=XMTY = X M^TY=XMT

因为每一行都是一个样本:

Rt,;Gt,;Bt=Rs,;Gs,;BsMTR_t,;G_t,;B_t = R_s,;G_s,;B_s M^TRt,;Gt,;Bt=Rs,;Gs,;BsMT

也有人写成列向量形式:

YT=MXTY^T = M X^TYT=MXT

本质完全一样。

5. 为什么能用最小二乘解

因为真实情况下不可能有一个矩阵让所有色块都精确满足:

Y=XMTY = X M^TY=XMT

于是我们只能找一个让误差最小的矩阵:

min⁡M∣XMT−Y∣F2\min_M |XM^T - Y|_F^2Mmin∣XMT−Y∣F2

这里 ∣∣⋅∣∣F||\cdot||_F∣∣⋅∣∣F 表示 Frobenius 范数,本质就是:所有色块、所有通道的平方误差总和最小

这是标准的线性最小二乘问题。

其解析解是:

MT=(XTX)−1XTYM^T = (X^T X)^{-1}X^T YMT=(XTX)−1XTY

所以:

M=YTX(XTX)−1M = Y^T X (X^T X)^{-1}M=YTX(XTX)−1

更常见的工程实现是直接写成:

bash 复制代码
M_T = np.linalg.lstsq(X, Y, rcond=None)[0]
M = M_T.T

6. 一个最简单的"刚好可精确求解"的手算例子

先举一个 3 个色块,刚好 9 个方程解 9 个未知数 的例子。

例子设定

假设相机拍到 3 个色块的传感器 RGB(输入)分别是:
色块 1
x1=1005020\mathbf{x}_1= \begin{bmatrix} 100\\ 50\\ 20 \end{bmatrix}x1= 1005020
色块 2
x2=3012040\mathbf{x}_2= \begin{bmatrix} 30\\ 120\\ 40 \end{bmatrix}x2= 3012040
色块 3
x3=2040150\mathbf{x}_3= \begin{bmatrix} 20\\ 40\\ 150 \end{bmatrix}x3= 2040150

对应的标准目标 RGB(输出)分别是:
色块 1
y1=1104818\mathbf{y}_1= \begin{bmatrix} 110\\ 48\\ 18 \end{bmatrix}y1= 1104818
色块 2
y2=2812535\mathbf{y}_2= \begin{bmatrix} 28\\ 125\\ 35 \end{bmatrix}y2= 2812535
色块 3
y3=1838160\mathbf{y}_3= \begin{bmatrix} 18\\ 38\\ 160 \end{bmatrix}y3= 1838160

7. 把问题拆成三个独立的小问题

这是最容易理解的方式。

因为:
Rt=m11Rs+m12Gs+m13BsR_t = m_{11}R_s + m_{12}G_s + m_{13}B_sRt=m11Rs+m12Gs+m13Bs

所以第一行的三个未知数 (m11,m12,m13) (m_{11},m_{12},m_{13})(m11,m12,m13) 可以单独求。

同理:

  • 第二行求 (m21,m22,m23)(m_{21},m_{22},m_{23})(m21,m22,m23)
  • 第三行求 (m31,m32,m33)(m_{31},m_{32},m_{33})(m31,m32,m33)

7.1 先求第一行:输出红色

根据 3 个色块列方程:
色块 1
100m11+50m12+20m13=110100m_{11} + 50m_{12} + 20m_{13} = 110100m11+50m12+20m13=110
色块 2
30m11+120m12+40m13=2830m_{11} + 120m_{12} + 40m_{13} = 2830m11+120m12+40m13=28
色块 3
20m11+40m12+150m13=1820m_{11} + 40m_{12} + 150m_{13} = 1820m11+40m12+150m13=18

写成矩阵:
100502030120402040150m11m12m13=1102818 \begin{bmatrix} 100 & 50 & 20\\ 30 & 120 & 40\\ 20 & 40 & 150 \end{bmatrix} \begin{bmatrix} m_{11}\\ m_{12}\\ m_{13} \end{bmatrix}= \begin{bmatrix} 110\\ 28\\ 18 \end{bmatrix} 100302050120402040150 m11m12m13 = 1102818

记:
A=100502030120402040150A= \begin{bmatrix} 100 & 50 & 20\\ 30 & 120 & 40\\ 20 & 40 & 150 \end{bmatrix}A= 100302050120402040150

则:
Am11m12m13=1102818A\begin{bmatrix}m_{11}\\m_{12}\\m_{13}\end{bmatrix}= \begin{bmatrix} 110\\28\\18 \end{bmatrix}A m11m12m13 = 1102818

这个目标红色大致表现为:

  • 第一块红多一点:110 对应原来 100
  • 第二、三块红都略减:30→28,20→18
    所以你大概能猜到这第一行接近:

    1.1,0,0\]\[1.1,0,0\]\[1.1,0,0

    再验证一下:
  • 色块1: 1.1×100=1101.1\times100=1101.1×100=110
  • 色块2: 1.1×30=331.1\times30=331.1×30=33,但目标是 28,说明还得减一些 G/B
  • 色块3: 1.1×20=221.1\times20=221.1×20=22,目标是 18,也得减一些 G/B

所以更合理地设:
Rt≈1.1Rs−0.02Gs−0.01BsR_t \approx 1.1R_s - 0.02G_s - 0.01B_sRt≈1.1Rs−0.02Gs−0.01Bs

验证:
色块1
1.1×100−0.02×50−0.01×20=110−1−0.2=108.81.1\times100 - 0.02\times50 - 0.01\times20 =110-1-0.2=108.81.1×100−0.02×50−0.01×20=110−1−0.2=108.8

还差一点。

把系数微调一下,设第一行为:

1.15,−0.08,−0.03\]\[1.15,-0.08,-0.03\]\[1.15,−0.08,−0.03

验证:
色块1
1.15×100−0.08×50−0.03×20=115−4−0.6=110.41.15\times100 - 0.08\times50 - 0.03\times20 =115-4-0.6=110.41.15×100−0.08×50−0.03×20=115−4−0.6=110.4
色块2
1.15×30−0.08×120−0.03×40=34.5−9.6−1.2=23.71.15\times30 - 0.08\times120 - 0.03\times40 =34.5-9.6-1.2=23.71.15×30−0.08×120−0.03×40=34.5−9.6−1.2=23.7

偏低。

所以这说明真实解还是需要正规线性求解,不适合全靠猜。下面我给你走"正规可手算"路径。

8. 用"矩阵求逆"手算思路讲清楚

对于第一行,有:
Amr=brA\mathbf{m}_r=\mathbf{b}_rAmr=br

其中:
mr=m11m12m13,br=1102818 \mathbf{m}r=\begin{bmatrix}m{11}\\m_{12}\\m_{13} \end{bmatrix}, \quad \mathbf{b}_r= \begin{bmatrix} 110\\28\\18 \end{bmatrix} mr= m11m12m13 ,br= 1102818

理论上:
mr=A−1br\mathbf{m}_r=A^{-1}\mathbf{b}_rmr=A−1br

同理:
mg=A−1bg,mb=A−1bb\mathbf{m}_g=A^{-1}\mathbf{b}_g,\qquad \mathbf{m}_b=A^{-1}\mathbf{b}_bmg=A−1bg,mb=A−1bb

其中:
bg=4812538,bb=1835160\mathbf{b}_g= \begin{bmatrix} 48\\125\\38 \end{bmatrix}, \quad \mathbf{b}_b= \begin{bmatrix} 18\\35\\160 \end{bmatrix}bg= 4812538 ,bb= 1835160

所以一旦你把 A−1A^{-1}A−1 求出来,三个通道都能解。

9. 真正适合手算的完整例子

我们换一组色块,让输入更容易算,但仍然符合 CCM 拟合原理。

假设相机测到 3 个色块输入为:
X=100000100000100X= \begin{bmatrix} 100 & 0 & 0\\ 0 & 100 & 0\\ 0 & 0 & 100 \end{bmatrix}X= 100000100000100

即:

  • 第一个色块主要是红
  • 第二个色块主要是绿
  • 第三个色块主要是蓝

对应目标标准值为:
Y=110−50 10955 08120Y= \begin{bmatrix} 110 & -5 & 0\ 10 & 95 & 5\ 0 & 8 & 120 \end{bmatrix}Y=110−50 10955 08120

这意味着:

  • 纯红输入,目标变成 (110,-5,0)
  • 纯绿输入,目标变成 (10,95,5)
  • 纯蓝输入,目标变成 (0,8,120)

这里虽然有负值,数学上是允许的;实际后面会 clamp。

9.1 建模

有:
Y=XMTY = X M^TY=XMT

由于
X=100000100000100=100IX= \begin{bmatrix} 100 & 0 & 0\\ 0 & 100 & 0\\ 0 & 0 & 100 \end{bmatrix} =100IX= 100000100000100 =100I

所以:
Y=100MTY = 100 M^TY=100MT

因此:
MT=1100YM^T = \frac{1}{100}YMT=1001Y

即:
MT=1.10−0.0500.100.950.0500.081.20M^T= \begin{bmatrix} 1.10 & -0.05 & 0\\ 0.10 & 0.95 & 0.05\\ 0 & 0.08 & 1.20 \end{bmatrix}MT= 1.100.100−0.050.950.0800.051.20

所以:
M=1.100.100−0.050.950.0800.051.20M= \begin{bmatrix} 1.10 & 0.10 & 0\\ -0.05 & 0.95 & 0.08\\ 0 & 0.05 & 1.20 \end{bmatrix}M= 1.10−0.0500.100.950.0500.081.20

这就是拟合出来的 CCM。

9.2 验证这 9 个值是什么意思

这个 CCM 为:
M=1.100.100−0.050.950.0800.051.20M= \begin{bmatrix} 1.10 & 0.10 & 0\\ -0.05 & 0.95 & 0.08\\ 0 & 0.05 & 1.20 \end{bmatrix}M= 1.10−0.0500.100.950.0500.081.20

展开后:
Rout=1.10Rin+0.10Gin+0BinR_{out}=1.10R_{in}+0.10G_{in}+0B_{in}Rout=1.10Rin+0.10Gin+0Bin
Gout=−0.05Rin+0.95Gin+0.08BinG_{out}=-0.05R_{in}+0.95G_{in}+0.08B_{in}Gout=−0.05Rin+0.95Gin+0.08Bin
Bout=0Rin+0.05Gin+1.20BinB_{out}=0R_{in}+0.05G_{in}+1.20B_{in}Bout=0Rin+0.05Gin+1.20Bin

含义就是:

  • 输出红色主要来自输入红色,但也吸收一点绿色
  • 输出绿色主要来自输入绿色,但要减一点红色、加一点蓝色
  • 输出蓝色主要来自输入蓝色,同时吸收少量绿色

9.3 代一个实际像素进去算

假设某个像素输入为:
RinGinBin=8010060 \begin{bmatrix} R_{in}\\G_{in}\\B_{in} \end{bmatrix}= \begin{bmatrix} 80\\100\\60 \end{bmatrix} RinGinBin = 8010060

则:

输出红色
Rout=1.10×80+0.10×100+0×60=88+10=98R_{out}=1.10\times80+0.10\times100+0\times60=88+10=98Rout=1.10×80+0.10×100+0×60=88+10=98

输出绿色
Gout=−0.05×80+0.95×100+0.08×60=−4+95+4.8=95.8G_{out}=-0.05\times80+0.95\times100+0.08\times60=-4+95+4.8=95.8Gout=−0.05×80+0.95×100+0.08×60=−4+95+4.8=95.8

输出蓝色
Bout=0×80+0.05×100+1.20×60=5+72=77B_{out}=0\times80+0.05\times100+1.20\times60=5+72=77Bout=0×80+0.05×100+1.20×60=5+72=77

所以校正后像素为:
9895.877\begin{bmatrix} 98\\95.8\\77 \end{bmatrix} 9895.877

10. 再讲真实工程里怎么做最小二乘拟合

上面那个例子是为了让你手算彻底理解。

但真实工程里不是 3 个色块,而是很多个,比如 24 个色块。

这时通常这样做。

1. 采集数据

对每个色块,先取中心区域均值,得到传感器线性 RGB:
(Rs1,Gs1,Bs1),...,(RsN,GsN,BsN)(R_{s1},G_{s1},B_{s1}),\dots,(R_{sN},G_{sN},B_{sN})(Rs1,Gs1,Bs1),...,(RsN,GsN,BsN)

同时查标准参考值,得到目标值:
(Rt1,Gt1,Bt1),...,(RtN,GtN,BtN)(R_{t1},G_{t1},B_{t1}),\dots,(R_{tN},G_{tN},B_{tN})(Rt1,Gt1,Bt1),...,(RtN,GtN,BtN)

注意这里一般要求:

  • 去黑电平
  • 去白平衡前或白平衡后要定义清楚
  • 在线性域做
  • 不要在 gamma 后拟合

2. 写成矩阵
X=Rs1Gs1Bs1Rs2Gs2Bs2⋮⋮⋮RsNGsNBsNX= \begin{bmatrix} R_{s1} & G_{s1} & B_{s1}\\ R_{s2} & G_{s2} & B_{s2}\\ \vdots & \vdots & \vdots\\ R_{sN} & G_{sN} & B_{sN} \end{bmatrix}X= Rs1Rs2⋮RsNGs1Gs2⋮GsNBs1Bs2⋮BsN
Y=Rt1Gt1Bt1Rt2Gt2Bt2⋮⋮⋮RtNGtNBtNY= \begin{bmatrix} R_{t1} & G_{t1} & B_{t1}\\ R_{t2} & G_{t2} & B_{t2}\\ \vdots & \vdots & \vdots\\ R_{tN} & G_{tN} & B_{tN} \end{bmatrix}Y= Rt1Rt2⋮RtNGt1Gt2⋮GtNBt1Bt2⋮BtN

3. 最小二乘解

求:
min⁡M∣XMT−Y∣F2\min_{M}|XM^T-Y|_F^2Mmin∣XMT−Y∣F2

解析解:
MT=(XTX)−1XTYM^T=(X^TX)^{-1}X^TYMT=(XTX)−1XTY

4. 为什么分三次回归也可以

因为每个输出通道都可以单独拟合:
拟合输出 R
min⁡mr∣Xmr−yr∣2\min_{\mathbf{m}_r}|X\mathbf{m}_r-\mathbf{y}r|^2mrmin∣Xmr−yr∣2
拟合输出 G
min⁡mg∣Xmg−yg∣2\min
{\mathbf{m}_g}|X\mathbf{m}_g-\mathbf{y}g|^2mgmin∣Xmg−yg∣2
拟合输出 B
min⁡mb∣Xmb−yb∣2\min
{\mathbf{m}_b}|X\mathbf{m}_b-\mathbf{y}_b|^2mbmin∣Xmb−yb∣2

最后把三组系数组起来,就是 CCM。

这也是你最容易理解的方式。

11. 真实工程里还会加哪些约束

真实 ISP 里,CCM 拟合通常不只是裸最小二乘,还会加一些工程约束。

11.1 行和约束(neutral preservation)

为了让中性灰尽量保持中性,常要求每一行系数和接近 1:
m11+m12+m13≈1m_{11}+m_{12}+m_{13}\approx 1m11+m12+m13≈1
m21+m22+m23≈1m_{21}+m_{22}+m_{23}\approx 1m21+m22+m23≈1
m31+m32+m33≈1m_{31}+m_{32}+m_{33}\approx 1m31+m32+m33≈1

这样灰色 (k,k,k) 经过变换后不容易明显偏色。

11.2 权重拟合

有些色块比别的更重要,例如:

  • 肤色
  • 中性灰
  • 高饱和主色

那么目标函数会写成加权形式:

min⁡M∑i=1Nwi∣Mxi−yi∣2\min_M \sum_{i=1}^N w_i |\mathbf{M}\mathbf{x}_i-\mathbf{y}_i|^2Mmini=1∑Nwi∣Mxi−yi∣2

11.3 在 XYZ 或 Lab 空间优化

实际工程中,经常不是直接最小化 RGB 误差,而是:

  • 先把目标标准值转到 XYZ
  • 或再转到 Lab
  • 再最小化色差 ΔE\Delta EΔE
    因为人眼对颜色误差的感知不是简单的 RGB 欧氏距离。

在实际相机标定中,通常先拍摄标准色卡,提取每个色块在线性 RAW 域中的平均 RGB 值,作为传感器颜色响应;再根据色卡在目标光源下的标准参考值构建目标颜色数据。设传感器响应矩阵为 XXX,目标颜色矩阵为 YYY,则通过最小化整体颜色误差
min⁡M∣XMT−Y∣F2\min_M |XM^T-Y|_F^2Mmin∣XMT−Y∣F2

求得最优解
MT=(XTX)−1XTYM^T=(X^TX)^{-1}X^TYMT=(XTX)−1XTY

其转置 MMM 即为颜色校正矩阵 CCM。矩阵中的 9 个系数分别表示输入 RGB 三通道对输出 RGB 三通道的线性贡献关系。

六、24 色 ColorChecker 做 CCM 标定

6 步展开:

  1. 拍图准备与采集规范
  2. 色卡检测与 24 个色块取值
  3. RAW 线性化与预处理
  4. 构建目标参考值
  5. 最小二乘拟合 CCM
  6. 用 ΔE 做验证、诊断与回调

1. 先明确:CCM 标定到底在标什么

CCM 标定的本质是:
在某个给定光源条件下,找到一个 3×3 矩阵,把相机传感器的线性 RGB 响应,映射到目标标准颜色空间,使 24 个色块尽量接近标准参考颜色。

所以它不是"任意场景万能矩阵",而是:

  • 传感器光谱响应 有关
  • 镜头透过率 有关
  • 光源 SPD 有关
  • RAW 预处理方式 有关

这也是为什么工程里通常不是一个 CCM 走天下,而是:

  • A 光一个 CCM
  • D65 一个 CCM
  • TL84 一个 CCM
  • 中间色温靠插值

2. 第一步:拍图准备与采集规范

这一步非常关键。

很多人后面拟合得不好,不是最小二乘有问题,而是前面的拍摄数据就不干净。

2.1 使用的色卡

常见是 X-Rite / Calibrite ColorChecker Classic 24,共 24 个色块,包括:

  • 6 个中性灰阶块
  • 6 个典型彩色块
  • 12 个常见自然/高饱和颜色块

它的价值在于:

  • 有中性灰,可检查中性轴
  • 有肤色、天空蓝、叶绿色等典型自然色
  • 有高饱和颜色,可看 gamut 边界表现

2.2 光源条件

CCM 必须在已知、稳定、均匀的光源下标定。常见标准光源有:

  • D65:日光
  • A:钨丝灯,偏暖
  • TL84:商场荧光灯
  • D50:印刷常用

要求:

  • 光源稳定,不闪烁
  • 光照尽量均匀打在色卡上
  • 尽量避免环境杂散光污染
  • 色卡表面不要有高光反射

工程建议

色卡最好与镜头光轴基本垂直,轻微倾角避开镜面反光。

如果色卡上有明显 specular highlight,这张图最好不要拿来做 CCM。

2.3 曝光设置

为了做 CCM,曝光不能乱设。

原则是:

  • 不过曝
  • 不欠曝太多
  • 色卡亮块有足够 SNR
  • 暗块不被噪声主导

通常做法:

  • 使用固定 ISO
  • 固定曝光时间
  • 固定增益
  • 尽量让白块/亮灰块接近但不达到饱和

一个经验点

不要把最亮白块打到饱和边缘,否则高光 roll-off 或 clipping 会破坏线性关系。

2.4 RAW 采集要求

必须尽量拿到接近传感器线性响应的 RAW ,不要用已经做过强 ISP 的 JPEG。

理想情况:

  • Bayer RAW
  • 保留 black level 信息
  • 保留 sensor metadata
  • 关闭自动增强
  • 不要 gamma
  • 不要 tone mapping
  • 不要 sharpen / saturation / local contrast

因为 CCM 是在线性域工作的。

如果你拿的是已经 gamma 过的 RGB,再去拟合 CCM,结果会很差。

3. 第二步:色卡检测与 24 个色块取值

这一步的目标是:
从拍到的 RAW 图里,稳定地提取每个色块对应的相机响应值。

3.1 色卡定位

有两种做法:
做法 A:手工标定

人工点击四角点,然后透视矫正。

做法 B:自动检测

通过边缘、轮廓、矩形检测、模板匹配等自动找到色卡,再做 homography 对齐。

工程上常见流程是:

  1. 检测色卡外框
  2. 做透视变换,把色卡拉正
  3. 分成 4×6 网格
  4. 对每个色块提取中心 ROI

3.2 为什么不能取整块平均

因为色块边缘容易受这些因素影响:

  • 色块边界印刷过渡
  • 透视误差
  • 镜头模糊
  • 去马赛克/插值混色
  • 阴影或高光边缘

所以通常不会取整块,而是取每个色块的中心区域,例如:

  • 中间 40%
  • 中间 50%
  • 中间 60%

这样更稳。

3.3 取 RAW 还是取 demosaic 后 RGB

做 CCM 标定时,更推荐:

在线性 RGB 域取值,而不是在 Bayer 单通道直接取值,也不是在 gamma RGB 上取值。

常见做法是:

  • 先对 Bayer 做基础预处理
  • 再 demosaic 成线性 RGB
  • 再在每个色块中心取均值

原因是 CCM 本身定义在 RGB 三通道空间里。

如果你直接在 Bayer 格式取值,需要先把 CFA 通道关系处理清楚,更麻烦。

3.4 每个色块得到什么数据

最后你会得到 24 组相机测量值:
(Rs1,Gs1,Bs1),...,(Rs24,Gs24,Bs24)(R_{s1},G_{s1},B_{s1}),\dots,(R_{s24},G_{s24},B_{s24})(Rs1,Gs1,Bs1),...,(Rs24,Gs24,Bs24)

这里下标 sss 表示 sensor / source。

通常这些值应是:

  • black level corrected
  • demosaiced
  • linear
  • white-balanced 或明确说明未 AWB

这个"是否已白平衡"非常重要,后面我单独说。

4. 第三步:RAW 线性化与预处理

这一步最容易被忽视,但它其实决定了 CCM 是否能拟合得像样。

4.1 黑电平校正(Black Level Correction)

传感器 RAW 一般不是从 0 开始,存在黑电平偏置。

假设某像素原始值为 xrawx_{raw}xraw,黑电平为 b,则应先做:
xlin=xraw−bx_{lin} = x_{raw} - bxlin=xraw−b

如果不减黑电平:

  • 暗色块会整体偏高
  • 中性灰轴会歪
  • 线性关系被破坏
  • CCM 拟合出的矩阵会补偿错误偏置

4.2 饱和点归一化

如果希望不同 bit depth 或不同曝光条件下可比,通常还会归一化:
xnorm=xraw−bw−bx_{norm} = \frac{x_{raw}-b}{w-b}xnorm=w−bxraw−b

其中:

  • b:black level
  • w:white level / saturation level

这样数据会落在 0,1 左右范围。

4.3 去白平衡还是加白平衡?

这是一个关键问题。

CCM 一般有两种标定口径:

口径 A:AWB 后再拟合 CCM

即先对白卡或灰卡做通道增益校正,再拟合 CCM。

流程:
RGBsensor→AWBRGBwb→CCMRGBtargetRGB_{sensor} \xrightarrow{AWB} RGB_{wb} \xrightarrow{CCM} RGB_{target}RGBsensorAWB RGBwbCCM RGBtarget

这也是最常见的 ISP 链路方式。
口径 B:不做 AWB,直接把 AWB 和 CCM 一起吸收到矩阵里

不太常见,因为会让矩阵与 illuminant 耦合更重,插值和维护更麻烦。

工程上更推荐
先做 AWB,再拟合 CCM。

因为这样 CCM 主要负责"色彩空间校正",AWB 负责"中性轴拉正",职责更清晰。

4.4 去 gamma / tone mapping

如果图像经过了 gamma:
xgamma=xlin1/γx_{gamma} = x_{lin}^{1/\gamma}xgamma=xlin1/γ

那通道关系已经不是线性的。

此时再拟合 3×3 线性矩阵,会把非线性问题错误地塞给 CCM。

所以 CCM 标定必须尽量在线性域做。

4.5 demosaic 的影响

理论上 CCM 作用于 RGB,所以你常常要先 demosaic。

但要注意:

  • demosaic 本身会引入通道混合
  • 色卡边缘可能受插值影响
  • 所以一定取色块中心区域

如果用的是低噪、高质量 RAW,demosaic 对色块中心均值的影响通常可接受。

5. 第四步:构建目标参考值

5.1 目标值从哪里来

ColorChecker 的每个色块都有标准参考值,但这个"标准值"要注意它的定义空间,常见有:

  • XYZ
  • Lab
  • 某标准 illuminant 下的反射参考
  • 有时也提供 sRGB 参考值

工程上最稳妥的方法通常是:
先拿到色卡的标准 XYZ 参考值,再根据目标颜色空间转换。

因为 XYZ 更接近颜色测量基础空间。

5.2 为什么很多流程喜欢拟合到 XYZ

因为 XYZ 是设备无关色空间。

传感器 RGB 是设备相关的,目标如果直接用某一套 sRGB 数值,有时会把显示端假设也混进来。

所以常见流程是:
RGBsensor→CCMXYZRGB_{sensor} \xrightarrow{CCM} XYZRGBsensorCCM XYZ

然后再通过标准矩阵:
XYZ→sRGBXYZ \rightarrow sRGBXYZ→sRGB

当然,也可以直接拟合到目标线性 sRGB,工程上两种都能做,只要口径统一。

5.3 如果目标是 sRGB,要注意什么

必须是线性 sRGB,不是 gamma 编码的 sRGB。

也就是要先把标准 sRGB 反 gamma,得到线性值,再拟合。

否则:

  • 左边是线性 sensor RGB
  • 右边是非线性 gamma RGB

矩阵很难拟好。

6. 第五步:最小二乘拟合 CCM

这一步就是数学核心。
1. 构建输入矩阵 X

把 24 个色块相机测得的 RGB 组成矩阵:
X=Rs1Gs1Bs1Rs2Gs2Bs2⋮⋮⋮Rs24Gs24Bs24X= \begin{bmatrix} R_{s1} & G_{s1} & B_{s1}\\ R_{s2} & G_{s2} & B_{s2}\\ \vdots & \vdots & \vdots\\ R_{s24} & G_{s24} & B_{s24} \end{bmatrix}X= Rs1Rs2⋮Rs24Gs1Gs2⋮Gs24Bs1Bs2⋮Bs24

维度是:24×324 \times 324×3

2. 构建目标矩阵 Y

把 24 个色块的参考目标值组成矩阵:
Y=Rt1Gt1Bt1Rt2Gt2Bt2⋮⋮⋮Rt24Gt24Bt24Y= \begin{bmatrix} R_{t1} & G_{t1} & B_{t1}\\ R_{t2} & G_{t2} & B_{t2}\\ \vdots & \vdots & \vdots\\ R_{t24} & G_{t24} & B_{t24} \end{bmatrix}Y= Rt1Rt2⋮Rt24Gt1Gt2⋮Gt24Bt1Bt2⋮Bt24

也是:24×324 \times 324×3

这里下标 t 表示 target。

3. 建立模型

我们假设存在一个 3×3 矩阵 MMM,满足:
Y≈XMTY \approx X M^TY≈XMT

也就是每一个色块:
Rt,Gt,BtRs,Gs,BsMTR_t,G_t,B_t \approx R_s,G_s,B_sM^TRt,Gt,BtRs,Gs,BsMT

4. 最小二乘目标

因为 24 个色块不可能被一个线性矩阵完美拟合,所以求:
min⁡M∣XMT−Y∣F2\min_M |XM^T - Y|_F^2Mmin∣XMT−Y∣F2

即总平方误差最小。

解析解:
MT=(XTX)−1XTYM^T = (X^TX)^{-1}X^TYMT=(XTX)−1XTY

所以:
M=YTX(XTX)−1M = Y^T X (X^TX)^{-1}M=YTX(XTX)−1

在代码里通常直接写:

bash 复制代码
M_T, _, _, _ = np.linalg.lstsq(X, Y, rcond=None)
M = M_T.T

5. 为什么分三次回归更容易理解

因为 3 个输出通道可以分别拟合。
输出 R
min⁡mr∣Xmr−yr∣2\min_{\mathbf{m}_r}|X\mathbf{m}_r-\mathbf{y}r|^2mrmin∣Xmr−yr∣2
输出 G
min⁡mg∣Xmg−yg∣2\min
{\mathbf{m}_g}|X\mathbf{m}_g-\mathbf{y}g|^2mgmin∣Xmg−yg∣2
输出 B
min⁡mb∣Xmb−yb∣2\min
{\mathbf{m}_b}|X\mathbf{m}_b-\mathbf{y}_b|^2mbmin∣Xmb−yb∣2

其中:

  • mr\mathbf{m}_rmr 是 CCM 第一行转置
  • mg\mathbf{m}_gmg 是第二行转置
  • mb\mathbf{m}_bmb 是第三行转置

最后把三组系数拼起来,就是 CCM。

6. 是否要加约束

真实 ISP 里通常会加一些约束或先验。
中性约束

希望中性灰尽量保持中性,常要求每行和接近 1:
m11+m12+m13≈1m_{11}+m_{12}+m_{13}\approx 1m11+m12+m13≈1
m21+m22+m23≈1m_{21}+m_{22}+m_{23}\approx 1m21+m22+m23≈1
m31+m32+m33≈1m_{31}+m_{32}+m_{33}\approx 1m31+m32+m33≈1

加权拟合

有些色块更重要,比如:

  • 灰阶
  • 肤色
  • 天空蓝
  • 叶绿

则可做加权最小二乘:
min⁡M∑i=124wi∣Mxi−yi∣2\min_M \sum_{i=1}^{24} w_i |M x_i - y_i|^2Mmini=1∑24wi∣Mxi−yi∣2

去异常点

如果某个色块取值被反光污染、SNR 太差、ROI 错了,可以剔除或降权。

7. 第六步:如何把 CCM 用起来

得到矩阵 MMM 后,对于任一线性 RGB 像素:
RoutGoutBout=MRinGinBin \begin{bmatrix} R_{out}\\G_{out}\\B_{out} \end{bmatrix}= M \begin{bmatrix} R_{in}\\G_{in}\\B_{in} \end{bmatrix} RoutGoutBout =M RinGinBin

这里的 Rin,Gin,BinR_{in},G_{in},B_{in}Rin,Gin,Bin 一般是:

  • 已黑电平校正
  • 已归一化
  • 已白平衡
  • 线性 RGB

输出是:

  • 目标线性 RGB
  • 或 XYZ
  • 再接后续 gamma / tone mapping / display transform

8. 第七步:用 ΔE 验证效果

这一步特别重要。

因为拟合出一个矩阵,不代表就真的"颜色准"。

工程上一定要做:

把 24 个色块经过 CCM 后,与标准参考做色差比较。

8.1 为什么不用只看 RGB 误差

因为 RGB 空间中的欧氏距离,不等于人眼感知的颜色差异。

例如:

  • 某些颜色在 RGB 上差一点点,人眼很敏感
  • 某些颜色 RGB 差不少,人眼不一定觉得大

所以更常用的是 CIE Lab 空间的 ΔE。

8.2 验证流程

对于每个色块:
第一步

用拟合得到的 CCM 把相机色块 RGB 变换到目标空间。
第二步

把结果转到 XYZ,再转到 Lab。
第三步

把标准参考值也转到同样的 Lab。
第四步

计算色差:

  • ΔE*ab(CIE76)
  • ΔE94
  • ΔE00(CIEDE2000,更常用)

8.3 ΔE 的直觉理解

一般可以粗略理解为:

  • ΔE < 1:几乎看不出差异
  • 1 ~ 2:非常小
  • 2 ~ 3:可接受
  • 3 ~ 5:明显可见
  • 5:偏差较大

工程上不能只看最大值,还要看:

  • 平均 ΔE
  • 最大 ΔE
  • 灰阶平均 ΔE
  • 肤色块 ΔE
  • 饱和色块 ΔE

8.4 验证时要看的统计量

常见指标包括:
平均色差 ΔEmean\Delta E_{mean}ΔEmean
最大色差 ΔEmax\Delta E_{max}ΔEmax
中位数色差
更抗异常点。

分类统计

分别看:

  • 灰阶块
  • 肤色块
  • 高饱和块
  • 蓝绿色块

因为 CCM 常见问题是:

  • 灰阶准,但高饱和炸
  • 高饱和准,但肤色偏
  • 平均不错,但个别块特别差

9. 第八步:如何根据 ΔE 结果诊断问题

这一步很像真实调参。

9.1 如果灰阶 ΔE 大

优先检查:

  • AWB 是否做对
  • 行和是否偏离 1 太多
  • black level 是否准
  • 目标白点口径是否一致

灰阶不好,往往说明中性轴没立住。

9.2 如果肤色块偏差大

优先检查:

  • 红绿通道混合比例
  • 第一行、第二行的交叉项
  • 权重是否对肤色不够重视

工程里肤色通常要单独照顾,因为用户最敏感。

9.3 如果蓝色/绿色饱和块偏差大

常见原因:

  • 传感器光谱响应与理想基色差异较大
  • 单个 3×3 CCM 无法完全覆盖 gamut 边缘
  • 目标色空间超出线性可拟合范围

这时可能需要:

  • 调整权重
  • 降低过饱和期望
  • 用更复杂的 3D LUT / Hue-Sat 映射补偿

9.4 如果平均 ΔE 不错,但最大 ΔE 很大

说明整体拟合还行,但某些块是异常点或特殊颜色拟合失败。

这时要检查:

  • ROI 是否取错
  • 是否有反光污染
  • 某色块是否接近饱和
  • 是否需对个别块降权

10. 工程里 CCM 标定的完整链路可以概括成这样

你可以把整个流程记成这条线:

  1. 拍图
    在标准光源下拍 24 色 ColorChecker RAW
  2. 预处理
    黑电平校正、归一化、demosaic、线性化、AWB
  3. 取块
    检测色卡,提取每个色块中心 ROI 均值
  4. 构建样本
    得到 24 组相机 RGB 与 24 组标准参考值
  5. 拟合
    用最小二乘求 3×3 CCM
  6. 应用
    把 CCM 作用到线性 RGB
  7. 验证
    转 Lab,计算每块 ΔE00,分析平均/最大/分组表现
  8. 回调
    根据灰阶、肤色、饱和色的偏差重新调权重或加约束,再拟合

11. 为什么实际手机 ISP 往往不止一个 CCM

因为单个 CCM 往往只对某个 illuminant 最优。

真实工程里一般会做:

  • A 光一套 CCM
  • D65 一套 CCM
  • TL84 一套 CCM

然后根据 AWB 估计的 CCT,在两套或多套 CCM 之间插值:
M(T)=αM1+(1−α)M2M(T) = \alpha M_1 + (1-\alpha)M_2M(T)=αM1+(1−α)M2

这样才能覆盖不同光源下的颜色变化。

在 ColorChecker 24 色卡的 CCM 标定中,首先在标准光源下采集相机 RAW 图像,并完成黑电平校正、归一化、去马赛克和线性化处理;随后在每个色块中心区域提取稳定的线性 RGB 均值,形成相机颜色响应矩阵 X。与此同时,根据色卡在对应光源下的标准参考数据构建目标颜色矩阵 Y。在 AWB 之后,采用最小二乘方法求解
min⁡M∣XMT−Y∣F2\min_M |XM^T - Y|_F^2Mmin∣XMT−Y∣F2

得到 3×3 颜色校正矩阵 CCM。为评价标定效果,将校正结果转换至 Lab 空间,并计算各色块与标准值之间的 ΔE,综合分析平均色差、最大色差以及灰阶、肤色和高饱和色块的表现,据此对矩阵权重、约束条件和异常样本进行进一步优化。

七、如何调试CCM

调试CCM的目标是通过调整矩阵系数来获得图像准确的颜色表现。为了实现这一目标,调试时需要考虑图像传感器的特性、光照条件、色彩误差、设备差异等因素。调试过程可以分为 手动调节自动化调节,并且通常会结合以下几种方法。

1. 使用标准色卡进行手动调节

标准色卡(Color Checker) 是调试CCM的一个重要工具。常见的标准色卡包括 X-Rite ColorChecker 和 Macbeth ColorChecker,这些色卡上包含了多种标准颜色,它们的RGB值是已知的,并且经过严格测量。通过将拍摄的标准色卡图像与色卡的标准颜色进行比对,可以计算出图像的颜色误差,并通过调整 CCM矩阵 来修正这些误差。

1.1 步骤:

1. 拍摄色卡图像:

使用摄像头或图像传感器拍摄色卡,记录标准色卡上的每个颜色的RGB值。

2. 计算颜色误差:

比较传感器输出的RGB值(例如通过RAW图像获取的RGB值)与色卡上已知的标准RGB值之间的差异。例如,对于某个颜色,标准色卡上的RGB值是 (Rs,Gs,Bs)(R_s, G_s, B_s)(Rs,Gs,Bs),而传感器输出的RGB值是 (Ri,Gi,Bi)(R_i, G_i, B_i)(Ri,Gi,Bi),误差可以通过如下公式计算:
ΔC=(RiGiBi)−(RsGsBs)\Delta C = \begin{pmatrix} R_i \\ G_i \\ B_i \end{pmatrix} - \begin{pmatrix} R_s \\ G_s \\ B_s \end{pmatrix}ΔC= RiGiBi − RsGsBs

3. 调节CCM矩阵:

使用最小二乘法(Least Squares)等优化算法,根据计算出的误差调整 CCM矩阵。这个优化的目标是最小化误差,使得传感器输出的RGB值尽可能接近标准色卡的RGB值。

4. 检查调节结果:

完成初步调节后,再次拍摄色卡图像,并计算新的误差。如果误差变小且色彩还原准确,说明调节成功。否则,重复调节过程。

1.2 注意事项:

  • 在调节过程中,必须确保传感器和标准色卡的光照条件相似,避免环境光源的变化引起不必要的误差。
  • 一些高级的色卡(如 X-Rite ColorChecker)会提供较为复杂的色彩标准,确保图像的RGB表现与这些标准尽量一致。

2. 逐通道调节

逐通道调节方法是针对 红色通道(R)、绿色通道(G) 和 蓝色通道(B) 分别进行细致调节,直到图像颜色表现准确。调节过程通常需要借助测试图案或者特定的场景,确保每个通道的颜色表现符合预期。

2.1 步骤:

1. 选择测试图案:

使用标准的测试图案,如 灰阶图、色轮、人脸图像等,来测试图像中的颜色还原和色调准确性。
2. 单独调节每个通道:

  • 调节 红色通道(R):例如,如果图像偏红,可以减小矩阵中对应的红色分量。
  • 调节 绿色通道(G):通常,绿色通道对于色彩的准确性至关重要,因为人眼对绿色较为敏感。如果图像偏绿,可以调整绿色分量。
  • 调节 蓝色通道(B):如果图像偏蓝或色彩显得过冷,可以调整蓝色通道的系数。

3. 评估图像颜色:

每次调节后,都要评估图像中的色彩表现,检查是否出现色偏、色彩失真等现象。通常,可以通过查看图像中白色物体的颜色是否正确,或者观察人脸色调的准确性来判断。

4. 反复调整:

根据调节结果继续调整矩阵中的系数,直到图像中的颜色达到理想状态。调节通常需要一些反复试验,特别是在多个摄像头之间进行一致性调节时。

2.2 注意事项:

  • 绿色通道的重要性:由于绿色通道对亮度信息的贡献最大,调节时需要特别注意绿色通道的表现。特别是在低光环境下,绿色通道的误差可能导致整个图像的颜色失衡。
  • 避免过度调节:过度调节可能导致图像产生明显的色偏,尤其是在饱和色调较强的区域。需要保持调节的精度和细致。

3. 自动化调节

随着图像处理技术的不断进步,自动化调节CCM 已成为现代ISP系统的主流方案。通过机器学习、图像处理和优化算法,自动化调节可以大大提高调节效率和准确性。

3.1 自动化调节流程:

1. 数据收集:

通过拍摄多个不同光照、场景、传感器状态下的图像,收集各种可能的输入数据。这些数据将用于训练自动化算法。

2. 误差计算与优化:

自动化调节通过误差计算(如均方误差、色差等)来评估图像的颜色准确性。优化算法(如 最小二乘法、梯度下降法、遗传算法 等)用来调整 CCM矩阵 的系数,使其误差最小。

3. 使用深度学习:

近年来,深度学习已经被广泛应用于颜色校正。通过训练神经网络模型,输入图像的RAW数据,输出图像的RGB值,自动推算出最优的CCM矩阵。训练数据包括大量不同拍摄条件下的图像,神经网络可以根据这些数据学习出合适的色彩校正模型。

4. 实时调整:

在实际应用中,自动化调节CCM能够根据传感器的不同输入自动调整矩阵。特别是在动态光照、移动拍摄等场景下,自动化调节能够快速响应并调整颜色。

3.2 优势与挑战:

  • 优势
    • 高效率:自动化调节比手动调节更加高效,尤其在需要处理大量数据或多个摄像头时。
    • 适应性强:自动化调节能够适应不同的传感器、光照条件、环境变化等。
    • 精准度高:通过机器学习和优化算法,自动化调节可以减少人为错误,提高颜色还原的准确性。
  • 挑战
    • 计算复杂度:自动化调节通常需要较强的计算能力,特别是使用深度学习时,计算需求更高。
    • 训练数据要求:需要大量标定数据来训练模型,这对于新设备或新传感器来说,可能需要额外的标定过程。
    • 硬件限制:实时调节可能受到硬件计算能力和延迟的限制,尤其是在高分辨率和高帧率的视频应用中。

4. 综合调试与优化策略

调试和优化 CCM矩阵 时,综合考虑多个因素非常重要。以下是常见的优化策略:

4.1 综合误差度量

调试过程中不仅要关注图像的色彩准确性,还要综合考虑误差度量。例如:

  • 均方误差(MSE):评估图像颜色的整体误差。
  • 色差(CIEDE2000):考虑人眼对颜色差异的感知。
  • 饱和度与亮度对比度:保持图像的自然感和清晰度。

4.2 各种光照条件下的测试

图像颜色会受到光照条件的显著影响。调试时需要确保 CCM矩阵 能适应不同光照条件下的变化。例如,低光照时可能需要增强绿色通道,而高光照时可能需要调整蓝色通道的增益。

4.3 跨摄像头一致性

当手机拥有多个摄像头时,需要确保每个摄像头的 CCM矩阵 一致性。通过自动化调节和优化算法,确保同一场景在不同摄像头下的颜色表现一致。

调试 CCM 的目标是让图像的颜色表现尽可能准确和自然,消除色偏并增强色彩还原。通过手动调节、逐通道调节和自动化调节等方法,图像的色彩校正可以得到精确优化。在多摄像头、多光照条件下,调节 CCM 也变得更加复杂,但通过自动化和机器学习技术,调节过程可以更加高效和准确。

八、总结

Color Correction Matrix 的挑战与优化

  1. 光照条件变化
    光照条件的变化会显著影响图像的色彩表现。低光照环境可能会导致传感器捕获到的图像颜色失真,而强光环境下,图像的高光区域可能会出现颜色过度饱和的问题。CCM需要能够适应这些不同的光照条件,确保图像颜色的准确性。
  2. 跨传感器一致性
    手机中的多个摄像头可能使用不同厂商的传感器,这会导致图像的颜色表现差异。如何在不同传感器之间保持一致的色彩表现是一个挑战。CCM能够帮助统一这些传感器的色彩输出,使得不同摄像头拍摄的图像具有一致的颜色。
  3. 高动态范围(HDR)
    在HDR模式下,图像的动态范围非常宽。CCM的调整不仅需要关注整体颜色的准确性,还需要考虑高光和暗部的色彩表现。如何在保持细节的同时,避免颜色失真,是CCM调试中的一个挑战。

颜色校正矩阵(CCM) 是在图像信号处理中用于将传感器的RGB数据转换为标准颜色空间的关键工具。通过矩阵变换,CCM可以去除色偏、调整白平衡、色彩还原和增强图像的色彩表现。调试CCM时,我们需要使用标准色卡、逐通道调节和自动化调试等方法,以确保图像颜色的准确性和一致性。尽管CCM在实际应用中面临光照变化、跨传感器一致性以及HDR等挑战,但通过优化和调节,我们可以得到更加真实和自然的图像颜色。


结~~~

相关推荐
地平线开发者6 小时前
profiler debug 工具用法与高一致性策略
算法·自动驾驶
编程大师哥6 小时前
匿名函数 lambda + 高阶函数
java·python·算法
我叫袁小陌7 小时前
算法解题思路指南
算法
地平线开发者7 小时前
Conv+BN+Add+ReLU 融合机制简介
算法·自动驾驶
yuanyuan2o27 小时前
模型预训练:Hugging Face Transformers 基础
算法·ai·语言模型·自然语言处理·nlp·深度优先
杨充7 小时前
1.3 浮点型数据设计灵魂
开发语言·python·算法
妄想出头的工业炼药师8 小时前
GS slam mono
算法·开源
_日拱一卒8 小时前
LeetCode:207课程表
java·数据结构·算法·leetcode·职场和发展
用户9874092388711 小时前
llamafactory 0.6.3 没有 llamafactory-cli
算法