根据您最终的到来方式,可能有多种类型的RGB可能会令人惊讶,因此,我将尽力介绍一些背景信息,并引入一个称为XYZ的色彩空间。然后,我们将逐步讲解在所有这些RGB变体之间进行转换的数学方法,并讨论为什么您甚至考虑这样做。最后,我将介绍一些用于实现色彩空间转换的示例代码。
当您告诉像素使用RGB组件进行绘制时,您将为输出到该像素的红色,绿色和蓝色原色光指定强度值。当显示器混合这些灯时,您将获得所需的颜色。问题在于那里有多种类型的显示器,并且主光源可能不尽相同。允许的指示灯将相似,但是一台显示器上的红色主显示器可能与另一台显示器上的红色主显示器不完全匹配。例如,旧的CRT电视的原色与新的高清LCD电视不匹配。当原色灯不匹配时,具有相同RGB成分的颜色也将不匹配,因为它将不同的原色相加。
对于许多应用程序来说,整个概念实际上并不需要担心。只要您的红色看起来偏红而您的绿色看起来偏绿,一切都很好。当颜色精度很重要时,或者如果您想成为某种颜色的纳粹,则需要从一种颜色空间转换为另一种颜色空间。一种用途是将在LCD监视器上绘制的纹理转换为匹配的(或尽可能接近匹配的)颜色值,以在CRT电视上显示。
XYZ色彩空间和色度
那么我们如何在数学上指定色度呢?我们可以使用CIE1931xyY颜色空间。从这里开始要当心,因为我将使用小写的x,y和z表示一组值,使用大写的X,Y和Z表示另一组值。不要为此怪我-这一切都是在奥地利决定的。
我们将按照X,Y和Z定义三个值x,y和z,如下所示(最后是一些数学运算!)。
因为我们可以从x和y得出z,所以实际上只需要指定x和y即可描述唯一的xyz值。如果给定xy值为(0.2,0.3),则我们知道z值为0.5。尽管这似乎有些武断,但它所做的是创建了一个二维xy空间,我们可以轻松地在二维表面上绘制图形。
您可能已经注意到,我将此空间列为CIE1931xyY颜色空间,而不是xy或xyz颜色空间。这里的理由是我们需要X,Y或Z来重新创建原始颜色。使用xyz值,我们丢失了一些信息。它们可以在二维中唯一表示的事实应该预示着一些阴暗现象正在逐渐消失。尽管可以将任何原始XYZ分量与xy一起使用以重新计算原始颜色,但我们使用Y分量是因为它表示光度,我们也可以使用该数字代替X或Z,但这并不表示任何含义。太容易理解了。
不用太偏离轨道,如果您熟悉线性代数或3D数学,您可能会想知道我们在空间上实际正在看什么(毕竟,这些都是从某个3DXYZ空间开始的)。创建xyz值时,我们实质上是将XYZ空间透视投影到包含点(1,0,0),(0,1,0),(0,0,1)的XYZ平面上。这样,XYZ空间的正八分圆都以这三个点创建的三角形结束。我们的xy色度图是此三维三角形沿Z轴的正交视图。
定义RGB颜色空间
为显示器的每个主要部分提供了色度坐标。对于sRGB空间,它们如下:
如果您回想起以前,颜色空间的色域是可以由该空间表示的颜色子集。现在,我们可以在色度图上绘制sRGB色域。将列出的色度坐标与该图进行比较,您会发现它们与三角形sRGB色域的角相匹配。
作为互联网的标准色彩空间(可能非常接近显示器的色彩空间),只能在网络上创建该三角形内的色度。我还专门渲染了这张图像,以准确表示那些色度,前提是您的显示器已正确校准(可能不是-我的当然不是)。
我第一次看到这些图表中的一个时,我有点担心,因为看起来我们的显示器似乎无法接近绿色。只需查看三角形左上角所有无法显示的可见颜色即可!幸运的是,事实证明事情并没有看起来那么糟。xy和XYZ空间相对于我们对颜色的感知方式都有所拉伸和倾斜。您会注意到一堆小点以舌形弯曲在图表周围。这些点表示以5纳米的间隔分开的波长的可见光谱。它的细节离我们的目标有些偏离,但是知道它们具有一定的线性间距可以帮助我们看到图中的变形。点从红光传播到绿光,并以蓝光结束。如你看到的,间距在绿色附近延伸,而在蓝色和红色附近则非常压缩。本质上,绿色的错误并不像该图所示那样严重。
乍一看可能有点奇怪,但是白点用于指定白色。可能有人会期望白色实际上是白色,但这仅是一半。我说这是“半正确的”,因为从某种意义上说,一种颜色可能看起来是白色的,而实际上它实际上具有某种颜色。人类的视觉系统是一个复杂的野兽,它将或多或少地根据视野选择将什么颜色感知为白色。这种应该视为白色的颜色称为白点。
例如,普通的白炽灯泡发出橘红色的光,而中午的太阳光则更接近中性白色。无论颜色如何变化,在阳光下在灯泡下或外面看时,您都会将白纸感知为白色。当然,您可能会在某种程度上意识到色相的变化,但要点是您不会感到困惑,因为当您在灯泡下时,所有纸张都会变红。在摄影中,了解这种感知效果实际上很重要。颜色校正通常应用于照片,以便将在其上捕获图像的白点转换为将在其上观看图像的白点。
伽马校正曲线用于将像素亮度从线性标度转换为指数标度。在对最终像素值进行编码时,该曲线用于将线性亮度伽玛压缩为伽玛校正值。解码像素值时,将使用反曲线将值伽马扩展回线性单位。
如果我们要以线性刻度存储图像值,则值的单个步长将对应于刻度低端的亮度大步长,而对应于刻度高端的小亮度步长。结果,我们将在深色中失去很多亮度保真度。
现在,让我们使用sRGB伽玛校正曲线查看亮度。
现在,我们在所有值上都获得了一致的亮度步骤,从而使我们可以在整个范围内以更高的保真度编码亮度。尽管此图像确实显示出线性亮度(人类感知),但应该强调的是,我们不再使用线性亮度(物理)。
这个概念是较旧的3D视频游戏中出现许多照明错误的根源(实际上,许多新游戏仍然存在问题)。由于硬件限制和性能问题,某些视频游戏仅在对其纹理进行编码的非线性RGB空间中执行照明。这导致添加了灯光,从而产生比预期更明亮的效果。在上图中很容易看到这一点。如果您在每个比例尺中定位具有(128,128,128)灰色的像素,您将看到它在线性亮度图像中大约占22%,在经过伽玛校正的图像中大约占50%。现在,假设您要在此亮度下添加两个灯光。在线性比例尺中,您将使22%翻倍,从而将44%放入具有正确值(178,178,178)的线性图像中。
除了对亮度进行编码的好处之外,还选择了伽玛曲线来匹配显示设备的物理输出属性。例如,CRT监视器的电子枪具有非线性输出,并且必须提供经过伽玛校正的输入,以便创建适当的实际亮度。