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) 画在二维平面上,就得到了著名的「马蹄形」色度图。边界是单色光谱轨迹,内部是混合色。
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)越混越暗
- 色温描述光源颜色,显色指数描述还原能力