【问题标题】:How does this Python/numpy code achieve white scaling for XYZ->rgb colour space transformation?这个 Python/numpy 代码如何为 XYZ->rgb 颜色空间转换实现白色缩放?
【发布时间】:2018-02-13 21:49:01
【问题描述】:

我正在阅读 this guide 关于将光谱转换为 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 矩阵的最后一行是什么?

【问题讨论】:

  • 我的理解是 self.wscale[:, np.newaxis] 正在将列向量(形状为 (3,))转换为 3x1 矩阵(形状为 (3,1))。然后,当您进行除法时,将应用广播,因此您将 3x3 矩阵 MI 除以通过重复 3 次 wscale 列向量获得的 3x3 矩阵。
  • “广播”?

标签: python numpy linear-algebra


【解决方案1】:

抱歉,回答迟了,这更像是对评论的回答,而不是对原始问题的回答,但是很长,而且有一些代码,所以不适合那里。让我重写一个独立的代码 sn-p 来完成你所要求的同样的事情(初始化数字取自你提供的链接):

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 中写入的值是以下矩阵:

数组([[ 6.20584986, -1.71746142, -1.04788582],

[-2.71554014, 5.51336937, 0.09687197],

[ 0.19384968, -0.39357359, 2.9841102 ]])

现在,这是如何计算的以及如何涉及广播?

正如“vstack”这个名字所暗示的那样,M 只是将红色、绿色和蓝色向量一个接一个地堆叠起来:

数组([[ 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 是它的倒数:

数组([[ 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]) 的标量积获得的向量(一维):

数组([0.27846178, 0.29833126, 0.42320696])

现在,广播。首先,wscale[:, np.newaxis] 和 wscale 是一样的,只是用形状 (3,1) 重新整形:

数组([[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] 的结果是:

数组([[ 6.20584986, -1.71746142, -1.04788582],

[-2.71554014, 5.51336937, 0.09687197],

[ 0.19384968, -0.39357359, 2.9841102 ]])

这只是将MI 的任何列除以wscale 的结果。例如,查看第一个,如果我们使用MI[:,0] / wscale,我们确实会得到:

数组([ 6.20584986, -2.71554014, 0.19384968])

即第一列。

【讨论】:

  • 因此,如果我们将MI 视为三个列向量(a b c),我们将a、b 和c 的每个x 分量除以wscale 的x 分量,然后其他组件也一样?
  • 是的,就是这样!
猜你喜欢
  • 2020-03-17
  • 2011-10-01
  • 2018-04-09
  • 2014-03-01
  • 2022-01-08
  • 2020-03-16
  • 1970-01-01
  • 1970-01-01
  • 2020-09-22
相关资源
最近更新 更多