La trasformata di fourier
La serie di Fourier consente di lavorare con funzioni periodiche, rappresentandole come coefficienti di una serie, ovvero come ampiezze di composizioni di seni e coseni di pari frequenza (notare che questo coincide alla soma di coseni di date ampiezza e fase).
La possibilità di manipolare una più ampia famiglia di funzioni chiaramente estende questo strumento a molte più applicazioni, la trasformata di Fourier elimina l'assunzione di periodicità. Per ottenere questo risultato è sufficiente far tendere il periodo T all'infinito. (Per i dettagli si veda il notebook Mathematica allegato.)
Data una funzione f(t) nel dominio del tempo, la sua rappresentazione nel dominio della frequenza diventa:
f(w) = \int_{-\infty}^{+\infty} f(t) e^{-i w t} dt
viceversa:
f(t) = 1/(2 pi) \int_{-\infty}^{+\infty} f(w) e^{i w t} dw
Molte proprietà sono ereditate dalla serie di Fourier, ad esempio la trasformata di Fourier di funzioni reali è simmetrica (pari).
Contents
Trasformata di funzioni discretizzate
Intuitivamente, se una funzione viene discretizzata (ammettiamo ad esempio un passo di discretizzazione = 1), l'integrale da valutare per la trasformata di Fourier torna ad essere una serie, il risultato è una trasformata periodica:
% Una funzione di esempio non periodica: t = -10:0.01:10; f_t = 1./(1+t.^2); % Discretizzazione: N = 50; f_tq = f_t; for ind=1:numel(f_t); f_tq(ind)=f_t(N*floor(ind/N)+1); end % La trasformata di Fourier (discreta, vedremo): f_w = fft(f_t); f_wq = fft(f_tq); % Notiamo che la periodicità esiste, l'utilizzo della trasformata discreta % invece che continua ha prodotto una sorta di "attenuazione": figure; subplot(221); plot(t,f_t); title('1/{1+t^2}'); subplot(222); plot(abs(f_w(100:300))); title('DFT(1/{1+t^2})'); subplot(223); plot(t,f_tq); title('Q_{50}(1/{1+t^2})'); subplot(224); plot(abs(f_wq(100:300))); title('DFT(Q_{50}(1/{1+t^2}))');
La convoluzione:
Immaginiamo di avere un segnale f(t) rappresentato in frequenza come f(w), immaginiamo di voler "filtrare" determinate frequenze, ovvero rimuoverle moltiplicando f(w) per una funzione h(w) = 0 per le frequenze da eliminare e 1 per le altre. Indipendentemente dall'utilità del filtro h ci interessa salere cosa avviene nel dominio spaziale, ovvero quale sia l'antitrasformata di f(w)h(w):
1/(2 pi) \int_{-\infty}^{+\infty} f(w)h(w) e^{i w t} dw = 1/(2 pi) \int_{-\infty}^{+\infty} [\int_{-\infty}^{+\infty} f(tau) e^{-i w tau} h(w) e^{i w t} dtau] dw = \int_{-\infty}^{+\infty} f(tau) [1/(2 pi)\int_{-\infty}^{+\infty} e^{-i w (t-tau)} h(w) dw] dtau = \int_{-\infty}^{+\infty} f(tau) h(t-tau) dtau = f(t)*h(t)
Chiamiamo questo integrale "di convoluzione", esso permette di applicare nel dominio del tempo un filtro lineare a una funzione. Si osservi che tramite un cambio di variabile si dimostra:
f(t)*H(t) = \int_{-\infty}^{+\infty} f(tau) h(t-tau) dtau = \int_{-\infty}^{+\infty} h(tau) f(t-tau) dtau = h(t)*f(t)
Inoltre, si vede che applicando il filtro h alla funzione delta di dirac delta(t)=1 sse t=0, 0 altrimenti, si ottiene:
\int_{-\infty}^{+\infty} delta(tau) h(t-tau) dtau = h(t)
per questo motivo h(t) si dice "risposta all'impulso".
% Il filtro: h_t = zeros(size(f_t)); from = round(numel(h_t)/2); h_t(from-2:from+2) = [-1 0 3 0 -1]; h_w = fftshift(fft(fftshift(h_t))); % Applichiamolo: fh_t = conv(h_t,f_t,'same'); fh_w = h_w.*fftshift(f_w); fh_t2 = ifft(fftshift(fh_w)); % I risultati sono identici: figure; subplot(211); plot(fh_t); title('fh_t'); subplot(212); plot(abs(fh_t2)); title('fh_t2');
Filtri ideali.. ma non particolarmente!
Proviamo ad utilizzare un filtro ideale in frequenza (passa-basso) per attenuare le alte frequenze presenti nello spigolo della funzione rect:
% La funzione: f_t = zeros(1,1000); f_t(400:600) = 1; % Il filtro: h_w = zeros(size(f_t)); N = 50; from = floor(numel(h_w)/2-N/2); h_w(from:from+N-1) = 1; % Applichiamolo: f_w = fftshift(fft(f_t)); fh_w = f_w.*h_w; fh_t = ifft(fftshift(fh_w)); % Vediamo: figure; subplot(211); plot(f_t); subplot(212); plot(real(fh_t));
Le alte frequenze rimosse hanno lasciato traccia non solo in prossimità delle discontinuità, l'influenza di un filtro lineare localizzato in frequenza non è localizzato nel dominio temporale.
Vediamo lo stesso effetto nelle immagini:
% Carico una immagine: img = imread('cameraman.tif'); % La trasformo: imgf = fftshift(fft2(img)); % La filtro con un filtro passa-basso ideale: H1 = false(size(img)); H2 = H1; [X,Y] = meshgrid(-size(H1,1)/2:size(H1,1)/2-1,-size(H1,2)/2:size(H1,2)/2-1); H1(sqrt(X.^2+Y.^2) < size(H1,1)/4) = true; H2(sqrt(X.^2+Y.^2) < size(H2,1)/8) = true; imgf1 = imgf.*H1; imgf2 = imgf.*H2; % Vediamo prima e dopo in frequenza: figure; subplot(131); imshowscale(log(abs(imgf))); title('Prima del filtro'); subplot(132); imshowscale(log(abs(imgf1)+eps)); title('Dopo il filtro 1'); subplot(133); imshowscale(log(abs(imgf2)+eps)); title('Dopo il filtro 2'); % Antitrasformata e visualizzazione: img1 = ifft2(fftshift(imgf1)); img2 = ifft2(fftshift(imgf2)); figure; subplot(131); imshowscale(img); title('Prima del filtro'); subplot(132); imshowscale(abs(img1)); title('Dopo il filtro 1'); subplot(133); imshowscale(abs(img2)); title('Dopo il filtro 2');
Si può cercare di creare filtri localizzati sia nel tempo che nelle frequenza tassellando il piano tempo-frequenza, in quel caso si parla di wavelet.
Trasformata di Fourier discreta (DFT)
In elaborazione dei segnali e delle immagini non abbiamo a che fare con segnali continui ma a campionamento discreto, pertanto è importante definire formalmente uno strumento simile alla trasformata di Fourier per segnali continui che operi su segnali discreti. La definizione della DFT proviene direttamente da quella della controparte continua considerando la funzione analizzata campionata, sorvolando i dettagli algebrici, considerando che l'integrale della trasformata di Fourier discretizzando diviene una serie bilatera, otteniamo:
f(n) = \sum_{t=-\infty}^{+\infty} f(t) e^{-i 2 pi n/T t}
La cui antitrasformata diventa:
f(t) = 1/(2 pi) \sum_{n=-\infty}^{+\infty} f(n) e^{i 2 pi n/T t}
Se il segnale a disposizione è nullo per n<0 e n>T (ovvero di supporto finito nel dominio del tempo) la DFT diviene:
f(n) = \sum_{t=0}^(T-1) f(t) e^{-i 2 pi n/T t}
e diviene algoritmicamente calcolabile:
% Inizializzazione: f_t = zeros(1,513); f_t(253:261) = 1; % Trasformata "vanilla" della funzione rettangolo: f_w = zeros(size(f_t)); T = numel(f_t); for n=1:T f_w(n) = 0; for t=1:T % Manteniamo l'origine nel centro: tc = (t-floor(T/2)-1); f_w(n) = f_w(n) + f_t(t)*exp(-1i*2*pi*n/T*tc); end end % Facciamo lo shift "a mano": from = floor(T/2); f_w = [f_w(from:end),f_w(1:from-1)]; % Vediamone il risultato... ovvero la funzione sinc: figure; subplot(211); plot(f_t); title('Funzione rect'); subplot(212); plot(real(f_w)); title('Funzione sinc come trasformata (reale) della funzione rect'); % La parte immaginaria è nulla a meno della precisione macchina: fprintf('Massimo valore immaginario (in valore assoluto): %g\n',max(abs(imag(f_w))));
Massimo valore immaginario (in valore assoluto): 6.66134e-016
FFT
Runge-Konig nel 1924 e Cooley-Tukey nel 1965) proposero degli algoritmi di calcolo della DFT con complessità O(N log(N)) basati sula metodologia del divide-et-impera, l'idea è di ricondurre la DFT di un segnale discreto finito di T campioni nella composizione di due DFT calcolate su due sottoinsiemi di dimensione T/2.
Si veda fftNostra per l'algoritmo, qui un esempio di utilizzo:
% Una funzione con una potenza di 2 campioni: % f_t = [ones(1,128),zeros(1,128)]; f_t = [ones(1,32),zeros(1,256-32)]; % Sua trasformata: f_w = fftNostra(f_t); % Mostriamo abs e angle: figure; subplot(211); plot(abs(f_w)); title('Modulo'); subplot(212); plot(angle(f_w)); title('Fase');
Separabilità in 2D
In 2 dimensioni la DFT assume la forma:
f(n,m) = \sum_{y=0}^(T-1)\sum_{x=0}^(T-1) f(x,y) e^{-i 2 pi (n x + m y)/T}
che separando gli esponenziali complessi diventa:
f(n,m) = \sum_{y=0}^(T-1) [\sum_{x=0}^(T-1) f(x,y) e^{-i 2 pi n/T x}] e^{-i 2 pi m/T y}
ovvero f(n,m) = FFT_y(FFT_x(f(x,y)) con FFT_x e FFT_y trasformate sulle x e sulle y rispettivamente.
Proviamo utilizzando la nostra fft su una immagine:
% La "funzione" immagine: f_xy = checkerboard(16); f_nm = f_xy; % Trasformo le colonne: for x=1:size(f_xy,1) f_nm(:,x) = fftNostra(f_nm(:,x)); end % Trasformo le righe: for y=1:size(f_xy,2) f_nm(y,:) = fftNostra(f_nm(y,:)); end % FFT shift "a mano": f_nm = [f_nm(:,size(f_nm,2)/2+1:end),f_nm(:,1:size(f_nm,2)/2)]; f_nm = [f_nm(size(f_nm,1)/2+1:end,:);f_nm(1:size(f_nm,1)/2,:)]; % Vediamo: figure; subplot(121); imshow(f_xy); subplot(122); imshowscale(abs(f_nm));