Scipy ndimage.convolve 跳过通道求和
Scipy ndimage.convolve skips the summation of channels
我正在尝试使用 scipy
的 ndimage.convolve
函数对 3 维图像(RGB、宽度、高度)执行卷积。
看这里:
很明显,对于任何输入,每个 kernel/filter 应该只有 NxN 的输出,深度严格为 1.
这是 scipy
的问题,因为当您使用大小 (3, 5, 5)
的输入和大小 (3, 3, 3)
的 filter/kernel 执行 ndimage.convolve
时,此操作的结果产生 (3, 5, 5)
的输出大小,显然不是对不同通道求和。
有没有办法在不手动的情况下强制求和?我尝试尽可能少地在 base python 中做,因为很多外部库都是用 c++ 编写的,并且可以更快地执行相同的操作。或者有其他选择吗?
否 scipy 不跳过通道求和。你得到 (3, 5, 5)
输出的原因是因为 ndimage.convolve
沿所有轴填充输入数组,然后在 "same" 模式下执行卷积(即输出具有与输入相同的形状,以 "full" 模式相关的输出为中心)。有关模式的更多详细信息,请参阅 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)滑动执行卷积,即(翻转)w0
与a_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
的 "same" 模式,我们需要将 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
我正在尝试使用 scipy
的 ndimage.convolve
函数对 3 维图像(RGB、宽度、高度)执行卷积。
看这里:
很明显,对于任何输入,每个 kernel/filter 应该只有 NxN 的输出,深度严格为 1.
这是 scipy
的问题,因为当您使用大小 (3, 5, 5)
的输入和大小 (3, 3, 3)
的 filter/kernel 执行 ndimage.convolve
时,此操作的结果产生 (3, 5, 5)
的输出大小,显然不是对不同通道求和。
有没有办法在不手动的情况下强制求和?我尝试尽可能少地在 base python 中做,因为很多外部库都是用 c++ 编写的,并且可以更快地执行相同的操作。或者有其他选择吗?
否 scipy 不跳过通道求和。你得到 (3, 5, 5)
输出的原因是因为 ndimage.convolve
沿所有轴填充输入数组,然后在 "same" 模式下执行卷积(即输出具有与输入相同的形状,以 "full" 模式相关的输出为中心)。有关模式的更多详细信息,请参阅 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)滑动执行卷积,即(翻转)w0
与a_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
的 "same" 模式,我们需要将 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