Contents
Wavelet e trasformata di Haar
Dato un segnale lo "scopo del gioco" è scomporlo come somma di componenti localizzate in tempo e in frequenza, in particolare la scomposizione deve essere efficientemente calcolabile. La wavelet più semplice da calcolare e utilizzare è senza dubbio la wavelet di Haar, la cui madre è la funzione seguente:
\Psi(t) = \left\{ \begin{matrix} +1 \hfill\quad\textbb{0\leq t<0.5} \\ -1 \hfill\quad\textbb{0.5\leq t<1} \\ 0 \hfill\quad\textbb{altrimenti} \end{matrix} \right.
PS: usare http://www.codecogs.com/latex/eqneditor.php per vedere le eq.
% Haar functions: Psi = @(t)(double((t>=0&t<0.5)-(t>=0.5&t<1))); Phi = @(t)(double(t>=0&t<1)); % La wavelet di Haar: X = -1:0.01:2; figure; plot(X,Psi(X)); axis equal; title('Wavelet di Haar');

Si tratta di un filtro derivativo (integra a 0), per poter ottenere una base completa va aggiunta al banco di filtri anche la seguente:
% Costante nell'unità: figure; plot(X,Phi(X)); axis equal; title('Costante nell''unità');

Le traslazioni e scalature per potenze di 2 possono facilmente essere ottenute:
\Psi_{i,j}(t) = \sqrt{2^j}\Psi(2^j t - i)
% Funzioni traslate e scalate: Psi_ij = @(i,j,t)(sqrt(2.^j).*Psi(2.^j.*t-i)); Phi_ij = @(i,j,t)(sqrt(2.^j).*Phi(2.^j.*t-i)); % Vediamole: figure; subplot(211); plot(X,Psi_ij(1,2,X)); axis equal; title('Wavelet di Haar'); subplot(212); plot(X,Phi_ij(1,2,X)); axis equal; title('Costante nell''unità');

Una base ortonormale
Creiamo una base in \Re^2 con le wavelet di Haar, in particolare ci è sufficiente combinare Psi e Phi sfruttando la separabilità:
% Genero le basi: X = 0:0.01:1; haar00 = Phi(X)'*Phi(X); haar01 = Phi(X)'*Psi(X); haar10 = Psi(X)'*Phi(X); haar11 = Psi(X)'*Psi(X); % Vediamole: figure; subplot(221); imshow(haar00); title('haar_{0,0}'); subplot(222); imshow(haar01); title('haar_{0,1}'); subplot(223); imshow(haar10); title('haar_{1,0}'); subplot(224); imshow(haar11); title('haar_{1,1}');

Verifichiamone l'ortonormalità:
% Tutti i possibili prodotti interni: haarAll = {haar00,haar01,haar10,haar11}; intProd = zeros(4); for i=1:4 for j=1:4 intProd(i,j) = dot(haarAll{i}(:),haarAll{j}(:)); end end % Vediamole: fprintf('Prodotti interni:\n'); disp(intProd);
Prodotti interni: 10000 0 0 0 0 10000 0 0 0 0 10000 0 0 0 0 10000
Decomponiamoci una immagine:
% Carico una immagine: img = double(imread('cameraman.tif'))/255; % Creo i filtri 2x2: haarAll = haarBank(1); % Applico il banco di filtri: C = bankApply(img,haarAll); % Mostro le componenti: figure; subplot(121); imshow(cell2mat(C)); title('Decomposizione con Haar (livello 1)'); % Ricostruiamo l'immagine: subplot(122); imshowscale(sum(cat(3,C{:}),3)); title('Ricostruzione');

Multirisoluzione
Come visto, una delle 4 componenti ottenute con haar altro non è che la media viaggiante 2x2, è quindi ammissibile una sua riduzione di scala e la successiva riapplicazione della stessa decomposizione:
% Riduco di scala: img2 = C{1}(1:2:end,1:2:end); % Decompongo nuovamente: C2 = bankApply(img2,haarAll); % Provo a ricostruire da zero: img1 = sum(cat(3,C2{:}),3); img1 = imresize(img1,2,'bilinear'); C1 = C; C1{1} = img1; imgr = sum(cat(3,C1{:}),3); % Assembo il tutto: tr2 = cell2mat(C2); C1{1} = tr2; tr1 = cell2mat(C1); % Vediamoli: figure; subplot(121); imshow(tr1); title('Trasformata di Haar al livello 2'); subplot(122); imshow(imgr); title('Ricostruzione');

Per sfruttare a pieno la separabilità delle wavelet di Haar, creiamo una matrice contenente tutte le scale e traslazioni della wavelet 1d, poi utiliziamo questa matrice sia come banco di filtri orizzontali che verticali.
Si consideri la matrice definita come segue:
H_i = \left\{\begin{matrix} 1 \hfill\quad\textbb{se\ }i=0 \\ \begin{pmatrix} H_{i-1}\otimes(1,1)\\ 2^{(i-1)/2}I_{2^(i-1)}\otimes(1,-1) \end{pmatrix}\hfill\quad\textbb{altrimenti} \end{matrix}\right.
dove il prodotto di Kronecker A \otimes B è definito a blocchi come:
A \otimes B = \begin{pmatrix} A_{1,1}B & A_{1,2}B \cdots \\ \cdots \\ A_{n,1}B & A_{n,2}B \end{pmatrix}
% La matrice per la nostra immagine: H = haar(8); % Vediamola, contiene tutte le wavelet 1d di Haar per scale a potenze di 2: figure; imshowscale(H);

La trasformata di Haar data la matrice H e l'immagine F è definita come:
S = \frac{1}{N} H F H^T
con N lato dell'immagine F; la sua inversa è:
F = \frac{1}{N} H^T S H
Si osservi nella trasformata che l'energia è concentrata in pochi coefficienti a valori assoluti elevati.
% Init: N = size(H,1); % Trasformo: S = H*img*H'/N; % Antitrasformo: F = H'*S*H/N; % Vediamo: figure; subplot(121); imshow(S); title('Trasformata di Haar'); subplot(122); imshow(F); title('Ricostruzione dalla Haar');

Osserviamo meglio cosa è successo identificando i coefficienti a valori superiori a una certa percentuale rispetto al massimo:
% Li identifico: perc = 0.0005; mask = S > max(abs(S(:)))*perc; % Vediamo: figure; imshow(mask); title('Coefficienti sopra lo 0.05% del massimo');

Image compression e Image denoising
Le wavelet trovano applicazione, oltre che nella caratterizzazione di punti nelle immagini (a scopo di classificazione/clustering) nella compressione e riduzione di rumore nelle immagini, facciamo un esempio usando le wavelet di Haar: il seguente filmato mostra l'approssimazione ottenuta codificando un'immagine tramite trasformata di Haar eliminando una percentuale (decrescente) di coefficienti a valore basso.
img = double(imread('cameraman.tif'))/255; S = haarTransf(img); figure; for perc=2.^(0.1:-0.5:-10) mask = abs(S)<=perc*max(abs(S(:))); S2 = S; S2(mask) = 0; F2 = haarInv(S2); clf; imshow(F2); drawnow; pause(0.1); end

Integral image
Le wavelet di Haar applicate a una immagine per una determinata posizione e scala producono come risposta la somma dei valori dei pixel contenuti in aree rettangolari, per poter calcolare rapidamente la risposta di una immagine ad una determinata wavelet di haar è indispensabile fornire un algoritmo che calcoli questa somma in maniera efficiente, in particolare è indispensabile produrre un algoritmo che permetta di spostare la complessità computazionale dal calcolo di una singola risposta ad una operazione di preprocessing. L'integral image è uno immagine calcolabile in tempo lineare nel numero di pixel, tramite essa è possibile calcolare la somma di valori di pixel contenuti in rettangoli arbitrari in tempo costante (3 operazioni di somma/sottrazione in tutto). L'integral image di una immagine I è definita come segue:
II(x_0,y_0) = \sum_{x=0}^{x_0} \sum_{y=0}^{y_0} I(x,y)
In una dimensione tali somme cumulate sono calcolabili iterativamente seguendo la regola di ricorsione:
II(t) = \left\{ \begin{matrix} I(0) \hfill\quad\textbb{per\ } t=0 \\ I(t-1)+I(t) \hfill\quad\textbb{altrimenti} \end{matrix} \right.
In Matlab la funzione cumsum svolge questo compito. Per ottenere il risultato desiderato su una immagine cumsum deve essere applicata a tutte le righe e poi a tutte le colonne.
% Somma cumulata per righe e poi per colonne: iimg = cumsum(img,2); iimg = cumsum(iimg,1); % Vediamola: figure; imshowscale(iimg);

Per quanto non visibile a occhio, questa immagine integrale contiene le informazioni che permettono di calcolare in maniera rapida la somma in un rettangolo di dimensione arbitraria:
% Funzione di somma in un rettangolo: sumRect = @(iimg,p1,p2)(iimg(p2(2),p2(1))-iimg(p1(2)-1,p2(1))-iimg(p2(2),p1(1)-1)+iimg(p1(2)-1,p1(1)-1)); % Un rettangolo di esempio: rect = [10,30,100,100]; % Somme in quel rettangolo: sum1 = sumRect(iimg,rect(1:2),rect(1:2)+rect(3:4)); sum2 = sum(sum(imcrop(img,rect))); % Verifichiamo: fprintf('Differenza: %d\n', abs(sum1-sum2));
Differenza: 1.818989e-012
Questa tecnica permette di calcolare efficientemente qualunque espressione del tipo \sum_{R} f(x,y) dove R è un rettangolo, ottenendo ad esempio le funzioni di dsitools fastcorr2Plane, fastsad2Plane e fastssd2Plane. Come building-block si veda imintegrate.
% Vediamolo:
figure;
subplot(121); imshow(img);
subplot(122); imshow(imintegrate(img,50));
