Python中速度最快的2D卷积或图像滤镜

时间:2011-04-19 02:09:08

标签: python optimization numpy python-imaging-library scipy

有几个用户询问了numpy或scipy [1234]中图像卷积的速度或内存消耗情况。从回答和我使用Numpy的经验来看,我认为与Matlab或IDL相比,这可能是numpy的一个主要缺点。

到目前为止,答案都没有解决整个问题,所以这里是:“在Python中计算2D卷积的最快方法是什么?”常见的python模块是公平游戏:numpy,scipy和PIL(其他?)。为了进行具有挑战性的比较,我想提出以下规则:

  1. 输入矩阵分别为2048x2048和32x32。
  2. 单精度或双精度浮点都可以接受。
  3. 将输入矩阵转换为适当格式所花费的时间不计算 - 只是卷积步骤。
  4. 用你的输出替换输入矩阵是可以接受的(任何python库支持吗?)
  5. 对常见C库的直接DLL调用没问题 - lapack或scalapack
  6. PyCUDA就出来了。使用自定义GPU硬件是不公平的。

5 个答案:

答案 0 :(得分:11)

在我的机器上,使用FFT的手工制作的循环卷积似乎禁食:

import numpy
x = numpy.random.random((2048, 2048)).astype(numpy.float32)
y = numpy.random.random((32, 32)).astype(numpy.float32)
z = numpy.fft.irfft2(numpy.fft.rfft2(x) * numpy.fft.rfft2(y, x.shape))

请注意,这可能会以不同的方式处理靠近边缘的区域,因为它是循环卷积。

答案 1 :(得分:9)

这实际上取决于你想要做什么......很多时候,你不需要完全通用的(读取:慢速)2D卷积...(即如果过滤器是可分离的,你使用两个相反,1D卷积...这就是为什么各种scipy.ndimage.gaussianscipy.ndimage.uniform比通用nD卷数实现的速度快得多。)

无论如何,作为比较点:

t = timeit.timeit(stmt='ndimage.convolve(x, y, output=x)', number=1,
setup="""
import numpy as np
from scipy import ndimage
x = np.random.random((2048, 2048)).astype(np.float32)
y = np.random.random((32, 32)).astype(np.float32)
""")
print t

我的机器需要6.9秒......

将此与fftconvolve

进行比较
t = timeit.timeit(stmt="signal.fftconvolve(x, y, mode='same')", number=1,
setup="""
import numpy as np
from scipy import signal
x = np.random.random((2048, 2048)).astype(np.float32)
y = np.random.random((32, 32)).astype(np.float32)
""")
print t

大约需要10.8秒。但是,使用不同的输入大小,使用fft进行卷积可以相当快(虽然我似乎无法想出一个很好的例子,目前......)。

答案 2 :(得分:4)

我也做了一些实验。我的猜测是SciPy卷积不使用BLAS库来加速计算。使用BLAS,我能够编写一个速度与MATLAB相当的2D卷积。这是更多的工作,但最好的办法是用C ++重新编码卷积。

这是循环的紧凑部分(请原谅基于奇怪()的数组引用,它是我的MATLAB数组的便利类)关键部分是你不迭代图像,迭代过滤器让BLAS迭代图像,因为通常图像比过滤器大得多。

for(int n = 0; n < filt.numCols; n++)
  {
    for(int m = 0; m < filt.numRows; m++)
    {
      const double filt_val = filt(filt.numRows-1-m,filt.numCols-1-n);
      for (int i =0; i < diffN; i++)
      {
        double *out_ptr = &outImage(0,i);
        const double *im_ptr = &image(m,i+n);
        cblas_daxpy(diffM,filt_val,im_ptr, 1, out_ptr,1);

      }
   }
 }

答案 3 :(得分:0)

我一直在努力提高应用程序中的卷积速度,而且我一直在使用signal.correlate,它比signal.correlate2d慢了约20倍,我的输入矩阵更小({{1} })。截至2018年,这是我在我的机器(Dell Inspiron 13,Core i5)上观察到的实际问题中指定矩阵的内容。

27x27 and 5x5做得最好,但需要注意的是,它没有给出&#34;模式&#34;选项。输入和输出大小相同。

OpenCV

答案 4 :(得分:0)

Scipy具有功能fftconvolve,可用于一维和二维信号。

from scipy import signal
from scipy import misc
import numpy as np
import matplotlib.pyplot as plt

face = misc.face(gray=True)
kernel = np.outer(signal.gaussian(70, 8), signal.gaussian(70, 8))
blurred = signal.fftconvolve(face, kernel, mode='same')

fig, (ax_orig, ax_kernel, ax_blurred) = plt.subplots(3, 1, figsize=(6, 15))
ax_orig.imshow(face, cmap='gray')
ax_orig.set_title('Original')
ax_orig.set_axis_off()
ax_kernel.imshow(kernel, cmap='gray')
ax_kernel.set_title('Gaussian kernel')
ax_kernel.set_axis_off()
ax_blurred.imshow(blurred, cmap='gray')
ax_blurred.set_title('Blurred')
ax_blurred.set_axis_off()
fig.show()