我正在尝试使用scipyndimage.convolve函数对3维图像(RGB,宽度,高度)进行卷积。

在这里看看:

python - Scipy ndimage.convolve跳过 channel 求和-LMLPHP

可以清楚地看到,对于任何输入,每个内核/过滤器仅应具有NxN的输出,其深度严格为1。

这是scipy的问题,因为当您使用大小为ndimage.convolve的输入和大小为(3, 5, 5)的过滤器/内核对(3, 3, 3)进行操作时,此操作的结果显然是输出大小为(3, 5, 5)不总结不同的渠道。

有没有一种方法可以强制进行这种求和而无需手动进行?我尝试在基本python中做尽可能少的事情,因为许多外部库都是用c ++编写的,并且可以更快地执行相同的操作。还是有其他选择?

最佳答案

没有秘诀不会跳过渠道的总和。之所以得到(3, 5, 5)输出,是因为ndimage.convolve沿所有轴填充输入数组,然后以“相同”模式执行卷积(即,输出具有与输入相同的形状,相对于输入的中心“完全”模式相关性的输出)。有关模式的更多详细信息,请参见scipy.signal.convolve

对于形状为(3 ,5, 5)的输入和形状为w0的过滤器(3, 3, 3),将填充输入,形成一个(7, 9, 9)数组。参见下文(为简单起见,我使用带有0的常量填充):

a = np.array([[[2, 0, 2, 2, 2],
               [1, 1, 0, 2, 0],
               [0, 0, 1, 2, 2],
               [2, 2, 2, 0, 0],
               [1, 0, 1, 2, 0]],

              [[1, 2, 1, 0, 1],
               [0, 2, 0, 0, 1],
               [0, 0, 2, 2, 1],
               [2, 0, 1, 0, 2],
               [0, 1, 2, 2, 2]],

              [[0, 0, 2, 2, 2],
               [0, 1, 2, 1, 0],
               [0, 0, 0, 2, 0],
               [0, 2, 0, 0, 2],
               [0, 0, 2, 2, 1]]])

w0 = np.array([[[0,  1, -1],
                [1, -1,  0],
                [0,  0,  0]],

               [[1,  0,  0],
                [0, -1,  1],
                [1,  0,  1]],

               [[ 1, -1,  0],
                [-1,  0, -1],
                [-1,  0,  1]]])

k = w0.shape[0]

a_p = np.pad(a, k-1)

array([[[0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0]],

       [[0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0]],

       [[0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 2, 0, 2, 2, 2, 0, 0],
        [0, 0, 1, 1, 0, 2, 0, 0, 0],
        [0, 0, 0, 0, 1, 2, 2, 0, 0],
        [0, 0, 2, 2, 2, 0, 0, 0, 0],
        [0, 0, 1, 0, 1, 2, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0]],

       [[0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 1, 2, 1, 0, 1, 0, 0],
        [0, 0, 0, 2, 0, 0, 1, 0, 0],
        [0, 0, 0, 0, 2, 2, 1, 0, 0],
        [0, 0, 2, 0, 1, 0, 2, 0, 0],
        [0, 0, 0, 1, 2, 2, 2, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0]],

       [[0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 2, 2, 2, 0, 0],
        [0, 0, 0, 1, 2, 1, 0, 0, 0],
        [0, 0, 0, 0, 0, 2, 0, 0, 0],
        [0, 0, 0, 2, 0, 0, 2, 0, 0],
        [0, 0, 0, 0, 2, 2, 1, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0]],

       [[0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0]],

       [[0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0]]])


在继续之前,请注意,在来自cs231n的图像中,执行的是相关而不是卷积,因此我们需要翻转w0或使用相关函数(我将做前者)。

然后,通过沿第一维(轴-0)滑动来执行卷积,即(翻转的)w0a_p[0:3],然后与a_p[1:4],然后与a_p[2:5],然后与a_p[3:6]进行卷积最后是a_p[4:7],由于通道上的总和,每个都生成一个(1, 7, 7)数组。然后将它们堆叠在一起,形成(5, 7, 7)数组。为了说明这一点,我使用scipy.signal.convolve允许使用full模式:

out = scipy.signal.convolve(a, np.flip(w0), mode='full')

array([[[ 2,  0,  0,  2,  0, -2, -2],
        [-1,  1, -5, -1, -4, -4, -2],
        [-1, -3,  2, -3,  1, -4,  0],
        [ 2,  1, -1, -3, -7,  0, -2],
        [-1, -2, -4, -1, -4, -2,  2],
        [-1, -2, -2, -2,  1, -2,  0],
        [ 0, -1,  1, -1, -1,  2,  0]],

       [[ 3,  2,  4,  0,  4,  2,  1],
        [ 2, -1,  1, -1, -1,  0, -2],
        [ 1, -3,  3,  5,  2,  1,  3],
        [ 4,  2,  1,  4,  0, -3, -2],
        [ 1,  1,  1, -1, -1,  3, -1],
        [ 1, -4,  3, -1, -3, -4,  0],
        [ 0,  0,  0, -1,  1,  2,  2]],

       [[ 1,  2,  4,  4,  2, -2, -1],
        [ 1,  2,  1, -3, -4, -4,  1],
        [-2,  2, -3,  3,  1,  2,  4],
        [ 1,  2,  5, -6,  6, -2,  3],
        [ 2, -5,  4,  1,  5,  4,  0],
        [-2,  0,  0,  1, -3, -4,  3],
        [-1,  1, -1, -2,  4,  3,  3]],

       [[ 0,  0,  2,  2,  4,  2,  2],
        [ 0,  0,  3,  3,  3, -2,  1],
        [-1,  0,  0,  4,  0,  4,  3],
        [ 0,  0,  2,  3,  1,  3,  3],
        [ 0,  0,  0,  1,  7,  1,  3],
        [-2,  2,  0,  2, -3,  1,  4],
        [ 0, -1, -1,  0,  2,  4,  1]],

       [[ 0,  0,  0,  0,  0,  0,  0],
        [ 0,  0,  0, -2,  0,  0,  2],
        [ 0,  0, -3, -1,  1,  3,  0],
        [ 0, -1, -1,  1, -1,  2,  0],
        [ 0,  0, -2,  0,  2, -2,  2],
        [ 0, -2,  2, -2, -2,  3,  1],
        [ 0,  0, -2,  0,  1,  1,  0]]])


要进入ndimage.convolve的“相同”模式,我们需要将out居中:

out = out[1:-1, 1:-1, 1:-1]

array([[[-1,  1, -1, -1,  0],
        [-3,  3,  5,  2,  1],
        [ 2,  1,  4,  0, -3],
        [ 1,  1, -1, -1,  3],
        [-4,  3, -1, -3, -4]],

       [[ 2,  1, -3, -4, -4],
        [ 2, -3,  3,  1,  2],
        [ 2,  5, -6,  6, -2],
        [-5,  4,  1,  5,  4],
        [ 0,  0,  1, -3, -4]],

       [[ 0,  3,  3,  3, -2],
        [ 0,  0,  4,  0,  4],
        [ 0,  2,  3,  1,  3],
        [ 0,  0,  1,  7,  1],
        [ 2,  0,  2, -3,  1]]])


如果运行scipy.ndimage.convolve(a, np.flip(w0), mode='constant', cval=0),这正是您得到的。最后,要获得所需的输出,我们需要忽略依赖于沿第一维填充的元素(即,仅保留中间部分),还使用步幅s=2(即out[1][::s, ::s]),最后添加偏差b = 1

out[1][::s, ::s] + b

array([[ 3, -2, -3],
       [ 3, -5, -1],
       [ 1,  2, -3]])


将所有内容放在一起:

scipy.ndimage.convolve(a, np.flip(w0), mode='constant', cval=0)[1][::2, ::2] + b

# or using scipy.signal.convolve
# scipy.signal.convolve(a, np.flip(w0), 'full')[2][1:-1,1:-1][::2, ::2] + b
# or
# scipy.signal.convolve(a, np.flip(w0), 'same')[1][::2, ::2] + b

关于python - Scipy ndimage.convolve跳过 channel 求和,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/59782158/

10-12 19:06