2011-04-19 104 views
19

几位用户询问了numpy或scipy中图像卷积的速度或内存消耗[1,2,3,4]。从响应和使用Numpy的经验来看,我相信这可能是Matlab或IDL相比numpy的一个主要缺点。Python中最快的2D卷积或图像过滤器

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

  1. 输入矩阵分别为2048x2048和32x32。
  2. 单精度浮点或双精度浮点均可接受。
  3. 将输入矩阵转换为适当格式花费的时间不计算 - 仅仅是卷积步骤。
  4. 更换你的输出输入矩阵是可以接受的(没有任何Python库的支持吗?)
  5. 直接DLL调用普通C库是好的 - LAPACK的ScaLAPACK或
  6. PyCUDA是正确的。使用您的定制GPU硬件是不公平的。
+0

“和您的输出更换输入矩阵是可以接受的(没有任何Python库的支持吗?)”对于它的价值,最numpy的和SciPy的功能做... – 2011-04-19 02:23:44

+0

我没有看到任何提及在convolve的文档中:http://docs.scipy.org/doc/numpy/reference/generated/numpy.convolve.html我错过了什么吗? – 2011-04-19 15:49:26

+1

它不支持numpy的convolve,但它是'scipy.ndimage.convolve'。 http://www.scipy.org/SciPyPackages/Ndimage此外,大多数numpy函数(例如'sqrt','mul','add')都会带出一个out参数。你可以使用'np.sqrt(x,x)'来就地使用sqrt。 – 2011-04-19 16:45:24

回答

9

这真的取决于你想做什么......很多时候,你不需要完全通用的(读取:较慢)二维卷积...(即如果过滤器是可分离的,你用两个一维卷积,而不是...这就是为什么各种scipy.ndimage.gaussianscipy.ndimage.uniform,比作为一个通用的第二卷积实现同样的事情快得多)

无论如何,作为一个比较点:

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进行卷积可能会快得多(尽管目前我似乎无法提供一个很好的示例)。

+0

谢谢乔。这是我已经使用的convolve函数的一个重大改进(我认为它只是numpy.convolve)。它消耗了大量的RAM并且运行缓慢(可能因此)。我希望获得更多参与,但也许我太乐观了。 – 2011-04-20 03:34:53

+2

对于那些有兴趣的人。我做了比原始文章晚5年的比较(OS X 10.10 Macbook Air)。 'signal.fftconvolve'需要约**。9秒**! 'ndimage.convolve'大约需要** 8秒**。显然已经在引擎盖下的'signal.fftconvolve'上做了巨大的改进。 – nmante 2016-03-11 09:15:26

10

在我的机器,采用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)) 

注意,这可能会区别对待靠近边缘的区域比其他的方式,因为它是一个循环卷积。

4

我也对此做了一些实验。我的猜测是SciPy卷积不使用BLAS库来加速计算。使用BLAS,我能够编写速度与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); 

     } 
    } 
}