%% 2D Recursive Gaussian Filter implementation % % %
%
  • % Sorgente di questo documento

    %
  • % File Matlab per la sola funzione GaussianR.m

    %
  • % % % L'algoritmo si basa sulla trasformata Z di una approssimazione della % trasformata di Fourier della funzione gaussiana continua. % La trasformata produce due equazioni alle differenze che % applicate al segnale in ingresso calcolano il segnale filtrato. % % Gli esperimenti eseguiti mostrano che la tecnica pruduce risultati molto % vicini all'algoritmo di convoluzione standard tramite FFT. % Dal punto di vista delle prestazioni l'algoritmo risulta piu' lento della % convoluzione tramite FFT tuttavia la sua implementazione è decisamente % semplice, al contrario di una FFT ottimizzata. % % Di seguito viene illustrata una implementazione con Matlab(r). I % risultati vengono poi confrontati con una convoluzione con FTT % % *Copyright (c) 2010 PkLab.net * % $Revision: 1.0 $ $Date: 2010/01/7 $ %% Un po di teoria sul filtro gaussiano ricorsivo % L'equazione per la gaussiana vale: % % $$G(x)=\frac{1}{\sigma \, \sqrt[]{2 \pi }}e^{-\frac{x^2}{2\sigma^2}}$$ % % La gaussiana nelle 2 dimensioni vale % % $$G(x,y)=\frac{1}{2\pi \sigma^2}e^{-\frac{x^2+y^2}{2\sigma^2}}$$ % % Dato che la gaussiana è una funzione separabile la sua versione 1d puo' % essere applicata ripetutamente a tutte le dimensioni del segnale da % filtrare. % % $$G(x,y)=G(x)G(y)$$ % % Data una immagine |I|, per ottenere l'immagine filtrata |Ig| applicare % il filtro Gaussiano 1d a cascata sulle due dimensioni dell'immagine % % $$I_{g}(c,r) = I(c,r)\otimes G(x,y) % = I(c,r)\otimes G(x)\otimes G(y) % = I(c,r)\otimes G(x)\otimes G^T(x) % $$ % % L'algoritmo ricorsivo approssima la funzione esponenziale: % % $$ g(t)=\frac{1}{\, \sqrt[]{2 \pi }}e^{-\frac{t^2}{2}} $$ % % con la seguente funzione polinomiale: % % $$a(t)=\frac{1}{a_{0}+a_{2}t^2+a_{4}t^4+a_{6}t^6} $$ % % $$ % \begin{tabular}{r r} % $a_{0}$=2.490895 & $a_{2}$=1.466003\\ % $a_{4}$=-0.024393 & $a_{6}$=0.178257 % \end{tabular} % $$ % % Questa approssimazione introduce un errore _epsilon_ facilmente calcolabile: % % $$ g(t)-a(t) =\epsilon (t) \; \Rightarrow \; max(\varepsilon)=\pm % 0.002531 = \pm 0.63\%g(t=0)$$ % clear; T = -5:0.01:5; % Pure exponential Gaussian Gt = exp(-T.^2/2) / sqrt(2*pi); % Polinomial Gaussian a0 = 2.490895; a2 = 1.466003; a4 = -0.024393; a6 = 0.178257; At = 1./(a0 + a2*T.^2 + a4*T.^4 + a6*T.^6); % Approsimation error rate relative to maximun G(t=0) Et = 100*(Gt-At)*sqrt(2*pi); % Locate max and min error maxe=find(Et==max(Et)); mine=find(Et==min(Et)); iptsetpref('ImshowBorder','loose'); iptsetpref('ImshowInitialMagnification','fit'); iptsetpref('ImshowAxesVisible','on'); win = 340; h = 1; %plot results h=figure(h); plot(T,Gt,'b'); hold on; plot(T,At,'m'); plot(T,Et,'r') stem(T(maxe),Et(maxe),'--');text(T(maxe)+.2,Et(maxe),'Max \epsilon [%]'); stem(T(mine),Et(mine),'--');text(T(mine)+.2,Et(mine),'Min \epsilon [%]'); legend('g(t) \it Pure Gaussian ','a(t) \it Approsimate Gaussian','\epsilon(t) \it Relative Error %'); grid; hold off; %% % Come si nota la funzione polinomiale approssimata è molto vicina alla funzione % esponenziale. L'errore di approssimazione e' circa 0.6% rapportato al % valore massimo per l'esponenziale. Relativamente all'andamento dell'errore, % i valori negativi corrispondono ad una leggera amplificazione della % funzione approssimata rispetto alla funzione esponenziale. Allo stesso % modo i valori positivi indicano una leggera riduzione. % % Particolarmente interessante e' l'abbinamento amplificazione sulla cuspide % seguita e preceduta da una da riduzione. Ciò produce un aumento del % contrasto in corrispondenza degli edge in una immagine %% Algoritmo per il filtro gaussiano ricorsivo % Dalla approssimazione polinomiale sopra descritta, con opportuni passaggi % e trasformate si ottengono le due equazioni alle differenze: % % *Equazione alle differenze in avanti* % % $$W[n] = B\,I[n] + (b_{1}W[n-1] + b_{2}W[n-2] + % b_{3}W[n-3])/b0$$ % % *Equazione alle differenze all'indietro* % % $$G[n] = B\,W[n] + (b_{1}G[n+1] + b_{2}G[n+2] + b_{3} % G[n+3])/b0$$ % % E' introdotto il parametro |q| funzione del parametro |sigma| della % gaussiana. I parametri |B,b0,b1,b2,b3| sono funzione di |q|. % L'algoritmo è molto semplice % % # Calcolare il parametro |q| % # Calcolare i coefficienti del filtro gaussiano |b0,b1,b2,b3,B| % # Sul segnale, applicare l'equazione alle differenze in avanti % # Sul risultato al passo precedente, applicare l'equazione alle differenze all'indietro % # Eventualmente scalare il risultato al passo precedente per il fattore di amplificazione della gaussiana. % % *Nota:L'algoritmo è definito solo per |sigma>=0.5|* % % Inquanto approssimazione, l'algoritmo è soggetto ad un errore che decresce % con l'aumentare del parametro |sigma| scelto. Per |sigma=5| vale circa % |0.5E-02|. A tal proposito è bene ricordare che tutti gli algoritmi per % il filtro gaussiano sono soggetti ad errore di approssimazione, % normalmente dipendente dal parametro |sigma| % % <> % % Per maggiori dettagli consultare [1] % %% Costruzione immagine in ingresso % sigma = 2.6; step=49; thin=1; num = 5; step=step+thin; dim = (num*step)+thin; Ig = ones(dim,dim); for g=1:step:dim Ig(g:g+thin,:) = 0; Ig(:,g:g+thin) = 0; end I = im2double(Ig); % conversione immagine in double h=figure(h+1);set(h,'Position',[100 100 win win]); imshow(Ig),title('Input Image'); %% % La conversione in double viene eseguita per applicare il metodo FFT %% Calcolo del parametro q % Il parametro |q| è funzione di |sigma| ed è definito come segue: % % $$\mathbf{if}(\sigma \geq 2.5) \;\mathbf{then} \;q = 0.98711 \sigma - 0.96330$$ % % $$\mathbf{if}(\sigma \geq 0.5) \;\mathbf{then} \;q = 3.97156 - 4.14554 \sqrt{1 - 0.26891 \sigma}$$ % S=0.5:0.01:8; Q = S; for n=1:length(S) if (S(n) >= 2.5) Q(n) = 0.98711 * S(n) - 0.96330; elseif (S(n) >= 0.5) Q(n) = 3.97156 - 4.14554 * sqrt(1 - 0.26891 * S(n)); end; end h=figure(h+1);set(h,'Position',[100 100 win win]); plot(S,Q);grid;xlabel('sigma');ylabel('q');title('q = f (sigma)'); %% disp(sprintf('Sigma = %3.2f',sigma)); if (sigma >= 2.5) q = 0.98711 * sigma - 0.96330; elseif (sigma >= 0.5) q = 3.97156 - 4.14554 * sqrt(1 - 0.26891 * sigma); else error('Gaussian sigma must be greater or equal than 0.5'); end; %% Calcolare i coefficienti del filtro gaussiano % Il parametro |q| ed i coefficienti sono stati derminati con la trasformata Z % b0 = 1.57825 + (2.44413 * q) + (1.4281 * q^2) + (0.422205 * q^3); b1 = (2.44413 * q) + (2.85619 * q^2) + (1.26661 * q^3); b2 = -(1.4281 * q^2) - (1.26661 * q^3); b3 = 0.422205 * q^3; B = 1 - ( ( b1 + b2 + b3 )/ b0 ); %% % Per migliorare l'efficienza del calcolo, i coefficienti vengono scalati % per il fattore b0 % B1 = b1/b0; B2 = b2/b0; B3 = b3/b0; %% Risoluzione dei bordi % Ogni convoluzione pone il problema della gestione dei bordi. % Per ogni pixel, il calcolo del filtro ricorsvivo richiede la presenza % di almeno 3 pixel precedenti e 3 pixel successivi. % Questo implica che le prime 3 % colonne (o righe) non possono essere calcolate. Per risolvere questo % problema l'immagine viene allargata di una quantità di pixel sufficiente % per applicare l'algoritmo, ovvero 3 pixel. % % Se nell'intorno dei bordi, immagine non è interessante ai fini delle % elaborazioni successive(come spesso accade), l'allargamento si può % evitare, considerato che per la gaussiana ricorsiva, riguarda 3 pixel, % indipendentemente dal valore di |sigma| % % Nel caso di convoluzione con kernel gaussiano standard, % i pixel interessati sono normalmente più numerosi e comunque in numero % dipendente da |sigma|. Ad esempio per un % |sigma=2.6| il kernel per la convoluzione gaussiana ha dimensioni 9x9 % In questi casi è consigliabile allargare l'immagine della quantità % pari alla metà del kernel. tic % start performace counter useborder = true; % if (useborder) border=3; G = padarray(I,[border border],'replicate','both'); else G = I; end [R C] = size(G); W = G; %preallocate matrix %% Applico equazione alle differenze in avanti sulle colonne % Una immagine è un segnale a 2 dimensioni(righe e colonne). Utilizzando la % proprietà della gaussiana di essere separabile, l'algoritmo viene % applicato a cascata prima lungo una direzione e sul risultato viene % riapplicato lungo l'altra direzione. % % Ogni riga dell'immagine viene considerata come un singolo vettore i cui % elmenti sono i pixel delle colonne per la data riga. Dunque la riga è % costante mentre la colonna e variabile. % % $$W[r,c] = B\,I[r,c] + (b_{1}w[r,c-1] + b_{2}w[r,c-2] + % b_{3}w[r,c-3])/b0$$ % % ovvero % % $$W[r,c] = B\,I[r,c] + B_{1}w[r,c-1] + B_{2}w[r,c-2] + B_{3}w[r,c-3]$$ % % *NOTA*: nel codice seguente l'immagine la variabile |G| contiene % l'immagine |I| precedentemente allargata per risolvere i bordi. for r = 1:R W(r,1) = B * G(r,1); W(r,2) = B * G(r,2) + B1 * W(r,1); W(r,3) = B * G(r,3) + B1 * W(r,2) + B2 * W(r,1); for c = 4:C W(r,c) = B * G(r,c) + ... B1 * W(r,c-1) + B2 * W(r,c-2) + B3 * W(r,c-3); end end %% Applico equazione alle differenze all'indietro sulle colonne % Equazione all'indietro viene applicata dall'ultimo elemento verso il % primo % % $$G[r,c] = B\,W[r,c] + (b_{1}G[r,c+1] + b_{2}G[r,c+2] + b_{3} % G[r,c+3])/b0$$ % % ovvero % % $$G[r,c] = B\,W[r,c] + B_{1}G[r,c+1] + B_{2}G[r,c+2] + B_{3} G[r,c+3]$$ % for r = 1:R G(r,C) = B * W(r,C); G(r,C-1) = B * W(r,C-1) + B1 * G(r,C); G(r,C-2) = B * W(r,C-2) + B1 * G(r,C-1) + B2 * G(r,C); % dall'ultima colonna verso la prima for c = C-3:-1:1 G(r,c) = B * W(r,c) + ... B1 * G(r,c+1) + B2 * G(r,c+2) + B3 * G(r,c+3); end; end; %% % A questo punto |G| contiene l'immagine filtrata lungo la direzione delle % colonne % rtime = toc; h=figure(h+1);set(h,'Position',[h*100 100 win win]); imshow(G);title('Gaussian applicato alle colonne'); tic %% Applico equazione alle differenze in avanti sulle righe % Se al risultato |G| applichiamo i due passi precedenti utilizzando la % riga come variabile e la colonna come costante otterremo l'immagine % filtrata nelle due direzioni. % L'equazione in avanti sulle righe diventa la seguente % % $$W[r,c] = B\,G[r,c] + B_{1}W[r-1,c] + B_{2}W[r-2,c] + B_{3}W[r-3,c]$$ % % *NOTA*: nel codice seguente la variabile |G| contiene % l'immagine allargata e filtrata lungo le colonne % for c=1:C W(1,c) = B * G(1,c); W(2,c) = B * G(2,c) + B1 * W(1,c); W(3,c) = B * G(3,c) + B1 * W(2,c) + B2 * W(1,c); for r=4:R W(r,c) = B * G(r,c) + ... B1 * W(r-1,c) + B2 * W(r-2,c) + B3 * W(r-3,c); end end %% Applico equazione alle differenze all'indietro sulle righe % Allo stesso modo del passo precedente l'equazione all'indietro diventa % % $$G[r,c] = B\,W[r,c] + B_{1}G[r+1,c] + B_{2}G[r+1,c] + B_{3} G[r+3,c]$$ % for c = 1:C % Backward equation is applied from last row down to first row G(R,c) = B * W(R,c); G(R-1,c) = B * W(R-1,c) + B1 * G(R,c); G(R-2,c) = B * W(R-2,c) + B1 * G(R-1,c) + B2 * G(R,c); for r = R-3:-1:1 G(r,c) = B * W(r,c) + ... B1 * G(r+1,c) + B2 * G(r+2,c) + B3 * G(r+3,c); end; end; %% Ripristino dimensione immagine % Ora la variabile |G| contiene immagine filtrata lungo entrambe le % direzioni, a meno degli eventuali bordi aggiunti per applicare l'agoritmo % % I bordi aggiunti in precedenza vengono rimossi cosi' |G| contiene il % risultato if (useborder) G = G (border + 1:R-border, border + 1:C-border); end rtime = rtime+toc; % stop performace counter %% Gaussian Smooting with Convolution (Use FFT) % Qui viene riportato a scopo dimostrativo e comparativo lo smoothing % gaussiano operato con la procedura standard di convoluzione % % *Nota*:Matlab utilizzaza FFT per la convoluzione % % Calcolo della dimensione del kernel per la convoluzione cutAt = .0001; t = 1:30; ssq = sigma^2; width = find( exp(-(t.^2)/(2*ssq))/(sigma*sqrt(2*pi)) >cutAt,1,'last'); if isempty(width) width = 1; end %% % Compilo il kernel con i valori della gaussiana 1d t = (-width:width); gau = exp(-(t.*t)/(2*ssq))/(sigma*sqrt(2*pi)); %% % Convoluzione con la gaussiana 1d , prima sulle righe con |gau| poi sulle % colonne utilizzando il kernel trasposto |gau'| tic Cx = imfilter(I,gau, 'conv', 'replicate'); Cxy = imfilter(Cx, gau' , 'conv', 'replicate'); ftime=toc; %% % *Ripristino scala dell'immagine* % % La gaussiana introduce un fattore di scala pari al suo valore massimo, % che dato da |x=0| nel caso 1d e |x=0 & y=0| nel caso 2d % % $$G(x)=\frac{1}{\sigma \, \sqrt[]{2 \pi }}e^{-\frac{x^2}{2\sigma^2}} % \Rightarrow G(x=0)=\frac{1}{\sigma \, \sqrt[]{2 \pi }}$$ % % $$G(x,y)=\frac{1}{2\pi \sigma^2}e^{-\frac{x^2+y^2}{2\sigma^2}} % \Rightarrow G(x=0,y=0)=\frac{1}{2 \pi \sigma^2}$$ % % Nel caso dell'algoritmo ricorsivo, il parametro |B| porta in scala il % segnale filtrato. La convoluzione come sopra operata non richiede la % scalatura dell'output %% Risultati % Di seguito vengono riportati i risultati comparati con l'algoritmo di % riferimento che fa uso della convoluzione (FFT Gaussian). % seleziono un profilo profile = round(2.5*step); h=figure(h+1);set(h,'Position',[h*100 100 win win]); imshow(Cxy);title('FFT Gaussian'); % mostro il profilo selezionato sull'immagine hold on;plot([0 C],[profile profile],'-.');hold off h=figure(h+1);set(h,'Position',[h*100 100 win win]); imshow(G);title('Recursive Gaussian Image'); % mostro il profilo selezionato sull'immagine hold on;plot([0 C],[profile profile],'m-.');hold off % Immagine differenza tra le due immagini filtrate DeltaG = (Cxy - G); h=figure(h+1);set(h,'Position',[h*100 100 win win]); imshow(DeltaG,[]);title('Immagine Delta (FFT - Recursive)'); % Istogramma differenza h=figure(h+1);set(h,'Position',[h*100 100 win win]); hist(DeltaG(:),1000); title('Difference distribution (FFT - Recursive)');grid; %% % Come era atteso esiste una differenza di risultato tra i due algoritmi. % Se si considera il risultato ottenuto tramite la FFT come gold standard, % allora la differenza tra le due immagini filtrate rappresenta l _immagine % delta_ dell'algoritmo ricorsivo (_l'immagine è opportunamente scalata nei % valori_) % % Osservando l _immagine delta_ si nota il reticolo chiaro. Questi sono gli % effetti della approssimazione polinomiale sopra descritti, infatti % seguono l'andamento dell'errore epsilon. % % L'istogramma dell _immagine delta_ fornisce una indicazione visuale % della distribuzione di frequenza della differenza stessa. % Si nota che la maggior parte della differenza si concentra intorno al % valore medio vicino allo 0 % disp(sprintf('Mean error = %d',mean2(DeltaG))); disp(sprintf('Var error = %d',var(DeltaG(:)))); %% % Per osservare a fondo le implicazione di questa differenza è possibile % osservare le derivate di un profilo (esempio lungo la linea tratteggiata) % che attraversa le due immagini filtrate %derivata lungo il profilo selezionato dg = diff(G(profile,:)); dc = diff(Cxy(profile,:)); %plot delle derivate h = figure(h+1);set(h,'Position',[h*100 100 win win]); plot(dc); hold on; plot(dg,'m'); seledge = 2*step; select =seledge-20:seledge+20; mind = 1.1*min(dg(select)); maxd = 1.1*max(dg(select)); % seleziono area per lo zoom rectangle('Position',[2*step-20,mind,40,maxd-mind],... 'Curvature',[1,1],... 'LineWidth',1,'LineStyle','--') hold off; grid;title('Derivata lungo profilo selezionato'); legend('FFT Gaussian','Recursive Gaussian'); xlabel(sprintf('Riga %3.0d',profile)); % Mostro zoom della derivata nel zona selezionata h=figure(h+1); set(h,'Position',[h*100 100 win win]); plot(select,dc(select)); hold on; % mostro edge dell'immagine originale rectangle('Position',[seledge,mind,thin,maxd-mind],'FaceColor','black'); plot(select,dc(select)); %derivata della gaussiana FFT plot(select,dg(select),'m'); %derivata della gaussiana ricorsiva hold off; grid;title('Zoom derivata su edge'); %legend('FFT Gaussian','Recursive Gaussian'); xlabel(sprintf('Riga %3.0d',profile)); %% % Come si nota, in corrispondenza degli edge, la derivata del per % l'algoritmo ricorsivo è leggermente maggiore rispetto al metodo FFT. % Anche questo risultato era atteso dopo l'analisi dell'errore _epsilon_ %% Velocità % Riguardo alle prestazioni dell'algoritmo in termini di velocità si % misura una decisa inversione rispetto a quanto dichiarato in [1]. % % %
    %             FFT exec time = 25.094 ms 
    % Recursive exec time = 42.957 ms
    % Recursive/FFT Speed ratio = 1.71
    %
    % I tempi di esecuzione su Pentium M 1.86Gz WinXp Sp.3 % % % % % In particolare l'algoritmo ricorsivo risulta essere circa 2 volte piu' lento di % Gaussian FFT, mentre in [1] è riportato essere circa 3 volte piu' veloce. % % A tal proposito va considerato che % % # L'implementazione dell'algoritmo ricorsivo qui realizzata può essere % ottimizzata % # L'implementazione dell'algoritmo FFT implementata da Matlab è altamente % ottimizzata % # L'articolo [1] risale al 1995 e si riferisce ad hardware e tecniche % ormai obsolete. % disp(sprintf(' FFT exec time = %3.3f ms',ftime*1000)); % disp(sprintf(' Recursive exec time = %3.3f ms',rtime*1000)); % disp(sprintf('Recursive/FFT Speed ratio = %3.2f',rtime/ftime)); %% Esempio con immagini reali CallGaussian; %% Riferimenti % # Young I.T. and L.J. Van Vliet, _Recursive Implementation of the Gaussian % Filter._ Signal Processing, 1995. 44(2): p. 139-151 % % # Young I.T., Gerbrands J.J and L.J. Van Vliet, _Image Processing % Fundamentals 2.3_: p 57 % # LIBROW, _Gaussian filter, or Gaussian blur_ % % # Image Processing Learning Resources, _Gaussian Smoothing_ % % # WikiPedia, _Gaussian blur_ %