Laboratorio di Strumentazione per Bioimmagini
A.A. 2010-2011
Esercizio 1: soluzione
Creiamo un’immagine fantoccio con la funzione “phantom” di Matlab:
% creo immagine
>> I = phantom();
Definiamo tre diversi campionamenti angolari: il primo campionando con 360 angoli tra 0 e 360
gradi, il secondo campionando ogni 5 gradi (73 angoli tra 0 e 360 gradi) e il terzo campionando
ogni 10 gradi (37 angoli tra 0 e 360 gradi):
% definisco diversi passi di campionamento angolare
>> angoli_1 = 0: 360/359 : 360; % campiono con 360 angoli
>> angoli_2 = 0: 5 : 360; % campiono ogni 5 gradi
>> angoli_3 = 0: 10 : 360; % campiono ogni 10 gradi
Calcoliamo ora i sinogrammi relativi ai campionamenti appena definiti, usando la funzione Matlab
“radon”:
% calcolo sinogrammi
>> s1 = radon(I, angoli_1);
>> s2 = radon(I, angoli_2);
>> s3 = radon(I, angoli_3);
Visualizziamo i tre diversi sinogrammi ottenuti:
>> figure(1)
>> subplot(131)
>> imagesc(s1)
>> title('Sinogramma con 360 angoli tra 0 e 360 gradi')
>> subplot(132)
>> imagesc(s2)
>> title('Sinogramma con 73 angoli tra 0 e 360 gradi')
>> subplot(133)
>> imagesc(s3)
>> title('Sinogramma con 37 angoli tra 0 e 360 gradi')
>> colormap(hot)
Come si può vedere al diminuire della frequenza angolare di campionamento diminuisce il numero
di proiezioni nel sinogramma.
Calcoliamo ora le immagini ricostruite, mediante retroproiezione filtrata, a partire dai tre diversi
sino grammi precedentemente ottenuti:
% calcolo immagine ricostruita
>> Irec1 = iradon(s1, angoli_1);
>> Irec2 = iradon(s2, angoli_2);
>> Irec3 = iradon(s3, angoli_3);
E le visualizziamo:
>> figure(2)
>> subplot(131)
>> imagesc(Irec1)
>> title('Immagine ricostruita dal sinogramma 1')
>> subplot(132)
>> imagesc(Irec2)
>> title('Immagine ricostruita dal sinogramma 2')
>> subplot(133)
>> imagesc(Irec3)
>> title('Immagine ricostruita dal sinogramma 3')
>> colormap(gray)
Appare evidente come al diminuire della frequenza di campionamento angolare peggiori la qualità
dell’immagine ricostruita (artefatti e scarsa resa di alcune regioni).
Esercizio 2: soluzione
La funzione che vogliamo creare dovrà ricevere in ingresso un singolo profilo proiettato e l’angolo
corrispondente di proiezione. Restituirà in uscita l’immagine ottenuta retroproiettando il profilo:
function img = retroproiezione_singola(s, theta)
%
%
%
%
%
%
%
%
%
%
Funzione che calcola la retroproiezione (non filtrata) di un singolo
profilo.
INPUT:
- s : singolo profilo
- theta: angolo di proiezione
OUTPUT:
- img : immagine ricostruita (retroproiezione)
La lunghezza del profilo rappresenta la massima lunghezza attraverso l’immagine, ossia la
lunghezza della sua diagonale, quindi a partire da questa possiamo ricavare la lunghezza del lato
dell’immagine che vogliamo ricostruire:
N = 2*floor( length(s)/(2*sqrt(2)) ); % lunghezza lato dell'immagine
Ora che conosciamo il lato dell’immagine allochiamo lo spazio in memoria per contenere la
retroproiezione e definiamo gli assi x e y dell’immagine ricostruita:
img = zeros(N); % allochiamo spazio in memoria per l'immagine
% definiamo gli assi x e y per l'immagine ricostruita
[x y] = meshgrid(1:N, N:-1:1);
x = x - floor(N/2); % spostiamo l’origine dell’asse x al centro dell’immagine
y = y - floor(N/2); % spostiamo l’origine dell’asse y al centro dell’immagine
Ora definiamo alcune variabili utili che ci permetteranno di calcolare direttamente la
retroproiezione:
costheta = cos(theta);
sintheta = sin(theta);
len = length(s); % lunghezza del profilo (proiezione)
ctrIdx = ceil(len/2); % indice del centro della proiezione
Infine retroproiettiamo il profilo:
% retroproiezione
proj = s;
% questa in iradon viene filtrata prima di essere
% retroproiettata("spalmata")
t = round(x*costheta + y*sintheta);
img = img + proj(t + ctrIdx); % retroproiezione del profilo
img = img / max(img(:)); % normalizza valori immagine tra zero e 1
La variabile proj contiene il profilo p(k) non filtrato. Nella retroproiezione filtrata, s viene appunto
filtrato prima di essere retroproiettato (“spalmato”) nell’immagine.
Nella matrice t, tutti i pixel (x, y) che stanno su una stessa retta x cos( )  y sin( )  r hanno valore
uguale a r. Ne risulta che nella matrice t c’è una mappa di tutte le rette che ricoprono il dominio
dell’immagine da ricostruire. Tutte le rette hanno pendenza tale da risultare perpendicolari alla
direzione di acquisizione della proiezione p(k). Vediamo sotto l’esempio di una matrice t calcolata
per un’immagine 200 x 200 ad un angolo di 45 gradi:
L’assegnazione img = img + proj(t + ctrIdx); replica il profilo proj fino a costruire
un’immagine 200 x 200. In ogni pixel (x, y) di tale immagine mette il valore assunto da proj nel suo
indice pari al valore assunto da t(x, y) + ctrIdx.
Infine l’immagine img viene normalizzata affinché assuma valori compresi tra 0 e 1.
Verifichiamo ora la correttezza della funzione, retroproiettando tre profili corrispondenti agli angoli
0, pi/4; pi/2 calcolati su un’immagine di lato 200 avente valore zero ovunque tranne in un quadrato
centrale di lato 100 con valore 1:
>> I = zeros(200);
>> I(51:150,51:150) = 1;
>> theta1 = 0;
>> theta2 = pi/4;
>> theta3 = pi/2;
>> p1 = radon(I, theta1*180/pi);
>> p2 = radon (I, theta2*180/pi);
>> p3 = radon (I, theta3*180/pi);
(NB: Volendo potremmo calcolare le singole proiezioni con la funzione creata nell’esercizio 2 dello
scorso laboratorio…)
>> img1 = retroproiezione_singola(p1, theta1);
>> img2 = retroproiezione_singola(p2, theta2);
>> img3 = retroproiezione_singola(p3, theta3);
E ora visualizziamo le immagini retroproiettate:
>>
>>
>>
>>
figure(1)
imagesc(img1)
colormap(gray)
title('Retroproiezione per il profilo con theta = 0')
>>
>>
>>
>>
figure(2)
imagesc(img2)
colormap(gray)
title('Retroproiezione per il profilo con theta = pi/4')
>>
>>
>>
>>
figure(3)
imagesc(img3)
colormap(gray)
title('Retroproiezione per il profilo con theta = pi/2')
Verifichiamo infine la correttezza di tali retroproiezioni confrontandole con le corrispondenti
ottenute usando la funzione iradon (che però esegue una retroproiezione filtrata). Nella
documentazione di Matlab si vede che la retroproiezione di un singolo profilo richiede l’utilizzo
della funzione iradon nel seguente modo:
>> img1_irad = iradon([p1 p1], [theta1*180/pi theta1*180/pi])/2;
>> img2_irad = iradon([p2 p2], [theta2*180/pi theta2*180/pi])/2;
>> img3_irad = iradon([p3 p3], [theta3*180/pi theta3*180/pi])/2;
>>
>>
>>
>>
figure(4)
imagesc(img1_irad);
colormap(gray)
title('Retroproiezione filtrata per il profilo con theta = 0')
>>
>>
>>
>>
figure(5)
imagesc(img2_irad);
colormap(gray)
title('Retroproiezione filtrata per il profilo con theta = pi/4')
>>
>>
>>
>>
figure(6)
imagesc(img3_irad);
colormap(gray)
title('Retroproiezione filtrata per il profilo con theta = pi/2')
Esercizio 3: soluzione
La funzione richiesta riceverà in ingresso il sinogramma s e il corrispondente passo di
campionamento angolare thetas usato per la sua acquisizione:
function img = retroproiezione(s, thetas)
%
%
%
%
%
%
%
%
%
%
%
Funzione che calcola la retroproiezione (non filtrata) di un'immagine
dato il sinogramma e il passo di campionamento angolare usato per
acquisirlo.
INPUT:
- s : sinogramma
- thetas: passo di campionamento angolare
OUTPUT:
- img : immagine ricostruita (retroproiezione)
D’ora in poi considereremo il sinogramma s come una matrice che ha come colonne i diversi profili
(proiezioni) ottenuti per diversi angoli theta.
Di fatto l’esercizio si risolve facilmente a partire dalla funzione scritta per l’esercizio 2, con minime
variazioni nella fase finale di retroproiezione (che ora coinvolge più profili anziché uno solo).
La prima parte riportata di seguito è del tutto analoga alla prima parte dell’esercizio 2 (si veda sopra
per un commento più dettagliato):
N = 2*floor( size(s,1)/(2*sqrt(2)) ); % lunghezza lato dell'immagine
img = zeros(N); % allochiamo spazio in memoria per l'immagine
% definiamo gli assi x e y per l'immagine ricostruita
[x y] = meshgrid(1:N, N:-1:1);
y = y - floor(N/2);
x = x - floor(N/2);
costheta = cos(thetas);
sintheta = sin(thetas);
len = size(s,1); % lunghezza di ogni proiezione
ctrIdx = ceil(len/2); % indice del centro delle proiezioni
Ora inseriremo la retroproiezione realizzata precedentemente per un singolo profilo, all’interno di
un ciclo for. In tal modo calcoleremo iterativamente un’immagine retroproiettata a partire da ogni
profilo contenuto nel sinogramma s e sommeremo (integrazione lungo theta) le diverse immagini:
% retroproiezione
for i=1:length(thetas)
proj = s(:,i); %ogni colonna del sinogramma è un profilo da retroproiettare
t = round(x*costheta(i) + y*sintheta(i));
img = img + proj(t + ctrIdx); % calcoliamo e sommiamo le immagini
end
img = img / max(img(:)); % normalizza valori immagine tra zero e 1
Testiamo ora la funzione creata. Calcoliamo la retroproiezione di un’immagine di lato 200 avente
valore zero ovunque tranne in un quadrato centrale di lato 100 con valore 1:
>> I = zeros(200);
>> I(51:150,51:150) = 1;
Calcoliamo il sinogramma s corrispondente ad un passo di campionamento angolare thetas usando
la funzione radon:
>> thetas = 0:pi/179:pi; % definiamo passo di campionamento angolare
>> s = radon(I, thetas*180/pi); % calcoliamo sinogramma
(NB: Volendo potremmo calcolare il sinogramma con la funzione creata nell’esercizio 3 dello
scorso laboratorio…)
Calcoliamo la retroproiezione usando la funzione retroproiezione appena sviluppata:
>> img = retroproiezione(s, thetas);
Calcoliamo la retroproiezione filtrata usando la funzione Matlab iradon:
>> img_IRAD = iradon(s, thetas*180/pi);
Confrontiamo le due immagini ottenute:
>>
>>
>>
>>
figure(1)
imagesc(img)
colormap(gray)
title('Immagine retroproiettata senza filtraggio')
>>
>>
>>
>>
figure(2)
imagesc(img_IRAD)
colormap(gray)
title('Retroproiezione filtrata ottenuta con iradon')
Scarica

Laboratorio di Strumentazione per Bioimmagini