颜色与相机成像专题(3):RGB 不等于 sRGB,Gamma、Lab 和"亮度"一次讲清
把 RGB 数组除以 255 就是线性图像吗?灰度图是否代表场景亮度?本文给出可直接用于代码的 sRGB 公式,并解释常见色彩空间各自解决什么问题。
关键词:sRGB、Gamma、CIE Lab、Luminance、Luma
1. "这是 RGB 图像"缺少了哪些信息?
RGB 表示三个分量,但要准确解释颜色,至少还需要知道基色、白点和传递函数。数值范围、通道顺序以及图像是否经过渲染也很重要。
同样的 ( 0.7 , 0.2 , 0.1 ) (0.7,0.2,0.1) (0.7,0.2,0.1),在 camera RGB、sRGB 和 Display P3 中可能代表不同颜色。把 camera RGB 的数组直接交给默认按 sRGB 显示的软件,不构成正确的颜色转换。
| 名称 | 数据含义 | 关键提醒 |
|---|---|---|
| camera RGB | 设备光谱响应对应的通道值 | 随传感器变化 |
| 线性 sRGB | sRGB 基色、D65 白点下的线性分量 | 仍可能已经过相机渲染 |
| 编码 sRGB | 对线性 sRGB 应用规定的传递函数 | 常见 8 位图像通常在此域 |
| CIE XYZ | 标准观察者下的三刺激值 | 注意白点和数值尺度 |
| CIE Lab | 相对于参考白的近似感知均匀表示 | 不存在对所有场景通用的默认白点 |
2. sRGB 规定了什么?
sRGB 使用规定的红、绿、蓝基色和 D65 白点,还规定非线性编码。它使不同设备有机会一致地呈现同一组颜色值,但不要求相机忠实还原场景。
讲义将 sRGB 与 Rec.709 放在一起介绍,容易让人以为二者完全等同。准确地说,它们共享基色色度和 D65 白点,但传递函数及使用约定不同,视频链路还可能涉及不同的量化范围。不能因此混用所有公式。
对以 D65 为参考、 Y Y Y 通常归一化到 1 的 XYZ,转换到线性 sRGB 可写为:
R G B = 3.2404542 − 1.5371385 − 0.4985314 − 0.9692660 1.8760108 0.0415560 0.0556434 − 0.2040259 1.0572252 X Y Z \begin{bmatrix} R \\ G \\ B \end{bmatrix} = \begin{bmatrix} 3.2404542 & -1.5371385 & -0.4985314 \\ -0.9692660 & 1.8760108 & 0.0415560 \\ 0.0556434 & -0.2040259 & 1.0572252 \end{bmatrix} \begin{bmatrix} X \\ Y \\ Z \end{bmatrix} RGB = 3.2404542−0.96926600.0556434−1.53713851.8760108−0.2040259−0.49853140.04155601.0572252 XYZ
如果输入 XYZ 的参考白是 D50,通常需要先进行适当的色适应,再使用 D65 版本矩阵。不能把不同白点的 XYZ 数值不加说明地代入同一矩阵。
3. sRGB 的精确编码和解码
把一个线性分量记为 u u u,编码分量记为 v v v,以下公式限定在非负、归一化的常规范围内:
v = { 12.92 u , u ≤ 0.0031308 1.055 u 1 / 2.4 − 0.055 , u > 0.0031308 v= \begin{cases} 12.92u,&u\le 0.0031308\\ 1.055u^{1/2.4}-0.055,&u>0.0031308 \end{cases} v={12.92u,1.055u1/2.4−0.055,u≤0.0031308u>0.0031308
逆变换为:
u = { v / 12.92 , v ≤ 0.04045 ( ( v + 0.055 ) / 1.055 ) 2.4 , v > 0.04045 u= \begin{cases} v/12.92,&v\le 0.04045\\ ((v+0.055)/1.055)^{2.4},&v>0.04045 \end{cases} u={v/12.92,((v+0.055)/1.055)2.4,v≤0.04045v>0.04045
口语中的"Gamma 约为 2.2"只是近似,不能替代标准的分段公式。 1 / 2.2 1/2.2 1/2.2 也不等于立方根指数 1 / 3 1/3 1/3;讲义对心理物理学的直觉介绍不应作为实现公式。

图 1:比较线性输入与编码值,黑白等权混合的结果。
一个很有用的检查点:编码值 v = 0.5 v=0.5 v=0.5 对应线性值约为 0.214 0.214 0.214,不是 0.5 0.5 0.5。8 位中灰数值 128 经解码约为 0.216 0.216 0.216。
4. 为什么平均、模糊和插值要关心 Gamma?
黑与白在光量意义上等权混合,应先在线性域计算:
u m i x = ( 0 + 1 ) / 2 = 0.5 u_{\mathrm{mix}}=(0+1)/2=0.5 umix=(0+1)/2=0.5
编码后约为 0.735 0.735 0.735,对应 8 位值约 188。如果直接平均两个编码值,得到 v = 0.5 v=0.5 v=0.5,约为 128,显示结果更暗。
这不是所有图像操作都必须线性化的理由。物理光量混合应在相应线性域完成;某些艺术编辑和感知优化则可能有意使用其他空间。应先确定运算目标,再选数据表示。
下面的实现不依赖图像库,输入输出均为浮点数组:
python
import numpy as np
def srgb_decode(v):
v = np.asarray(v, dtype=np.float64)
if np.any((v < 0) | (v > 1)):
raise ValueError("Expected encoded sRGB in [0, 1]")
return np.where(v <= 0.04045, v / 12.92,
((v + 0.055) / 1.055) ** 2.4)
def srgb_encode(u):
u = np.asarray(u, dtype=np.float64)
if np.any((u < 0) | (u > 1)):
raise ValueError("Expected linear sRGB in [0, 1]")
return np.where(u <= 0.0031308, 12.92 * u,
1.055 * u ** (1 / 2.4) - 0.055)
print(srgb_decode(0.5)) # 约 0.214041
print(srgb_encode(0.5)) # 约 0.735357
实际颜色变换可能产生负值或大于 1 的值。这些值应在明确的色域映射、曝光或输出策略下处理,不要在每个中间步骤都盲目裁剪。上面函数为了让教学边界清晰,直接拒绝超范围输入。
5. Luminance 和 Luma,不是同一种"Y"
对线性 sRGB,XYZ 的相对亮度分量可近似写为:
Y = 0.2126 R + 0.7152 G + 0.0722 B Y=0.2126R+0.7152G+0.0722B Y=0.2126R+0.7152G+0.0722B
系数经过四舍五入。若使用较高精度矩阵,会出现 0.7151522 0.7151522 0.7151522 等更精细的值。
而类似下面的公式经常用于非线性编码分量:
Y ′ = 0.299 R ′ + 0.587 G ′ + 0.114 B ′ Y'=0.299R'+0.587G'+0.114B' Y′=0.299R′+0.587G′+0.114B′
这是一种常见的 BT.601 类 luma 加权形式。 Y ′ Y' Y′ 是用于编码或处理的亮度样分量,不等于 CIE 的线性 Y Y Y。灰度转换函数返回的数组不能仅凭名称就解释为物理亮度。
还要再问一步:即使用正确公式计算了线性化照片的 Y Y Y,它通常也只是渲染后图像的相对亮度。没有辐射标定,不能宣称得到场景的 cd/m²。
6. 为什么还需要 Lab、HSV 和 YCbCr?
XYZ 便于定义和转换颜色,却不感知均匀。同样大小的 XYZ 数值差,在不同颜色区域可能产生不同强度的视觉差异。

图 2:讲义 PDF 第 101 页,以 MacAdam 椭圆说明颜色差异的感知尺度并不处处相同。
Lab 将颜色表示为明度 L ∗ L^* L∗ 和两条对立色轴 a ∗ , b ∗ a^*,b^* a∗,b∗,通常用于颜色差异分析。给定参考白 ( X n , Y n , Z n ) (X_n,Y_n,Z_n) (Xn,Yn,Zn),例如:
L ∗ = 116 f ( Y / Y n ) − 16 L^*=116f(Y/Y_n)-16 L∗=116f(Y/Yn)−16
其中 f f f 是低值区线性、高值区立方根的分段函数。 L ∗ L^* L∗ 是 lightness(明度),不是物理 luminance。
Lab 转换必须明确参考白。ICC 工作流常见 D50;部分图像库常用 D65;不能把任一实现的习惯写成 Lab 的普遍默认值。不同库还可能把 L ∗ L^* L∗ 映射到 0~255,并给 a ∗ , b ∗ a^*,b^* a∗,b∗ 添加偏移。
HSV 便于按色相、饱和度等直觉属性操作,但不是感知均匀空间。YCbCr 常用于图像与视频编码,以 luma 和色差信号组织信息;具体矩阵、范围与标准应明确。
CIEDE2000 是基于 Lab 的色差公式。它不是百分制分数,也没有普遍的 0~100 硬上界。"小于 2 就看不出差别"只能作为特定观察条件下的经验说法。
7. 写代码前的五项检查
- 输入是 RGB 还是 BGR,整数还是浮点,范围是什么?
- 数据属于 camera RGB、sRGB、P3 还是其他空间?
- 当前处于线性域还是编码域?
- 白点、观察者、ICC 配置是否一致?
- 目标是物理运算、显示呈现,还是感知距离?
参考与出处
- Michael S. Brown, PDF 第 85~105、165、190 页。
- sRGB 的标准依据为 IEC 61966-2-1;本文给出常用标准矩阵与分段传递函数,未将讲义中的近似式作为精确实现。
- 本文对 Rec.709、参考白、Lab 与 CIEDE2000 范围作了必要的限定和校正。
上一篇:光谱与 CIE 颜色科学
下一篇:RAW 与传感器前端处理