色彩空间基础:RGB、XYZ、Lab、HSV 的定义与转换

做传感器标定和色彩校正,绕不开一个核心问题——色彩空间。说白了,就是用什么数学方式去描述颜色。

我刚开始接触这行时,以为 RGB 就是全部。后来在工业相机项目里被色差问题折磨得够呛,才明白:选错色彩空间,校正精度直接崩盘。今天咱们就把几个最常用的色彩空间掰开揉碎讲清楚。

1. RGB 色彩空间——最直观,也最不靠谱

RGB 是硬件最爱的格式。传感器输出 RAW 数据后,经过去马赛克、白平衡,出来的就是 RGB。每个像素三个通道,范围 0-255(8bit)或 0-1023(10bit)。

但 RGB 有个致命问题:它和设备强相关。同一张图片,在不同显示器上颜色完全不同。为什么?因为 RGB 没有定义“什么是红色”。

关键认知: RGB 是设备依赖色彩空间。它适合显示,不适合做色彩计算和跨设备传递。

我在一个手机摄像头项目里,用 RGB 直接做色彩校正矩阵,结果换了个屏幕模组,所有参数都得重调。从那以后,我养成了习惯:做校正前,先把 RGB 转到设备无关空间。

2. XYZ 色彩空间——色彩科学的“坐标系”

XYZ 是 CIE 在 1931 年定义的。它基于人眼视觉实验,把颜色映射到三个假想原色 X、Y、Z 上。Y 通道直接对应亮度,X 和 Z 负责色度信息。

XYZ 最大的价值:它是所有色彩空间转换的桥梁。RGB→XYZ→Lab,RGB→XYZ→LMS,都绕不开它。

色彩空间 设备依赖 主要用途 我常用的场景
RGB 是 显示、存储 传感器原始数据、预览
XYZ 否 色彩科学、标准定义 色域映射、校正矩阵计算
Lab 否 色差评估、感知均匀 色彩校正效果验证
HSV 是 图像处理、分割 颜色识别、阈值调整
小技巧: 做传感器标定时,我习惯把白平衡后的 RGB 先转成 XYZ,再计算校正矩阵。这样矩阵对传感器物理特性更敏感,对显示设备不敏感。

3. Lab 色彩空间——人眼说了算

Lab 是 XYZ 的“感知均匀”版本。L 代表亮度,a 代表红绿方向,b 代表黄蓝方向。为什么说它感知均匀?因为在 Lab 空间里,两个颜色之间的欧氏距离,和人眼感知到的色差基本一致。

这太重要了。你想想看,在 RGB 空间里,两个颜色数值差 10,人眼可能觉得差别很大,也可能觉得几乎一样。但在 Lab 空间里,差 10 就是差 10,感知上差不多。

我曾经用 Lab 空间做色彩校正的验收标准。客户要求 ΔE < 3,我直接在 Lab 空间算距离,一目了然。要是用 RGB 算,根本没法跟客户解释清楚。

注意: Lab 空间的计算依赖白点参考。D65、D50、A 光源下的 Lab 值完全不同。做校正时,一定要统一白点,否则 ΔE 算出来没有意义。

4. HSV 色彩空间——调色师的直觉

HSV 把颜色拆成色相(Hue)、饱和度(Saturation)、明度(Value)。H 是 0-360° 的角度,S 和 V 是 0-1 的归一化值。

这个空间特别适合做颜色分割和阈值调整。比如在工业视觉里,要识别红色零件,用 RGB 阈值很难调——光照一变,R 和 G 值全变了。但用 HSV,H 通道基本不受光照影响,调起来省心得多。

我记得有个项目,要在流水线上检测标签颜色是否印错。用 RGB 做分类,误检率 15%。换成 HSV 后,误检率降到 2% 以下。原因很简单:HSV 把颜色和亮度分开了。

5. 色彩空间转换——代码实战

下面是我在项目中常用的转换代码。注意,转换矩阵和 Gamma 曲线必须和传感器特性对齐,不能随便抄网上的值。

import numpy as np

def rgb_to_xyz(rgb, matrix):
    """
    rgb: (H, W, 3) 归一化到 [0, 1]
    matrix: 3x3 转换矩阵,由传感器标定得到
    """
    rgb_linear = np.where(rgb <= 0.04045, rgb / 12.92, ((rgb + 0.055) / 1.055) ** 2.4)
    xyz = rgb_linear @ matrix.T
    return xyz

def xyz_to_lab(xyz, white_point):
    """
    xyz: (H, W, 3)
    white_point: [Xw, Yw, Zw] 参考白点
    """
    epsilon = 0.008856
    kappa = 903.3
    
    xyz_n = xyz / white_point
    
    f = np.where(xyz_n > epsilon, xyz_n ** (1/3), (kappa * xyz_n + 16) / 116)
    
    L = 116 * f[..., 1] - 16
    a = 500 * (f[..., 0] - f[..., 1])
    b = 200 * (f[..., 1] - f[..., 2])
    
    return np.stack([L, a, b], axis=-1)
避坑指南: 我曾经直接用 sRGB 的转换矩阵去处理工业相机的 RAW 数据,结果色差大得离谱。后来才意识到,每个传感器的光谱响应不同,转换矩阵必须单独标定。别偷懒,老老实实拍色卡算矩阵。

6. 色域与色深——能显示多少颜色

色域是色彩空间能覆盖的颜色范围。sRGB 覆盖约 35% 的 CIE 色域,Adobe RGB 约 50%,DCI-P3 约 45%。做传感器标定时,要确保目标色域和传感器原生色域匹配,否则校正后颜色会“溢出”或“压缩”。

色深是每个通道用多少 bit 表示。8bit 每通道 256 级,10bit 1024 级,12bit 4096 级。色深不够,校正后会出现色带——平滑渐变区域出现明显条纹。

色深 每通道级数 总颜色数 适用场景
8bit 256 16.7M 预览、Web
10bit 1024 1.07B 工业相机、广播
12bit 4096 68.7B 科学成像、医疗
14bit 16384 4.4T 高动态范围、天文
重要提醒: 色深不是越高越好。12bit 传感器配 8bit 处理链路,等于白费。我见过有人花大价钱买 14bit 相机,结果后端存储用 JPEG(8bit),校正精度全丢了。整个链路必须对齐。

7. 知识体系总览

下面这张图总结了本章的核心逻辑。你可以把它当作色彩空间选择的决策地图。

色彩空间知识体系 色彩空间 RGB - 设备依赖 XYZ - 坐标 Lab - 感知均匀 HSV - 直觉调色 色域 - 颜色范围 色深 - 量化精度 选择色彩空间 = 根据任务选工具 校正用 XYZ/Lab,分割用 HSV,显示用 RGB

嗯,到这里色彩空间的基础就讲完了。记住一句话:没有最好的色彩空间,只有最合适的。做传感器标定时,我通常先在 XYZ 空间算校正矩阵,再用 Lab 空间验证效果,最后转回 RGB 输出。这套流程,稳得很。