3. 光度学与色度学:从物理量到人眼感知

好,咱们进入第三章。前两章我们聊了光的波动性和几何路径,那些都是物理世界的客观规律。但从这一章开始,事情变得有点「主观」了——我们要开始考虑人眼怎么看光。

光度学和色度学,说白了就是给「人眼感知」建立一套数学描述。我当年刚接触这块时也觉得挺绕,明明有辐射度量了,为什么还要搞一套光度量?后来做显示项目才明白,你测出来的辐射功率再高,人眼不敏感也是白搭。

3.1 辐射度量与光度量:物理量与生理量的桥梁

先搞清楚一个核心概念:辐射度量是纯物理量,光度量是经过人眼视见函数加权后的生理量。

物理量 辐射度量(符号/单位) 光度量(符号/单位)
能量 辐射能 Qe (J) 光量 Qv (lm·s)
功率 辐射通量 Φe (W) 光通量 Φv (lm)
单位立体角功率 辐射强度 Ie (W/sr) 发光强度 Iv (cd)
单位面积功率 辐照度 Ee (W/m²) 照度 Ev (lx)
单位面积·立体角功率 辐射亮度 Le (W/(m²·sr)) 亮度 Lv (cd/m²)

转换的关键是 光谱光视效率函数 V(λ)。人眼对 555nm 的黄绿光最敏感,V(555)=1;对红光和蓝紫光就弱很多。

核心公式:

光通量 Φv = 683 × ∫ Φe(λ) × V(λ) dλ

其中 683 lm/W 是最大光谱光视效能,记住这个常数就行。

我在做 LED 照明设计时踩过一个坑:供应商给的辐射通量数据很漂亮,但算出来的光通量却不够。后来发现是光谱峰值偏到了 450nm,V(λ) 只有 0.1 左右。嗯,这就是典型的「物理量达标,生理量不及格」。

3.2 CIE 色度系统:给颜色上户口

颜色怎么定量描述?1931 年 CIE 搞了一套标准,一直用到现在。

3.2.1 CIE 1931 RGB 系统

最早的想法很简单:用红(R)、绿(G)、蓝(B)三种原色光去匹配任意颜色。但问题来了——有些颜色需要「负值」的 R 才能匹配,这在实际中没法操作。

3.2.2 CIE 1931 XYZ 系统

为了解决负值问题,CIE 做了数学变换,搞出了三个假想的基色 X、Y、Z。其中 Y 直接代表亮度,X 和 Z 只负责色度信息。

色度坐标 (x, y) 由下式得到:

x = X / (X + Y + Z)
y = Y / (X + Y + Z)
z = Z / (X + Y + Z)  // 注意 x + y + z = 1

所以一般只用 (x, y) 就能表示颜色,亮度由 Y 单独给出。

我的经验: 做显示屏校准时,x,y 坐标差 0.005 人眼就能看出偏色。我曾经因为光谱仪没预热,测出来的白点坐标偏了 0.01,结果整批屏幕都被客户退货。从那以后,我每次测量前都先跑 30 分钟预热流程。

3.2.3 CIE 色度图

把 (x, y) 画在二维平面上,就得到了著名的「马蹄形」色度图。边界是单色光谱轨迹,内部是混合色。

CIE 1931 色度图结构示意 紫 蓝 青 绿 黄 红 E (0.333, 0.333) sRGB 色域 马蹄形边界 = 单色光谱轨迹 | 内部 = 混合色 | 三角形 = 设备色域

3.3 颜色混合原理:加色法与减色法

颜色混合分两种,应用场景完全不同。

3.3.1 加色混合(Additive Mixing)

用于显示器、投影仪、舞台灯光。红+绿+蓝=白。你想想看,手机屏幕每个像素就是三个子像素发光混合。

数学上,加色混合就是色度坐标的加权平均:

X_mix = (X1·Y1 + X2·Y2) / (Y1 + Y2)
Y_mix = (Y1·Y1 + Y2·Y2) / (Y1 + Y2)  // 注意:这是简化示意
// 实际用色度坐标加权时,权重是各自的 Y 值

避坑指南: 加色混合时,混合色的色度坐标落在两原色连线上。但如果你用不同亮度的原色混合,混合点会偏向亮度高的那一端。我曾经在调投影仪白平衡时,以为只要 R:G:B 比例对就行,结果忽略了亮度差异,调出来的白色偏青——因为绿色 LED 亮度太高了。

3.3.2 减色混合(Subtractive Mixing)

用于印刷、颜料、滤光片。青(C)+品红(M)+黄(Y)=黑(理论上)。实际上因为颜料不纯,还得加个黑(K)做成 CMYK。

减色混合的本质是「吸收」:每种颜料吸收一部分光谱,剩下的反射光就是看到的颜色。混合越多,吸收越多,颜色越暗。

混合类型 典型应用 原色 混合结果
加色混合 显示器、投影仪 R, G, B 越混越亮,最终白色
减色混合 印刷、打印 C, M, Y, K 越混越暗,最终黑色

3.4 实战:用 Python 计算色度坐标

光说不练假把式。下面给一段我常用的代码,用来把光谱数据转成 CIE 1931 色度坐标。

import numpy as np

# CIE 1931 标准色匹配函数(380-780nm,5nm间隔,示例数据)
# 实际使用时请加载完整数据表
wavelengths = np.arange(380, 785, 5)
x_bar = np.loadtxt('cie_x_bar.txt')  # 实际项目中从标准文件读取
y_bar = np.loadtxt('cie_y_bar.txt')
z_bar = np.loadtxt('cie_z_bar.txt')

def spectrum_to_xyz(spectrum):
    """
    输入:光谱功率分布数组(与波长对应)
    输出:X, Y, Z 三刺激值
    """
    K = 683  # 最大光视效能
    X = K * np.trapz(spectrum * x_bar, wavelengths)
    Y = K * np.trapz(spectrum * y_bar, wavelengths)
    Z = K * np.trapz(spectrum * z_bar, wavelengths)
    return X, Y, Z

def xyz_to_xy(X, Y, Z):
    """三刺激值转色度坐标"""
    total = X + Y + Z
    if total == 0:
        return 0, 0
    x = X / total
    y = Y / total
    return x, y

# 示例:计算 555nm 单色光的色度坐标
spectrum_555 = np.zeros_like(wavelengths)
spectrum_555[35] = 1  # 555nm 对应索引 35
X, Y, Z = spectrum_to_xyz(spectrum_555)
x, y = xyz_to_xy(X, Y, Z)
print(f"555nm 色度坐标: ({x:.4f}, {y:.4f})")
# 输出应为 (0.3333, 0.3333) 附近

个人习惯: 我一般把色匹配函数和光谱数据都存成 .npy 格式,加载快。另外注意积分用 np.trapz 而不是简单求和,波长间隔不均匀时尤其重要。

3.5 色温与显色性

最后提两个工程中绕不开的概念。

  • 相关色温 (CCT):光源颜色与黑体辐射在某个温度下的颜色最接近,这个温度就是 CCT。单位 K。比如正午阳光约 5500K,白炽灯约 2800K。
  • 显色指数 (CRI, Ra):光源对物体颜色的还原能力。满分 100,太阳光就是 100。一般室内照明要求 Ra > 80,博物馆、手术室要求 Ra > 95。

我记得有一次做博物馆照明项目,甲方指定用 3000K 的射灯。我测了 CRI 只有 72,结果把一幅油画照得颜色发灰。后来换了 Ra > 95 的灯,虽然色温一样,但画作的色彩层次一下就出来了。所以色温不是唯一指标,显色性同样关键。

本章要点速记:

  • 辐射度量 × V(λ) = 光度量,683 是桥梁常数
  • CIE XYZ 系统用 (x, y) + Y 描述颜色
  • 加色混合(RGB)越混越亮,减色混合(CMYK)越混越暗
  • 色温描述光源颜色,显色指数描述还原能力