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.convolve2dscipy.signal.convolvescipy.signal.fftconvolvescipy.ndimage.convolve 都会以不同的方式处理 2D 卷积(最后三个是 N-d)。

scipy.signal.fftconvolve 在 fft 域中进行卷积(这是一个简单的乘法)。这在许多情况下要快得多,但与离散情况相比,可能会导致边缘效应的差异非常小,并且您的数据将通过这种特定实现强制转换为浮点数。此外,在将小数组与大得多的数组进行卷积时,还会使用不必要的内存。总而言之,基于 fft 的方法可以显着加快,但在一些常见用例中,scipy.signal.fftconvolve 并不是一个理想的解决方案。

scipy.signal.convolve2dscipy.signal.convolvescipy.ndimage.convolve 都使用用 C 实现的离散卷积,但是它们以不同的方式实现它。

scipy.ndimage.convolve 保持相同的数据类型,并让您控制输出的位置以最大限度地减少内存使用。如果您正在卷积uint8(例如图像数据),它通常是最好的选择。输出将始终与第一个输入数组的形状相同,这对图像有意义,但可能不适用于更一般的卷积。 ndimage.convolve 让您可以通过mode kwarg(其功能与scipy.signalmode 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 所提到的,可能是您的最佳选择。它针对将一维数组与二维数组进行卷积的特定用例进行了优化。希望有帮助! 另外,如果您不想用零填充Ascipy.signal.convolve2d 可能是更好的选择。默认情况下,返回的数组的形状就像 matlab 的 conv2。 (我最初忘记在我的答案中包含该信息)但是,如果您正在使用图像,我会假设您希望返回的数组与原始数组的形状相同。在这种情况下,ndimage 是您最好的选择。 Ftr:我在尝试使用 scipy.signal.convolve2dscipy.ndimage.convolve 时被咬了,因为我的权重不是 2D... 足够 -.- Cf。 np.array([0.707, 0.707]).shapenp.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()进行卷积运算及其可视化

tensorflow中的1维卷积--conv1d用法

python中的一维卷积conv1d和二维卷积conv2d

python实现类似于Matlab中的magic函数

matlab中关于卷积有一个这样的例子 conv([1 1 1],[1 1 1]) 运行后的结果为 1 2 3 2 1

python里面类似于Matlab中的fminunc方法或minfunc方法叫啥名字,怎么调用