RGB 颜色空间

RGB 颜色空间几乎是最常见的颜色空间,它是一个加色系统,通过三个基色的混合来描述其中的颜色。最常见的 RGB 颜色空间是 sRGB(standard Red Green Blue),由惠普和微软在 1996 年共同推出,并逐渐获得各行业和软件的广泛支持。

以 sRGB 为代表的 RGB 颜色空间主要包含三部分定义:色度学上的 RGB 及白点定义、非线性传递函数和观看环境。例如,sRGB 定义红、绿、蓝三基色的 xy 色度坐标为(0.64, 0.33)、(0.30, 0.60)、(0.15, 0.06),白点坐标为(0.3127, 0.3290);采用一个近似 Gamma 2.2 的传递函数;以及观看环境的照度、环境反射率甚至炫光。

具体请看 sRGB 的定义

按照定义的前两部分:三基色和白点,传递函数,可以得出转换的顺序是 RGB -> 线性 RGB -> XYZ。其中,线性 RGB 到 XYZ 的转换通过一个矩阵实现,这个矩阵实际上描绘了三基色在 CIEXYZ 中的位置以及他们以何种比例混合出白点。

构建转换矩阵

关键:确定三基色的“比例”

定义中只给出了三基色的“色度坐标”,色坐标是没有亮度信息的,假设我们现在有 RGB 三色的 LED 光源,调节他们的亮度,能混合出不同的颜色,现在,我们希望找到一个比例,使其混合后产生定义中的白点色坐标。

一种简便的方法如下(由 Claude 3.7 Sonnet 提供):

  1. 首先将三原色及白点的亮度预设为 1 (Y=1),并计算出它们对应的 CIEXYZ 值(从 xyY 计算 XYZ)。

  2. 由于 CIEXYZ 是线性的,求解此时亮度为 1 的三基色按何种比例能加出白点,得到三基色的实际 XYZ。

  3. 按照矩阵乘法的定义,将三基色的线性组合用矩阵描述,构建出转换矩阵。

色度坐标的混合

以下是我思考这个问题时候写的繁琐过程,上面那个方法足够简洁和直观,您可以直接跳过下面的内容,继续阅读传递函数部分,放在这里纯粹是因为我不舍得删。

如何确定三个色度坐标混合后的新色度坐标?

首先,将 xy 色坐标变换到 CIEXYZ 三刺激值,Z 在后续的混合中不重要,此处省略其变换,而 Y 就是我们要找的比例。

$$ \begin{align*} &X+Y+Z=\frac{Y}{y} \\ &X=x(X+Y+Z)=\frac{xY}{y} \\ \end{align*} $$

给三个分量添加下标来表示,无下标的则表示混合后的颜色,CIEXYZ 是线性的,可以加和,此处计算了混合后颜色的 X,Y 和 XYZ 之和(用于后续计算色坐标)。

$$ \begin{align*} & Y=Y_{1}+Y_{2}+Y_{3} \\ & X=\frac{x_{1}Y_{1}}{y_{1}}+\frac{x_{2}Y_{2}}{y_{2}}+\frac{x_{3}Y_{3}}{y_{3}} \\ & X+Y+Z=\frac{Y_{1}}{y_{1}}+\frac{Y_{2}}{y_{2}}+\frac{Y_{3}}{y_{3}} \\ \end{align*} $$

令 $X+Y+Z=S$,三刺激值求和后再回到 xyY 色坐标,混合后的色度坐标为:

$$ \begin{align*} \\ & y=\frac{Y}{S} \\ & x=\frac{X}{S} \end{align*} $$

即:

$$ \begin{align*} &S=\frac{Y}{y} \\ &X=xS=\frac{xY}{y} \end{align*} $$

当三基色的色度坐标和白点色度坐标已知时,可构建如下的方程组。

$$ \left\{ \begin{align*} & Y_{1}+Y_{2}+Y_{3}=Y \\ & \frac{1}{y_{1}}Y_{1}+\frac{1}{y_{2}}Y_{2}+\frac{1}{y_{3}}Y_{3}=\frac{Y}{y} \\ & \frac{x_{1}}{y_{1}}Y_{1}+\frac{x_{2}}{y_{2}}Y_{2}+\frac{x_{3}}{y_{3}}Y_{3}=\frac{xY}{y} \end{align*} \right. $$

用矩阵表示为:

$$ \begin{bmatrix} 1 & 1 & 1 \\ \frac{1}{y_1} & \frac{1}{y_2} & \frac{1}{y_3} \\ \frac{x_1}{y_1} & \frac{x_2}{y_2} & \frac{x_3}{y_3} \end{bmatrix} \begin{bmatrix} Y_1 \\ Y_2 \\ Y_3 \end{bmatrix} = \begin{bmatrix} Y \\ \frac{Y}{y} \\ \frac{xY}{y} \end{bmatrix} $$

通常将 Y 设为 1(即白点亮度归一化),可求解得到三原色按什么比例(每个基色的亮度 Y)混合能获得指定的白点。

转换矩阵

求得三个基色的 xyY 后,将其转换为 XYZ,不难注意到此时已经计算出 RGB 空间中基向量对应的 XYZ 三刺激值。

因此,RGB 到 XYZ 的转换矩阵如下,如需从 XYZ 到 RGB,只需求逆矩阵。

$$ \begin{bmatrix} X_{r} & X_{g} & X_{b} \\ Y_{r} & Y_{g} & Y_{b} \\ Z_{r} & Z_{g} & Z_{b} \\ \end{bmatrix} \begin{bmatrix} R \\ G \\ B \end{bmatrix} = \begin{bmatrix} X \\ Y \\ Z \end{bmatrix} $$

非线性传递函数

为了更好的适应人眼的感知特性,RGB 空间中的值通常会经过非线性传递函数,将线性 RGB 转换为非线性 RGB。例如,sRGB 选用了一个接近伽马 2.2 的非线性传递函数,并修正了逆变换时在零附近可能存在的问题。

从线性 sRGB 到非线性 sRGB 的转换函数为,虽然指数上是 2.4,但实际上它更接近于 $x^{2.2}$。

$$ \begin{align*} & R' = \begin{cases} 12.92R & R \leq 0.0031308 \\ 1.055R^{1/2.4}-0.055 & R > 0.0031308 \end{cases} \end{align*} $$

思考

如何判断一个给定的三刺激值是否在某个 RGB 空间内?

How to determine if given XYZ tristimulus values fall within a color gamut.
The gamut is defined by four CIE xy coordinates representing red, green, blue, and white (assume white point luminance has been normalized to 1.0).
Please provide the Python code.

第一个能不联网正确回答这个提问的是 Deepseek R1(此处指的是老 R1),大概要输出一万 tokens,多数平台部署的 R1 没有这么长的输出。

之后能做对的模型:Gemini 2.5 Pro,OpenAI o3 Pro,Deepseek V3-0324,Claude 3.7 Sonnet,Grok 4,Kimi K2。

做不对的:GPT 4.1,Qwen,Claude Sonnet 4(3.7 思考能做对,4 思考也做不对)。

关键在于是否考差了亮度。正确的做法是构建转换矩阵,将 XYZ 转到 RGB,然后观察是否有小于 0 或大于 1 的数。典型错误是,将 XYZ 转到 xy 色坐标,观察是否落在三基色围成的三角形里。