如何绘制光谱图底色

134 阅读5分钟

光场是指在特定空间和时间条件下光的强度和相位分布。当我们关注光场在不同波长下的强度分布,即光谱功率分布(SPD)。

光谱图在光学处理中是常见的,可以直观显示光在不同波长下的强度分布。横轴表示波长,纵轴表示相对强度或功率。

对光谱功率进行测量之后,绘图阶段需要呈现光谱图。例如,CIE 1931 标准光谱刺激值 曲线图如图所示。

CIE 1931 光谱三刺激值曲线图

CIE 1931 光谱三刺激值曲线图

在这里,不同的RGB基色使用了不同的曲线颜色进行区分。

一种更炫酷的光谱图是添加底色,以某百科中图为例:

1723172854110.png

炫酷底色图的例子

绘制如上所示的光谱底色能让图片变得更加直观,即直观获知某波长的光强。

这也是论文绘图常复用的一个小模块。本文将实现并详细呈现这类底色光谱图的绘制过程。主要以LED RGB三基色典型光谱作为例子,实现这个纯functional小模块。

所用数据

因为只是小demo,直接使用开放获取的数据。

德国电影器材商 (ARRI) 图像科学团队的负责人 Harald Brendel 在其主页上开放了 29 种市 售发光二极管 (LED) 的归一化测量光谱功率分布 (Spectral power distribution, SPD) 数据。 数据 由 350 至 700 nm 波长段的 176 个样本给出。

导入数据、绘制曲线是简单的。

色谱图

光谱图底色填色可以使用MATLAB 二维绘图填充函数area实现。为使波长段填充相应可见光的颜色,我们需要将波长段映射到相应的绘图颜色 编码。

最严谨的方式,应该是参考CIE系列标准给出的比色观察标准。例如CIE 1964 Supplementary Standard Colorimetric Observer,即可查找到对应波段的CIE匹配颜色函数。

image.png

因此,还需要进行颜色匹配函数的计算、确定颜色空间坐标。下面简单演示颜色刺激值->色品坐标的计算过程。

人类视觉系统不是一个完美的传感器,不同的颜色匹配功能试图模拟人类颜色感知的不均匀性和变化。颜色匹配函数即试图建立物理刺激与人类颜色感知之间的数学方程。

当原色以不同的组合混合在一起时,会产生一个色域。色域是一组给定的基色和所使用的颜色匹配函数可以产生的颜色范围。不同的光源技术具有不同的色域,例如高端 LED 显示器往往具有比标准 LCD 显示器更宽的色域;同时,不同的颜色匹配功能也可以产生不同的色域,因为它们可能具有不同的光谱灵敏度,因此对于相同的物理刺激产生不同的感知颜色。

要计算某颜色的色品坐标,需要先求得颜色的三刺激值,公式为:

X=kλφ(λ)xˉ(λ)dλY=kλφ(λ)yˉ(λ)dλZ=kλφ(λ)zˉ(λ)dλ\begin{gathered} \text{X} =k\int_{\lambda}\varphi(\lambda)\bar{x}(\lambda)\mathrm{d}\lambda \\ \text{Y} =k\int_{\lambda}\varphi(\lambda)\bar{y}(\lambda)\mathrm{d}\lambda \\ Z=k\int_{\lambda}\varphi(\lambda)\bar{z}(\lambda)\mathrm{d}\lambda \end{gathered}

对于LED GRB,式中积分范围可取 380~780 nm.

在数值计算中,可应用求和以近似积分的处理方法,有

X=kφ(λ)xˉ(λ)ΔλY=kφ(λ)yˉ(λ)ΔλZ=kφ(λ)zˉ(λ)Δλ\begin{gathered} X=k\sum\varphi(\lambda)\bar{x}(\lambda)\Delta\lambda \\ Y=k\sum\varphi(\lambda)\bar{y}(\lambda)\Delta\lambda \\ Z=k\sum\varphi(\lambda)\bar{z}(\lambda)\Delta\lambda \end{gathered}

式中 φ(λ) 称为颜色刺激函数,x\overline{x}y\overline{y}z\overline{z} 为标准色度观察的三刺激值。

对于自发光体 LED 器件, 有 φ(λ) 为 OLED 器件是自发光体辐射的相对光谱功能分布:

φ(λ)=S(λ)\varphi(\lambda)=S(\lambda)

进一步,可计算出色品坐标值:

x=XX+Y+Zy=YX+Y+Zz=ZX+Y+Z\begin{gathered} x=\frac{X}{X+Y+Z} \\ y=\frac{Y}{X+Y+Z} \\ z=\frac{Z}{X+Y+Z} \end{gathered}

即对于一个单位颜色 C 的色品,只决定于三原色的刺激值在 R + G + B 总量中的相对比例。由 于 x + y + z = 1,故而一个三维色坐标可以仅使用NTSC色域平面上的投影表示。

当然,从三刺激值开始演算,这着实有点复杂和哈人了。这里我们采用一个更简单的方式:在国外大学课程资源网站上,给出了可见波长对应的近似 RGB 值的 FORTRAN 程序实现,这将作为我们撰写 MATLAB 程序的很好的参考。

这实际上即一个频谱colormap(即波长与近似 RGB 值的映射函数),对应的MATLAB程序如下:

function color_code = spectrum_colormap(lambda)
% Input: wavelength (nm)
% Output: color code
% conversion from wavelength to visible light
color_code = [0, 0, 0];

if lambda<380 
   color_code = [1,0,1]; end
if (lambda>=380)&&(lambda<440)
   color_code = [(440-lambda)/(440-380),0,1]; end
if (lambda>=440)&&(lambda<490)
   color_code = [0,(lambda-440)/(490-440),1]; end
if (lambda>=490)&&(lambda<510)
   color_code = [0,1,(510-lambda)/(510-490)]; end
if (lambda>=510)&&(lambda<580)
   color_code = [(lambda-510)/(580-510),1,0]; end
if (lambda>=580)&&(lambda<645)
   color_code = [1,(645-lambda)/(645-580),0]; end
if (lambda>=645)
   color_code = [1,0,0]; end
% intensities fall off near limits of vision
if lambda>700
   color_code = color_code * (0.3 + 0.7*(780-lambda)/(780-700)); end
if lambda<420
   color_code = color_code * (0.3 + 0.7*(lambda-380)/(420-380)); end
end

调用数据读取与绘图函数plot_led_data.m,

绘图例

给出调用数据读取与绘图函数plot_led_data.m如下所示。

function [wavelength, Intensity] = plot_led_data(filename, fig)
% Inuput: Data file, drawing handle.
% Output: Interpolated wavelength and normalized SPD.

fid = fopen(filename, 'r');
data = textscan(fid,'%f %f','HeaderLines', 0);
fclose(fid);
% read data
wavelength0 = data{1,1}; Intensity0 = data{1,2};
wavelength = linspace(wavelength0(1),wavelength0(end),length(wavelength0)*2);
% interpolation
Intensity = interp1(wavelength0, Intensity0, wavelength, 'PCHIP');
plot(wavelength, Intensity);

% draw spectrum
h_area = area(wavelength,Intensity);
set(h_area,'FaceColor',[0 0 0]); set(h_area,'EdgeColor','none');
hold on;
for index = 1 : length(wavelength)-1
    if (wavelength(index) > 380) && (wavelength(index) < 780)
    % Limit the visible light range
        color_code = spectrum_colormap(wavelength(index));
        h_area = area(wavelength(index:index + 1), Intensity(index:index + 1));
        set(h_area,'FaceColor',color_code);
        set(h_area,'EdgeColor',color_code);
    end
end
end

导入数据进行绘图,即可实现光谱底色图。使用Harald Brendel提供的LED数据,有绘图效果例图。

1723172956087.png

闹着玩的,可以依据个人需求微调细节。