Python中最快的2D卷积或图像过滤器

| 一些用户以numpy或scipy [1、2、3、4]来询问图像卷积的速度或内存消耗。从回答和我使用Numpy的经验来看,与Matlab或IDL相比,我认为这可能是numpy的主要缺点。 到目前为止,没有一个答案能解决总体问题,所以这里是:\“在Python中计算2D卷积最快的方法是什么?\”常见的python模块是公平的游戏:numpy,scipy和PIL(其他? )。为了比较具有挑战性,我想提出以下规则: 输入矩阵分别为2048x2048和32x32。 单精度或双精度浮点都可以接受。 将输入矩阵转换为适当格式所花费的时间不计算在内-仅是卷积步骤。 用输出替换输入矩阵是可以接受的(任何python库都支持吗?) 可以直接对常见C库进行DLL调用-lapack或scalapack PyCUDA就可以了。使用您的自定义GPU硬件是不公平的。
已邀请:
它实际上取决于您要执行的操作...很多时候,您不需要完全通用的(阅读:较慢的)2D卷积...(即,如果滤波器是可分离的,则可以使用两个1D卷积而是...这就是为什么各种
scipy.ndimage.gaussian
scipy.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秒... 与with3比较
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进行卷积可能会更快(尽管目前我似乎还没有提出一个很好的例子……)。
在我的机器上,似乎固定了使用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))
请注意,这可能是圆形卷积,因此对边缘附近区域的处理方式可能不同于其他方式。
我也做了一些实验。我的猜测是SciPy卷积没有使用BLAS库来加速计算。使用BLAS,我能够编写2D卷积,其速度可与MATLAB媲美。它的工作量更大,但最好的办法是重新编码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);

      }
   }
 }
我一直在尝试提高应用程序中的卷积速度,并且一直在使用
signal.correlate
,它刚好比ѭ8slow慢20倍,而我的输入矩阵更小(
27x27 and 5x5
)。截至2018年,这就是我在计算机(Dell Inspiron 13,Core i5)上针对实际问题中指定矩阵观察到的结果。
OpenCV
表现最好,但要注意的是它没有提供“ mode”选项。输入和输出的大小相同。
>>> img= np.random.rand(2048,2048)
>>> kernel = np.ones((32,32), dtype=np.float)
>>> t1= time.time();dst1 = cv2.filter2D(img,-1,kernel);print(time.time()-t1)
0.208490133286
>>> t1= time.time();dst2 = signal.correlate(img,kernel,mode=\'valid\',method=\'fft\');print(time.time()-t1)
0.582989931107
>>> t1= time.time();dst3 = signal.convolve2d(img,kernel,mode=\'valid\');print(time.time()-t1)
11.2672450542
>>> t1= time.time();dst4 = signal.correlate2d(img,kernel,mode=\'valid\');print(time.time()-t1)
11.2443971634
>>> t1= time.time();dst5 = signal.fftconvolve(img,kernel,mode=\'valid\');print(time.time()-t1)
0.581533193588
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()

要回复问题请先登录注册