2011-03-08 12 views
13

Tengo dos matrices de 2-D con las mismas dimensiones del primer eje. En Python, me gustaría convolucionar las dos matrices solo a lo largo del segundo eje. Me gustaría obtener C debajo sin calcular la convolución a lo largo del primer eje también.Convolución a lo largo de un solo eje

import numpy as np 
import scipy.signal as sg 

M, N, P = 4, 10, 20 
A = np.random.randn(M, N) 
B = np.random.randn(M, P) 

C = sg.convolve(A, B, 'full')[(2*M-1)/2] 

¿Hay una manera rápida?

Respuesta

3

Con ndimage.convolve1d, puede especificar el eje ...

+1

Gracias por señalarlo. El argumento 'pesos', sin embargo, debe ser 1-D. Es 2-D en mi caso. – Paul

+0

@Paul: ¿cuál es el contexto? ¿Cuáles son los pesos, B? – Benjamin

+0

Cada fila en A se está filtrando por la fila correspondiente en B. Podría implementarlo así, solo pensé que podría haber una manera más rápida. A es del orden de 10s de gigabytes en tamaño y yo uso overlap-add. – Paul

8

Usted puede utilizar np.apply_along_axis aplicar np.convolve a lo largo del eje deseado. Aquí está un ejemplo de la aplicación de un filtro de vagón a una matriz 2D:

import numpy as np 

a = np.arange(10) 
a = np.vstack((a,a)).T 

filt = np.ones(3) 

np.apply_along_axis(lambda m: np.convolve(m, filt, mode='full'), axis=0, arr=a) 

Esta es una manera fácil de generalizar muchas funciones que no tienen un argumento axis.

+3

¿Tiene esto alguna aceleración frente a un bucle a lo largo de cada fila? – endolith

+0

@endolith No, no es así. Vea [esto] (http://stackoverflow.com/a/23849233/525169) answer ... – Praveen

+0

'np.apply_along_axis' realmente no se puede usar aquí, ya que el OP quiere iterar sobre dos matrices. La manera de hacerlo es simplemente usar un bucle, como se describe [aquí] (http: // stackoverflow.com/questions/28898858/python-apply-along-axis-of-multiple-arrays). – Praveen

3

np.apply_along_axis realmente no lo ayudará, porque está tratando de iterar sobre dos matrices. Efectivamente, tendría que usar un bucle, como se describe en here.

Ahora, los bucles están bien si sus matrices son pequeñas, pero si N y P son grandes, entonces es probable que desee utilizar FFT para convolver en su lugar.

Sin embargo, es necesario adecuadamente cero plataforma de sus matrices en primer lugar, para que su convolución "lleno" tiene la forma esperada:

M, N, P = 4, 10, 20 
A = np.random.randn(M, N) 
B = np.random.randn(M, P) 

A_ = np.zeros((M, N+P-1), dtype=A.dtype) 
A_[:, :N] = A 
B_ = np.zeros((M, N+P-1), dtype=B.dtype) 
B_[:, :P] = B 

A_fft = np.fft.fft(A_, axis=1) 
B_fft = np.fft.fft(B_, axis=1) 
C_fft = A_fft * B_fft 

C = np.real(np.fft.ifft(C_fft)) 

# Test 
C_test = np.zeros((M, N+P-1)) 
for i in range(M): 
    C_test[i, :] = np.convolve(A[i, :], B[i, :], 'full') 

assert np.allclose(C, C_test) 
2

para matrices 2D, el scipy.signal.convolve2d función es más rápido y scipy .signal.fftconvolve puede ser aún más rápida (dependiendo de las dimensiones de las matrices):

Aquí el mismo código con N = 100000

import time  
import numpy as np 
import scipy.signal as sg 

M, N, P = 10, 100000, 20 
A = np.random.randn(M, N) 
B = np.random.randn(M, P) 

T1 = time.time() 
C = sg.convolve(A, B, 'full') 
print(time.time()-T1) 

T1 = time.time() 
C_2d = sg.convolve2d(A, B, 'full') 
print(time.time()-T1) 

T1 = time.time() 
C_fft = sg.fftconvolve(A, B, 'full') 
print(time.time()-T1) 

>>> 12.3 
>>> 2.1 
>>> 0.6 

respuestas son toda la mismas con ligeras diferencias debido a los diferentes métodos de cálculo utilizados (por ejemplo, FFT vs multiplicación directa, pero yo no sé qué usos exaclty convolve2d):

print(np.max(np.abs(C - C_2d))) 
>>>7.81597009336e-14 

print(np.max(np.abs(C - C_fft))) 
>>>1.84741111298e-13 
Cuestiones relacionadas