我正在尝试使用数学绘制 CIE 1931 色域。
我采用 xyY 颜色,Y 固定为 1.0,然后将 x 和 y 从 0.0 更改为 1.0。
如果我将生成的颜色绘制为图像(即 (x,y) 处的像素是我转换为 RGB 的 xyY 颜色),我会得到一张漂亮的图片,其中 CIE 1931 色域位于中间某处,如下所示:
xyY 从 0.0 到 1.0:

现在我想要经典的舌形图像,所以我的问题是:如何剔除 CIE 1931 色域范围之外的像素?
IE。如何判断我的 xyY 颜色是否在 CIE 1931 颜色范围内/外?
我在搜索一个稍微不同但相关的问题时偶然发现了这个问题,立即引起我注意的是顶部的渲染。它与我几个小时前制作的渲染相同,并试图找出为什么它没有意义,部分原因是将我带到这里。
对于读者:渲染是当您从 转换{x ? [0, 1], y ? [0, 1], Y = 1}为XYZ,将该颜色转换为sRGB,然后将各个组件钳制为 时的结果[0, 1]。
乍一看,它看起来不错。乍一看,它看起来有点……它似乎没有预期的饱和,并且在奇怪的角度有可见的过渡线。经过仔细检查,很明显初选并没有顺利地相互转换。多的范围内,例如,之间的红色和蓝色仅品红两者R和B是几乎在它们之间的整个距离100%。当您然后添加一个检查以跳过绘制任何具有超出范围组件的颜色时,而不是钳制,一切都消失了。这都是色域外的。发生什么了?
我想我已经了解了至少 80% 的比色法的这一小部分,所以我将其列出,大大简化了,以启发可能会发现它有趣或有用的任何其他人。我也试着回答这个问题。
(??我开始之前,一个重要的注意:在有效RGB显示颜色xyY空间可以是CIE 1931 2°标准观察者的边界之外,这是不适合的sRGB的情况下,但它是用于显示P3的情况下, Rec. 2020、CIE RGB 和其他宽色域。这是因为三个原色需要自己加起来形成白点,因此即使是单色原色,与同等光照下的相同波长相比,也必须令人难以置信地、不自然地发光。 )
xy 色度图不仅仅是xyY空间的一个切片。它本质上是二维的。在A点xy平面表示色度除了亮度,所以,有一个色彩那里它是代表尽可能最佳的程度仅色度,而不是任何特定的颜色。通常,颜色似乎是该色度的最亮、最饱和的颜色,或者是显示器颜色空间中最接近的颜色,但这是一个任意的设计决定。
也就是说:在一定程度上绘制了说明性的颜色,它们必然是虚构的,就像给选举地图着色纯粹是数据可视化的问题一样:便于理解。只是,在这种情况下,我们使用颜色来可视化色度的一个方面,因此很容易将这两件事混为一谈。
当我们考虑xyY空间中可见光谱的完整 3D 形状时,颜色的虚假性及其必要性变得显而易见。经典的光谱轨迹(“马蹄形”)可以很容易地被看作是准直布罗陀体积的底部,在光谱轨迹处最宽,在 处变窄到顶点(白点){Y = 1}。如果将其视为自上而下的投影,那么位于光谱轨迹上和附近的颜色将非常暗(尽管对于该色度仍然是最亮的颜色),并且会朝着中心逐渐变亮。如果被视为xyY体积的一部分,通过特定的值Y,颜色将同样明亮,但整体会变得更亮,边界的形状会再次不均匀地收缩,随着 的增加Y,直到完全消失。据我所知,这两种可能性都没有看到太多(如果有的话)实际用途,尽管它们可能很有趣。
相反,该图是由内而外着色的:绘制的色域以最大强度着色(每个原色最亮,然后是内部的线性混合),色域外颜色从内色域三角形投影到光谱轨迹。这很烦人,因为您不能简单地使用矩阵变换将xy平面上的一个点转换为合理的颜色,但不幸的是,就实际传达有用且有些准确的信息而言,这似乎是不可避免的。
(澄清一下:实际上可以将单个色度点移动到 sRGB 空间中,并用尽可能最亮的饱和 sRGB 颜色为色度图逐个像素着色——这只是比简单的矩阵变换更复杂。要做因此,首先将三坐标xyz色度移动到 sRGB。然后将任何负值钳位到0。最后,均匀缩放组件,使最大组件值为1。请注意,这可能比绘制白点和原色慢得多,然后在它们之间进行插值,具体取决于您的渲染方法以及数据表示及其操作的效率。)
获得特征马蹄形形状的最直接方法就是使用经验数据表。
(http://cvrl.ioo.ucl.ac.uk/index.htm,向下滚动以找到最接近为外行提供的其他来源的“历史”数据集。他们用于选择数据的过于聪明的图标方案是虚线图标表示以 5nm 采样的数据,实线图标表示以 1nm 采样的数据。)
以点为顶点构造一条路径(您可能想从顶部修剪一些,我将其剪回 700nm,即 CIERGB 红色原色),并将生成的形状用作遮罩。对于 1nm 样本,折线应该足够平滑以接近任何分辨率:无需拟合贝塞尔曲线或诸如此类的东西。
如果我们想要做的就是绘制标准马蹄由三角形界{x = 0, y = 0},{0, 1}和{1, 0}那么这应该足够了。请注意,我们可以通过跳过其中的任何坐标来节省渲染时间x + y >= 1。如果我们想做更复杂的事情,比如绘制不同Y值的变化边界,那么我们谈论的是定义空间的颜色匹配函数XYZ。
(图片来源:用户:Acdx - 自己的作品,CC BY-SA 4.0)
XYZ空间的基本事实是三个函数的形式,这些函数将光谱功率分布映射到{X, Y, Z} 三刺激值。大量数据和计算用于构建XYZ空间,但所有这些都融入了这三个函数,它们唯一地确定{X, Y, Z}了给定光谱的值。实际上,这些函数所做的是定义 3 种想象的原色,它们不能用任何实际光谱创建,但可以混合在一起以创建可感知的颜色。因为它们可以混合,所以XYZ空间中的每个非负点在数学上都是有意义的,但并不是每个点都对应一种真实的颜色。
函数本身实际上被定义为查找表,而不是可以精确计算的方程。孟塞尔色彩科学实验室 ( https://www.rit.edu/science/munsell-color-lab ) 提供 1nm 分辨率样品:向下滚动到“教育资源”下的“有用的色彩数据”。不幸的是,它是 Excel 格式。其他来源可能会提供 5nm 数据,任何比 1nm 更精确的数据都可能是现代重建,可能无法与 1931 年的空间相通。
(感兴趣的是:这篇论文——http://jcgt.org/published/0002/02/01/——提供了在原始人类受试者数据的可变性内具有误差的分析近似值,但它们主要用于特定用例。就我们的目的而言,最好且更简单的是坚持使用经验抽样数据。)
功能被称为x?,y?和z?(或x bar,y bar和z bar)。总之,他们被称为CIE 1931 2度标准观察。有一个单独的 1964 标准观察器,由更宽的 10 度视场构成,略有不同,可以用来代替 1931 标准观察器,但可以说它创造了不同的色彩空间。(不应将 1964 标准观察者与单独的 CIE 1964 色彩空间混淆。)
要计算三色值,请取 (1) 颜色光谱和 (2) 颜色匹配函数的内积。这只是意味着光谱中的每个点(或样本)都乘以颜色匹配函数中的相应点(或样本),用于重新加权数据。然后,您在整个可见光范围([360nm,830nm])上进行积分(或求和,更准确地说,因为我们正在处理离散样本)。这些函数被归一化,以便它们在曲线下具有相等的面积,所以相等的能谱(每个波长的采样值都相同)将具有{X = Y = Z}。(FWIW,Munsell Color Lab 数据已正确归一化,但由于某种原因,它们的总和为 106,并且会发生变化。)
再看一下xyY空间的3D 绘图,我们再次注意到熟悉的光谱轨迹形状似乎是 处的体积形状{Y = 0},即这些颜色实际上是黑色的。这现在有某种意义,因为它们是单色的,并且它们的光谱应该由一个点组成,因此当你对一个点进行积分时,你总是会得到 0。然而,这就提出了一个问题:它们如何具有色度,因为其他两个函数也应该是 0?
最简单的解释是,Y在形状的底部实际上总是略大于零。采样的使用意味着单色源的光谱不被视为瞬时值。相反,它们是波长附近的窄谱带。您可以任意接近瞬时,并且仍然期望在精度范围内有意义的色度,因此采样带宽变为 0 时的限制是理想的光谱轨迹,即使它恰好在 0 处消失。 然而,光谱轨迹实际上是导出只是从单样本值计算的x?,y?以及z?颜色匹配函数。
这意味着,你真的只需要一组数据的查找表x?,y?和z?。光谱轨迹可以从每个波长仅通过分割来计算x?(wl)和y?(wl)通过x?(wl) + y?(wl) + z?(wl)。
(图片来源:Apple,来自 ColorSync Utility 的屏幕截图)
有时您会看到这样的图,有一条戏剧性的弧形彩虹线在图周围俯冲而下,然后在光谱的远红色端回落到 0。这只是y?沿着谱轨迹绘制的函数,按比例缩放使得y? = Y。请注意,这不是可见色域的 3D 形状的轮廓。当在 2 维中绘制时,这样的轮廓将很好地位于蓝绿色范围内的光谱轨迹内。
最后一个问题变成:给定这三个颜色匹配函数,我们如何使用它们来确定给定{X, Y, Z}的颜色是否在人类颜色感知的范围内?
有用的事实:你不能单独拥有光度。对于其他函数中的一个或两个,任何真实颜色也将具有非零值。我们也知道Y根据定义有一个范围[0, 1],所以我们实际上只是在谈论确定{X, Z}对于给定的是否有效Y。
现在问题变成了:什么光谱(为了我们的目的而简化:波长 [360nm, 830nm]、带宽 1nm 的 471 个值的数组,0 或 1),当由 加权时y?,将总和为Y?
的XYZ空间是添加剂,如RGB,因此任何非单色光等效于在各种强度的单色色彩的线性组合。换句话说,光谱轨迹内的任何点都可以由恰好位于边界上的点的某种组合创建。如果您采用单色 CIE RGB 原色,然后将它们的三色值相加,您会得到白色,而该白色的光谱就是叠加的三个原色的光谱,每个原色的波长处都有一个细带。
因此,单色的每一种可能组合都在人类视觉的范围内。然而,存在大量重叠:不同的光谱可以产生相同的感知颜色。这称为同色异谱。因此,虽然枚举可以产生它们的每种可能的单独可感知颜色或光谱可能不切实际,但从一组简单可枚举的光谱中计算空间的整体形状实际上相对容易。
我们所做的是逐个波长地遍历色域,对于给定的波长,我们从该点开始迭代地对更大的光谱切片求和,直到我们达到Y目标或用完光谱。你可以把这想象成绕一个圆,从一个起点画越来越大的圆弧,然后画出结果形状的中心——当你得到一个圆圆的圆弧时,圆心重合,你会得到白色,但是在那之前,您绘制的点将从边缘向内螺旋。从圆周上的每个点重复此操作,您将有点沿着每条可能的路径螺旋式进入,覆盖整个色域。实际上,您有时可以在 3D 色彩空间图中看到这种螺旋效果。
在实践中,这采用两个循环的形式,外循环从 360 到 830,内循环从 1 到 470。在我的实现中,我为内循环所做的是保存当前和最后的总和值,一旦总和超过目标,我就使用差值来计算带的分数并将外循环的计数器和内插宽度推到一个数组上,然后跳出内循环。对波段进行插值可以极大地平滑曲线,尤其是在船首。
一旦我们有了正确亮度的光谱集,我们就可以计算它们的X和Z值。为此,我有一个高阶summation函数,该函数将函数传递给 sum 和区间。从那里,色度图上色域的形状Y只是由导出{x, y}坐标形成的路径,因为这种方法只枚举色域的表面,没有内点。
实际上,这是接受的答案中提到的库所做的更简单的版本:它们通过耗尽连续光谱空间来创建 3D 网格,然后在点之间进行插值以确定确切的颜色是在色域内还是色域外. 是的,这是一种非常暴力的方法,但它简单、快速且有效,足以用于演示和可视化目的。在浏览器中渲染色度空间整体形状的 20 步等高线图实际上是即时的,例如,几乎完美的曲线。
有几个地方无法完全消除精度不足:特别是,橙色附近的两个角被剪掉了。这是因为该区域中部分和的线的形状是 (1) 几乎完全水平和 (2) 在拐角处具有硬尖的组合。由于恰好位于尖端的点不是很好,即使 的值Y,轮廓的平坦度更成问题,因为它们垂直于尖顶的大部分垂直线,因此插值点以适合任何给定的Y该地区将最为悲观。另一个问题是点不是均匀分布的,集中在非常靠近尖端的地方:拐角的剪裁对应于插入外围点的情况。所有这些问题都可以在这张图中清楚地看到(为了清晰起见,用 20nm 分档呈现,但同样,更高的精度并不能消除这个问题):
当然,这是一种高度技术性和易陷入陷阱的问题 (PPP),通常最好外包给高质量的 3rd 方库。然而,了解其背后的基本技术和科学,可以揭开整个过程的神秘面纱,帮助我们有效地使用这些库,并根据需求变化调整我们的解决方案。
您可以使用颜色和colour.is_within_visible_spectrum定义:
>>> import numpy as np
>>> is_within_visible_spectrum(np.array([0.3205, 0.4131, 0.51]))
array(True, dtype=bool)
>>> a = np.array([[0.3205, 0.4131, 0.51],
... [-0.0005, 0.0031, 0.001]])
>>> is_within_visible_spectrum(a)
array([ True, False], dtype=bool)
Run Code Online (Sandbox Code Playgroud)
请注意,此定义需要 CIE XYZ 三刺激值,因此您必须使用colour.xyY_to_XYZ定义将 CIE xyY 颜色空间值转换为 XYZ。