Calcolo efficiente dell'autocorrelazione mediante FFT


12

Sto cercando di calcolare un'autocorrelazione su una piattaforma in cui l'unica primitiva accelerata che ho a disposizione è la (I) FFT. Sto avendo un problema però.

L'ho prototipato in MATLAB . Sono, tuttavia, leggermente confuso. Ho supposto che funzioni semplicemente come segue (questo è dalla memoria, quindi mi scuso se ho sbagliato leggermente).

 autocorr = ifft( complex( abs( fft( inputData ) ), 0 ) )

Comunque ottengo un risultato diverso da quello che ottengo dall'uso della xcorrfunzione. Ora mi aspetto completamente di non ottenere il lato sinistro della correlazione automatica (in quanto è un riflesso del lato destro e quindi non è necessario comunque). Tuttavia, il problema è che il mio lato destro sembra, di per sé, riflesso intorno alla metà. Il che significa effettivamente che ottengo circa la metà della quantità di dati che mi aspetto.

Quindi sono sicuro di dover fare qualcosa di molto semplice, ma non riesco proprio a capire cosa.


1
Stai attento. A meno che i dati non siano deterministici, in genere possiamo solo stimare la sequenza di autocorrelazione. Esistono due versioni comuni delle stime di autocorrelazione: distorte e imparziali. Risultati non distorti nelle stime di autocorrelazione statisticamente imparziali. Tuttavia, la varianza può essere molto elevata per ritardi di ordine elevato, causando problemi se, ad esempio, si utilizza la stima di autocorrelazione nelle inversioni di matrici. I campioni distorti mostrano un bias statistico ma con minore varianza (ed errore quadratico medio). Entrambi sono statisticamente coerenti. Hai una stima distorta non normalizzata sopra.
Bryan,

Risposte:


16

le pichenettes hanno ragione, ovviamente. La FFT implementa una convoluzione circolare mentre xcorr () si basa su una convoluzione lineare. Inoltre, è necessario quadrare anche il valore assoluto nel dominio della frequenza. Ecco uno snippet di codice che gestisce tutto lo zero padding, shifting e troncamento.

%% Cross correlation through a FFT
n = 1024;
x = randn(n,1);
% cross correlation reference
xref = xcorr(x,x);

%FFT method based on zero padding
fx = fft([x; zeros(n,1)]); % zero pad and FFT
x2 = ifft(fx.*conj(fx)); % abs()^2 and IFFT
% circulate to get the peak in the middle and drop one
% excess zero to get to 2*n-1 samples
x2 = [x2(n+2:end); x2(1:n)];
% calculate the error
d = x2-xref; % difference, this is actually zero
fprintf('Max error = %6.2f\n',max(abs(d)));

Caspita che ha funzionato a meraviglia. Una versione C diretta (Single threaded, no SIMD) del mio pitch tracker è stata eseguita in 0,8 secondi utilizzando il metodo sopra rispetto a una versione basata su primitive di prestazioni Intel che ha funzionato in 0,4 secondi. È stupefacente! Grazie
Goz

7

Il xcorr di Matlab calcola un FFT , dove è la lunghezza dei dati di input (ovvero, l'ingresso è riempito con zero ). Questo evita il problema della circolarità.2N1NN1


3

Per elaborare un po 'di più le risposte precedenti, calcolando l'auto-correlazione di un segnale di lunghezza ottiene un'auto-correlazione (campionata) di dimensione . Bene, in realtà, dovrebbe essere infinito, ma l'auto-correlazione al di fuori di equivale comunque a .N2N1[(N1),N1]0

Ora, desideri utilizzare la trasformata discreta di Fourier (DFT) per calcolarla, e la formula è davvero la DFT inversa della grandezza quadrata della DFT del tuo segnale. Ma pensaci: se lo facciamo al contrario e calcoliamo il DFT dell'autocorrelazione, ti ritroverai con uno spettro di dimensioni , se non vuoi perdere campioni in questo modo! Pertanto, tale spettro deve essere della dimensione , ed è per questo che è necessario azzerare il segnale del dominio del tempo fino a , calcolare il DFT (su punti ) e procedere con esso.2N12N12N12N1

Un altro modo di vedere questo è analizzare cosa succede se si calcola il DFT su punti: questo equivale a sottocampionare la trasformata di Fourier (DTFT) a tempo discreto (frequenza continua). Recuperare l'auto-correlazione, che dovrebbe essere di dimensione , con uno spettro sottocampionato di dimensione porta quindi all'aliasing temporale (di cui parlava la pichenette di circolarità), il che spiega perché hai questo modello simmetrico nella "destra lato "se l'output.N2N1N

In realtà, funziona anche il codice fornito da Hilmar, perché fintanto che si esegue il pad zero fino a una dimensione superiore a (nel suo caso, calcola un FT di dimensione ), si "sovracampiona" il FT e ottieni comunque i tuoi campioni "utili" (gli altri dovrebbero essere s). Quindi, per efficienza, solo da zero pad a , questo è tutto ciò di cui hai bisogno (beh, forse sarebbe meglio zero-pad fino alla potenza successiva di 2 di , se usi gli FFT).N1N2N102 N - 12N12N1

In breve: avresti dovuto farlo (per adattarti al tuo linguaggio di programmazione):

autocorr = ifft( complex( abs(fft(inputData, n=2*N-1))**2, 0 ) )

O in MATLAB:

autocorr = ifft(abs(fft(inputData, 2*N-1)).^2)

0

Il motivo principale per cui l'output desiderato della funzione xcorr non è simile a quello dell'applicazione delle funzioni FFT e IFFT è perché durante l'applicazione di questa funzione ai segnali il risultato finale è contorto circolarmente .

La differenza principale tra Convoluzione lineare e Convoluzione circolare si trova in Convoluzione lineare e circolare .

Il problema può essere risolto Inizialmente zero padding il segnale e troncando output finale del IFFT .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.