Python中的2D卷积类似于Matlab的conv2
Posted
技术标签:
【中文标题】Python中的2D卷积类似于Matlab的conv2【英文标题】:2D Convolution in Python similar to Matlab's conv2 【发布时间】:2013-04-13 19:54:40 【问题描述】:我一直在尝试使用 SciPy 和 Numpy 对 2D 矩阵进行卷积,但失败了。对于我尝试过的 SciPy,sepfir2d 和 scipy.signal.convolve 以及用于 Numpy 的 Convolve2D。 Matlab for Python中是否有像conv2这样的简单函数?
这是一个例子:
A= [ 5 4 5 4;
3 2 3 2;
5 4 5 4;
3 2 3 2 ]
我想将它与[0.707 0.707]
进行卷积
Matlab 中 conv2 的结果是
3.5350 6.3630 6.3630 6.3630 2.8280
2.1210 3.5350 3.5350 3.5350 1.4140
3.5350 6.3630 6.3630 6.3630 2.8280
2.1210 3.5350 3.5350 3.5350 1.4140
用 Python 计算这个输出的一些函数?我将不胜感激。
【问题讨论】:
【参考方案1】:scipy
有多种不同的方法,但 2D 卷积并未直接包含在 numpy
中。 (如果您需要避免 scipy 依赖,仅使用 numpy 的 fft 也很容易实现。)
scipy.signal.convolve2d
、scipy.signal.convolve
、scipy.signal.fftconvolve
和 scipy.ndimage.convolve
都会以不同的方式处理 2D 卷积(最后三个是 N-d)。
scipy.signal.fftconvolve
在 fft 域中进行卷积(这是一个简单的乘法)。这在许多情况下要快得多,但与离散情况相比,可能会导致边缘效应的差异非常小,并且您的数据将通过这种特定实现强制转换为浮点数。此外,在将小数组与大得多的数组进行卷积时,还会使用不必要的内存。总而言之,基于 fft 的方法可以显着加快,但在一些常见用例中,scipy.signal.fftconvolve
并不是一个理想的解决方案。
scipy.signal.convolve2d
、scipy.signal.convolve
和 scipy.ndimage.convolve
都使用用 C 实现的离散卷积,但是它们以不同的方式实现它。
scipy.ndimage.convolve
保持相同的数据类型,并让您控制输出的位置以最大限度地减少内存使用。如果您正在卷积uint8
(例如图像数据),它通常是最好的选择。输出将始终与第一个输入数组的形状相同,这对图像有意义,但可能不适用于更一般的卷积。 ndimage.convolve
让您可以通过mode
kwarg(其功能与scipy.signal
的mode
kwarg 完全不同)对边缘效果的处理方式进行大量控制。
如果您使用二维数组,请避免使用scipy.signal.convolve
。它适用于 N-d 情况,但对于 2d 数组来说不是最理想的,并且 scipy.signal.convolve2d
的存在可以更有效地完成完全相同的事情。 scipy.signal
中的卷积函数使您可以使用 mode
kwarg 控制输出形状。 (默认情况下,它们的行为就像 matlab 的 conv2
。)这对一般数学卷积很有用,但对图像处理用处不大。但是,scipy.signal.convolve2d
通常比scipy.ndimage.convolve
慢。
有很多不同的选项,部分原因是 scipy
的不同子模块中存在重复,部分原因是实现卷积的不同方法具有不同的性能权衡。
如果您能提供更多关于您的用例的详细信息,我们可以推荐一个更好的解决方案。如果您要卷积两个大小大致相同的数组,并且它们已经是浮点数,那么fftconvolve
是一个很好的选择。否则,scipy.ndimage.convolve
可能会击败它。
【讨论】:
感谢乔的详细回复。我正在尝试对要用于去噪的图像进行平稳小波变换。这就是我想使用它的地方。 在这种情况下,scipy.ndimage.convolve1d
,正如 Bitwise 所提到的,可能是您的最佳选择。它针对将一维数组与二维数组进行卷积的特定用例进行了优化。希望有帮助!
另外,如果您不想用零填充A
,scipy.signal.convolve2d
可能是更好的选择。默认情况下,返回的数组的形状就像 matlab 的 conv2
。 (我最初忘记在我的答案中包含该信息)但是,如果您正在使用图像,我会假设您希望返回的数组与原始数组的形状相同。在这种情况下,ndimage
是您最好的选择。
Ftr:我在尝试使用 scipy.signal.convolve2d
或 scipy.ndimage.convolve
时被咬了,因为我的权重不是 2D... 足够 -.- Cf。 np.array([0.707, 0.707]).shape
与 np.array([[0.707, 0.707]]).shape
。字面翻译代码时的愚蠢错误。
请注意,虽然scipy.convolve2d
只执行“直接”卷积(即在原始域中),但scipy.convolve
(N 维)在原始域或 FFT 域中都有效,取决于哪个在计算上更好。对于相对较大的图像,由于 FFT 节省的时间可以保持一致!通过使用 scipy.convolve
而不是 scipy.convolve2d
,在将 3000x3000 与 41x41 数组进行卷积时,我的速度平均提高了 40 倍。【参考方案2】:
scipy 的 convolved1d() 做你想做的事,只是处理边缘有点不同:
sp.ndimage.filters.convolve1d(A,[0.707,0.707],axis=1,mode='constant')
会给你:
array([[ 6.363, 6.363, 6.363, 2.828],
[ 3.535, 3.535, 3.535, 1.414],
[ 6.363, 6.363, 6.363, 2.828],
[ 3.535, 3.535, 3.535, 1.414]])
如果您想要完全相同的结果,只需将一列零添加到 A 中,如下所示:
sp.ndimage.filters.convolve1d(np.c_[np.zeros((4,1)),A],[0.707,0.707],axis=1,mode='constant')
你会得到:
array([[ 3.535, 6.363, 6.363, 6.363, 2.828],
[ 2.121, 3.535, 3.535, 3.535, 1.414],
[ 3.535, 6.363, 6.363, 6.363, 2.828],
[ 2.121, 3.535, 3.535, 3.535, 1.414]])
根据我的经验,您可以在 scipy/numpy 中轻松完成大部分在 Matlab 中所做的事情(甚至更多)。
【讨论】:
非常感谢 Bitwise,我要试试。【参考方案3】:为什么不自己实现呢?请注意,conv2 使用空间形式的二维卷积方程的直接形式实现。如果 a 和 b 是两个离散变量 n1 和 n2 的函数,则 a 和 b 的二维卷积公式为:
然而,实际上,conv2 计算有限区间的卷积。
【讨论】:
我当然可以,如果我没有找到方法,我一定有。但是我猜我的实现不会优化,所以我正在寻找一个包。 @downvoter:请你解释一下为什么不赞成 @Daniel - 因为当已经有工具可以为您计算卷积时,您提出的解决方案效率相当低。此外,从 OP 问题的上下文来看,他们想使用一个包。我没有对你投反对票,但如果我感觉不好,我会的。以上是关于Python中的2D卷积类似于Matlab的conv2的主要内容,如果未能解决你的问题,请参考以下文章
Tensorflow2 tf.nn.con2d()进行卷积运算及其可视化