4、颜色空间变换:RGB到RAW的逆向映射、颜色抖动、白平衡模拟、色彩偏移增强
颜色空间变换,说白了就是让数据在「人眼看到的颜色」和「传感器记录的颜色」之间来回折腾。
我们做RAW域增强,核心思路是:先把sRGB图像逆向映射回RAW空间,然后在RAW域里做各种颜色层面的数据增强,最后再映射回去。这样训练出来的模型,才能学会传感器层面的颜色特性。
核心观点:RAW域的颜色增强,不是简单的RGB调色。它要模拟真实ISP管线的物理过程——白平衡、颜色校正、色彩偏移。你做的每一步,都要有物理依据。
4.1 RGB到RAW的逆向映射
先说说逆向映射。sRGB图像是经过ISP管线处理后的结果,它已经包含了白平衡、Gamma校正、颜色空间转换等操作。我们要做的,就是把这些操作「反着来一遍」。
我习惯把逆向映射拆成三步:
- 反Gamma:sRGB的Gamma大约是2.2,反Gamma就是做一次幂运算,指数取1/2.2。
- 反颜色校正矩阵(CCM):ISP会把RAW的RGB映射到标准颜色空间,逆向就是乘一个逆矩阵。
- 反白平衡:把R、G、B通道的增益去掉,恢复到传感器原始响应。
嗯,这里要注意:逆向映射不是完美的。ISP管线里有很多非线性操作(比如色调映射、去马赛克),这些操作不可逆。所以我们只能做一个「近似逆向」,保留主要物理过程就好。
def srgb_to_raw_approx(srgb_img, ccm_matrix, wb_gains):
"""
sRGB → RAW 近似逆向映射
srgb_img: [H, W, 3], 范围 [0, 1]
ccm_matrix: 3x3 颜色校正矩阵
wb_gains: [R_gain, G_gain, B_gain]
"""
# 1. 反Gamma
linear_img = np.where(srgb_img <= 0.04045,
srgb_img / 12.92,
((srgb_img + 0.055) / 1.055) ** 2.4)
# 2. 反CCM
inv_ccm = np.linalg.inv(ccm_matrix)
raw_linear = linear_img @ inv_ccm.T
# 3. 反白平衡
raw_img = raw_linear / np.array(wb_gains)
return np.clip(raw_img, 0, 1)
我的经验:逆向映射时,我一般会保留一个「残差项」。因为ISP管线里还有去马赛克、降噪等操作,这些没法逆向。保留残差可以让模型自己学这部分差异。
4.2 颜色抖动
颜色抖动,就是在RAW域里对颜色做随机扰动。为什么要做这个?因为真实场景下,传感器的颜色响应会有波动——温度变化、曝光时间、镜头老化,都会影响颜色。
我常用的颜色抖动方法有三种:
| 方法 | 原理 | 适用场景 |
|---|---|---|
| 通道独立抖动 | 对R、G、B通道分别加随机增益 | 模拟传感器通道响应差异 |
| 颜色相关抖动 | 在颜色空间(如YUV)里做扰动 | 保持亮度不变,只改变色度 |
| 局部颜色抖动 | 按区域随机调整颜色偏移 | 模拟镜头渐晕或色差 |
我个人习惯用通道独立抖动,因为它最简单,而且效果足够好。抖动幅度一般控制在5%~15%之间,太大了会破坏颜色结构。
def color_jitter_raw(raw_img, jitter_strength=0.1):
"""
RAW域颜色抖动
raw_img: [H, W, 3], 范围 [0, 1]
jitter_strength: 抖动幅度
"""
# 生成随机增益,范围 [1 - strength, 1 + strength]
gains = 1.0 + np.random.uniform(-jitter_strength, jitter_strength, 3)
# 逐通道应用
jittered = raw_img * gains.reshape(1, 1, 3)
return np.clip(jittered, 0, 1)
避坑指南:我曾经在颜色抖动时忘了限制增益范围,结果训练出来的模型对极端颜色很敏感,一遇到偏色就崩。后来我加了截断,抖动幅度不超过20%,效果就稳了。
4.3 白平衡模拟
白平衡模拟,是RAW域增强里最实用的一个技巧。为什么?因为真实场景的光源千变万化——日光、白炽灯、荧光灯、LED,每种光源的色温都不一样。模型如果只在一种光源下训练,换个场景就废了。
白平衡模拟的核心思路:在RAW域里,通过调整R和B通道的增益,模拟不同色温下的白平衡效果。
具体做法:
- 设定色温范围(比如2500K~7500K)
- 根据色温计算R和B通道的增益
- 随机采样一个色温,应用对应的增益
你想想看,这样做的好处是什么?模型在训练时见过各种色温下的数据,推理时就能自动适应不同光源。
def simulate_white_balance(raw_img, temp_range=(2500, 7500)):
"""
白平衡模拟
temp_range: 色温范围 (K)
"""
# 随机采样色温
temp = np.random.uniform(*temp_range)
# 简化的色温-增益映射
# 实际项目中可以用查表法或多项式拟合
r_gain = 1.0 + 0.5 * (temp - 5000) / 5000
b_gain = 1.0 - 0.5 * (temp - 5000) / 5000
g_gain = 1.0
gains = np.array([r_gain, g_gain, b_gain])
wb_img = raw_img * gains.reshape(1, 1, 3)
return np.clip(wb_img, 0, 1), temp
我的经验:白平衡模拟的色温范围不要设得太宽。我曾经试过1000K~10000K,结果模型学了一堆极端偏色,反而影响了正常场景的表现。现在我用2500K~7500K,覆盖了绝大多数室内外场景。
4.4 色彩偏移增强
色彩偏移增强,比白平衡模拟更「激进」一些。它不只是调整色温,而是在颜色空间里做更复杂的变换。
我常用的色彩偏移增强方法:
- HSV偏移:在HSV空间里随机调整H(色相)、S(饱和度)、V(明度)
- 颜色混合:把当前颜色和另一个随机颜色做线性混合
- 通道置换:随机交换R、G、B通道(这个比较暴力,慎用)
说白了,色彩偏移增强就是让模型学会「颜色变了,但内容不变」。这样模型就不会过度依赖颜色特征,而是更关注纹理和结构。
def color_shift_raw(raw_img, shift_strength=0.2):
"""
RAW域色彩偏移增强
"""
# 转HSV
hsv = cv2.cvtColor(raw_img, cv2.COLOR_RGB2HSV)
# 随机偏移H、S、V
hsv[:,:,0] = (hsv[:,:,0] + np.random.uniform(-shift_strength, shift_strength)) % 1.0
hsv[:,:,1] = np.clip(hsv[:,:,1] * np.random.uniform(0.8, 1.2), 0, 1)
hsv[:,:,2] = np.clip(hsv[:,:,2] * np.random.uniform(0.9, 1.1), 0, 1)
# 转回RGB
shifted = cv2.cvtColor(hsv, cv2.COLOR_HSV2RGB)
return shifted
避坑指南:色彩偏移增强一定要控制幅度。我曾经在一次项目里把饱和度偏移设到0.5,结果模型把红色和绿色搞混了,在测试集上直接崩了。后来我把偏移幅度控制在0.2以内,效果才稳定。
4.5 本章知识体系
下面这张图,把本章的核心逻辑串起来了:
这张图把本章的四个核心方法串起来了。你可以看到,逆向映射是基础,它把sRGB数据拉回RAW域;颜色抖动、白平衡模拟、色彩偏移是三种不同的增强策略,它们可以组合使用,也可以单独使用。
我个人建议:白平衡模拟是必选项,因为它最贴近真实场景;颜色抖动和色彩偏移可以根据任务需求选择。如果任务对颜色敏感(比如肤色检测),色彩偏移的幅度要小一点;如果任务对颜色不敏感(比如边缘检测),可以大胆用。
总结:颜色空间变换是RAW域增强的核心环节。它让模型学会「颜色变了,但内容不变」,从而提升模型的泛化能力。记住:物理依据 + 可控幅度 = 有效增强。