将RGB数组乘以颜色变换矩阵向量化,以进行图像处理

2 投票
1 回答
4300 浏览
提问于 2025-04-29 17:34

我正在自学使用Python 3进行彩色图像处理(这次主要用到NumPy)。

我有一个三维数组image,它表示图像中每个像素的RGB值,所以它的形状是(高度,宽度,3)。在每个像素上,我想生成新的RGB值,这些新值是原始RGB值的某种线性组合。我将通过将每个像素的RGB向量与W(一个3x3的权重矩阵)相乘来实现这一点。

我可以用嵌套的for循环来完成这个任务,但这样做速度很慢:

newRGB = np.zeros((height,width,3))   # make empty array to update with RGB values
for i in range(height):
    for j in range(width):                     
        RGB = image[i,j,:]            # RGB vector at given pixel with size 3 since is [R,G,B]
        new = np.dot(W,RGB)           # W is 3x3 matrix of weights
        newRGB[i,j,:] = new           # put new RGB values into the empty matrix

另外,有一种更快的向量化方法是:

image = mpimg.imread('test.png')   # reading image file into matplotlib.image
print(image.shape)                 # image has shape (height,width,3)
W = np.array([...])                # arbitrary 3x3 matrix of weights  
x = np.rollaxis(image,2,1)         # moving the RGB axis to 2nd position
print(x.shape)                     # x has shape (height,3,width)
Wx = np.dot(W,x)                   # matrix multiplication
print(Wx.shape)                    # Wx has shape (3,height,width)
y = np.rollaxis(Wx,0,3)            # moving RGB axis back to 3rd position to have image shape
print(y.shape)                     # y has shape (height,width,3) like original image

有没有更简单的方法来实现这个,比如使用numpy.tensordot()?

另外,由于我是在进行RGB值的线性组合,我是否可以创建某种3D线性滤波器,并通过在FFT空间中进行简单的逐元素乘法来与我的图像进行卷积?

现在我的图像大约是1000x1000像素,所以RGB数组的形状大致是(1000,1000,3)。但我也对其他应用中的向量化感兴趣,这些应用可能会有更大的数组(或者更高的维度),所以关于更大数组大小和维度的相关答案也非常欢迎。

暂无标签

1 个回答

3

是的,你可以使用 np.tensordot 或者 np.einsum

In [9]: np.tensordot(image, W, ([2], [1])).shape
Out[9]: (1000, 1000, 3)

In [13]: np.einsum('ijk,lk->ijl', image, W).shape
Out[13]: (1000, 1000, 3)


In [19]: x = np.rollaxis(image,2,1)

In [20]: Wx = np.dot(W,x)

In [21]: y = np.rollaxis(Wx,0,3)

In [22]: np.allclose(np.tensordot(image, W, ([2], [1])), y)
Out[22]: True

In [14]: np.allclose(np.tensordot(image, W, ([2], [1])), np.einsum('ijk,lk->ijl', image, W))
Out[14]: True

在这两种方法中,np.tensordot 在这个情况下看起来更快。

In [15]: %timeit np.einsum('ijk,lk->ijl', image, W)
10 loops, best of 3: 31.1 ms per loop

In [16]: %timeit np.tensordot(image, W, ([2], [1]))
100 loops, best of 3: 18.9 ms per loop

撰写回答