我正在阅读有关将光谱转换为 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
只是一个向量(一维),作为MI
与white=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])
即第一列。