简介:个人学习分享,如有错误,欢迎批评指正。
一、什么是颜色校正矩阵(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矩阵。
步骤:
- 选择训练数据集:通过选择多组具有已知颜色目标的图像数据集来进行训练。通常需要涵盖多种光照条件、环境变化和传感器特性。
- 计算误差:在每组数据中,计算实际传感器输出的RGB与目标标准RGB之间的误差。常见的误差度量方式是计算均方误差(MSE)或者色差(如CIEDE2000)。
- 优化矩阵:通过最小化所有误差的平方和,使用优化算法(如最小二乘法或梯度下降法)来优化矩阵系数,得到最优的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图像之间的差异,自动优化矩阵中的权重系数。
训练步骤:
- 准备训练数据集:收集大量有标定图像的训练数据集,包括各种光照、场景、传感器的颜色输出。每一组数据包括传感器拍摄的RAW图像和其对应的标准RGB图像。
- 网络模型设计:设计一个卷积神经网络或其他深度学习模型,通过模型学习RGB与标准颜色之间的映射关系。
- 训练网络:使用梯度下降法或其他优化算法,训练神经网络,使得其输出与标准RGB图像尽可能接近,从而自动推导出最优的CCM矩阵。
3.2 自动化和实时调节
神经网络还可以应用于实时调节CCM矩阵,尤其是在动态环境中。当光照条件或传感器参数发生变化时,神经网络可以根据新的输入图像自动调整CCM矩阵,使得图像颜色始终保持真实和一致。
通过上述三种方法,我们可以获得 Color Correction Matrix (CCM):
- 通过标定板数据计算:通过拍摄标准色卡图像,比较传感器输出和标准色卡之间的误差,通过最小二乘法等优化算法计算得到CCM矩阵。
- 通过算法优化计算:通过误差最小化和算法优化(如梯度下降法),自动调整矩阵系数,使得传感器输出的颜色接近标准色卡。
- 基于机器学习的自动化计算:使用深度学习技术,通过训练神经网络自动计算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\]=\[m11m12m13m21m22m23m31m32m33\]\[RinGinBin\]\\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\]=\[m11m12m13m21m22m23m31m32m33\]\[RsGsBs\] \\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⋮⋮⋮RtNGtNBtN\]Y= \\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,;Bs\]MT\[R_t,;G_t,;B_t\] = \[R_s,;G_s,;B_s\] M\^T\[Rt,;Gt,;Bt\]=\[Rs,;Gs,;Bs\]MT 也有人写成列向量形式: YT=MXTY\^T = M X\^TYT=MXT 本质完全一样。 ### 5. 为什么能用最小二乘解 因为真实情况下不可能有一个矩阵让所有色块都精确满足: Y=XMTY = X M\^TY=XMT 于是我们只能找一个让误差最小的矩阵: minM∣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 写成矩阵: \[100502030120402040150\]\[m11m12m13\]=\[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=\[100502030120402040150\]A= \\begin{bmatrix} 100 \& 50 \& 20\\\\ 30 \& 120 \& 40\\\\ 20 \& 40 \& 150 \\end{bmatrix}A= 100302050120402040150 则: A\[m11m12m13\]=\[1102818\]A\\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=[100000100000100]X= \begin{bmatrix} 100 & 0 & 0\\ 0 & 100 & 0\\ 0 & 0 & 100 \end{bmatrix}X= 100000100000100
即:
- 第一个色块主要是红
- 第二个色块主要是绿
- 第三个色块主要是蓝
对应目标标准值为:
Y=[110−50 10955 08120]Y= \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.20]M^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.20]M= \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.20]M= \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⋮⋮⋮RsNGsNBsN\]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}X= Rs1Rs2⋮RsNGs1Gs2⋮GsNBs1Bs2⋮BsN Y=\[Rt1Gt1Bt1Rt2Gt2Bt2⋮⋮⋮RtNGtNBtN\]Y= \\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. 最小二乘解** 求: minM∣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** minmr∣Xmr−yr∣2\\min_{\\mathbf{m}_r}\|X\\mathbf{m}_r-\\mathbf{y}_r\|\^2mrmin∣Xmr−yr∣2 **拟合输出 G** minmg∣Xmg−yg∣2\\min_{\\mathbf{m}_g}\|X\\mathbf{m}_g-\\mathbf{y}_g\|\^2mgmin∣Xmg−yg∣2 **拟合输出 B** minmb∣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 权重拟合 有些色块比别的更重要,例如: * 肤色 * 中性灰 * 高饱和主色 那么目标函数会写成加权形式: minM∑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,则通过最小化整体颜色误差 minM∣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⋮⋮⋮Rs24Gs24Bs24\]X= \\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⋮⋮⋮Rt24Gt24Bt24\]Y= \\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,Bt\]≈\[Rs,Gs,Bs\]MT\[R_t,G_t,B_t\] \\approx \[R_s,G_s,B_s\]M\^T\[Rt,Gt,Bt\]≈\[Rs,Gs,Bs\]MT **4. 最小二乘目标** 因为 24 个色块不可能被一个线性矩阵完美拟合,所以求: minM∣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** minmr∣Xmr−yr∣2\\min_{\\mathbf{m}_r}\|X\\mathbf{m}_r-\\mathbf{y}_r\|\^2mrmin∣Xmr−yr∣2 **输出 G** minmg∣Xmg−yg∣2\\min_{\\mathbf{m}_g}\|X\\mathbf{m}_g-\\mathbf{y}_g\|\^2mgmin∣Xmg−yg∣2 **输出 B** minmb∣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 **加权拟合** 有些色块更重要,比如: * 灰阶 * 肤色 * 天空蓝 * 叶绿 则可做加权最小二乘: minM∑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\]=M\[RinGinBin\] \\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 之后,采用最小二乘方法求解 minM∣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等挑战,但通过优化和调节,我们可以得到更加真实和自然的图像颜色。 *** ** * ** *** 结\~\~\~