Analisi d’immagine e pattern recognition in matlab
Introduzione
Matlab
Sistemi CAD per la Medicina
Esercizi generali
Ricette:
Generazione di numeri interi compresi tra a e b, con distribuzione uniforme, senza
usare randi:
a = 5, b = 12, n = 10000, z = floor(rand(1,n) * (b+1-a) + a); hist(z, a:b)
Notare che l’uso di round (e conseguentemente con b anziché b+1 nella parentesi
tonda al fattore moltiplicativo) non sarebbe corretto perché darebbe una distribuzione non
uniforme ma con primo e ultimo bin riempiti a metà.
Stessa cosa per numeri double!
Esercizi di analisi d’immagine e pattern recognition
ESERCIZI GENERALI IN MATLAB
1. Scrivere un programma che chieda all’utente di inserire un numero N, e poi calcoli la somma dei numeri naturali da 1 a N sia “normalmente” (ciclo for? sum di un vettore?) sia con la formula di Gauss:
M = N(N+1)/2.
2. Data una matrice A, verificare che essa sia quadrata3. In una gara a staffetta, i valori seguenti sono le distanze percorse in metri e i tempi
impiegati in secondi:dist 560 440 490 530 370
tempo 10.3 8.2 9.1 10.1 7.5
1
(a) calcolare la velocità media di ogni percorso, (b) individuare quello con velocità media più alta (max), (c) individuare i percorsi con velocità media maggiore della media di tutte le velocità medie dei vari percorsi (find…).
find(x == max(x)) (più di un punto di massimo)
x(logical([0 0 0 1 1]))
x(find(x>0.6))
x(x>0.6)
4. Scrivere una funzione che, dati n, a, b, generi un vettore riga di n numeri casuali con distribuzione uniforme (non interi, o sarebbe risolto dalla sintassi della randi; usare rand) compresi tra a e b esclusi, poi calcoli la media che sarà scritta a video, e infine restituisca gli indici dei numeri che superano (>=) la media.
5. Scrivere una funzione che, dati n, a, b, generi n numeri casuali interi tra a e b inclusi, poi calcoli la media, e infine restituisca gli indici dei numeri che superano (>=) la media.
6. scrivere una funzione che, dati i cateti di un triangolo rettangolo, calcoli l’ipotenusa.7. Scrivere un programma per il calcolo delle radici reali di un’equazione algebrica di
secondo grado.8. Scrivere un programma per il calcolo delle radici reali distinte / reali coincidenti /
complesse coniugate di un’equazione algebrica di secondo grado.9. Data una matrice quadrata A in ingresso realizzare una funzione Matlab chiamata
traccia(A) che restituisce la traccia della matrice, ossia la somma degli elementi sulla diagonale principale.
10. Si scriva una funzione denominata “doppio_per” che accetti in ingresso due matrici A e B qualunque, e restituisca due matrici C= A*B e D = B*A; ove le matrici non fossero quadrate, il programma deve comunicarlo all’utente senza fermarsi per errore.
11. Scrivere una funzione MATLAB che accetti come input un vettore (e.g. di interi) V, e due scalari a e b, e restituisca tre valori: uno scalare che dica quanti sono i numeri compresi tra a e b (limiti inclusi), un vettore che dica quali sono questi elementi, e un altro che dica in quale posizione si trovano nel vettore V. length(find(V>=a & V <= b)), V(find(V>=a & V <= b)), find(V>=a & V <= b) (oppure usare due find separate e poi intersect(). E se fosse stato <a oppure > b?
12. Scrivere una funzione Matlab che effettui la somma di tutti i numeri memorizzati nel vettore V, di lunghezza arbitraria, passatole come parametro, che occupano le posizioni pari (ossia, calcolare V (2)+V (4)+V (6)+· · ·). Riscrivere il codice in modo che effettui la somma di tutti i numeri in posizione dispari.
function s = sommaDispari(v)
% lunghezza
% genero indici dispari
% indicizzo v con gli indici generati
% sommo gli elementi rimasti
end
2
13. La distribuzione triangolare è una distribuzione di probabilità continua la cui funzione di densità di probabilità descrive un triangolo, ovvero è nulla all’esterno di due valori estremi ed è lineare tra questi ed un valore intermedio (la moda) arbitrariamente situato tra a e b. Scrivere una funzione triangle.m per il calcolo della densità di probabilità triangle(t), e si disegnino i grafici di triangle(t), triangle(t/2), triangle(t – 0:5), in un opportuno range di valori di t. Suggerimento: la funzione dev’essere normalizzata, per cui il valore nel punto di massimo è 2/(b-a).
14. Scrivere una funzione che genera una matrice 2x2 di 2 se si passa come argomento 2, una matrice 3x3 di 3 se si passa come argomento 3 e così via fino a 5 compreso. In tutti gli altri casi deve restituire 1.
15. Generare una matrice quadrata di ordine n (ove n è un dato introdotto dall'esterno) con elementi generati a caso da una distribuzione uniforme nell'intervallo [0,1] e calcolare
a. un vettore che contiene la somma degli elementi di ciascuna colonna;b. un vettore che contiene la somma degli elementi di ciascuna riga;c. un vettore che contiene la somma degli elementi di ciascuna riga elevati al
quadrato;d. il massimo degli elementi della matrice;e. la somma di tutti gli elementi della matrice.
16. Date le matrici A=[1 2 3; 4 5 6; 7 8 9]; B=[2 -1 0; -1 2 -1; 0 -1 2]; spiegare cosa producono le seguenti istruzioni:
a. A(:,[1,3])=B(:, 1:2);b. C=A./B;c. C=A.^B;d. A([1:2],:)=[];e. D=B([3,2],1:2:3);
17. Sia b un vettore di 10 componenti con elementi pseudocasuali, si crei il vettore d che abbia come elementi solo gli elementi di b maggiori di 0:5;
18. Scrivere una funzione MATLAB che accetti come input un vettore (e.g. di interi) V, e due scalari a e b, e restituisca tre valori: uno scalare che dica quanti sono gli elementi di V compresi tra a e b, un vettore che riporti questi elementi, e un altro vettore che riporti le posizioni di questi elementi in V.
X = 0:5;
Y = X + 0.05*(rand(1,6)-0.5)
3
scatter(X,Y) o plot(X,Y,’o’)
p = polyfit(X,Y,1); % p(1) e’ la pendenza, p(2) l’intercetta
FX= polyval(p,X);
hold on
plot(X,FX)
function provatictoc
disp('per partire premi un tasto)
pause
tic
disp('ora aspetta quanto vuoi e poi premi nuovamente invio')
pause
t = toc
19. scrivere tre funzioni; la prima, denominata euclDist, calcoli la distanza euclidea tra due punti nel piano, usando il teorema di Pitagora (in seguito, controllare cosa fa la funzione di Matlab pdist); la seconda, denominata meanPoint, date le coordinate di due punti calcoli e restituisca le coordinate del punto medio del segmento avente i due punti per estremi; la terza, denominata triangles, date le coordinate di tre punti nel piano:
a. tracci tali punti con plot usando come marker un cerchietto (plot), b. scriva nei pressi di ogni punto tracciato le lettere A, B, C per indicare i vertici
(text); non ci si preoccupi eccessivamente di posizionare precisamente le lettere A, B, C rispetto ai punti e alle linee tracciate al seguente punto (c).
c. congiunga i punti, precedentemente tracciati, con segmenti di retta (plot o line), generando così un triangolo; nei pressi dei punti medi dei lati dei triangoli (calcolati tramite la funzione meanPoint) scriva a, b, c, dove a indica il lato opposto al vertice A, etc
d. calcoli la lunghezza dei lati tramite la funzione euclDist scritta in precedenza, e scriva sulla command window qual è la lunghezza di ogni lato
e. calcoli l’area A del triangolo tramite la formula (det):
area=12|xA y A 1xB yB 1xC yC 1|
e la scriva nella command window (“Area del triangolo: …”).
Note:
Usare, come tipo di variabile per esprimere le coordinate di un punto nel piano, un vettore (riga o colonna) a due componenti.
4
Teorema di Pitagora: sqrt(sum((A-B).^2))
Riflessometro
Scrivere un programma che effettui un test dei riflessi dell’utente. Il programma ripete un
numero prestabilito di volte le seguenti operazioni:
a. genera un numero casuale x compreso tra un valore minimo e un valore massimo;
tale numero sarà usato come ritardo casuale; è naturalmente possibile e consigliato
generare in un’unica operazione (vettorialmente) tutti i numeri casuali xi da
adoperare come tempi di ritardo, e ciclare poi su di essi;
b. scrive a video “attenzione!” e attende x secondi;
c. scrive a video “premi un tasto” e si dispone ad attendere la pressione di un tasto da
parte dell’utente;
d. quando il tasto è premuto, il programma misura il tempo trascorso tra il messaggio
“premi un tasto” e la pressione effettiva del tasto, e memorizza tale tempo (di
reazione, legato alla rapidità dei riflessi dell’utente).
Alla fine dell’esecuzione, sarà disponibile un vettore di misure di tempi di riflesso e un
vettore di tempi di ritardo iniziale dato dal programma; sarà possibile studiare:
se vi è una tendenza all’aumento o alla diminuzione dei tempi di reazione con
l’aumentare del numero di prove (l’utente si stanca, oppure diventa più abile, o la sua
performance non degrada né migliora con il trascorrere del tempo);
se vi è correlazione tra i tempi di risposta e i tempi di ritardo (l’utente è più bravo se il
ritardo è breve o se invece è più lungo, o la sua abilità non dipende dal ritardo
iniziale).
la distribuzione dei tempi di reazione, confrontandola visualmente (o con un test di
gaussianità) con una distribuzione normale; avendo a disposizione un gran numero
di misure (almeno 100 o 200!) è possibile confrontare le distribuzioni dei tempi di
risposta dei primi run con quelli degli ultimi run, verificando se i campioni
appartengono alla medesima popolazione, oppure l’utente si è stancato o ha
migliorato le proprie prestazioni, e quindi le popolazioni differiscono
Naturalmente, sotto le mentite spoglie di un semplice test di psicologia sperimentale, si
nasconde un modo ludico per imparare qualcosa sulla programmazione e su Matlab, e
magari rispolverare qualche nozione di statistica elementare. In particolare:
l’uso delle istruzioni pause, tic, toc; come salvare e caricare dati da file;
graficare una serie temporale, e dedurne delle informazioni;
come calcolare un fit lineare di una serie di punti sperimentali;
5
come graficare un istogramma, come normalizzarlo, come tracciare una funzione
gaussiana con cui confrontare detto istogramma normalizzato;
come calcolare un coefficiente di correlazione (corrcoef);
come eliminare da un vettore dei dati evidentemente spuri; rimpiazzando
eventualmente questi dati con NaN, si può imparare cosa rappresenti questo
termine, e come trattare dati in cui esso è presente (nanmean, nanstd).
Prerequisiti: pause, tic, toc, pause, rand…
Aggiungere plot
Eliminazione dei dati spuri
E’ possibile che l’utente prema il tasto prima che il computer abbia emesso il segnale “premi
un tasto”. In questo caso i tempi registrati saranno molto brevi, dell’ordine di 0.10 o 0.15
secondi. Come fare per eliminare questi dati dal vettore dei risultati?
(Svuotare il buffer di tastiera prima del tic? NON E’ NECESSARIO, in matlab, perché se si
preme un tasto prima della pause tale pressione è eliminata dalla pause stessa!)
y(y < 0.15) = NaN;
poi si può usare nanmean e nanstd per avere la media e la deviazione standard.
Provare che cosa succede se non si mette il pause: qual è il tempo da sottrarre per evitare
un errore sistematico? (?) (eseguendo senza la pause, l’utente non deve premere nulla e il
programma va avanti da solo registrando non i tempi di risposta bensì i tempi di ritardo
dovuti all’esecuzione dell’istruzione tic (o della disp se questa è fatta dopo). Si tratta di tempi
sensibili?
Confronto con gaussiana
G ( x )= 1√2π σ
e−( x−x )2
2σ 2
Normalizzazione dell’istogramma
Considerare che occorre dividere l’istogramma Ni per l’area sottesa:
6
A=∑iN i∆ xi=∆ x∑
iN i=∆ x N
dove la somma è calcolata sui bin usati per l’istogramma, e N è il numero di run, ossia di dati
disponibili. Quindi:
N i'=N i /A
Calcolando, infatti, l’area dell’istogramma N i':
A'=∆ x∑iN i'=∆ x∑
i
N iN ∆ x
=1.
Plot tendenza, plot correlazione, corrcoef
L'indice o coefficiente di correlazione di Pearson tra due variabili statistiche x e y esprime
un’eventuale relazione di linearità tra di esse. L’indice di Pearson è definito in termini di
covarianza e deviazioni standard:
ρ=σ xyσx σ y
con:
Il coefficiente assume valori compresi tra -1 e 1: −1≤ρ≤1. Se ρ è positivo, la correlazione è
diretta, se è nullo, le variabili si dicono incorrelate o scorrelate, se ρ è negativo la
correlazione è inversa. Inoltre, la correlazione si considera debole se 0<|ρ|≤0.3, moderata
se 0.3<|ρ|≤0.7, forte se 0.7<|ρ|≤1, però tale distinzione è molto grossolana e insufficiente,
in quanto per valutare il risultato del calcolo del coefficiente di Pearson occorre anche tenere
conto della significatività statistica (il p-value) e della numerosità del campione. Per una
trattazione completa si rimanda a un trattato di statistica, quale per esempio []. Per gli scopi
di questo testo, basterà verificare se, visualmente, lo scatterplot dei dati di ritardo dello
stimolo e dei tempi di risposta dell’utente mostrano una certa correlazione: in caso di
correlazione positiva, si potrà dedurre che il particolare soggetto ha prestazioni migliori per
piccoli tempi di ritardo di presentazione dello stimolo; nella correlazione inversa, il soggetto
sarà più “bravo” per tempi di ritardo maggiori; infine, nel caso non vi sia una correlazione
7
importante, per il soggetto non vi sarà particolare dipendenza dal tempo di presentazione
dello stimolo.
[r,p] = corrcoef(M)
r =
1.0000 -0.0888
-0.0888 1.0000
p =
1.0000 0.3820
0.3820 1.0000
http://www.stat-project.com/forum/topics/correlazione
p = polyfit(t,y,2); p = polyfit(x,t,1);
y1 = polyval(p,t); t1 = polyval(p,x);
plot(t,y,'o',t,y1, '-')p(1)
polyfit NON funziona con NaN! Eliminare con idx = isnan(…)
Usare la funzione randn per generare i numeri casuali
function testGaussiana(n,bin,m,st) % testGaussiana(10000, 100, 0, 1)
8
% 10000, 100, 0, 1 numero di lanci, numero di bin, media e std della distrib % Generazione dei numeri casuali con distribuzione normaleq = randn (1,n); % simulazione dei lanci, valori tra 0 e 1, distribuiti normalmente!P = q * st + m; % simulazione dei lanci, valori con media m e std st[N,X] = hist (P,bin); % N: altezza dei bin; X: centri dei binA = (X(2)-X(1)) * n; % Area dell'istogramma sum(Dx*Ni) = Dx*sum(Ni) = Dx*numero_di_puntiN = N/A; % Normalizzo le altezze dei binbar (X,N) % Grafico; l'area di questo istogramma e' 1 hold % Tracciamento della gaussiana con pari media e stdmedia = mean (P); % dovrebbe uscire ms = std (P); % dovrebbe uscire stxMin = media - 4 * s;xMax = media + 4 * s;Xg = xMin:0.01:xMax;Y = 1/(sqrt(2*pi)*s) * exp(-(Xg-media).^2/(2*s^2));plot (Xg,Y,'r-')hold disp('media effettiva')disp(media)disp('std effettiva')disp(s)end
2 4 6 8 10 12 14 16 180
0.05
0.1
0.15
0.2
0.25
0.3
9
Il gioco della vita
Il “gioco della vita” (“game of life”, o semplicemente “life”) è un automa cellulare
ideato dal matematico John Conway (Princeton University) negli anni sessanta. Esso
comparve in pubblico per la prima volta in Scientific American (ottobre 1970), nella rubrica
“Giochi matematici” di Martin Gardner [gar1970]. Non si tratta di un gioco nel senso classico
del termine, poiché non vi sono giocatori, non si vince e non si perde. Lo scopo è mostrare
come da regole semplici e da interazioni a molti corpi possano emergere comportamenti
complessi simili a quelli che si potrebbero riscontrare nella vita di una colonia di organismi
unicellulari. Una volta che i pezzi (le cellule) siano stati collocati nella loro posizione di
partenza su una “scacchiera” (usualmente una griglia idealmente infinita di caselle
quadrate), le regole determinano ciò che accade in seguito. L’andamento del gioco dipende
dunque esclusivamente dal numero e dalla posizione iniziale dei pezzi, dati che possono
essere variati a piacere, e dalle regole del gioco.
Le regole sono le seguenti:
1. Le cellule possono “essere vive” (e quindi occupano un posto sulla
scacchiera), o possono “morire” (e scompaiono dalla griglia).
2. Ogni casella ha otto caselle vicine, per cui una cellula può avere fino a otto
cellule vicine (i “primi vicini”).
3. Una casella vuota, che abbia esattamente tre cellule vicine (non due, non
quattro…), dà origine a un nuovo individuo (“nascita” di una nuova cellula,
Fig 1)
4. Una cellula vivente (casella piena) con due o tre vicini, è in buona compagnia
(sufficiente, e non eccessiva) e continua a vivere (“sopravvivenza”, Fig 2)
5. Una cellula vivente circondata da meno di due vicini (nessuno o uno solo) o
da più di tre vicini (da quattro a otto) muore, rispettivamente per “isolamento”
o per “sovraffollamento” (Fig 3).
Figura 1. Due casi di "nascita" di una cellula.
10
Figura 2. Tre casi di sopravvivenza di una cellula: le cellule segnate da un punto colorato sopravvivono perché accompagnate da due o tre vicini.
Figura 3. Morte di cellule, per sovraffollamento (secondo diagramma da sinistra) e per isolamento (gli altri diagrammi, in cui tutte le cellule presenti muoiono).
Il gioco progredisce a step temporali: ogni iterazione applica le regole e dà un output
grafico con il quale lo “spettatore” assiste all’evoluzione della colonia di cellule. Il gioco
finisce quando subentra una situazione di equilibrio (o “stallo”), ossia se tutte le cellule sono
morte, oppure se si è stabilita una condizione di oscillazione tra diversi schemi di
disposizione delle cellule sopravvissute.
Nota importante: a ogni ciclo del gioco, il numero di vicini da considerare
nell’applicazione delle regole è sempre basato sulle cellule presenti prima dell’applicazione
delle regole stesse. In altre parole, l’aggiornamento della scacchiera avviene solo alla fine
dell’applicazione delle regole.
Le configurazioni che le cellule possono via via assumere sono spesso interessanti e
graficamente gradevoli. Esse assumono spesso dei nomi caratteristici, e sono costituite da
gruppi di cellule che oscillano tra configurazioni, oppure gruppi di cellule che si muovono
insieme, e così via. Alcuni esempi sono:
Figura 4. Alcune configurazioni tipiche (da https://it.wikipedia.org/wiki/Gioco_della_vita).
Da un punto di vista informatico, il gioco comporta:
11
1. una matrice di caselle che possono essere occupate da una cellula oppure
vuote (quindi, una matrice sostanzialmente booleana, che costituisce il
“mondo” in cui vivono le cellule)
2. una distribuzione spaziale iniziale delle cellule, decisa a caso oppure per
intervento manuale del “giocatore”, da inserire nel “mondo”
3. una funzione che calcoli il numero di vicini di ciascuna cella, considerando la
scacchiera infinita, oppure limitandosi a trascurare il bordo della scacchiera
stessa (in cui non si porranno cellule)
4. una funzione che applichi le regole a ciascuna casella della matrice “mondo”
e decida se una cellula vive, muore, o “nasce”
5. una seconda matrice di caselle, identica al “mondo”, che contenga le
variazioni via via apportate alla configurazione di cellule in base alle regole, e
che alla fine di ciascun ciclo sia copiata sulla matrice “mondo” visibile da
“giocatore”.
Codice (mylife.m):
sx = 100; sy = 100;L = zeros(sx, sy);%L(1,:) = -1;%L(sy,:) = -1;%L(:,1) = -1;%L(:,sx) = -1; L(80,80) = 1;L(81,80) = 1;L(80,81) = 1;L(81,82) = 1;L(80,83) = 1;L(80,83) = 1;L(81,83) = 1;L(82,83) = 1;L(83,80) = 1;L(83,80) = 1;L(83,81) = 1;L(84,82) = 1;L(85,83) = 1;L(84,83) = 1;L(83,83) = 1;L(86,83) = 1; L(74,83) = 1;L(73,87) = 1;L(76,83) = 1; for time = 1:1000 pause(0.1) % imshow(L, []) spy(L), drawnow
12
L1 = zeros(size(L)); % buffer per i calcoli, inizialm. caselle vuote (metto fuori?) for x = 2 : sx - 1 for y = 2 : sy - 1 n = L(x-1,y-1)+L(x,y-1)+L(x+1,y-1)+L(x-1,y)+... L(x+1,y)+L(x-1,y+1)+L(x,y+1)+L(x+1,y+1); if L(x,y) == 0 && n == 3 L1(x,y) = 1; elseif L(x,y) == 1 && (n == 2 || n == 3) L1(x,y) = 1; % sopravvivenza else % L1(x,y) = 0; % morte per solitudine o sovrappopolazione end end % for y end % for x L = L1; % copia il buffer sullo "schermo" da mostrare (il mondo)% L(:,:) = 0; % In questo caso, l’istruzione L1 = zeros(size… va messa fuori!
% salva immagine %fname = ['life_', num2str(time,'%05d')];%fid = fopen(fname, 'wb');%fwrite(fid, L*255, 'int8');%fclose(fid); end % for time
Notare che, avendo deciso di utilizzare un mondo booleano che contenga solo i
valori 0 e 1, è possibile capire quanti primi vicini viventi abbia una cellula semplicemente
sommando il contenuto delle caselle della matrice. Inoltre, nel caso di morte per solitudine o
sovrappopolazione, è superfluo scrivere 0 in L1, in quanto L1 nasce completamente
azzerata (l’istruzione L1(x,y) = 0 è stata inserita comunque, per chiarezza, ma è
commentata perché inutile).
Per riempire inizialmente il “mondo” è possibile impiegare la funzione ginput.
Quest’istruzione, se usata nella sintassi [u, v] = ginput(m), permette di cliccare m
volte sull’immagine corrente (quella visualizzata per ultima) restituendo le coordinate degli m
punti. Se, anziché cliccare, si preme il tasto <invio>, la funzione torna prematuramente, e sia
u che v sono matrici vuote. Notare che il significato di u e v è invertito rispetto alle righe e
alle colonne delle matrici: in questo contesto, u è riferito all’asse delle ascisse (quindi le
colonne) mentre v è riferito alle ordinate (quindi le righe). Occorre quindi poi invertire u con v
prima di usarle per indicizzare la matrice L.
Dopo:
L = zeros(sx, sy);
13
Inserire:
while(1) spy(L) [u, v] = ginput(1); % acquisisco un solo click if isempty(u) || u < 1 || u > sy || v < 1 || v > sx break end u = round(u); v = round(v); L(v, u) = 1 - L(v, u); % “toggle” della cellaend
in cui si è anche considerate l’eventualità che si clicchi al di fuori della matrice delle caselle
(vedere la condizione dell’if) e si è provveduto ad arrotondare a numeri interi i valori delle
coordinate, che l’istruzione ginput restituisce come numeri con virgola, non necessariamente
interi.
Risultato finale.
sr = 100; sc = 100;L = zeros(sr, sc);%L(1,:) = -1;%L(sc,:) = -1;%L(:,1) = -1;%L(:,sr) = -1; L(80,80) = 1;L(81,80) = 1;L(80,81) = 1;L(81,82) = 1;L(80,83) = 1;L(80,83) = 1;L(81,83) = 1;L(82,83) = 1;L(83,80) = 1;L(83,80) = 1;L(83,81) = 1;L(84,82) = 1;L(85,83) = 1;L(84,83) = 1;L(83,83) = 1;L(86,83) = 1; L(74,83) = 1;L(73,87) = 1;L(76,83) = 1; while(1) spy(L) [u, v] = ginput(1); % acquisisco un solo click if isempty(u) || u < 1 || u > sc || v < 1 || v > sr break
14
end u = round(u); v = round(v); L(v, u) = 1 - L(v, u); % toggle della cellaend for time = 1:1000 pause(0.1) % imshow(L, []) spy(L), drawnow L1 = zeros(size(L)); % buffer per i calcoli for r = 2 : sr - 1 for c = 2 : sc - 1 n = L(r-1,c-1)+L(r,c-1)+L(r+1,c-1)+L(r-1,c)+L(r+1,c)+L(r-1,c+1)+L(r,c+1)+L(r+1,c+1); if L(r,c) == 0 && n == 3 L1(r,c) = 1; elseif L(r,c) == 1 && (n == 2 || n == 3) L1(r,c) = 1; % sopravvivenza else % L1(r,c) = 0; % altrimenti morte per solitudine o sovrappopolazione end end % for end % for L = L1; % porta il buffer sullo "schermo" da mostrare % salva immagine %fname = ['life_', num2str(time,'%05d')];%fid = fopen(fname, 'wb');%fwrite(fid, L*255, 'int8');%fclose(fid); end % for time
[gar1970] “The fantastic combinations of John Conway's new solitaire game "life" di
Martin Gardner”, …….
Assorbimento di fotoni e coefficiente di attenuazione lineare
Sia N il numero di bersagli (atomi) per unità di volume nel mezzo. Il coefficiente di
attenuazione μ rappresenta la probabilità di interazione per unità di percorso, ha come
dimensioni l’inverso di una lunghezza e si misura in cm -1. Quando un fascio di fotoni penetra
in un mezzo, a causa delle interazioni con il mezzo stesso l’intensità del fascio decresce
esponenzialmente.
15
Sia infatti NI il numero di fotoni che interagiscono nel tratto di materiale dx e che
quindi vengono sottratti al fascio originario.
dN = -N μ dx
N(x) = No e−μx
N(x) è il numero di fotoni ancora presenti alla profondità x, essendo No il numero di
fotoni iniziale.
Rappresentando N(x) in funzione di x su grafico semilogaritmico (oppure calcolando
il logaritmo di ambo i membri dell’equazione e rappresentando log (N(x) / No) in funzione di
x) si ottiene un andamento lineare:
N(x) / No = e−μx
da cui, calcolando il logaritmo di ambo i membri e applicando la definizione di logaritmo, si
ottiene:
log (N(x) / No) = – μ x
dove la pendenza della retta è –μ.
function p = assorbimento(h, w, d, rho)%h = 10; % dimensioni della matrice, altezza, ...%w = 10; % ...larghezza e...%d = 100; % spessore%rho = 0.005; % probabilita' di occupazionespazio = zeros(h,w,d);spazio2 = zeros(h,w);N = floor(rho*h*w*d);x = ceil (h * rand (N,1));y = ceil (w * rand (N,1));z = ceil (d * rand (N,1));for i = 1 : N % oppure potrei inserire in tutto il campione dei numeri casuali tra 0 e 1 e applicare una soglia che e' la densita' spazio (x(i),y(i),z(i)) = 1; spazio2 (x(i),y(i)) = 1;endspazio2;%pcolor (spazio2)nfot0 = 1000;nfot = 0;for j = 1 : nfot0 rf = ceil (h * rand); cf = ceil (w * rand); if spazio2 (rf,cf) == 0 nfot = nfot + 1; endendp = nfot/nfot0
16
assorbmain.m
h = 10; % dimensioni della matrice, altezza, ...w = 10; % ...larghezza e...rho = 0.007; % probabilita' di occupazione k=0spessori = 10:10:1000for d = spessori k = k + 1; v(k) = assorbimento(h, w, d, rho);end % Siccome devo fare poi il logaritmo, devo fermarmi a risultati di% assorbimento diversi da 0!!! Trovo il primo uguale a 0 e prendo i dati% precedenti; e’ un esercizio utile di per se’!% Alternativa, sommare eps al vettore v. In questo caso pero’ il fatto di % avere pochi fotoni porta a un salto nel grafico dei log! ind = find (v==0);ind1 = ind (1);v = v (1:ind1-1);spessori = spessori (1:ind1-1); figure('color', 'w')subplot(1,3,1)f = fit(spessori', v', 'exp1'); % fit vuole vettori colonna!plot (f, spessori, v, 'o')xlabel('d'), ylabel('N/N_o') subplot(1,3,2)semilogy (spessori, v, 'o')xlabel('d'), ylabel('N/N_o') p = polyfit (spessori,log(v),1)f = polyval(p,spessori);subplot(1,3,3)plot (spessori,log(v), 'o', spessori, f, '-')xlabel('d'), ylabel('log(N/N_o)') disp(['Il coefficiente di assorbimento e'' ' num2str(-p(1))])
SI PUO’ fare break dal loop che calcola a diversi spessori cosi’ appena si trova il
primo zero non si continua piu’ a calcolare; inoltre poi indicizzo solo la parte della matrice
utile
Relazione tra mu e rho
17
Miscele con mu differente, non si puo’ fare se non si fa dipendere mu da una sigma!
f = fit(…, exp1)
fit.a e fit.b per graficare nel semilog dove non posso inserire direttamente f
PARLARE DI WAITBAR
Ancora numeri casuali!
Scrivere un programma (in forma di script) che generi una sequenza di numeri casuali X
(compresi tra 0 e 1 e tratti da una distribuzione uniforme, istruzione rand()), di cui a priori
non si conosca la lunghezza, e individui:
1. quanti numeri della sequenza occorra sommare per superare il valore 5 (il risultato
sia n1, e quindi sum(X) >= 5 per la prima volta quando length(X) sia pari a n1);
2. quanti numeri della sequenza siano estratti affinché ne venga estratto uno compreso
tra 0.80 e 0.85 limiti inclusi (il risultato sia n2, ossia il numero casuale al posto n2 è il
primo della sequenza ad essere compreso tra i limiti assegnati: 0.80 <= X(n2) <=
0.85);
3. quanti numeri della sequenza occorra estrarre prima che la media sia situata tra 0.01
e 0.50 limiti inclusi (sia n3, ovvero 0.01 <= mean(X) <= 0.50 con length(X) = n3).
Si noti che il programma deve segnalare solo la prima volta che tali condizioni si verifichino
(per esempio, dato n1, è chiaro che anche per n1+1 la prima condizione sarà soddisfatta,
ma ciò non dev’essere segnalato!).
Essendo la sequenza a priori di lunghezza non nota, conviene generare i numeri uno alla
volta e accodarli al vettore che contiene la sequenza, ossia X. Ad ogni generazione di
numero casuale, si testerà ciascuna condizione, segnalandone l’effettiva soddisfazione, e –
per evitare che la medesima condizione sia segnalata anche in seguito – si attiverà un flag
18
opportuno (una variabile booleana, che da falsa diventerà vera) che indicherà l’effettivo
raggiungimento di un goal del programma, e sarà a sua volta testata per evitare che tale
goal sia segnalato più volte. Quando tutte e tre le “bandierine” saranno alzate, il programma
avrà finito il proprio compito e potrà terminare.
Conviene far girare il programma più volte per vedere come n1, n2 e n3 varino. E’ anche
interessante chiedersi se e quali dei risultati indicati possano essere in qualche modo
previsti, in base a considerazioni elementari di teoria della probabilità.
A chiarimento di quanto suggerito, se la sequenza di numeri casuali è:
0.6557, 0.0357, 0.8491, 0.9340, 0.6787, 0.7577, 0.7431, 0.3922, 0.6555, 0.1712, 0.7060,
0.0318, 0.2769, 0.0462, 0.0971, 0.8235, 0.6948, 0.3171, 0.9502, 0.0344
si verifica che sum(X(1:7)) = 4.6542 mentre sum(X(1:8)) = 5.0464, quindi n1 = 8
si constata immediatamente che n2 = 3
si verifica che mean(X(1:2)) = 0.3457, quindi n3 = 2.
Naturalmente, il programma smetterà di generare numeri casuali dopo averne estratti 8,
perché tutte le condizioni saranno state soddisfatte.
Conversione di scale di temperatura
Print a table that converts °C (from 0°C to 100°C with intervals of 10°C) to °F. Hint: c
= 5* (f − 32) / 9;
Radice quadrata: metodo di Newton
Il metodo di Newton per il calcolo della radice quadrata di un numero è un approccio
iterativo, ad approssimazioni successive. Dato un numero positivo A, la prima
approssimazione per la radice quadrata di A è xo = 1, e le iterazioni successive sono pari a:
xn+1=xn2
+ A2 xn
L’iterazione termina quando il valore xn elevato al quadrato differisce da A meno di
una quantità definita a priori (errore). La differenza massima accettabile può essere imposta
come valore assoluto o come valore relativo (o percentuale), ovvero:
19
|(x¿¿n2−A)|<∆¿
oppure:
|( xn2−A )|A <ε
Scrivere una funzione, chiamata mysqrt, che abbia due parametri, il valore A di cui
calcolare la radice quadrata, e l’errore desiderato. La funzione restituisca la radice quadrata
di A. Inserire anche l’help, in modo che la funzione risponda correttamente alla richiesta
help mysqrt.
function x=mysqrt(A) % My square root function. % % x = mysqrt(A) % % x is the square root of A. x=1; %First guess err=1; %Set error to something to get started while(err>0.0001) x = myfunction(A,x); % call to local function below end err = abs(A -x.^2); % calculate the errorend
function y = myfunction(A,x) % Local function to calculate the % Newton Raphson Iteration equation y = (x.^2+A)/(2*x);end
Sinusoidi
20
Tracciare il grafico della funzione seguente, con t compreso tra 0 e 10, e passo 0.1.
(s2 indica secondi al quadrato)
Si tratta di un’onda sinusoidale con frequenza che aumenta linearmente (in
particolare da 0 Hz a 5 Hz, tra 0s e 10 s). [Matlab-answers.pdf]
Trovare i punti nei quali la funzione attraversa lo 0 (zero crossing). Purtroppo un
semplice confronto dei valori con lo 0 non è sempre soddisfacente, perché la funzione è
campionata a intervalli regolari che non necessariamente includono i punti in cui essa passa
“esattamente” per lo zero. Questo è un problema generale: per una variabile che assume
valori reali (nel senso di non interi) è rischioso effettuare confronti con quantità precise (cioè
21
x == 0): è sempre meglio individuare metodi basati su una soglia di tolleranza, quali abs(x) <
delta, con delta opportunamente scelto in base al problema.
clcclose allcleart = 0:0.01:10;f = sin(pi*t.^2/2);figure('color', 'w')plot(t,f,'.'), grid on z = t == 0; % non va benez = abs(f) < 0.05 % non va bene fA = f(2:end);fB = f(1:end-1);ff = fA.*fB;ff = ff < 0; iff = find(ff);t2 = t(iff);f2 = zeros(1, length(t2));holdplot(t2, f2, 'o')figure('color', 'w')plot(t,f,'-', t2, f2, 'o'), grid on
problema zeri iniziale, finale, e “veri zeri”!!! In questi ultimi il prodotto e’ 0, dovrei mettere <=
ma cosi’ mi trovo due elementi del vettore posti a 0, potrei di due farne uno solo.
22
t = 0:0.01:10;f = sin(pi*t.^2/2); plot(t,f)
Il calcolo di
Il valore di può essere calcolato a partire da serie numeriche come le seguenti:
23
π 2−816
=∑n=1
∞ 1(4 n2−1 )2
oppure tramite metodi iterativi come il seguente:
Algoritmo di Gauss-Legendre o di Brent-Salamin
1. Valori iniziali; a = 1; b = 1 / sqrt(2); t = ¼; x = 1
2. Repeat the following statements until the difference of a and b is within the desired accuracy; y = a a = (a+b) / 2 b = sqrt(b*y) t = t − x * (y−a)² x = 2 * x
Ripetere le seguenti istruzioni finché la differenza fra e è della precisione voluta:
3. Il valore di p è approssimato dalla formula:
π = ((a+b)²) / (4*t)
Le prime tre iterazioni danno:
How many terms are needed to obtain an accuracy of 1e-12? How accurate is the sum of 100 terms of this series?
How many repeats are needed to estimate pi to an accuracy of 1e-8? 1e-12?
24
Compare the performance of this algorithm to the result from Exercise 1, above.
INVECE DI USARE (A+B)/2, vedere che un pigreco disti dal precedente meno di una
soglia
Vedere anche questo link
http://www.facstaff.bucknell.edu/maneval/help211/progexercises.html
Poi vedere in Matlab_probs2.pdf il calcolo dell’esponenziale, e la discussione sull’uso
di for oppure no, e di una variabile per contenere x^2
Poi
http://mathworld.wolfram.com/PiFormulas.html
https://it.wikipedia.org/wiki/Algoritmo_di_Gauss-Legendre
9. Compute and plot the path(s) of a set of random walkers which are confined by a pair of barriers at +B units and -B units from the origin (where the walkers all start from). A random walk is computed by repeatedly performing the calculation xj+1 = xj + s where s is a number drawn from the standard normal distribution (randn in MATLAB). For example, a walk of N steps would be handled by the code fragment x(1) = 0; for j = 1:N x(j+1) = x(j) + randn(1,1); end There are three possible ways that the walls can "act": a. Reflecting - In this case, when the new position is outside the walls, the walker is "bounced" back by the amount that it exceeded the barrier. That is, when xj+1 > B, xj+1 = B - |B - xj+1|
when xj+1 < (-B), xj+1 = (-B) + |(-B) - xj+1|
25
If you plot the paths, you should not see any positions that are beyond |B| units from the origin. b. Absorbing - In this case, if a walker hits or exceeds the wall positions, it "dies" or is absorbed and the walk ends. For this case, it is of interest to determine the mean lifetime of a walker (i.e., the mean and distribution of the number of steps the "average" walker will take before being absorbed). c. Partially absorbing - This case is a combination of the previous two cases. When a walker encounters a wall, "a coin is flipped" to see if the walker relfects or is absorbed. Assuming a probability p (0 < p < 1) for reflection, the pseudo-code fragment that follows uses the MATLAB uniform random-number generator to make the reflect/absorb decision: if rand < p reflect else absorb end
What do you do with all the walks that you generate? Compute statistics, of course. Answering questions like What is the average position of the walkers as a function of time? What is the standard deviation of the position of the walkers as a function of time? Does the absorbing or reflecting character influence these summaries? For the absorbing/partial-reflection case, a plot of the number of surviving walkers as a function of step numbers is a very interesting thing. is useful, informative and interesting, particularly if graphically displayed.
BWLABEL, REGIONPROPS
useBlobs.m
BW = logical([1 1 1 0 0 0 0 0 1 1 1 0 1 1 0 0 1 1 1 0 1 1 0 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 0 1 1 0 1 1 1 0 0 0 0 0]);figure, imshow(BW, []) L = bwlabel(BW, 4); % ,4 oppure ,8 (default)s = regionprops(L, 'basic'); % area, centroid, bboxs(1).Centroids(3).BoundingBox %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
26
[I, map] = imread('blobs.gif');figure, imshow(I, [])I = ind2gray(I,map);figure, imshow(I, []) IM = I < 130;figure, imshow(IM, []) IM = imclearborder(IM); I2 = I .* uint8(IM);figure, imshow(I2, []) L = bwlabel(IM);figure, imshow(L, []) % impixelinfo s = regionprops(L, 'basic'); % area, centroid, bboxlength(s) s(4).Areaareas = [s.Area]; figure, hist(areas, 20) %idx = find(areas > 800);idx = find(areas > 500);L1 = L == idx(1);figure, imshow(L1, []) % conserva l'oggetto "grosso"; potrei cercare l'oggetto di area massima e visualizzarlo figure, imshow(I, [])hold onfor j = 1 : length(idx) c = s(idx(j)).Centroid plot(c(1), c(2), '+g')endhold off% falsi positivi e falsi negativi % dall'help:%centroids = cat(1, s.Centroid);%imshow(BW)%hold on%plot(centroids(:,1), centroids(:,2), 'b*')%hold off cs = [s.Centroid];x = cs(1:2:end);y = cs(2:2:end);holdplot(x, y, 'sg')hold %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
27
s = regionprops(L, 'Area', 'Centroid', 'Eccentricity', 'Perimeter'); % area, centroid, etc% notare: non posso accoppiare basic con altre opzioni!! % CIRCULARITY (shape factor) https://en.wikipedia.org/wiki/Shape_factor_(image_analysis_and_microscopy)% circ = 4*pi*A/p^2 areas = [s.Area];perimeters = [s.Perimeter];circularities = 4*pi*areas./perimeters.^2;eccentricities = [s.Eccentricity]; figure, hist(circularities, 50), title('circularities')figure, hist(eccentricities, 20), title('eccentricities') % quali sono gli oggetti con circolarita' > 1.5 idx = find(circularities > 1.5);figure, imshow(I, [])hold onfor j = 1 : length(idx) c = s(idx(j)).Centroid plot(c(1), c(2), '+g')endhold off % quali sono gli oggetti con circolarita' > 0.9 ma < 1.5 idx = find(circularities > 0.9 & circularities < 1.5);figure, imshow(I, [])hold onfor j = 1 : length(idx) c = s(idx(j)).Centroid plot(c(1), c(2), '+g')endhold off % che area hanno gli oggetti che stiamo conservando areaOggettiBuoni = areas(idx);figure, hist(areaOggettiBuoni, 10)
Esercizio xx: “Media”
SCRIVERE UN PROGRAMMA CHE:
28
1) Sia composto da due function, di cui una svolga il ruolo di main program e l’altra di
function esecutrice
2) Il main chiede all’utente su quanti numeri deve lavorare (valore intero N), e i due
estremi di un intervallo (A e B)
3) Il main chiama poi la function passandole N, A, B
4) La function crea un vettore di N numeri casuali compresi tra A e B…
5) …ne calcola la media e la restituisce al programma principale
6) Il programma principale stampa il vettore e la media, chiedendo all’utente di inserire
un numero. In base al numero inserito scrive se il numero è maggiore o minore della
media trovata prima
ANALISI D’IMMAGINE
Esercizio 1: “Diatomee”
SCRIVERE UN PROGRAMMA CHE:
1) carichi l'immagine Diatoms.jpg; il path dell'immagine è passato come parametro; si
noti dal workspace che l'oggetto caricato è una matrice 3D (colori RGB, anche se
l’immagine appare a vista monocromatica) per cui è necessario trasformarla in
immagine a toni di grigio
2) la visualizzi (figura 1)
3) crei una maschera che comprenda tutti gli oggetti, e sia bianca dove vi sono pixel
appartenenti alle diverse diatomee. Per farlo, occorre
a. applicare all’immagine una soglia ad hoc che sfrutti il bordo nero (figura 2)
b. adoperare l’operazione di closing morfologico per riempire i piccoli gap nel
bordo (figura 3)
c. riempire i buchi (imfill(..., 'holes'))(figura 4)
4) visualizzi la nuova immagine
29
5) escluda le diatomee a contatto con il bordo, in quanto visualizzate solo in parte
(imclearborder) (figura 5)
6) etichetti (bwlabel) gli oggetti e poi ne cerchi le statistiche (regionprops) (figura 6)
7) individui la diatomea di lunghezza massima, scrivendone il valore a video e
segnando il centroide dell’oggetto con una crocetta (figura 7)
8) individui la diatomea di area massima, scrivendone il valore a video e segnando il
centroide dell’oggetto con una crocetta.
1) 2)
3) 4)
30
5) 6) 7)
(Immagine tratta da http://bioweb.uwlax.edu/bio203/2010/ramsby_jord/classification.htm)
Esercizio 2: “Pseudo OCR”
31
SCRIVERE UN PROGRAMMA CHE:
1) carichi un'immagine che mostra alcune lettere dell'alfabeto (optik.jpg); il path
dell'immagine è passato come parametro; si noti dal workspace che l'oggetto
caricato è una matrice 3D, cioè a colori RGB (righe x colonne x 3)
2) la visualizzi (figura 1);
3) conservi dell'immagine solo i pixel grigi molto scuri, cioè quelli che hanno tutte le
componenti RGB minori di (per esempio) 50
4) visualizzi la nuova immagine (figura 2);
5) Ingrandendo l'immagine, se si nota che le lettere hanno dei difetti al loro interno
(buchi), li si eliminino con un'operazione di closing morfologico con un piccolo
elemento strutturante (un disco di raggio 1). Si verifichi che così non vi sono buchi
nelle lettere (figura 3);
6) si etichettino (bwlabel) gli oggetti e poi si cerchino le statistiche (regionprops).
Vogliamo individuare la lettera D, l'unica provvista di un buco. Statistica consigliata:
EulerNumber; il numero di eulero è dato dal numero di oggetti in ciascuna regione (in
questo caso sempre 1) meno il numero di buchi (1 per la D, 2 per la B, 0 per la A,
etc)
7) si individui la lettera D cercando, ad esempio con un ciclo for, l'oggetto che ha
numero di eulero opportuno, e si crei una figura in cui essa sola risulta visibile (una
volta conosciuto l'indice dell'oggetto, nell'immagine di etichette bisogna accendere
solo i pixel che hanno quel valore); (figura 4);
32
1) 2)
3) 4)
(Immagine tratta con modifiche da http://www.ocr-systeme.de/images/optik.jpg)
Esercizio 3: “Linea mediana”
33
Individuare la linea mediana di un’immagine CT cerebrale
Scopo….. Immagine: CT-head.jpg (tratta dal software ImageJ (https://imagej.net/).
1) posso partire dalla maschera del cervello (quindi non dall'immagine originale)
2) il cervello occupa una parte dell'immagine: trovo due valori di riga, minima e
massima, entro i quali sicuramente il cervello c'e' tutto; Individuo riga massima e riga
minima manualmente (eg tra 210 e 440) ma provo anche a individuare
automaticamente, ossia calcolando un profilo integrato dell'immagine (proietto,
sommando, sull'asse verticale, e studio l'istogramma; banalmente, prendo i valori
estremi non nulli)
3) faccio un loop sulla riga, per ogni riga row tra 210 e 440 prendo il profilo: p =
improfile(I, [1 512], [row row])
4) faccio la "derivata", o meglio il rapporto incrementale, del profilo dp = diff(p)
5) trovo i punti in cui dp e' diverso da 0: ci sono due punti, uno di gradiente positivo e
uno negativo, che corrispondono al fronte di salita e al fronte di discesa del profilo (i
punti in cui si entra e poi si esce dalla maschera). I punti potrebbero essere piu' di
due solo verso i bordi dell'immagine: se succede magari si butta via il profilo, per
evitare problemi (dopo aver visto che succede).
6) si fa la media delle x (le colonne) dei due fronti di ingresso nel tessuto cerebrale
(discesa e salita rispettivamente): questo da' la posizione, su quella particolare riga,
della linea mediana, m(row)
7) invece di plottare (row, m(row)) per tutte le row tra 210 e 440, si fa un fit lineare, per
trovare la riga che meglio approssima quei punti (che non saranno realmente
allineati): pol = polyfit(X,Y,N) dove le X sono le colonne trovate, le Y sono le
righe (210:440), N = 1. Poi si disegna sull'immagine con Y2 = polyval(pol,X); e poi il
plot (X,Y2).
34
Esercizio 4: “Monete”
Sul bancone di un negozio, lasciamo alcune monete come pagamento di un
acquisto. Il negoziante, molto pigro, vuole un programma che riconosca le monete (dandone
il valore in euro), e ne calcoli automaticamente il totale.
Il programma che scriveremo, sfrutterà il fatto che le diverse monete hanno tutte diametro diverso tra loro, secondo la tabella riportata nel seguito (tratta da
http://euro.raddos.de/italiano/monete.php).
35
Un programma versatile, dovrebbe far fronte a qualunque combinazione di monete,
anche tra loro in contatto o parzialmente sovrapposte, e probabilmente sfrutterebbe anche il
colore, per distinguere i diversi pezzi.
Per semplificare, noi assumeremo (in maniera molto arbitraria!!!) che:
a) tutti i pezzi possano essere presenti, ma vi sia necessariamente la moneta da 1
cent (la più piccola!)
b) le monete siano ben separate tra loro.
SCRIVERE UN PROGRAMMA CHE:
1) abbia forma di function, supponiamo sia la funzione “main”
2) carichi l'immagine eire-euros.jpg (tratta da http://www.katsandogz.com/ireland/)
il nome dell'immagine sia passato come parametro alla funzione “main”
3) la visualizzi (figura 1)
4) si noti dal workspace che l'oggetto caricato è una matrice 3D (colori RGB) per cui
(volendo usare il diametro come feature discriminante, e non i colori) il programma
trasformi l’immagine in toni di grigio (rgb2gray), e la visualizzi (figura 2)
5) visualizzi l’istogramma, allo scopo di trovare una soglia opportuna per la
segmentazione delle monete rispetto allo sfondo (figura 3); si usi anche
eventualmente impixelinfo per il medesimo scopo (e si commenti nel programma il
valore trovato)
6) crei una maschera che comprenda tutti gli oggetti, e sia bianca dove vi sono pixel
appartenenti alle diverse monete. Per farlo, occorre
i. applicare all’immagine la soglia ad hoc in precedenza definita (figura 4)
ii. riempire i buchi nelle maschere delle monete (figura 5)
iii. eliminare il rumore tipo “sale” con operazioni morfologiche (che non
modifichino le dimensioni degli oggetti) (figura 6)
7) etichetti (bwlabel) gli oggetti e poi ne cerchi le proprietà (regionprops), in
particolare il diametro e la posizione dei centroidi
8) ora, la parte di elaborazione d’immagine è terminata: sapendo che nell’immagine vi è
certamente la moneta più piccola (1 cent), come scritto nella prima delle assunzioni
36
alla base del programma, si calcoli il rapporto tra le dimensioni in pixel dell’oggetto
più piccolo, e il diametro in mm di tale moneta (sia per esempio la variabile “ratio”, le
cui unità di misura sono pixel/mm). Questo rapporto di scala permetterà di calcolare
le dimensioni in mm di tutti gli oggetti presenti nell’immagine, allo scopo di
confrontare queste dimensioni con quelle conosciute delle monete;
9) per effettuare il confronto, conviene mettere in un vettore i valori delle monete (in
euro: euro = [0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1, 2]); in un altro vettore i rispettivi
diametri in mm (diametri = [16.25, 18.75, 21.25 etc]). In questo modo si potrà cercare
nel secondo vettore la dimensione di una moneta individuata, e poi dedurre (dalla
medesima posizione nel primo vettore) di che moneta si tratti.
10) Siccome c’è sempre un certo errore nel calcolo delle dimensioni di una moneta
dall’immagine, per cercare un particolare valore di diametro (“valore_cercato”)
ottenuto dal diametro in pixel e dalla variabile “ratio”, conviene non usare l’operatore
==, ma scrivere una funzione che trovi nel vettore “diametri” il valore più vicino a
quello cercato:
deltas = abs(diametri – valore_cercato); [delta idx] =
min(deltas); ora idx è l’indice del valore cercato.
11) Via via che si individuano le monete, occorre marcarle (funzione text) sulla figura 1
con il loro valore (figura 7) e sommare i valori per avere il totale: totalValue = euro
3.89
12) Si verifichi se il programma funziona anche con l’immagine ‘euros-verso.jpg', ed
eventualmente si aggiusti la soglia iniziale allo scopo di correggere il codice.
37
Figura 1
Figura 2
0
500
1000
1500
2000
2500
3000
3500
4000
4500
5000
0 50 100 150 200 250
Figura 3
38
Figura 6
0.2 euro
1 euro
0.02 euro
2 euro
0.01 euro
0.5 euro
0.01 euro
0.1 euro
0.05 euro
Figura 7
Misure e peso delle monete
1 centesimodiametro:
16,25 mm
spessore: 1,67 mm
peso: 2,30 g
40
2 centesimidiametro:
18,75 mm
spessore: 1,67 mm
peso: 3,06 g
5 centesimidiametro:
21,25 mm
spessore: 1,67 mm
peso: 3,92 g
10 centesimidiametro:
19,75 mm
spessore: 1,93 mm
peso: 4,10 g
20 centesimidiametro:
22,25 mm
spessore: 2,14 mm
peso: 5,74 g
50 centesimidiametro:
24,25 mm
spessore: 2,38 mm
peso: 7,80 g
41
1 eurodiametro:
23,25 mm
spessore: 2,33 mm
peso: 7,50 g
2 eurodiametro:
25,75 mm
spessore: 2,20 mm
peso: 8,50 g
http://www.mentecritica.net/soldi-soldi-soldi/informazione/il-bello-della-politica/
ilbuonpeppe/4084/
Esercizio 5 (Testo d’esame del 25/7/2011 (a)) “Simulazione di cellule”
Sia data l’immagine mostrata nella figura seguente, contenuta nel file circlesx.png
(derivata da una delle immagini di cui MATLAB è corredato, circles.png):
42
Con un po’ di fantasia, l’immagine potrebbe rappresentare un insieme di cellule
(bianche) viste al microscopio, così vicine tra loro da toccarsi, alcune delle quali
internamente corredate di due corpuscoli (“nuclei”, rappresentati dalle macchiette ellittiche
grigie), altre da uno solo.
Si richiede di:
a) trovare il raggio (in pixel) delle “cellule”, supposte circolari e tutte uguali
b) individuare e segnalare (tramite una crocetta nel rispettivo centroide) le cellule che
hanno al loro interno due nuclei anziché uno.
Se le cellule non fossero a contatto, il compito sarebbe semplice: consisterebbe in:
applicare una soglia opportuna per binarizzare l’immagine, in modo che
nell’immagine bianco/nero risultassero visibili le maschere delle varie “cellule” come
cerchi bianchi su sfondo nero (senza differenziazione dei nuclei);
trovare in essa gli oggetti connessi (i cerchi) chiedendone la misura del raggio e la
posizione dei centroidi;
per ciascun oggetto dell’immagine binaria, isolare dall’immagine originale la cellula
corrispondente (tramite prodotto per la singola maschera), applicare una soglia per
evidenziare solo i nuclei, e trovare gli oggetti connessi individuati da ciascuna
maschera.
43
Invece, essendo le cellule a contatto, il lavoro da fare è maggiore!
Suggerimenti:
Per separare le cellule, si può ricorrere ad operazioni di morfologia matematica (dopo
aver trasformato l’immagine in bianco/nero). In seguito, si potrà individuare il
centroide di ciascuna cellula. Le operazioni morfologiche per il distacco, però,
ridurranno la dimensione delle cellule, per cui la misura del raggio sarà alterata.
Per trovare il raggio effettivo (originale) delle cellule, si suggerisce di ricostruire, a
partire dal centroide, una delle cellule tramite (ad esempio) una function di disegno di
cerchi (già vista in altri esercizi fatti durante il Corso). Si farà variare il raggio della
cellula ricostruita iterativamente, da un valore minimo ad uno massimo, confrontando
– ad ogni ricostruzione – l’immagine ottenuta con quella originale (trasformata in
bianco/nero). Il confronto potrà essere ottenuto con l’AND tra l’immagine ricostruita e
l’originale binarizzata: tale AND sarà identico all’immagine ricostruita, fintantoché il
raggio usato per la ricostruzione sarà più piccolo o uguale a quello reale (i cerchi
disegnati cadono dentro quelli originali); appena il raggio supererà quello effettivo, i
cerchi ricostruiti conterranno anche alcune parti che nell’immagine iniziale binarizzata
sono neri, e quindi l’AND tra le due immagini sarà più piccolo dell’immagine
ricostruita. Calcolando il rapporto tra la aree accese nelle due immagini, questo sarà
dunque pari all’unità all’inizio, per poi decrescere quando il raggio usato per la
ricostruzione supererà quello effettivo dei cerchi.
Trovato R, occorrerà usare i centroidi delle singole cellule, e il raggio R, per
ricostruire le maschere delle singole cellule; con queste maschere si isoleranno
nell’immagine iniziale (opportunamente binarizzata) i nuclei che ad esse competono
(oggetti connessi), che saranno contati per decidere se sono uno o due. Si
metteranno infine le crocette in base a quest’ultima misura.
Il grafico per cercare il raggio dei cerchi è ad esempio:
44
5 10 15 20 25 30 350.55
0.6
0.65
0.7
0.75
0.8
0.85
0.9
0.95
1
che identifica R=18.
Il risultato dell’esecuzione del programma dev’essere:
Esercizio 6: “Pentagoni e triangoli: distinguere figure geometriche”
45
Preliminarmente, consideriamo l'immagine pentagoni_e_triangoli.ppm; essa raffigura
i contorni di due figure geometriche diverse (pentagoni e triangoli, appunto), e di varie
dimensioni.
Effettuiamo dapprima le operazioni necessarie per preparare l'immagine ad essere
trattata con bwlabel: ossia trasformiamo la figura data in un'immagine in bianco e nero in
cui in bianco vi sono gli oggetti, e poi riempiamo l'interno degli oggetti. Ci accorgiamo che,
per fare ciò, la soglia usata nell'istruzione im2bw è critica, in quanto c'è il rischio che i lati
degli oggetti risultino spezzati e quindi le operazioni di riempimento non vadano a buon fine.
Con un'opportuna scelta del livello di soglia, il problema si risolve e l'immagine finale
presenta le figure piene, in bianco su sfondo nero.
Ora passiamo all'immagine effettivamente da trattare per risolvere il problema.
Ripetendo le operazioni già effettuate, ci accorgiamo che le cose non funzionano
correttamente sulla pentagoni_e_triangoli_difettosi.ppm! La ragione è che i bordi di molte
figure geometriche, in questa seconda immagine, sono frammentati in uno o più punti:
caricare l'immagine e verificare che, ad esempio, il primo pentagono in alto a sinistra ha il
bordo difettoso.
Inseriamo nello script un'istruzione che permetta di ricostruire i bordi rovinati. Si tratta
di un'operazione morfologica che ricongiunge i bordi con un elemento strutturante e, così,
chiude le figure.
A questo punto l'output sarà ciò di cui abbiamo bisogno: oggetti bianchi chiusi, su
sfondo nero.
Vogliamo ora distinguere i pentagoni dai triangoli (si tratta di figure regolari).
Suggerimento: i pentagoni appaiono molto più "compatti", ossia simili ad un cerchio,
dei triangoli. Una diffusa misura di compattezza di una figura geometrica è il rapporto tra
l'area della figura, e l'area di un cerchio di uguale perimetro (circularity ratio):
R=4 πAp2
Usare il rapporto di circolarità per distinguere le figure: disegnare un istogramma di
R, verificare se nell'istogramma compare una soglia naturale, usarla per distinguere triangoli
46
da pentagoni, porre una crocetta al centro dei triangoli, e nulla, o un altro simbolo, al centro
dei pentagoni (nella figura originale).
Esercizio 7: “Segmentazione di una slice CT polmonare, e individuazione di un
nodulo subpleurico”
E’ data una slice di CT polmonare (lungs.png), appartenente a un paziente con
manifestazioni neoplastiche di natura nodulare. In particolare, si osserva un nodulo
subpleurico nel polmone destro (convenzione radiologica), in alto (Fig. 1):
Figura 1
Vogliamo scrivere un programma Matlab che produca due risultati:
a. segmentare il tessuto polmonare, in modo da eliminare tutte le strutture esterne al
parenchima visualizzando solo quest’ultimo
47
b. evidenziare (eventualmente con la presenza di falsi positivi) il nodulo pleurico,
dandone posizione e dimensioni approssimative in mm, considerando che le
dimensioni del pixel dell’immagine sono 0.8 mm x 0.8 mm.
Allo scopo, ecco la sequenza di operazioni consigliata, da eseguire da programma:
1) Caricare e visualizzare l’immagine (file cdPi110-sl249.png).
2) Calcolare e visualizzare l’istogramma dei grigi (scegliendo un numero di bin
opportuno, e facendo attenzione al fatto che l’immagine è formalmente a colori,
quindi va convertita), Fig. 2. L’istogramma è manifestamente bimodale; il picco di
bassa intensità di grigi rappresenta tutte le regioni scure, compreso il parenchima
polmonare che desideriamo evidenziare, mentre il picco di intensità maggiore
comprende muscoli, vasi, osso. Dall’istogramma, si individui un valore corretto di
soglia, per la separazione tra parenchima polmonare e le strutture dense di contorno
(ma anche in esso contenute, come i vasi!). La soglia sarà circa a metà tra le due
regioni modali;
-50 0 50 100 150 200 250 3000
1
2
3
4
5
6x 10
4
Figura 2
3) Applicare la soglia; dopo la segmentazione, il tessuto polmonare dovrà essere
bianco (“oggetto”), mentre i tessuti densi dovranno essere posti a 0 (Fig. 3). Notiamo
però la presenza di “oggetti” spuri, dovuti allo sfondo dell’immagine originale,
ovviamente sotto soglia. Occorrerà quindi eliminare opportunamente gli “oggetti” a
contatto con i bordi dell’immagine. Essendo poi il parenchima attraversato dai vasi,
avremo piccole zone vuote nei polmoni, anch’esse da riempire opportunamente.
Eventualmente si potrà eliminare rumore di tipo “sale” (che comunque andrà via nelle
operazioni seguenti).
48
Figura 3
4) Individuare gli oggetti rimasti nella maschera (bwlabel etc): saranno i polmoni, ma
non solo (è presente la trachea); con una soglia sull’area, si conserverà solo la
maschera dei polmoni, da visualizzare.
Figura 4
5) Ottenere quindi il risultato (a), adoperando l’immagine prodotta come maschera su
quella originale (si può usare l’istruzione roifilt2, oppure altro metodo di
filtraggio). Si conservi e visualizzi dunque solo il parenchima polmonare (Fig. 5).
49
Figura 5
6) Ci si occupi ora del nodulo polmonare subpleurico: si effettui un’opportuna
operazione morfologica per lo smussamento del bordo della maschera, che diverrà
più dolce contenendo ora all’interno della maschera anche il nodulo, e poi si
sottragga dalla maschera così ottenuta quella pre-morfologia: i noduli pleurici
eventuali risalteranno. Per l’operazione morfologica si scelga un disco di diametro
opportuno: se è troppo piccolo, la forma del nodulo così estratto non sarà aderente
alla realtà.
7) A questo punto, si verifichi che effettuare l’operazione morfologica sui due polmoni
contemporaneamente porta a problemi nella regione tra essi compresa (i polmoni
rischiano di essere collegati da un “ponte” di pixel, che poi resta nella sottrazione,
Fig.6, dando sia il nodulo effettivamente presente che una struttura spuria nella zona
centrale dell’immagine). Si organizzi quindi il programma in modo che lavori su un
polmone alla volta.
50
Figura 6
8) Si calcoli la statistica dei candidati noduli eventualmente trovati (bwlabel etc), con
le loro caratteristiche distintive (e.g. feature come area, perimetro, circolarità =
4A/p2) e si deduca una regola, se possibile, per individuare il nodulo; la dimensione
minima dei noduli identificabili è dell’ordine di qualche mm, per cui si può assumere
(dato che la dimensione di un voxel nell’immagine fornita è 0.8 mm x 0.8 mm) che
oggetti più piccoli di 3 o 4 voxel possono essere eliminati a priori. Si costruisca una
matrice di feature, in cui ogni riga compete a uno dei candidati, e le colonne sono:
etichetta (0 se non nodulo, 1 se nodulo), area, perimetro, circolarità.
9) Evidenziare il nodulo nell’immagine di figura 5, con una circonferenza che lo circondi.
Per generare una circ onferenza, si usi un metodo a piacere, come l’istruzione strel o
altro, creando due cerchi di raggio diverso e sottraendo le due immagini una
dall’altra. La corona circolare così costruita, sarà poi sovrapposta alla figura 5, dando
la figura 7. Sarà così raggiunto il risultato (b).
51
Figura 7
Note: uso di roifilt2 per l’applicazione di una maschera.
E’ possibile adoperare l’istruzione roifilt2 per applicare una maschera all’immagine. La
sintassi è:
Im = roifilt2(0, I, MASKc);
dove I è l’immagine originale, MASKc è la maschera da applicare, complementata, ossia
con pixel a 0 in corrispondenza della parte dell’immagine da conservare, e a 1 nella parte da
azzerare. Il primo parametro vale 0 e rappresenta un filtro “nullo”, che restituisce appunto il
valore 0 per tutti i pixel dell’immagine che hanno, in MASKc, valore 1. Laddove MASKc sia
0, invece, il valore restituito è quello originale, non filtrato. Il risultato è la filtratura
dell’immagine come desiderata, equivalente a I .* MASK.
Note: uso di medfilt2 per l’eliminazione di rumore di tipo “sale” (o “pepe”).
E’ possibile adoperare medfilt2 per eliminare rumore del tipo sale o pepe, ossia con netti
picchi bianchi al di sopra di uno sfondo scuro, o viceversa. La sintassi è:
BW = medfilt2(BW,[3 3])
Si applica cioè un filtro mediano che ha, come noto, la proprietà di eliminare outlier, in
questo caso costituiti dai picchi o dai pozzi netti rispetto allo sfondo mediamente uniforme.
52
Esercizio 7: “Cellule a X”
Sia data l’immagine FigP1036-DIP.tif, tratta da “Digital Image Processing” (Gonzalez, Woods, & Eddins, 3rd ed) e rappresentante delle cellule. Vogliamo evidenziare quelle a forma di X, sia nell’immagine stessa (figura 1, a sx), che nella medesima immagine ruotata di 90° (Figura 1, a dx). Gli step da realizzare sono indicati tra parentesi nel testo.
Figura 1
Dopo aver visualizzato l’immagine (step 1) innanzitutto occorre (step 2) applicare all’immagine una soglia opportuna che evidenzi le cellule rispetto allo sfondo (applicazione di una maschera, che mostri le cellule in bianco su sfondo nero), poi (step 3) si riempiano i buchi visibili in ciascuna cellula, infine (step 4) si elimini il rumore tramite un’operazione morfologica con opportuno (piccolo) elemento strutturante (figura 2, da sinistra a destra e poi dall’alto in basso, prime quattro immagini). A questo punto (step 5) si etichettino gli oggetti (bwlabel), si trasformi il risultato in immagine a colori (label2rgb), si visualizzi, e sull’immagine a colori si visualizzino nella posizione giusta anche le etichette (con l’istruzione text coadiuvata da num2str, basandosi su regionprops con attributo centroid).
1
2
3
4 5
6
7
8
9
1011
12
13
14
15
16
17
1819
20
21
22 23
2425
26
27
28
29
30
31
32
33
3435
36
3738
3940
41
42
43
44
45
46
47
48
49
50
51
0.2 0.4 0.6 0.8 1Solidity
0
10
20
30
40
50
60
Equ
ivD
iam
eter
1
2
3
4
5
6 7 8 9
1011
12
13
14
15
16
17
18
1920
2122
23
24
25
26
27
2829
30
31
32
33
3435
36
37
38
39
404142
43 4445
46
47
48
49
50
51
Figura 2
53
Sempre con regionprops, si calcolino le grandezze: Solidity, Eccentricity, EquivDiameter; si visualizzino a coppie (6) su uno scatterplot, e si decida quale coppia di queste grandezze è in grado di identificare bene e con facilità gli oggetti 1 e 30, che sono quelli di nostro interesse (ultima immagine della fig. 1, che mostra già la giusta coppia di feature, sebbene con le etichette mascherate). Si sfrutti tale conoscenza per decidere che soglie usare, e poi mettere, sull’immagine originale, un marker sulle due cellule a forma di X. A questo punto si faccia ruotare l’immagine di 90° (rot90) e si veda se le soglie sono ancora adatte alla necessità, ossia evidenzino ancora i due oggetti a X, sebbene ruotati. Per verificare completamente l’invarianza per rotazioni occorrerebbe provare ovviamente anche con angoli qualunque. Usare l’istruzione imrotate per ruotare l’immagine di un angolo arbitrario e verificare se l’intera procedura continua a funzionare (effettuare il test per qualche angolo a piacere).
Ricorsione
function f = fattorialeR(n)if n == 1 f = 1;else f = n * fattorialeR(n-1)endend
function f = fattorialeI(n)f = 1;for j = 1 : n f = f * j;endend
function n = fibonacci2(k)disp('Eccomi')if k == 1 n = 1;else if k == 2 n = 1; else n = fibonacci2(k-2) + fibonacci2(k-1); endendend
MANCA CRABS e BRATS; inserire un esempio sulla mammografia; inserire i vulcani
di venere.
54
SOLUZIONI
Riflessometro
function [z,y]=riflessi(m, t1, t2)y = zeros(m,1); % conterra' i tempi di rispostaz = rand(m,1) * (t2-t1) + t1; % tempi di ritardo
55
for i = 1 : m disp('attenzione...') fflush(stdout) % Solo in OCTAVE! https://stackoverflow.com/questions/2633019/how-can-i-flush-the-output-of-disp-in-matlab-or-octave pause(z(i)); disp('premi un tasto') tic pause y(i)=toc;enddata = [y z];save('data.txt', 'data', '-ascii')
function [z,y]=riflessi(m,t1,t2)y=zeros(1,m); % conterra' i tempi di rispostaz=zeros(1,m); % tempi di ritardofor i=1:m r=rand(1,1); t=r*(t2-t1)+t1; disp('attenzione') pause(t); disp('premi un tasto') tic pause y(i)=toc; z(i)=t;enddata = [y z];save('data.txt', 'data', '-ascii')
SEGMENTAZIONE
% NON TERMINATO: CALCOLA LA CIRCOLARITA' MA NON LA USA close allclear I1 = imread('cdPi110-sl249.png'); figure('color', 'w'), imshow(I1, []) % calculate the histogramI1 = rgb2gray(I1);[c,b] = imhist(I1,50);%figure, stem(b,c)figure('color', 'w'), bar(b,c)
56
% imhist(I1,1000); % commentare il plot, cambiare assi % individuare la soglia, a occhio!!! Il vero algoritmo la fa automaticamente th = 100; BW = I1 < th; % only consider "dark" objectsfigure('color', 'w'),imshow(BW); BW = imclearborder(BW); % drop objects in contact with the image border (they cannot be the lungs)figure('color', 'w'),imshow(BW); BW = imfill(BW, 'holes'); % imfill will eliminate background regions within the (foreground) lungsfigure('color', 'w'),imshow(BW); %BW = medfilt2(BW,[3 3]); % now we have to eliminate foreground (white) "salt" noise from the background (black), alternative? %BW = imopen(BW, strel('disk',1));%figure('color', 'w'),imshow(BW); L = bwlabel(BW, 8); % put 1, 2, .. in connected objects (8 is connectivity)figure('color', 'w'); imshow(L, []); RGB = label2rgb(L, 'jet', 'k'); % colora secondo la mappa "jet" di colorifigure('color', 'w'); imshow(RGB, []); % get statistics... stats = regionprops(L, 'Area', 'Centroid'); stats.AreaallAreas = [stats.Area]idx_area = find(allAreas > 10000)stats=stats(idx_area) MASK = ismember(L, idx_area);MASKc = imcomplement(MASK);figure('color', 'w'), imshow(MASKc,[]) I = roifilt2(0, I1, MASKc);figure('color', 'w'), imshow(I,[]) %----- figure('color', 'w'), imshow(MASK,[]) closedMASK = imclose(MASK, strel('disk', 10));figure('color', 'w'), imshow(closedMASK,[]) delta = closedMASK - MASK;
57
figure('color', 'w'), imshow(delta,[]) %------ for j = 1 : length(stats)MASKj = ismember(L, idx_area(j));figure('color', 'w'), imshow(MASKj,[]) closedMASKj = imclose(MASKj, strel('disk', 10));figure('color', 'w'), imshow(closedMASKj,[]) delta = closedMASKj - MASKj;figure('color', 'w'), imshow(delta,[]) Lj = bwlabel(delta);stats = regionprops(Lj, 'Area', 'Perimeter', 'Eccentricity');areas = [stats.Area];perimeters = [stats.Perimeter]circularity = (4*pi*areas) ./ (perimeters.^2); end
Cellule ad X
close allI = imread('FigP1036-DIP.tif');figure('color', 'w')subplot(2,3,1), imshow(I, []);J = I > 170; % anche 200 funzionerebbe, ed eviterebbe l'opening, ma sarebbe meno conservativosubplot(2,3,2), imshow(J, []);K = imfill(J, 'holes');subplot(2,3,3), imshow(K, []);L = imopen(K, strel('disk', 1));subplot(2,3,4), imshow(L, []); M = bwlabel(L, 8);N = label2rgb(M);subplot(2,3,5), imshow(N, []); S = regionprops(M, 'Solidity', 'EquivDiameter', 'Centroid')solidity = [S.Solidity];equivDiameter = [S.EquivDiameter];centroid = [S.Centroid];centroid = reshape(centroid, 2, length(centroid)/2)'; text(centroid(:,1), centroid(:,2), num2str([1 : size(centroid, 1)]')) subplot(2,3,6), plot(solidity, equivDiameter, 'o');text(solidity, equivDiameter, num2str([1 : size(centroid, 1)]'))xlabel('Solidity')ylabel('EquivDiameter')
58
thSol = 0.5;thEqDiam = 40; idx = solidity < thSol & equivDiameter > thEqDiam;goodCentroid = centroid(idx, :); figure, subplot(1,2,1), imshow(I), hold, plot(goodCentroid(:,1), goodCentroid(:,2), 'rs', 'MarkerSize', 20), hold % ruotiamo l'immagine, vediamo se funziona ancora I1 = rot90(I); J1 = I1 > 170;K1 = imfill(J1, 'holes');L1 = imopen(K1, strel('disk', 1));M1 = bwlabel(L1, 8);S1 = regionprops(M1, 'Solidity', 'EquivDiameter', 'Centroid')solidity1 = [S1.Solidity];equivDiameter1 = [S1.EquivDiameter];centroid1 = [S1.Centroid];centroid1 = reshape(centroid1, 2, length(centroid1)/2)'; idx1 = solidity1 < thSol & equivDiameter1 > thEqDiam;goodCentroid1 = centroid1(idx1, :);subplot(1,2,2), imshow(I1), hold, plot(goodCentroid1(:,1), goodCentroid1(:,2), 'rs', 'MarkerSize', 20), hold
59