【问题标题】:3x3 matrix conversion without loop (RGB color conversion)3x3 无循环矩阵转换(RGB 颜色转换)
【发布时间】:2011-11-04 14:38:55
【问题描述】:

我通过 PIL 将 RGB 图像加载到 numpy 数组中。我得到一个 rows x cols x 3 数组。修修补补后,我得到了以下代码。我想学习如何在没有循环的情况下进行这样的数组/矩阵操作。

# Note using matrix not array.
rgb_to_ycc = np.matrix(
     (0.2990,  0.5870,  0.1140,
    -0.1687, -0.3313,  0.5000,
     0.5000, -0.4187, -0.0813,)
).reshape( 3,3 )

ycc_to_rgb = np.matrix(
    ( 1.0, 0.0, 1.4022,
      1.0, -0.3456, -0.7145,
      1.0, 1.7710, 0, )
).reshape( 3, 3 )

def convert_ycc_to_rgb( ycc ) :
    # convert back to RGB
    rgb = np.zeros_like( ycc )
    for row in range(ycc.shape[0]) :
        rgb[row] = ycc[row] * ycc_to_rgb.T
    return rgb

def convert_rgb_to_ycc( rgb ) :
    ycc = np.zeros_like( rgb )
    for row in range(rgb.shape[0]):
        ycc[row] = rgb[row] * rgb_to_ycc.T
    return ycc

我可以使用http://pypi.python.org/pypi/colormath(通过Using Python to convert color formats?),但我将其用作学习numpy 的练习。

前面提到的 Colormath 库使用点积。

# Perform the adaptation via matrix multiplication.
result_matrix = numpy.dot(var_matrix, rgb_matrix)

我的数学不是应该的。 np.dot() 是我最好的选择吗?

编辑。在深入阅读 colormath 的 apply_RGB_matrix()-color_conversions.py 之后,我发现如果我的转换 3x3 是 not 矩阵,则 np.dot() 有效。诡异的。

def convert_rgb_to_ycc( rgb ) :
    return np.dot( rgb, np.asarray( rgb_to_ycc ).T )

【问题讨论】:

    标签: python image-processing numpy


    【解决方案1】:

    我不确定您对convert RGB to YCC 使用的公式,所以我不想声称这是完整的计算,但为了简化您发布的函数,是的,将np.dot 与numpy 数组一起使用而不是 numpy 矩阵。

    np.dot 比带有 numpy 矩阵的* 更通用。将* 与 numpy 矩阵一起使用时,这两个矩阵必须是二维的。 但是np.dot 可以产生具有不同形状的数组的结果。这对您的应用程序很重要,因为 rgb 是 3 维的(例如,当它具有形状 (1470, 2105, 3) 时)。

    np.dot 的文档说:

        For N dimensions it is a sum product over the last axis of `a` and
        the second-to-last of `b`::
    
            dot(a, b)[i,j,k,m] = sum(a[i,j,:] * b[k,:,m])
    

    这是常规矩阵乘法的推广。


    我建议调用您的终极函数rgb_to_ycc,而不是将该名称指定给常量矩阵。 (它更短,并且准确地说明了您希望该函数做什么。)

    所以在下面,rgb_to_ycc 是我建议的函数,我做了一些小的修改以使 convert_rgb_to_ycc 不会引发异常并进行我认为您打算的计算。

    最后一行 np.allclose(...) 显示两个函数返回相同的结果。

    import numpy as np
    
    def rgb_to_ycc(rgb):
        M = np.array(
             (0.2990,  0.5870,  0.1140,
            -0.1687, -0.3313,  0.5000,
             0.5000, -0.4187, -0.0813,)
            ).reshape( 3,3 )
        return np.dot(rgb, M.T)
    
    def convert_rgb_to_ycc( rgb ) :
        M = np.matrix(
             (0.2990,  0.5870,  0.1140,
            -0.1687, -0.3313,  0.5000,
             0.5000, -0.4187, -0.0813,)
            ).reshape( 3,3 )
        shape=rgb.shape
        rgb=rgb.reshape((-1,3))
        ycc = np.zeros_like( rgb )
        for i in range(len(rgb)):
            ycc[i] = rgb[i] * M.T
        return ycc.reshape(shape)
    
    rgb=np.random.random((100,100,3))
    assert np.allclose(rgb_to_ycc(rgb),convert_rgb_to_ycc(rgb))
    

    【讨论】:

    • 啊!使用 3x3 作为数组而不是矩阵确实是解决方案。并且内联转换矩阵确实可以使代码看起来更好。谢谢!而 np.allclose() 是值得学习的新东西。
    【解决方案2】:
    def convert_ycc_to_rgb(ycc):
        return ycc * ycc_to_rgb.T
    
    def convert_rgb_to_ycc(rgb):
        return rgb * rgb_to_ycc.T
    

    就这么简单,记住矩阵乘法是如何根据行和列的内积来定义的。

    编辑:

    我假设 rgb 和 ycc 矩阵只是一个矩阵,它的行数与像素一样多,每个颜色分量有一列。所以我们首先需要做的是将它们重塑为(rows*cols,3),然后再回到(rows, cols, 3)

    所以代码最终是:

    def convert_ycc_to_rgb(ycc):
        shape = ycc.shape
        return np.array(ycc.reshape(-1,3) * ycc_to_rgb.T).reshape(shape)
    
    def convert_rgb_to_ycc(rgb):
        shape = rgb.shape
        return np.array(rgb.reshape(-1,3) * rgb_to_ycc.T).reshape(shape)
    

    【讨论】:

    • 我最初尝试过。例如,rgb.shape 是 (1470, 2105, 3)。 rgb_to_ycc 是 (3,3)。 ValueError:形状太大而不能成为矩阵。
    • 哦,我明白了,我在考虑序列化像素颜色...让我稍微破解一下 ;-)
    猜你喜欢
    • 1970-01-01
    • 2011-06-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-22
    • 1970-01-01
    相关资源
    最近更新 更多