convolve python math numpy scipy convolution

python - convolve - Mejorando el rendimiento de Numpy



opencv convolution python (5)

A partir de 2018, parece que el combo SciPy / Numpy se ha acelerado mucho. Esto es lo que vi en mi computadora portátil (Dell Inspiron 13, i5). OpenCV hizo lo mejor pero no tienes control sobre los modos.

>>> img= np.random.rand(1000,1000) >>> kernel = np.ones((3,3), dtype=np.float)/9.0 >>> t1= time.time();dst1 = cv2.filter2D(img,-1,kernel);print(time.time()-t1) 0.0235188007355 >>> t1= time.time();dst2 = signal.correlate(img,kernel,mode=''valid'',method=''fft'');print(time.time()-t1) 0.140458106995 >>> t1= time.time();dst3 = signal.convolve2d(img,kernel,mode=''valid'');print(time.time()-t1) 0.0548939704895 >>> t1= time.time();dst4 = signal.correlate2d(img,kernel,mode=''valid'');print(time.time()-t1) 0.0518119335175 >>> t1= time.time();dst5 = signal.fftconvolve(img,kernel,mode=''valid'');print(time.time()-t1) 0.13204407692

Me gustaría mejorar el rendimiento de la convolución utilizando python, y esperaba obtener alguna información sobre cómo mejorar el rendimiento.

Actualmente estoy usando scipy para realizar la convolución, usando un código parecido al siguiente fragmento de código:

import numpy import scipy import scipy.signal import timeit a=numpy.array ( [ range(1000000) ] ) a.reshape(1000,1000) filt=numpy.array( [ [ 1, 1, 1 ], [1, -8, 1], [1,1,1] ] ) def convolve(): global a, filt scipy.signal.convolve2d ( a, filt, mode="same" ) t=timeit.Timer("convolve()", "from __main__ import convolve") print "%.2f sec/pass" % (10 * t.timeit(number=10)/100)

Estoy procesando datos de imagen, usando escala de grises (valores enteros entre 0 y 255), y actualmente obtengo alrededor de un cuarto de segundo por convolución. Mi pensamiento fue hacer uno de los siguientes:

Use corepy, preferiblemente con algunas optimizaciones. Recompile numpy con icc & ikml. Utilice python-cuda.

Me preguntaba si alguien tenía alguna experiencia con alguno de estos enfoques (qué tipo de ganancia sería típica, y si vale la pena el tiempo), o si alguien está al tanto de una mejor biblioteca para realizar la convolución con Numpy.

¡Gracias!

EDITAR:

Acelere aproximadamente 10x reescribiendo el bucle de python en C sobre Numpy.


Antes de ir a decir C con ctypes, sugiero ejecutar una convolución en C para ver dónde está el límite.
Del mismo modo para CUDA, cython, scipy.weave ...

Se agregó 7feb: convolve33 Los datos de 8 bits con clipping toman ~ 20 ciclos de reloj por punto, 2 ciclos de reloj por acceso mem, en mi PC Mac g4 pcc con gcc 4.2. Su kilometraje puede variar.

Un par de sutilezas:

  • ¿Te importa el recorte correcto a 0..255? np.clip () es lento, cython, etc. no lo sé.
  • Numpy / scipy puede necesitar memoria para temperaturas del tamaño de A (así que mantenga 2 * sizeof (A) <tamaño de caché).
    Si su código C, sin embargo, realiza una actualización in situ, eso es la mitad del mem pero un algoritmo diferente.

Por cierto, google theano convolve => "Una operación de convolución que debería imitar a scipy.signal.convolve2d, pero más rápido! En desarrollo"


El código en scipy para hacer convoluciones en 2d es un poco desordenado y no optimizado. Consulte http://svn.scipy.org/svn/scipy/trunk/scipy/signal/firfilter.c si desea echar un vistazo al funcionamiento de bajo nivel de scipy.

Si todo lo que desea es procesar con un kernel pequeño y constante como el que mostró, una función como esta podría funcionar:

def specialconvolve(a): # sorry, you must pad the input yourself rowconvol = a[1:-1,:] + a[:-2,:] + a[2:,:] colconvol = rowconvol[:,1:-1] + rowconvol[:,:-2] + rowconvol[:,2:] - 9*a[1:-1,1:-1] return colconvol

Esta función aprovecha la capacidad de separación del kernel como lo sugirió DarenW anteriormente, además de aprovechar las rutinas aritméticas numpy más optimizadas. Es más de 1000 veces más rápido que la función convolve2d por mis medidas.


Para el ejemplo particular del kernel 3x3, observaría que

1 1 1 1 -8 1 1 1 1 1 1 1 0 0 0 = 1 1 1 + 0 -9 0 1 1 1 0 0 0

y que el primero de estos es factorable: puede ser convolucionado por convolving (1 1 1) para cada fila, y luego nuevamente para cada columna. Luego resta nueve veces los datos originales. Esto puede o no ser más rápido, dependiendo de si los programadores scipy lo hicieron lo suficientemente inteligente como para hacer esto automáticamente. (No me he registrado en un tiempo.)

Es probable que desee hacer giros más interesantes, donde la factorización puede o no ser posible.


Una optimización típica para la convolución es usar la FFT de su señal. La razón es: la convolución en el espacio real es un producto en el espacio FFT. A menudo es más rápido calcular la FFT, luego el producto y la iFFT del resultado que convolucionar de la manera habitual.