2011-01-14 15 views

Odpowiedz

28

scipy udostępnia funkcję korelacji, która działa dobrze przy małych wejściach, a także w przypadku korelacji niekołowej, co oznacza, że ​​sygnał nie będzie zawijany. Zauważ, że w mode='full' wielkość tablicy zwróconej przez signal.correlation jest sumą rozmiarów sygnału wejściowego - 1, więc wartość z argmax jest wyłączona (wielkość sygnału -1 = 20) z tego, co wydaje się oczekiwać.

from scipy import signal, fftpack 
import numpy 
a = numpy.array([0, 1, 2, 3, 4, 3, 2, 1, 0, 1, 2, 3, 4, 3, 2, 1, 0, 0, 0, 0, 0]) 
b = numpy.array([0, 0, 0, 0, 0, 1, 2, 3, 4, 3, 2, 1, 0, 1, 2, 3, 4, 3, 2, 1, 0]) 
numpy.argmax(signal.correlate(a,b)) -> 16 
numpy.argmax(signal.correlate(b,a)) -> 24 

Dwa różne wartości odpowiadają czy zmiany w a lub b.

Jeśli chcesz korelacji kołowej i dużego rozmiaru sygnału, możesz użyć twierdzenia o przekształceniu Fouriera z zastrzeżeniem, że korelacja jest bardzo podobna do splotu, ale nie jest identyczna.

A = fftpack.fft(a) 
B = fftpack.fft(b) 
Ar = -A.conjugate() 
Br = -B.conjugate() 
numpy.argmax(numpy.abs(fftpack.ifft(Ar*B))) -> 4 
numpy.argmax(numpy.abs(fftpack.ifft(A*Br))) -> 17 

znowu dwie wartości odpowiadają czy Twój interpretacji przesunięcie a lub przesunięcie b.

Koniugacja negatywna wynika z przewracania się jednej z funkcji, ale w korelacji nie ma odwracania. Możesz cofnąć przerzucanie, odwracając jeden z sygnałów, a następnie biorąc FFT, lub biorąc FFT sygnału, a następnie biorąc negatywną koniugat. to jest następujące: Ar = -A.conjugate() = fft(a[::-1])

+0

1UP: Nie bardzo obeznany z przetwarzaniem sygnału , ale wygląda na to, że wiesz o co mi chodzi – MattH

+0

Dziękuję za odpowiedź: po raz pierwszy widzę coś, co ma sens. Teraz jeszcze jedno pytanie, w zależności od "znaku" wartości przesunięcia czasowego Odejmuję lub dodaję przesunięcie czasowe Jak uzyskać znak? – Vishal

+3

Czekaj ... dlaczego potrzebujesz negatywu? Nie sądzę, że potrzebujesz negatywu Niech X (t) przekształci X (f). Po odwróceniu czasu, x (-t) przekształca X (-f) .Jeśli x (t) jest prawdziwe, to X (-f) = conj (X (f)). Dlatego jeśli x (t) jest prawdziwe, następnie x (-t) ma tr ansform conj (X (f)). Bez negatywnych. –

9

Jeśli jeden jest przesunięty w czasie przez drugi, zobaczysz szczyt w korelacji. Ponieważ obliczanie korelacji jest kosztowne, lepiej jest użyć FFT. Tak, coś jak to powinno działać:

af = scipy.fft(a) 
bf = scipy.fft(b) 
c = scipy.ifft(af * scipy.conj(bf)) 

time_shift = argmax(abs(c)) 
+1

Próbowałem robić to, co pan sugeruje, dla przypadku, w ręku dała zły wynik. Przykład: >>> a21 array ([0, 1, 2, 3, 4, 3, 2, 1, 0, 1, 2, 3, 4, 3, 2, 1, 0, 0, 0 , 0, 0]) >>> a22 array ([0, 0, 0, 0, 0, 1, 2, 3, 4, 3, 2, 1, 0, 1, 2, 3, 4, 3 , 2, 1, 0]) >>> fa21 = np.fft.fft (a21) >>> fa22 = np.fft.fft (a22) >>> c = np.fft.ifft (fa21 * fa22) >>> time_shift = np.argmax (abs (c)) >>> time_shift Jak widać, rzeczywista zmiana czasu ma 4 punkty, a nie 20. jestem brakuje czegoś tutaj? – Vishal

+1

-1. Niepoprawnie, ponieważ 'c' jest po prostu' a' splecione z 'b', nie jest skorelowane. Odwrócenie czasu naruszy sprawność i nie da pożądanego rezultatu. –

+1

Masz rację, Steve. Napisałem odpowiedź jako przybliżony pomysł. Poprawiłem go, aby odzwierciedlić koniugację. – highBandWidth

2

To zależy od rodzaju sygnału masz (okresowe ...?), Czy oba sygnały mają taką samą amplitudę, a na co precyzja szukasz.

Funkcja korelacji wspomniana przez highBandWidth może rzeczywiście zadziałać. To proste, że powinieneś spróbować.

Inną, bardziej precyzyjną opcją jest ta, której używam do precyzyjnego dopasowania linii spektralnych: modelujesz sygnał "master" za pomocą splajnu i dopasowujesz do niego sygnał przesunięty w czasie (jeśli to konieczne, ewentualnie skalując sygnał) być). Zapewnia to bardzo dokładne przesunięcia czasowe. Zaletą tego podejścia jest to, że nie trzeba badać funkcji korelacji. Możesz na przykład utworzyć splajn łatwo za pomocą interpolate.UnivariateSpline() (z SciPy). SciPy zwraca funkcję, która jest łatwo dopasowana do optimize.leastsq().

+0

Dzięki! Po prostu użyłem optimize.leastsq: Nie miałem pojęcia, że ​​to było możliwe do przesunięcia w czasie; znacznie łatwiejsze niż podejście splotu. Czy wiesz, czy istnieją odniesienia do tego, jak działa optimize.leastsq? Sądziłem, że najmniejsze kwadraty muszą działać z liniowymi kombinacjami podstawowych funkcji wejściowych. –

+1

W [documentation] (http://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.leastsq.html) czytamy, że "leastsq" jest opakowaniem algorytmów lmdif i lmder firmy MINPACK. " Możesz znaleźć więcej informacji w kodzie MINPACK: http://www.netlib.org/minpack/lmdif.f i http://www.netlib.org/minpack/lmder.f. – EOL

6

Ta funkcja jest prawdopodobnie bardziej wydajna w przypadku sygnałów o wartościach rzeczywistych. Wykorzystuje rfft i zero Podkładki wejść do potęgi 2 wystarczająco duże, aby zapewnić liniowy (tzn nieokrągłe) korelacji:

def rfft_xcorr(x, y): 
    M = len(x) + len(y) - 1 
    N = 2 ** int(np.ceil(np.log2(M))) 
    X = np.fft.rfft(x, N) 
    Y = np.fft.rfft(y, N) 
    cxy = np.fft.irfft(X * np.conj(Y)) 
    cxy = np.hstack((cxy[:len(x)], cxy[N-len(y)+1:])) 
    return cxy 

Wartość zwracana jest długość M = len(x) + len(y) - 1 (hacked razem z hstack aby usunąć dodatkowe zera z zaokrąglanie do potęgi 2).Nie ujemne opóźnienia wynoszą cxy[0], cxy[1], ..., cxy[len(x)-1], podczas gdy ujemne opóźnienia wynoszą cxy[-1], cxy[-2], ..., cxy[-len(y)+1].

Aby dopasować sygnał odniesienia, należy obliczyć rfft_xcorr(x, ref) i poszukać szczytu. Na przykład:

def match(x, ref): 
    cxy = rfft_xcorr(x, ref) 
    index = np.argmax(cxy) 
    if index < len(x): 
     return index 
    else: # negative lag 
     return index - len(cxy) 

In [1]: ref = np.array([1,2,3,4,5]) 
In [2]: x = np.hstack(([2,-3,9], 1.5 * ref, [0,3,8])) 
In [3]: match(x, ref) 
Out[3]: 3 
In [4]: x = np.hstack((1.5 * ref, [0,3,8], [2,-3,-9])) 
In [5]: match(x, ref) 
Out[5]: 0 
In [6]: x = np.hstack((1.5 * ref[1:], [0,3,8], [2,-3,-9,1])) 
In [7]: match(x, ref) 
Out[7]: -1 

To nie jest mocny sposób na dopasowanie sygnałów, ale jest to szybkie i łatwe.

1

Oto kolejna opcja:

from scipy import signal, fftpack 

def get_max_correlation(original, match): 
    z = signal.fftconvolve(original, match[::-1]) 
    lags = np.arange(z.size) - (match.size - 1) 
    return (lags[np.argmax(np.abs(z))])