Свертка только по одной оси - PullRequest
14 голосов
/ 08 марта 2011

У меня есть два двумерных массива с одинаковыми размерами первой оси. В Python я хотел бы свернуть две матрицы только вдоль второй оси. Я хотел бы получить C ниже, не вычисляя свертку также и по первой оси.

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]

Есть ли быстрый путь?

Ответы [ 4 ]

10 голосов
/ 06 марта 2014

Вы можете использовать np.apply_along_axis, чтобы применить np.convolve вдоль желаемой оси. Вот пример применения фильтра boxcar к 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)

Это простой способ обобщить многие функции, которые не имеют аргумента axis.

5 голосов
/ 06 августа 2016

np.apply_along_axis на самом деле вам не поможет, потому что вы пытаетесь перебрать два массива.По сути, вам придется использовать цикл, как описано здесь .

Теперь, циклы в порядке, если ваши массивы маленькие, но если N и P большие, то вы, вероятно, захотитевместо этого использовать FFT для свертки.

Однако, вам нужно сначала обнулить массивы, чтобы ваша «полная» свертка имела ожидаемую форму:

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)
4 голосов
/ 10 марта 2011

С помощью ndimage.convolve1d вы можете указать ось ...

3 голосов
/ 08 ноября 2016

для двумерных массивов функция scipy.signal.convolve2d работает быстрее, а scipy.signal.fftconvolve может быть еще быстрее (в зависимости от размеров массивов):

Здесь тот же код с 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

Все ответы одинаковы с небольшими различиями из-за различных используемых методов вычисления (например, fft против прямого умножения, но я не знаю, что использует exaclty convolve2d):

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

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