颜色

颜色:物理还是心理?

牛顿用三棱镜把白光分解成彩虹的瞬间,他相信自己揭开了颜色的本质——颜色是光的属性,存在于物体之中。一个世纪后,歌德在《论颜色》中针锋相对:颜色不在物体里,也不在光里,它产生于眼睛。

谁对?

两人都没错,但都不完整。现代视觉科学给出的答案是:颜色是光与视觉系统交互的产物。同样一束光,对不同物种、甚至同一个人在不同环境下,可以唤起完全不同的颜色感知。颜色不是世界的属性,而是大脑给世界涂上的解释。

这个看似抽象的问题决定了本讲所有内容的结构。我们将沿着光—眼—脑的路径,回答四个层层递进的问题:

  1. 颜色物理学:光的物理本质是什么?为什么不同光源、不同表面会让我们看到不同颜色?
  2. 人类颜色感知:连续的光谱是怎么被压缩成大脑能处理的几个数值的?为什么我们总是用「三种」颜色来描述世界?
  3. 颜色表示:如何用一套坐标系定量描述每一种可感知的颜色?为什么 RGB 不够用,还需要 XYZ、HSV、L*a*b* 这么多种「颜色空间」?
  4. 颜色感知特性:为什么同一张白纸在烛光下、在 LED 灯下、在阳光下看起来都「白」?为什么照片中根本没有红色像素,我们却看到了「红色」的草莓?

读完这一讲,你会理解一个看似简单的事实背后藏着多少奇妙的设计:从光子击中视网膜到大脑产生「这是红色」的判断,整个过程既是物理学,又是生物学,更是一场视觉系统与世界的复杂协商。

Color is the result of interaction between physical light in the environment and our visual system.

Color is a psychological property of our visual experiences when we look at objects and lights, not a physical property of those objects or lights.

—— S. Palmer, Vision Science: Photons to Phenomenology

颜色物理学

光是电磁波

颜色的故事从光开始。物理学告诉我们,光是一种电磁波(Electromagnetic Wave),同样的物理本质涵盖了从无线电波到伽马射线的整个电磁波谱(Electromagnetic Spectrum)。在这个跨越 17 个数量级的巨大频谱中,人眼能感知的可见光(Visible Light)只占极窄的一段——波长大约从 400 nm\pu{400nm}(紫端)到 700 nm\pu{700nm}(红端)。

为什么我们能看到的恰恰是这一小段?这绝非偶然。太阳辐射在地球大气中能通过的窗口正好集中在这一段,水(生命之源)也对这一段最透明。视觉系统在亿万年的进化中选择了与环境信号最匹配的频段——人类能看见的那点光,恰是地球生命可以利用的那一点光。

人眼对可见光段内的不同波长敏感度并不均匀。人类亮度灵敏度函数(Human Luminance Sensitivity Function)在约 555 nm\pu{555nm}(黄绿色)处达到峰值,向两端迅速下降。同等物理功率的绿光看起来比红光或蓝光更亮——这一性质后续会反复出现:从 Bayer 阵列绿色滤光片占一半,到 CIE XYZ 颜色空间的 Y 分量被特意设计为亮度,都源于此。

光谱与光谱功率分布

可见光极少是单一波长的纯色。日常的「白光」由各种波长混合而成;蜡烛偏红、白炽灯偏暖、荧光灯偏冷,不同光源的「颜色」差异,源于它们光谱组成的不同。

光谱功率分布

一束光在物理上可以完全地被它的光谱功率分布(Spectral Power Distribution, SPD)描述:单位时间内每个波长发射的能量。SPD 通常记为 E(λ)E(\lambda),是一个把波长映射到强度的函数。

E ⁣:[380,780]R0,λE(λ)E\colon [380, 780] \to \R_{\ge 0}, \quad \lambda \mapsto E(\lambda)

给定 SPD,光的所有物理性质(包括颜色)都可以推导出来。

不同光源具有截然不同的 SPD,从而产生不同的色调。

太阳光具有近乎连续的宽带光谱,覆盖整个可见光范围;白炽灯依靠灯丝热辐射,能量主要集中在长波端(红/黄),所以看起来偏暖;白光 LED 通常是「蓝光 LED + 黄色荧光粉」的组合,光谱在 450 nm\pu{450nm} 处有一个尖锐峰值(蓝光主峰),加上荧光粉激发出的宽带绿—黄—红覆盖;紧凑型荧光灯的光谱则是几条尖锐的线状峰,看起来不如自然光「连续」。

这些差异看似微妙,但足以让同一件衣服在不同光源下呈现完全不同的颜色——这也是后面要讨论的「白平衡」问题的根源。

表面反射光谱

光照到物体上,并非所有波长都被等量反射。不同材料对不同波长有不同的吸收和反射比例,这一波长依赖的反射特性记作反射光谱(Reflectance Spectrum)R(λ)[0,1]R(\lambda) \in [0, 1]

红色番茄之所以「红」,因为它的表面对长波(红光)反射率高,而对中短波(绿、蓝)大量吸收;蓝莓的反射光谱则相反——它将大部分长波吸收,仅反射短波。黄色香蕉的反射光谱在中长波段都较高(黄 = 红 + 绿),紫色物体则在两端都有反射峰(紫 = 红 + 蓝)。

每一种「颜色」其实对应一条特定形状的反射曲线,这是物体颜色的物理基础。

颜色信号

到达人眼的光既不是光源本身,也不是物体本身,而是两者交互的产物。设环境光的 SPDE(λ)E(\lambda),物体表面的反射光谱为 R(λ)R(\lambda),则反射后到达人眼的颜色信号(Color Signal)为:

C(λ)=E(λ)R(λ)C(\lambda) = E(\lambda) \cdot R(\lambda)

这是一个逐波长的乘积——每个波长上独立计算。直觉上:物体只能反射光源里有的波长。如果光源里某段波长根本没有能量,无论物体反射率多高,那段波段也贡献不了颜色。

单色光下的世界

艺术家 Olafur Eliasson 的著名装置《Room for one color》给整个房间打上单一波长的钠灯光。结果观众看到的世界褪成了各种深浅的橙黄色——所有颜色信息都消失了。这是因为光源 E(λ)E(\lambda) 只在一个窄波段非零,所以不论物体的 R(λ)R(\lambda) 多么丰富,乘积 ERE \cdot R 都只剩这一个波段。

这个例子直观地说明了:颜色不是物体的固有属性,而是光与表面共同作用的结果。换光源,颜色就变。

flowchart LR
    E["光源 SPD<br/>E(λ)"] -->|"逐波长相乘"| C["颜色信号<br/>C(λ) = E(λ)·R(λ)"]
    R["表面反射率<br/>R(λ)"] --> C
    C --> EYE["视觉系统<br/>三种视锥细胞"]
    EYE --> P["颜色感知"]

    classDef src fill:#fff3e0,stroke:#ef6c00,stroke-width:2px
    classDef sig fill:#e3f2fd,stroke:#1565c0,stroke-width:2px
    classDef ppl fill:#e8f5e8,stroke:#2e7d32,stroke-width:2px

    class E,R src
    class C sig
    class EYE,P ppl

物理过程到此结束——下一步,连续的光谱信号 C(λ)C(\lambda) 进入眼睛,碰到三种视锥细胞,开始一场剧烈的信息压缩。

人类颜色感知

从光子到知觉

光子击中视网膜并不立刻成为「我看到了」。视觉感知是一段高度结构化的神经处理流程,每一步都有独特的功能与时延:

时刻 阶段 功能
0 ms 光刺激进入眼睛 光子被视网膜接收
10 ms 视网膜光电转换 视杆/视锥将光子能量转化为神经电信号
35 ms 视网膜「模数」转换 神经节细胞对信号编码并发放神经脉冲
45 ms 外侧膝状体(LGN)中继 信号经丘脑转发到视觉皮层
55 ms 初级视觉皮层 V1 方向选择性神经元开始响应边缘、方向
160-220 ms 高级视觉区 物体识别、语义判断

整个链路在不到 1/4 秒内完成,但每一站都有信息的提取与压缩。本节聚焦最早的那一段——从光子到三种视锥细胞输出的三个数字。

人眼的光学结构

人眼是一个精巧的光学系统:

光线先穿过透明的角膜(Cornea,主要负责折射),通过可调大小的瞳孔(Pupil,直径在亮处约 1 mm\pu{1mm}、暗处可张大至 8 mm\pu{8mm}),再经过晶状体精细聚焦——总焦距约 1517 mm\pu{15-17mm}——最终在视网膜(Retina)上成像。视神经从视网膜后端将编码后的信号送往大脑。

视网膜中央有一个直径仅 1-2° 视角的小区域,称为中央凹(Fovea),那里视锥细胞密度极高,是视觉分辨率最高的部位。中央凹之外是密度逐渐升高的视杆细胞带。

视杆与视锥

视网膜上分布着两种感光细胞,分工迥异:

细胞 数量 功能 工作环境 颜色感知
视杆细胞(Rods) ≈ 1 亿 感知亮度 低光照(暗视觉,Scotopic Vision) 不感知颜色
视锥细胞(Cones) ≈ 500 万 感知颜色与细节 明亮环境(明视觉,Photopic Vision) 感知颜色

为什么视锥分三种?这就是色觉的物理基础。

三色视觉理论

三色视觉理论(Trichromacy Theory)由 Thomas Young(1773-1829)提出、Hermann von Helmholtz(1821-1894)完善:人眼之所以能感知颜色,是因为视网膜上有三种对不同波长敏感的视锥细胞,分别称为 S(短波)、M(中波)、L(长波)。

类型 峰值波长 俗称
S(Short) 420 nm\pu{420nm} 附近 蓝锥
M(Medium) 534 nm\pu{534nm} 附近 绿锥
L(Long) 564 nm\pu{564nm} 附近 红锥

现代分子遗传学已经从 DNA 层面证实了三种视锥色素的存在。

三种视锥的光谱敏感度曲线如下:

注意 M 和 L 的峰值离得很近(仅相差约 30 nm\pu{30nm}),覆盖范围大量重叠——这并不是巧合,下一节会看到这个重叠正是我们感知细微色差的关键。

视锥细胞在视网膜上的分布也很特别。在约 600 万个视锥中,L 锥(红)约占 65%、M 锥(绿)约占 33%、S 锥(蓝)仅占 2%。中央凹中几乎没有 S 锥——蓝色信号实际上来自中央凹外围。S 锥比例如此之少,但单个 S 锥对蓝光的响应很强,整体上人眼对蓝色仍有合理的辨识能力。

为什么暗处看不清颜色,也读不了书

视杆细胞遍布视网膜(除中央凹外),数量是视锥的 20 倍,对极弱光也能响应。但代价是:视杆只能感知亮度,无法分辨颜色;同时,视杆细胞集中在外围,中央凹(最清晰的部位)几乎没有视杆。

所以在暗处,颜色消失(视锥失能),同时你最清晰的「中心视野」也基本失明(中央凹只有视锥)——你必须斜眼用余光去看,才能利用周边的视杆细胞模糊地辨认轮廓。

视锥作为光谱滤波器

视锥细胞如何把入射光变成神经信号?数学上,每一种视锥都像是一个光谱滤波器:它对某波长的光有一个敏感度 Scone(λ)S_{\text{cone}}(\lambda),对入射光谱 C(λ)C(\lambda) 的响应是两者逐波长乘积的积分:

响应=λC(λ)Scone(λ) ⁣dλ\text{响应} = \int_\lambda C(\lambda) \cdot S_{\text{cone}}(\lambda) \d\lambda

每种视锥最终输出一个数字——三种视锥共输出三个数字。

这是视觉系统中第一次也是最重要的一次信息压缩:一个连续函数(理论上无穷维)被压缩成三个标量。视锥是「波长上的积分器」,它不知道入射光是单一波长还是多种波长混合的——它只关心积分结果。

同色异谱:信息压缩的代价

把无穷维信号压缩成三维必然丢失大量信息。直接的后果是:两条完全不同的光谱可能产生完全相同的 (S,M,L)(S, M, L) 响应——人眼会把它们感知为同一种颜色。这种现象称为同色异谱(Metamers,或称条件等色)。

同色异谱不是缺陷,而是显示器、印刷、摄影等所有彩色技术得以工作的根基:屏幕只能发出三种原色光(R、G、B),通过调节它们的亮度比例,制造出与真实物体反射光谱完全不同但视觉上无法区分的颜色信号。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 同色异谱的概念示意(不严格还原 CIE 标准)
import numpy as np

wl = np.linspace(380, 780, 401)

# 视锥敏感度(高斯近似)
def cone(peak, width):
    return np.exp(-((wl - peak) / width) ** 2)

S, M, L = cone(437, 35), cone(533, 50), cone(564, 55)

def response(spectrum):
    return np.array([np.sum(spectrum * c) for c in (S, M, L)])

# 两条非常不同的光谱
spec_A = 0.2 + 0.6 * np.exp(-((wl - 580) / 60) ** 2)
spec_B = 0.2 + 0.55 * np.exp(-((wl - 540) / 18) ** 2) \
              + 0.55 * np.exp(-((wl - 620) / 22) ** 2)

print(response(spec_A))  # → 三个数
print(response(spec_B))  # → 几乎相同的三个数
# 物理上完全不同,对人眼几乎不可分辨

人眼的「Bayer 阵列」

上一讲学过的 Bayer 滤色阵列与人眼的视锥分布在原理上惊人地相似——都是在每个空间位置只采样一个颜色通道,再通过后处理(数码相机的去马赛克、大脑的视觉填充)恢复整张彩色图像。两套系统都建立在同一假设之上:相邻区域的颜色变化是平滑的。

色觉的趣味事实

三色视觉看似是「人类」的标配,实则只是动物视觉的一种方案。

  • 色盲:编码 M 和 L 视锥色素的基因都位于 X 染色体上。男性只有一条 X 染色体,一旦其上的基因突变就无法补偿——因此约 8% 的男性有不同程度的色觉缺陷,远高于女性
  • 四色视觉:L 锥色素基因有较高的变异率,少数携带两条 X 染色体的女性可能同时表达两种不同的「红」锥,从而具有四种视锥——理论上能感知比常人更多的颜色,但这种「四色视觉」(Tetrachromacy)的实际表现因人而异
  • 动物的色觉:夜行动物常只有 1 种视锥(无色觉,仅亮度),狗有 2 种视锥(部分色盲),鱼和鸟普遍有 4 种视锥(能看到紫外线),鸽子有 5 种,螳螂虾(Mantis Shrimp)拥有多达 12 种视锥——但讽刺的是,研究表明它们辨别颜色的能力反而不如人类,因为大脑没有充分整合所有通道的信息

色觉不是越多越好——三色刚好是「信息压缩成本」与「色彩区分能力」的一个性价比平衡点。

颜色表示

我们已经知道:人眼把连续的光谱压缩成三个数字。那么自然的下一步是——能否用三维坐标来描述世界上所有的颜色?这就引出了颜色空间(Color Space)这一核心概念。

颜色匹配实验

颜色是主观体验,不能用普通的物理仪器测量。要量化「人眼如何看颜色」,唯一的办法是请人来匹配。

20 世纪 20 年代,伦敦帝国理工学院的 W. David Wright 与英国国家物理实验室的 John Guild 各自独立开展了一系列颜色匹配实验(Color Matching Experiments),后来被国际照明委员会(CIE)综合为标准数据,成为现代色度学的奠基。

实验装置是一个二分屏:左半边由待测的单色光照亮,右半边由三种可调亮度的原色光(如红、绿、蓝单色光)混合照亮。观察者调节三种原色的亮度,直到两半看起来颜色完全一致。此时三种原色的亮度值 (w1,w2,w3)(w_1, w_2, w_3) 就是该单色光在这组原色下的「坐标」。

把整个可见光范围内每一个波长都做一次这样的实验,就能得到三条匹配函数(Color Matching Functions)c1(λ),c2(λ),c3(λ)c_1(\lambda), c_2(\lambda), c_3(\lambda)——它们记录了匹配波长 λ\lambda 处单位强度的单色光所需的三种原色量。

负值与「负光」

实验中很快就出现了一个棘手的问题:对于某些单色光(特别是青绿色波段),无论怎么调节三种原色的亮度,混合出的颜色总不够「纯」、不够饱和。这种情况下,三种正的原色根本无法匹配某些可感知的颜色

实验者的解决方案非常巧妙:允许观察者把某种基本光添加到待测光那一侧——形式上等价于在右侧「减去」该原色,记为负值。

虽然「负光」在物理上不存在,但这一约定让所有可见颜色都可以用三个实数(含负值)描述,于是数学结构得以保持简洁。这同时也揭示了一个深刻的事实:用三种真实单色光作原色的颜色空间不可能用全正坐标覆盖所有可见颜色——这个观察直接催生了后面要讲的 CIE XYZ 颜色空间。

Grassmann 定律

Hermann Grassmann 在 19 世纪从颜色匹配实验中归纳出了一组经验定律,奠定了线性颜色匹配的基础。

Grassmann 定律

固定三种原色 P1,P2,P3P_1, P_2, P_3。设光 AA 可被匹配为 A=(a1,a2,a3)A = (a_1, a_2, a_3),光 BB 可被匹配为 B=(b1,b2,b3)B = (b_1, b_2, b_3),则:

  1. 匹配相等性:若 AABB 用相同的三元组匹配,则在人眼看来 AABB 颜色完全相同
  2. 混合可加性:将 AABB 物理混合后的光,其匹配三元组为对应分量之和:

    A+B(a1+b1, a2+b2, a3+b3)A + B \sim (a_1 + b_1,\ a_2 + b_2,\ a_3 + b_3)

  3. 强度齐次性:将 AA 整体放大 ww 倍,其匹配三元组也按比例缩放:

    wA(wa1, wa2, wa3)w \cdot A \sim (w a_1,\ w a_2,\ w a_3)

这两条性质(可加性 + 齐次性)合起来恰好定义了线性变换——颜色匹配在物理混合下是一个线性运算。这个看似平淡的结论意味着:颜色可以用线性代数的语言来研究,所有混合、缩放、坐标变换都可以表示为矩阵乘法。

直觉上 Grassmann 定律为什么成立?因为视锥细胞的响应是积分(线性运算),所以光谱的线性组合在视锥响应层面也是线性组合——人眼无法区分两条光谱,等价于它们的视锥响应相同,等价于它们的匹配权重相同。

线性颜色空间

固定三种原色 P1,P2,P3P_1, P_2, P_3 后,根据 Grassmann 定律,任何颜色都可以用一个三元组 (w1,w2,w3)(w_1, w_2, w_3) 表示。这就构成了一个线性颜色空间

那么,对于任意光谱 t(λ)t(\lambda),如何找到它在这个空间中的坐标?答案藏在匹配函数里。

由于光谱可以看作不同波长单色光的「线性组合」,根据 Grassmann 定律的可加性与齐次性,匹配 t(λ)t(\lambda) 所需的原色权重就是各波长所需权重按强度加权之和:

wi=λt(λ)ci(λ) ⁣dλ,i=1,2,3w_i = \int_\lambda t(\lambda) \cdot c_i(\lambda) \d\lambda, \quad i = 1, 2, 3

这与视锥响应 t(λ)Scone(λ) ⁣dλ\int t(\lambda) S_{\text{cone}}(\lambda)\d\lambda 在结构上完全一致——匹配函数实质上扮演了与视锥敏感度曲线相同的角色,只不过用了不同的「原色基」。

RGB 颜色空间

在 1931 年的 CIE RGB 颜色空间中,三种原色取自三个特定波长的单色光:

  • P1=700.0 nmP_1 = \pu{700.0nm}(红 R)
  • P2=546.1 nmP_2 = \pu{546.1nm}(绿 G)
  • P3=435.8 nmP_3 = \pu{435.8nm}(蓝 B)

由于这是三种真实的单色光,按前面所说,它们的匹配函数 rˉ(λ),gˉ(λ),bˉ(λ)\bar{r}(\lambda), \bar{g}(\lambda), \bar{b}(\lambda) 在某些波长(特别是青绿色段 440540 nm\pu{440-540nm})会出现负值

负的匹配函数意味着:RGB 颜色空间无法用全正坐标覆盖所有可见颜色,部分颜色的 R 分量必须为负。这在工程实践中非常不便——显示器、打印机都无法发出「负光」。

CIE XYZ 颜色空间

为了根除负坐标问题,1931 年 CIE 在 RGB 实验数据的基础上做了一次线性变换,定义了 CIE XYZ 颜色空间。它选择了三种虚构的原色(不对应任何真实光源),使得新的匹配函数 xˉ(λ),yˉ(λ),zˉ(λ)\bar{x}(\lambda), \bar{y}(\lambda), \bar{z}(\lambda) 在所有可见波长上都非负。

CIE XYZ 几个关键性质:

  • 匹配函数处处非负:所有可见颜色的 (X,Y,Z)(X, Y, Z) 都是正值
  • 原色虚构但合法X,Y,ZX, Y, Z 三种「原色」物理上不存在(它们位于色度图的可见颜色范围之外),但由于数学结构是线性的,这并不妨碍计算
  • YY 等于亮度CIE 特意把 YY 的匹配函数 yˉ(λ)\bar{y}(\lambda) 设计为人眼亮度灵敏度函数,因此 YY 值直接对应感知到的亮度(Luminance)

XYZ 与 RGB 之间是一个固定的线性变换:

[XYZ]=10.17697[0.490000.310000.200000.176970.812400.010630.000000.010000.99000][RGB]\begin{bmatrix} X \\ Y \\ Z \end{bmatrix} = \frac{1}{0.17697}\begin{bmatrix} 0.49000 & 0.31000 & 0.20000 \\ 0.17697 & 0.81240 & 0.01063 \\ 0.00000 & 0.01000 & 0.99000 \end{bmatrix}\begin{bmatrix} R \\ G \\ B \end{bmatrix}

任意光谱 t(λ)t(\lambda)CIE XYZ 中的坐标为:

X=t(λ)xˉ(λ) ⁣dλ,Y=t(λ)yˉ(λ) ⁣dλ,Z=t(λ)zˉ(λ) ⁣dλX = \int t(\lambda) \bar{x}(\lambda)\d\lambda, \quad Y = \int t(\lambda) \bar{y}(\lambda)\d\lambda, \quad Z = \int t(\lambda) \bar{z}(\lambda)\d\lambda

色度图

三维 XYZ 不便于可视化。一个常用的降维方式是把三维坐标投影到二维平面上:

x=XX+Y+Z,y=YX+Y+Zx = \frac{X}{X + Y + Z}, \quad y = \frac{Y}{X + Y + Z}

这两个量称为色度坐标(Chromaticity Coordinates),舍弃了亮度信息只保留「颜色种类」(即色相和饱和度)。所有可感知颜色在 xyxy 平面上构成一个马蹄形区域,称为色度图(Chromaticity Diagram)。

这张图蕴含的信息密度极高,值得仔细解读:

  • 马蹄形外缘光谱轨迹(Spectral Locus),对应纯单色光(最饱和),曲线上标记着对应波长
  • 底边的直线称为品红线(Line of Purples),它连接红端(约 700 nm\pu{700nm})与紫端(约 380 nm\pu{380nm}),但不存在任何单色光对应这条线——品红、洋红只能由长波 + 短波混合而成
  • 马蹄内部是各种饱和度递减的颜色,越靠近中心越接近白色
  • 白点(如 D65,对应 6500 K\pu{6500K} 的标准日光)位于马蹄内部偏中央的位置
  • 任意两种颜色的混合位于它们连线上——这是 Grassmann 可加性的几何体现
  • 三种实际原色构成的颜色空间(如 sRGB)只能在色度图上覆盖一个三角形——三角形外的颜色无法用这三种原色重现

色度图最有价值的一点:它直观地说明了为什么 RGB 不能覆盖所有可见颜色——因为单色光在马蹄外缘是凸的,而任何用三种实际单色光构成的色域都是三角形(凸包),三角形必有一部分在马蹄外。

不同颜色空间的演进

回到现实需求:光在物理上是连续光谱,但在工程中我们必须选一组原色与坐标。不同应用对「好坐标」的要求不同:

  • 科学计算 → 数学性质好(线性、非负) → CIE XYZ
  • 屏幕显示 → 与硬件原色匹配 → RGB(sRGB、Adobe RGB)
  • 印刷 → 颜料是减色而非加色 → CMY/CMYK
  • 视频压缩 → 利用人眼对亮度更敏感而对色度不敏感 → YUV、YIQ
  • 用户交互 → 直观、可调 → HSV、HSI、HSL
  • 感知差异计算 → 颜色距离 ≈ 主观差异 → CIE L*a*b*

每一种颜色空间都解决前一种的某种缺陷,同时引入新的折中。

CMY 与 CMYK:减色法的原色

显示器是加色(Additive)系统——黑色背景上叠加 R、G、B 三色光,光越多越亮,三色全开得到白色。

但纸张呢?纸张本身是白色(反射所有波长),印上的颜料吸收部分波长、反射其余——颜料越多越「黑」,这是减色(Subtractive)系统。减色法的原色不是 RGB,而是它们的「补色」:

减色原色 含义 吸收 反射
C(Cyan,青) 绿 + 蓝 绿、蓝
M(Magenta,品红) 红 + 蓝 绿 红、蓝
Y(Yellow,黄) 红 + 绿 红、绿

这三种颜料的颜色刚好等于光的三补色,颜料的三补色刚好等于光的三基色。CMY 与 RGB 之间有简单的转换关系:

C=1R,M=1G,Y=1BC = 1 - R, \quad M = 1 - G, \quad Y = 1 - B

理论上等比例混合 C、M、Y 应得到黑色(吸收所有波长),但实际颜料并不完美,三色混合往往产生一种偏褐的「脏黑」。印刷工业为此引入第四种墨水——纯黑色 K(Key),形成 CMYK 模型。专门用一种黑色油墨打印文字和阴影,既省墨又能得到真正的黑。

YUV 与 YIQ:电视的颜色编码

电视广播需要兼容黑白电视——黑白机只能解码亮度信号。如何在不破坏黑白兼容性的前提下传输颜色?解决方案是把颜色信息分成「亮度 + 色度」两路。

  • YUV 模型(PAL 制电视、视频压缩):
    • YY = 亮度(Luminance),等同于 CIE 亮度
    • U,VU, V = 两个色差信号(Chrominance),UBYU \propto B - YVRYV \propto R - Y
    • 黑白电视只取 YY,彩色电视额外解码 U,VU, V
  • YIQ 模型(NTSC 制电视):与 YUV 类似,色差通道做了进一步旋转,使 II 通道对应人眼最敏感的橙—青轴

这个设计还带来一个意外收益:人眼对亮度变化远敏感于色度变化,所以视频压缩可以降采样色度通道(如 YUV420 让色度采样率是亮度的 1/4)而保持感觉上的清晰——这是几乎所有视频编码(JPEG、H.264、H.265)的基石。

HSI / HSV:感知导向的坐标

RGB 三个分量在工程上有用,但对人来说极不直观:给你 (0.8,0.2,0.3)(0.8, 0.2, 0.3),很难一眼说出这是什么颜色。如果想要「让某种红更红一点」,得同时调三个数。

HSI / HSV 模型采用三个更接近直觉的维度:

  • H(Hue,色相):颜色的「种类」——红、橙、黄、绿、蓝、紫,对应一个色环上的角度(红 0°,绿 120°120°,蓝 240°240°
  • S(Saturation,饱和度):颜色的「纯度」——从灰(S=0S=0)到完全饱和的纯色(S=1S=1
  • I(Intensity,密度)/ V(Value,明度):颜色的「明暗」

HSI 与 HSV 的差别主要在第三个维度的具体定义:HSI 中 I=(R+G+B)/3I = (R + G + B)/3,HSV 中 V=max(R,G,B)V = \max(R, G, B)。两者的几何形状也略有不同——HSI 通常画成双锥,HSV 是一个倒置的锥(或圆柱)。

几何上,HSV 可以理解为把 RGB 立方体「竖起来」(让黑—白对角线变成垂直轴),从顶面看下去就是一个色环。沿垂直方向走改变明度,沿径向走改变饱和度,沿角度走改变色相。

HSV 在图像处理中非常实用——例如要在图像中提取「所有绿色物体」,只需在 HSV 空间中筛选 H[80°,160°]H \in [80°, 160°] 的像素,无需在 RGB 立方体里画复杂的边界。

1
2
3
4
5
6
7
8
9
10
11
import numpy as np
import cv2

img_bgr = cv2.imread("scene.jpg")
img_hsv = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2HSV)

# OpenCV 中 H ∈ [0, 180],S, V ∈ [0, 255]
# 提取绿色:H ≈ 40~80
lo = np.array([40, 40, 40])
hi = np.array([80, 255, 255])
green_mask = cv2.inRange(img_hsv, lo, hi)

均匀颜色空间与 CIE L*a*b*

CIE XYZ 解决了负坐标问题,但带来了新的不便:在 xyxy 色度图上,等距的两对颜色不一定有相同的感知差异。绿色区域中相隔 0.05 的两个点几乎看不出区别,蓝色区域中相隔 0.05 已经是明显的色差。

McAdam 在 1942 年通过大规模心理物理实验测出了McAdam 椭圆:在色度图各个位置画一个椭圆,椭圆内的所有点对人眼来说与中心是「不可分辨」的。结果发现绿色区域的椭圆比蓝色区域的椭圆大约 20 倍——色度图上的距离根本不能等同于感知距离。

均匀颜色空间(Uniform Color Space)的目标是修正这一缺陷:让坐标距离尽量正比于人眼感知差异。

  • CIE u'v' 色度图:通过 xyxy 的射影变换得到,使 McAdam 椭圆变得更接近圆形
  • CIE L*a*b*(1976):三维空间,比 u'v' 更接近感知均匀
    • L*(Lightness,明度):从黑(0)到白(100)
    • a*:红—绿轴(正 = 红,负 = 绿)
    • b*:黄—蓝轴(正 = 黄,负 = 蓝)

L*a*b* 的设计基于对立色理论(Opponent Color Theory)——视觉系统在视锥之后的神经处理把 L、M、S 视锥信号重组为「亮—暗」「红—绿」「黄—蓝」三对互相对立的通道。这也解释了为什么我们从未见过「带红的绿色」或「带黄的蓝色」——它们在同一通道的两端,互相抑制。

L*a*b* 中两个颜色的欧氏距离

ΔEab=(ΔL)2+(Δa)2+(Δb)2\Delta E_{ab} = \sqrt{(\Delta L^*)^2 + (\Delta a^*)^2 + (\Delta b^*)^2}

可作为感知差异的近似度量,称为色差(Color Difference)。一般认为 ΔE<1\Delta E < 1 人眼无法分辨,ΔE>5\Delta E > 5 是明显的色差。

设备相关颜色

至此讨论的都是「绝对」颜色空间——它们由人眼的生理或心理特性定义,与硬件无关。但实际相机和显示器并不直接工作在 CIE XYZ 中,它们用的是各自的「设备相关」颜色。

数码相机的传感器前是彩色滤波阵列CFA,如 Bayer 阵列);每个滤光片有自己的光谱敏感度函数(Spectral Sensitivity Function, SSF)——但 SSF 通常不等同于 CIE 的颜色匹配函数 xˉ,yˉ,zˉ\bar{x}, \bar{y}, \bar{z},更不等同于人眼视锥的敏感度。不同厂商的 SSF 不同,结果是同一场景由不同相机拍出,得到不同的「device RGB」。

要把 device RGB 映射到一致的、设备无关的颜色,相机内部要走一条复杂的渲染流水线:

flowchart LR
    A["device RGB<br/>(传感器原始)"] --> B["XYZ<br/>(标准空间)"]
    B --> C["LMS<br/>(视锥空间)"]
    C --> D["白平衡<br/>(光照补偿)"]
    D --> E["XYZ"]
    E --> F["sRGB<br/>(标准显示空间)"]
    F --> G["色调映射<br/>(动态范围压缩)"]

    classDef raw fill:#fff3e0,stroke:#ef6c00,stroke-width:2px
    classDef std fill:#e3f2fd,stroke:#1565c0,stroke-width:2px
    classDef per fill:#f3e5f5,stroke:#4a148c,stroke-width:2px
    classDef out fill:#e8f5e8,stroke:#2e7d32,stroke-width:2px

    class A raw
    class B,E,F std
    class C,D per
    class G out

每一步的处理细节因厂商而异,这正是为什么同一个场景用 iPhone、Canon、Sony 拍出,颜色「风格」会有差异——硬件 SSF 不同 + 软件渲染策略不同。

颜色感知特性

到此为止我们建立了一个看似自洽的体系:光谱通过视锥被压缩成三个数字,再由颜色空间表示成一组坐标。但人对颜色的感知远不止「测量光谱再做积分」这么简单——视觉系统会主动「修正」、「补偿」、「推断」,以应对真实世界中千变万化的光照条件。

颜色恒常性

回想最早的等式 C(λ)=E(λ)R(λ)C(\lambda) = E(\lambda) \cdot R(\lambda):到达眼睛的光是光源 EE 与表面 RR 的乘积。换光源(EE 变),CC 也变——按理说我们看到的颜色应当跟着变。

但事实并非如此。一张白纸在午后阳光(5500 K\pu{5500K})、白炽灯(2700 K\pu{2700K})、阴天蓝调(10000 K\pu{10000K})下到达眼睛的光谱完全不同,但我们始终把它感知为「白色」。

颜色恒常性

颜色恒常性(Color Constancy)指人类视觉系统在光照条件发生变化时,仍然能感知到物体「本来」的反射特性的能力。

形式化地讲:视觉系统试图从 C=ERC = E \cdot R 中分离出 RR(表面固有的反射率),并以此作为感知的颜色——这是一种隐式的「光照估计 + 反推」过程。

颜色恒常性是人类视觉系统的一项关键能力——它让我们可以在不同光照下识别出同一个物体。如果失去这种能力,每次场景的光照变化都会让我们认为周围的物体「变了颜色」,世界将变得难以理解。

但这个机制并非完美:

  • 只对真实场景生效,对照片不生效——大脑通过整体场景上下文估计光源,而单独的一张照片缺乏可靠的上下文线索
  • 当场景中多种光源混合时(如室内灯光 + 窗外日光),大脑可能做出错误的光源估计,导致感知偏差
  • 不同人对同一场景的光源估计可能不同,导致感知到的颜色不同——这就是 2015 年风靡全网的「The Dress」现象的根源

色温

讨论光源时常用「色温」(Color Temperature)来描述其颜色倾向。色温的物理基础是黑体辐射(Blackbody Radiation)——一个理想黑体加热到温度 TT(以开尔文 K\pu{K} 为单位)时辐射的光谱由 Planck 公式确定。

不同温度的黑体辐射呈现不同的颜色:

色温 光源例子 视觉感受
1500 K\pu{1500K} 烛光 强烈橙红
2700 K\pu{2700K} 白炽灯、暖色 LED 暖黄
3200 K\pu{3200K} 卤素灯、影棚暖光 偏黄
4500 K\pu{4500K} 冷白荧光灯、晨/暮阳光 中性偏暖
5500 K\pu{5500K} 正午日光 中性
6500 K\pu{6500K} D65 标准白点 中性偏冷
8000 K\pu{8000K} 阴天 偏蓝
10000 K+\pu{10000K+} 蓝色天空 强烈蓝调

直觉上「冷光(蓝)」反而对应高色温,「暖光(红)」对应低色温——这是因为色温名词来自黑体物理,与日常感受相反。把铁块加热,先变暗红、再橙、再黄,最终白热再蓝白——温度越高,光中蓝色成分越多。

CIE 在色度图上为各种标准光源指定了位置:A 光(白炽灯)、B 光(午后日光)、C 光(一般日光)、D50/D55/D65(不同色温的自然光)、E 光(等能量恒定光源)、F 光(各种荧光灯)。

色度适应

人眼对光照的「自动校正」分为两个层次:

适应不同亮度水平:通过改变虹膜(瞳孔)的开口大小,调节进入眼睛的光通量。从阳光下走进室内,瞳孔会逐渐放大;从黑屋走到亮处,瞳孔会迅速收缩。这一机制约可应对约 2 个数量级的亮度变化。

适应不同色温——也称色度适应(Chromatic Adaptation):视网膜上的视锥细胞在长时间接受某种色光后会主动降低对该色光的敏感度。例如,在偏红的灯光下待上十几分钟,视网膜的 L 锥(红锥)敏感度会下调,使整个场景看起来不再那么红。

机理上具体如何实现的,目前生理学还没有完全研究清楚——一种主流模型是「von Kries 适应」:每种视锥的响应被独立按比例缩放,缩放因子由该视锥对当前场景的平均响应决定。这与现代相机白平衡的「灰世界假设」(Gray World Assumption)不谋而合。

色度适应的一个有趣副作用:在昏暗光线下,颜色恒常性变弱。烛光下的场景仍然偏黄——大脑能补偿一部分,但补偿不全,因为亮度太低导致视锥工作不充分。所以餐厅、酒吧偏爱昏暗暖光——它能营造特定氛围。

棋盘阴影错觉

颜色恒常性的强大常常超过我们的想象,以至于产生一些反直觉的视觉错觉。最经典的是 Edward Adelson 在 1995 年设计的棋盘阴影错觉(Checker Shadow Illusion)。

经典演示中,一个绿色圆柱在棋盘上投下阴影。阴影外的某个深色格子 A 与阴影内的某个浅色格子 B 在图像中的像素亮度完全相同——但 B 看上去明显比 A 亮得多。

为什么会这样?视觉系统检测到 B 处的暗淡是阴影造成的(光照边界),而 A 处的暗是材质本身的(反射率边界),于是「补偿」掉阴影:把 B 的实际亮度向上修正以反映其本来的反射率。结果是我们看到 B 比 A 亮——尽管像素值相同。

可能的解释包括:

  • 同时对比(Simultaneous Contrast):B 周围都是阴影中的格子,相对而言 B 显得突出
  • 反射率边界 vs 光照边界:视觉系统能区分这两类亮度变化,并在感知物体本色时忽略光照变化造成的部分

这一错觉揭示了一个根本事实:视觉系统从不报告原始像素值——它报告的是它对物理世界的解释。这也意味着,所谓「眼见为实」其实是经过视觉系统大量「加工」的结果。

没有红色像素的草莓

另一个匪夷所思的例子:日本视错觉研究者 Akiyoshi Kitaoka 制作了一张著名的「草莓蛋糕」图片,整张图经过青色滤镜处理后不包含任何红色像素(每个像素的 R 通道都不是该位置 RGB 三个分量的最大值)——但绝大多数观察者仍然把草莓看成「红色」。

原因在于视觉系统的全局色彩平衡:大脑察觉到整张图片偏青蓝色,于是「假设」这张图是在偏冷的光照下拍摄的,并自动把这一光源色调减去。在这个假想的「白光」下,草莓部分相对偏暖,于是被感知为红色——尽管实际像素中并没有任何红色。

这同时说明:颜色恒常性是一个全局机制,不只是基于局部对比。

白平衡

人眼能在不同光照下感知一致的颜色,但相机做不到——传感器记录的就是 device RGB,光源偏红就拍出红、偏蓝就拍出蓝。要让相机模拟人眼的颜色恒常性,需要白平衡(White Balance)。

白平衡

白平衡指对相机捕获的图像做色调校正,使得场景中本应是中性色(白或灰)的物体在最终图像中也显示为中性色。

数学上常用的方法是分别对 R、G、B 三个通道独立缩放:

[RGB]=[gR000gG000gB][RGB]\begin{bmatrix} R' \\ G' \\ B' \end{bmatrix} = \begin{bmatrix} g_R & 0 & 0 \\ 0 & g_G & 0 \\ 0 & 0 & g_B \end{bmatrix}\begin{bmatrix} R \\ G \\ B \end{bmatrix}

其中 gR,gG,gBg_R, g_G, g_B 由场景光源的色温估计而来——这本质上是 von Kries 模型在工程上的实现。

实现白平衡的几种方式:

  • 胶片相机:依靠不同类型的胶片(日光型、灯光型)和外置滤镜应对不同色温的光照
  • 数码相机的预设白平衡:用户从「日光、阴天、白炽灯、荧光灯、闪光灯」等预设中手动选择,相机使用对应的预设增益
  • 自动白平衡(Auto White Balance, AWB):相机基于「灰世界假设」(场景平均颜色为灰)或最亮像素假设(最亮处通常反射全部光源)自动估计光源色温
  • 自定义白平衡:让相机对一张白纸或灰卡拍照,相机以此为参考确定增益——最准确,常用于专业摄影

白平衡的局限

白平衡假设场景中只有单一光源。一旦场景中存在两种或更多色温不同的光源(如室内灯光 + 窗外日光,或舞台灯光多色),无论怎么校正都不可能让所有区域同时呈现「正确」的颜色——选了其中一种作参考,另一种就会偏色。这正是新闻摄影、室内摄影中常见的难题。

颜色作为视觉理解的线索

颜色不只是「好看」——它在多种视觉任务中都是有用的特征。

图像分割:把图像在 HSI/HSV 空间中按色相分类,可以快速分离出不同颜色的物体。例如把场景中的红色交通锥从背景中分离出来。

1
2
3
4
5
6
7
8
9
10
11
12
import cv2
import numpy as np

img = cv2.imread("scene.jpg")
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

# 红色在 HSV 中跨越 0° 边界,需要两段
mask1 = cv2.inRange(hsv, np.array([  0, 80, 80]), np.array([ 10, 255, 255]))
mask2 = cv2.inRange(hsv, np.array([170, 80, 80]), np.array([180, 255, 255]))
red_mask = mask1 | mask2

cv2.imshow("red regions", cv2.bitwise_and(img, img, mask=red_mask))

图像分类:颜色统计可以作为图像类别的强线索。秋天的景物以橙黄红为主,春天则以嫩绿为主——简单的颜色直方图就能给出有意义的分类信号,是深度学习时代之前的常用基线方法。

人脸检测:肤色在 YCbCr 等空间中聚集在一个相对集中的区域,早期的人脸检测器常用「肤色滤波 + 形状校验」的两步方案。

目标识别中的颜色不变性:但要注意,颜色容易受光照影响。在工业视觉、识别等任务中,常需要把图像转换到相对光照不变的颜色空间(如归一化 RGB、HSI 中只用 HS 通道)以提高鲁棒性。

小结

本讲沿着光—眼—脑的物理与生理路径,建立了完整的颜色理论框架:

flowchart TD
    A["颜色物理学<br/>SPD × 反射率 = 颜色信号"]
    B["人类颜色感知<br/>三种视锥 → 三个数字"]
    C["颜色表示<br/>线性/感知/均匀颜色空间"]
    D["颜色感知特性<br/>恒常性 + 适应 + 白平衡"]

    A --> B
    B --> C
    C --> D

    classDef phys fill:#fff3e0,stroke:#ef6c00,stroke-width:2px
    classDef phys2 fill:#e8f5e8,stroke:#2e7d32,stroke-width:2px
    classDef rep fill:#e3f2fd,stroke:#1565c0,stroke-width:2px
    classDef perc fill:#f3e5f5,stroke:#4a148c,stroke-width:2px

    class A phys
    class B phys2
    class C rep
    class D perc

四个核心结论值得记住:

  • 颜色不是物体的固有属性:它来自光、表面、人眼三者的交互。换光源、换观察者,颜色都会改变
  • 三色视觉是一种信息压缩:连续光谱被三种视锥压成三个数字,必然产生同色异谱——这既是限制,也是显示器、印刷、摄影得以工作的基础
  • 不同颜色空间各有其用CIE XYZ 用于精确描述,RGB / CMYK 用于设备,HSV 用于交互,L*a*b* 用于感知差异计算——没有「最好」的颜色空间,只有「最适合任务」的颜色空间
  • 视觉系统会主动「解释」颜色:颜色恒常性、色度适应、阴影补偿——这些机制让我们在变化的世界中保持稳定的物体识别,但也制造出大量奇妙的视觉错觉

下一讲将把视角从「图像怎么生成」转到「图像在空间中如何变换」——介绍计算机视觉中的几何变换、图像重采样与插值。