Filtri Lineari (per bordi e non) e trasformata di hough
I filtri lineari hanno proprietà matematiche ben definite dalla teoria dell'elaborazione dei segnali nel dominio delle frequenze, descritti dalla trasformata di Fourier (nel nostro caso bidimensionale) e
Contents
Estrazione dei bordi tramite Sobel
Alcuni pattern possono essere localizzati sfruttandone le proprietà geometriche, si pensi ad una scacchiera piuttosto che un rettangolo distorto "solo" dalla prospettiva. Per poter lavorare con la geometria è indispensabile poter estrarre i bordi dall'immagine, così da poter identificare delle forme come linee, circonferenze o altro.
% Carico un'immagine contenente un pattern: img = imtype(imread('coins.png'),'gF'); % Estraggo i bordi con Sobel: edges = edge(img,'sobel'); % Sovrappongo: imgEdge = cat(3,min(img,not(edges)),min(img,not(edges)),max(img,edges)); figure; imshow(imgEdge);

Da dove proviene questo risultato? L'algoritmo di Sobel "vanilla" è un filtro lineare la cui risposta all'impulso è:
1 2 1 0 0 0 -1 -2 -2
per i bordi orizzontali, e:
1 0 -1 2 0 -2 1 0 -1
per quelli verticali. Si osservi che l'integrale della risposta all'impulso è 0 (condizione necessaria per i filtri passa-alto). Applichiamo l'operatore di convoluzione (bidimensioale) all'immagine utilizzando questi due filtri:
% I filtri: So = fspecial('sobel'); Sv = So'; % Le convoluzioni: edgeso = filter2(So, img, 'valid'); edgesv = filter2(Sv, img, 'valid'); % Vediamo cosa salta fuori: figure; subplot(121); imshowscale(edgeso); subplot(122); imshowscale(edgesv);

Vediamo i bordi in valore asoluto e scalati:
% Valore assoluto e scalatura tra 0 e 1:
figure;
subplot(121); imshowscale(imscale(abs(edgeso)));
subplot(122); imshowscale(imscale(abs(edgesv)));

Una sola immagine di bordi la otteniamo calcolando la norma del gradiente che stiamo approssimando con Sobel:
edges = imscale(sqrt(edgeso.^2 + edgesv.^2)); figure; imshow(edges);

Su questo risultato una sogliatura permette di ottenere i bordi dell'immagine, si osservi dal risultato che i bordi NON sono definiti da un solo pixel di larghezza, per ottenere un solo pixel di larghezza dobbiamo eseguire l'algoritmo morfologico di thinning:
% Sogliatura: BW = threshold(edges,thresholdValue(edges,'otsu')); % Il thinning: BWthinned = bwmorph(BW,'thin',inf); % Vediamo: figure; subplot(121); imshow(BW); subplot(122); imshow(BWthinned);

Altri filtri lineari derivativi
Problemi con Sobel (Prewit, Roberts): eventuale rumore (ad esempio sale e pepe) può generare bordi non desiderati, un filtro spesso utilizzato per attenuare il noise è il filtro Gaussiano.
NOTA: il filtro Gaussiano è un filtro passa-basso, il filtro di Sobel è un filtro passa-alto, la combinazione dei due è un filto passa-banda. Per combinare i due filtri è sufficiente moltiplicarne le risposte agli impulsi nel dominio delle frequenze... oppure convolverli nel dominio spaziale. Il filtro che otteniamo è una approssimazione del filtro derivativo Gaussiano.
% Filtro gaussiano: Gf = fspecial('gauss'); % Filtro risultante (verticale): Hv = conv2(Gf, Sv); % Applicazione del filtro: edgesv = filter2(Hv, img, 'valid'); % Vediamo: figure; subplot(131); surf(Sv); axis equal; title(gca, 'Sobel verticale'); subplot(132); surf(Gf); axis equal; title(gca, 'Filtro Gaussiano'); subplot(133); surf(Hv); axis equal; title(gca, 'Filtro risultante (verticale)'); figure; imshowscale(edgesv);


Proviamo anche ad ottenere la composizione dei filtri nelle due direzioni principali come abbiamo fatto con Sobel:
% Il filtro: Ho = conv2(Gf, So); % Applichiamolo: edgeso = filter2(Ho, img, 'valid'); % Composizione: gradNorm = sqrt(edgeso.^2 + edgesv.^2); % Thinning: edges = bwmorph(threshold(gradNorm,thresholdValue(gradNorm)),'thin',inf); % Vediamo: figure; subplot(121); imshow(gradNorm); subplot(122); imshow(edges);

Derivata della Gaussiana
La funzione di Gauss è:
g(x,mu,sigma) = exp(-0.5(x-mu)^2/sigma^2);
con massimo in mu di valore g(mu) = 1. Calcolandone la derivata otteniamo un filtro derivativo a una dimensione (consideriamo mu=0):
dg_x = -x/sigma^2 exp(-0.5 x^2/sigma^2)
Utiliziamo dg_x in una direzione e g nell'altra componendo i due filtri tramite la convoluzione, il risultato è il seguente:
% Le funzioni dei due filtri: g = @(x,sigma)(exp(-0.5*x.^2./sigma.^2)); dg_x = @(x,sigma)(-x./sigma.^2.*exp(-0.5*x.^2./sigma.^2)); % Calcoliamo due supporti validi per questi filtri: sigma = 1; Xvalues = -3*sigma:3*sigma; Hg = g(Xvalues,sigma); Hg = Hg / sum(Hg(:)); % Integrale deve dare 1. Hdg_x = dg_x(Xvalues,sigma); Hdg_x = Hdg_x - mean(Hdg_x(:)); % Integrale deve dare 0. % Composizione: H = conv2(Hg',Hdg_x); % Applichiamolo: edgeso = filter2(H, img, 'valid'); figure; imshowscale(abs(edgeso));

Il filtro ottenuto somiglia tanto a Sobel convoluto con Gauss:
figure; surf(H);

Nulla ci vieta in questo caso di calcolare derivate direzionali, è infatti sufficiente "pesare" le componenti orizzontale e verticale dipendentemente dall'angolo:
% 30 gradi: alpha = 30*pi/180; % Applico il filtro in entrambe le direzioni: edgeso = filter2(H, img, 'valid'); edgesv = filter2(H', img, 'valid'); % Ottengo la derivata direzionale: edgesDir = edgeso*cos(alpha) + edgesv*sin(alpha); figure; imshowscale(abs(edgesDir));

Trasformata di Hough
Trovati gli edge in questo esempio dobbiamo identificare le circonferenze delle monete, per farlo utiliziamo la trasformata di Hough per circonferenze.
Eseguo la trasformata, notare i parametri (non facili da settare): * ipotizzo di conoscere un valore approssimato di raggio (r=28) * esiste da settare una soglia (th=30)
% Bordi e trasformata: [Accumulator,y0detect,x0detect] = houghcircle(edges,28,30); % Mostro l'accumulatore ed il risultato: figure; subplot(121); imshowscale(Accumulator); subplot(122); imshow(img); hold on; plot(x0detect,y0detect,'ro');

Ma come funziona la trasformata di Hough?
Per ogni punto fornito con gli edge viene prodotto un voto nello spazio dei parametri, in questo caso il raggio è dato e i parametri rimasti sono c_x e c_y (coordinate del centro c), ovvero ciò che vediamo nell'immagine di Accumulator. Il risultato su una immagine BW contenente pochi punti aiuta a comprendere il meccanismo:
% Creo una immagine apposta: BW = rand(300) < 0.0001; Accumulator = houghcircle(BW,28); % Cosa genera? figure; imshow(cat(3,max(BW,Accumulator),BW,BW));

NOTA: lo strumento che stiamo utilizzando campiona in maniera errata i punti su cui generare i voti (si osservino le circonferenze)... ...correggiamolo assieme!