python - scipy ndimage.convolve 跳过 channel 求和

标签 python numpy scipy conv-neural-network convolution

我正在尝试使用 scipy 的 ndimage.convolve 函数对 3 维图像(RGB、宽度、高度)执行卷积。

看这里:

http://cs231n.github.io/convolutional-networks/

很明显,对于任何输入,每个内核/过滤器都应该只有 NxN 的输出,深度严格为 1。

这是 scipy 的问题,就像当您使用大小为 (3, 5, 5) 的输入执行 ndimage.convolve 时一样和大小为 (3, 3, 3) 的过滤器/内核,此操作的结果产生的输出大小为 (3, 5, 5),显然没有求和不同的 channel 。

有没有办法强制进行求和而不需要手动执行?我尝试尽可能少地使用基础 Python,因为许多外部库都是用 C++ 编写的,并且可以更快地执行相同的操作。或者有其他选择吗?

最佳答案

没有 scipy 不会跳过 channel 的求和。得到 (3, 5, 5) 输出的原因是 ndimage.convolve 沿所有轴填充输入数组,然后在“相同的轴”中执行卷积“模式(即输出具有与输入相同的形状,相对于“完整”模式相关的输出居中)。请参阅scipy.signal.convolve有关模式的更多详细信息。

对于形状 (3 ,5, 5) 的输入和形状 (3, 3, 3) 的过滤器 w0,输入被填充,生成一个 (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 或改为使用相关函数(我将执行前者)。

然后,沿着第一维(axis-0)滑动进行卷积,即(翻转后的)w0a_p[0:3]进行卷积,然后使用 a_p[1:4],然后使用 a_p[2:5],然后使用 a_p[3:6],最后使用 a_p[4:7],由于 channel 上的求和,每个结果都会产生一个 (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/

相关文章:

在 Google 应用引擎上运行的 Python 应用程序。如何调用 Google Maps API?

Python 连接和堆叠多个矩阵

python - 在 python2 与 python3 中映射

python - 将 'decimal-mark' 千位分隔符添加到数字

python - 由于 AKI DirName 扩展 : Python [SSL: CERTIFICATE_VERIFY_FAILED] . 在 openSSL 中找到根本原因 ..:无法获取本地颁发者证书

python - 在 python 中,如何删除 numpy 3d 数组中每第 n 个元素的一系列列?

python - 比较 scipy、torch 和 Fourier 周期卷积时的不一致

python - SciPy leastsq 适合正弦波失败

python - scipy中二维数组的最小值计算

python - Sqlalchemy 在尝试连接到 clickhouse 数据库时显示 "Code 516 Authentication failed"