这个 Python/numpy 代码如何实现 XYZ->rgb 色彩空间转换的白色缩放?



我正在阅读有关将光谱转换为 rgb 颜色坐标的指南。

我基本上了解代数在做什么,但是作者并没有真正解释处理白点的代数,我无法阅读完成这项工作的Python/numpy代码

import numpy as np
class ColourSystem:
def __init__(self, red, green, blue, white):
self.red, self.green, self.blue = red, green, blue
self.white = white
# The chromaticity matrix (rgb -> xyz) and its inverse
self.M = np.vstack((self.red, self.green, self.blue)).T 
self.MI = np.linalg.inv(self.M)
# White scaling array
self.wscale = self.MI.dot(self.white)
# xyz -> rgb transformation matrix
self.T = self.MI / self.wscale[:, np.newaxis]

最后两行让我感到困惑。我的解释是self.white是一个列向量,所以self.MI.dot(self.white)是一个矩阵向量乘法产生另一个列向量。

但在这种解释中,最后一行读起来就像将矩阵除以向量,这对我来说毫无意义。

最后一行通过修改 rgb->xyz 矩阵的逆函数来生成 xyz->rgb 矩阵做什么?

很抱歉回答晚了,这更像是对评论的回答,而不是对原始问题的回答,但很长并且有一些代码,所以它不适合那里。让我重写一个独立的代码片段,它执行您所要求的相同操作(初始化编号取自您提供的链接):

import numpy as np
def xyz_from_xy(x, y):
"""Return the vector (x, y, 1-x-y)."""
return np.array((x, y, 1-x-y))
red=xyz_from_xy(0.67, 0.33)
green=xyz_from_xy(0.21, 0.71)
blue=xyz_from_xy(0.15, 0.06)
white=xyz_from_xy(0.3127, 0.3291)
# The chromaticity matrix (rgb -> xyz) and its inverse
M = np.vstack((red, green, blue)).T 
MI = np.linalg.inv(M)
# White scaling array
wscale = MI.dot(white)
# xyz -> rgb transformation matrix
T = MI / wscale[:, np.newaxis]

这在 T 中写入的值是以下矩阵:

array([[ 6.20584986, -1.71746142, -1.04788582],

[-2.71554014, 5.51336937, 0.09687197],

[ 0.19384968, -0.39357359, 2.9841102 ]])

现在,这是如何计算的,广播是如何参与的?

正如名称"vstack"所暗示的那样,M只是将红色、绿色和蓝色向量一个堆叠在一起:

array([[ 6.70000000e-01, 2.10000000e-01, 1.50000000e-01],

[ 3.30000000e-01, 7.10000000e-01, 6.00000000e-02],

[-5.55111512e-17, 8.00000000e-02, 7.90000000e-01]])

(其中,根据函数xyz_from_xy,最后一个分量总是只是一个减去前两个的总和)。MI 是它的反面:

array([[ 1.72809198, -0.47824736, -0.29179615],

[-0.81013052, 1.64481044, 0.02889994],

[ 0.08203853, -0.16656308, 1.26289621]])

而且,正如您正确提到的,wscale只是一个向量(一维),作为MIwhite=array([0.3127, 0.3291, 0.3582])的标量乘积获得:

array([0.27846178, 0.29833126, 0.42320696])

现在,谈谈广播。首先,wscale[:, np.newaxis]与wscale相同,但形状(3,1)重新塑造:

array([[0.27846178],

[0.29833126],

[0.42320696]])

(同样的效果可以通过写作来达到:wscale.reshape(3,1))。当你在MI(形状(3,3))和另一个对象(形状(3,1))之间进行算术运算时,无论是求和、乘法、除法,甚至是像**这样明显更奇怪的东西,它只是按元素进行。但由于维度不同,它试图"广播",即通过多次重复相同的向量,使等于 1 的每个维度都等于另一个对象(在本例中为 3)中的相应维度。明确地说,您将 MI 元素除以水平重复 wscale 三次获得的矩阵。您可以通过编写以下内容以更详细的方式获得相同的结果:

T = MI / np.hstack((wscale.reshape(3,1), wscale.reshape(3,1), wscale.reshape(3,1)))

如果你想像点积一样做矩阵运算,你需要显式编写MI.dot(wscale[:, np.newaxis])(在这种情况下,这将返回一个(3,1)形张量)。但这不是要做的:T = MI / wscale[:, np.newaxis]的结果是:

array([[ 6.20584986, -1.71746142, -1.04788582],

[-2.71554014, 5.51336937, 0.09687197],

[ 0.19384968, -0.39357359, 2.9841102 ]])

这只是将任何MI列按元素除以wscale的结果。例如,看看第一个,如果我们这样做MI[:,0] / wscale我们确实得到:

array([ 6.20584986, -2.71554014, 0.19384968])

即第一列。

最新更新