here - Department of Mathematics

Transcript

here - Department of Mathematics
Politecnico di Milano
Facoltà di Ingegneria dei Sistemi
Corso di Laurea Magistrale in Ingegneria Matematica
Progetto per i corsi di
Analisi Numerica delle Equazioni a Derivate Parziali II
Programmazione Avanzata per il Calcolo Scientifico
Tecniche di level-set con elementi finiti adattativi per il problema
della segmentazione di immagini
Matteo Giacomini
Matricola 754590
Anno Accademico 2011-2012
Indice
Indice
1 Introduzione
2
2 Segmentazione di immagini region-based
2.1 Il funzionale di Mumford-Shah . . . . . . . .
2.2 Il modello di Chan-Vese . . . . . . . . . . . .
2.3 Il modello con le statistiche regionali . . . . .
2.3.1 Distribuzione di probabilità gaussiana
2.3.2 Distribuzione di probabilità ricostruita
2.4 Formulazione mediante level-set . . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
mediante una
. . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
stima non-parametrica .
. . . . . . . . . . . . . .
2
3
4
4
5
6
6
3 Approssimazione numerica
3.1 Regolarizzazione numerica delle funzioni . . .
3.2 Formulazione discreta . . . . . . . . . . . . .
3.3 Criterio di arresto . . . . . . . . . . . . . . .
3.4 Una considerazione sulla funzione di level-set
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
4 Adattività di griglia
4.1 Stimatori dell’errore . . . . . . . . . . . . . . . . . . . . . . . .
4.1.1 Stimatore dell’errore iniziale . . . . . . . . . . . . . . . .
4.1.2 Stimatore dell’errore residuale a ogni passo temporale .
4.2 Strategie di marcamento: Guaranteed Error Reduction Strategy
4.2.1 Algoritmi di adattazione di griglia basati su GERS . . .
4.3 Strategie di raffinamento . . . . . . . . . . . . . . . . . . . . . .
4.3.1 Raffinamento Red Green Blue . . . . . . . . . . . . . . .
4.3.2 Bisezione Longest Edge . . . . . . . . . . . . . . . . . .
4.3.3 Bisezione Newest Vertex . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
7
7
8
9
10
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
10
11
11
11
12
13
13
14
15
15
5 Implementazione del codice C++
5.1 GAS Framework . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.2 Il corpo del programma: main.cpp . . . . . . . . . . . . . . . . . . . . .
5.3 Lettura dati da input . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.4 Gestione della mesh: la classe triangulation . . . . . . . . . . . . . . .
5.5 Le informazioni iniziali: le classi initial_image e initial_curve . . .
5.6 Il termine forzante: le classi Force e ForceFactory . . . . . . . . . . . .
5.7 Formulazione del problema: la classe problem . . . . . . . . . . . . . . .
5.8 Stimatori dell’errore: la classe posterior . . . . . . . . . . . . . . . . .
5.9 Algoritmi di adattazione di griglia: la classe AdaptMesh . . . . . . . . .
5.10 Tecniche di raffinamento: le classi RefineMethod e RefinementFactory
5.11 Visualizzazione dei dati: la classe svg . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
17
17
17
18
19
21
22
26
30
30
31
34
.
.
.
.
.
35
35
35
36
40
43
7 Conclusioni
7.1 Possibili sviluppi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
46
47
6 Simulazioni numeriche
6.1 Parametri del problema . . . .
6.2 Segmentazione di immagini con
6.3 Adattività di griglia . . . . . .
6.4 Segmentazione di immagini con
6.5 Segmentazione di immagini con
. . . . . . . . . . . .
regioni uniformi . .
. . . . . . . . . . . .
rumore . . . . . . .
pattern spazialmente
1
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
non uniformi
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Segmentazione di immagini region-based
1
Introduzione
Il problema della segmentazione delle immagini costituisce un ambito di ricerca di grande attualità
per le numerose applicazioni in differenti campi, dal trattamento dei segnali (statici o video) a
livello industriale fino al settore biomedico in cui può rappresentare una valida tecnica per facilitare
la lettura e la valutazione quantitativa degli output provenienti da strumenti diagnostici complessi
come la risonanza magnetica.
Delle ottime referenze generali per le tematiche relative al trattamento delle immagini sono
[Sch10] e [AK06]. In questo progetto focalizzeremo la nostra attenzione sulla segmentazione delle
immagini, cioè sull’individuazione all’interno di una figura di regioni con caratteristiche specifiche.
In particolare, l’obiettivo primario consiste nell’identificare i contorni degli oggetti presenti in
un’immagine in scala di grigio. Le intensità assumono 256 possibili valori nell’intervallo [0, 255],
dove il minimo rappresenta il nero e il massimo il bianco.
Detto Ω il dominio dell’immagine, si indica con u : Ω → [0, 255] la funzione che descrive l’intensità
del grigio nei vari pixel. Questo progetto analizza da un punto di vista numerico un metodo basato
sugli elementi finiti per segmentare l’immagine. In particolare, la segmentazione S
dell’immagine si
concretizza con la decomposizione di Ω in un insieme di aree disgiunte Ωi tali che i Ωi = Ω e u|Ωi
presenta una certa regolarità; si osserverà nelle simulazioni numeriche che in alcune circostanze tale
regolarità è sinonimo di omogeneità del colore mentre in altri casi rappresenta una distribuzione
periodica di sfumature con la formazione di particolari pattern spaziali. L’obiettivo di S
individuare i
bordi degli oggetti presenti in un’immagine si riduce quindi a identificare la curva Γ = i ∂Ωi , dove
∂Ω1 sono le frontiere delle regioni Ωi .
L’elaborato si divide in cinque sezioni principali. Dopo una breve introduzione al problema
della segmentazione delle immagini e alla letteratura sull’argomento, si passa alla sezione 2 in cui
si procede alla presentazione dei modelli region-based - nello specifico il modello di Chan-Vese e
quello basato sulle statistiche regionali - e alla loro formulazione come problemi di minimizzazione
di funzionale. Nella sezione 3 si descrive l’approssimazione numerica delle equazioni in analisi
mentre la sezione 4 è dedicata alla stima dell’errore di approssimazione e ai temi dell’adattazione
di griglia. Le strategie scelte per l’implementazione del codice e alcuni dettagli di programmazione
sono descritti nella sezione 5. Nella sezione 6 si presentano i risultati delle simulazioni numeriche
realizzate sia con immagini sintetiche sia con immagini reali e infine l’elaborato si chiude con le
conclusioni e la presentazione di possibili ambiti di sviluppo futuro per le tematiche affrontate.
2
Segmentazione di immagini region-based
I modelli di segmentazione di immagini si dividono in due categorie principali, i modelli edge-based
e i modelli region-based. I primi prevedono la costruzione di un funzionale con un indicatore della
presenza di un bordo che separi due regioni - intuitivamente un gradiente, in quanto ci si aspetta che
la soluzione vari fortemente in corrispondenza di un bordo -, mentre i secondi sono caratterizzati da
una minore dipendenza dalle informazioni associate ai lati dell’oggetto e una maggiore importanza
dei dati legati alla distribuzione regionale dell’intensità di colore nell’immagine.
Il principale vantaggio associato all’approccio region-based può quindi essere individuato nella minore sensibilità del metodo a variazioni dell’inizializzazione dei dati e alla maggiore efficienza computazionale rispetto ai metodi edge-based ([Bro05]).
Nei paragrafi seguenti si descriveranno il primo modello proposto per la segmentazione di immagini
basato su una strategia regionale, il funzionale di Mumford-Shah, evidenziandone i principali limiti
e si presenteranno in seguito delle formulazioni alternative in grado di risolvere le maggiori criticità
dello stesso.
2
Segmentazione di immagini region-based
2.1
Il funzionale di Mumford-Shah
In [MS89] si presenta il problema di segmentazione delle immagini come un problema di minimizzazione di un funzionale dell’energia, che verrà successivamente nominato funzionale di Mumford-Shah:
Z
Z
2
EM S (u, Γ) =
|u0 (x) − u(x)| dx + α
|∇u(x)|2 dx + βH1 (Γ)
ΩrΓ
ΩrΓ
In questa formulazione, u0 (x) è l’immagine di partenza, Γ è un insieme chiuso e α, β > 0 sono
delle costanti di scala. H1 (Γ) rappresenta infine la misura di Hausdorff unidimensionale associata
all’insieme Γ: richiedere a priori una particolare regolarità su Γ sarebbe infatti troppo restrittivo,
pertanto si ammettono soluzioni non continue e la lunghezza di Γ viene generalizzata mediante la
misura di Hausdorff.
Analizzando il funzionale di Mumford-Shah si osserva che il primo termine tiene conto dello scostamento tra l’immagine iniziale u0 e l’approssimazione u in norma L2 (Ω r Γ); il secondo quantifica
la variazione di u permettendo quindi l’individuazione delle diverse regioni in cui si suddivide la
figura e l’ultimo rappresenta la lunghezza del contorno, impedendo un’eccessiva frammentazione in
micro-regioni che potrebbe portare a un numero elevato di punti isolati all’interno dell’immagine.
Il problema di segmentazione delle immagini si può formalizzare pertanto come la minimizzazione
di EM S (u, Γ) rispetto a una funzione continua a tratti u e a un insieme Γ che ne contiene le
discontinuità.
Le coppie (u, Γ) devono essere tali che Γ ⊂ Ω sia chiuso e abbia misura di Hausdorff finita H1 (Γ) < ∞
e u ∈ W 1,2 (Ω r Γ). La principale difficoltà nello studio di tale problema deriva quindi dalla diversa
natura delle incognite coinvolte e l’analisi della regolarità di Γ assume un ruolo di primo piano per
le proprietà di esistenza e unicità della soluzione.
In [MS89] si propone la seguente congettura:
Congettura 1. Esiste una coppia (u, Γ) che minimizza EM S (u, Γ) tale che l’insieme delle discontinuità di Γ è dato dall’unione di un numero finito di curve C 1,1 . Inoltre, ogni curva può terminare
solo con un’estremità libera o con una giunzione tripla in cui tre curve si incontrano con un angolo
di 2π/3.
Sebbene la congettura di Mumford-Shah non sia ancora stata dimostrata, numerosi passi avanti
sono stati fatti nello studio del problema di segmentazione delle immagini mediante il calcolo delle
variazioni. In particolare, il seguente teorema stabilisce un risultato interessante sull’unicità della
soluzione:
Teorema 1. Per ogni Ω insieme aperto e limitato di R2 e per ogni u0 tale che 0 ≤ u0 ≤ 1 q.o. in
Ω, esiste Γ ⊂ R2 chiuso e unione numerabile di curve C 1 ed esiste u ∈ C 1 (Ω r Γ) tali che la coppia
(u, Γ) è una soluzione del problema di minimizzazione del funzionale di Mumford-Shah. In generale
tale coppia non è unica.
Grazie a queste ulteriori ipotesi di regolarità si giunge quindi a una formulazione alternativa del
funzionale di Mumford-Shah in cui la misura di Hausdorff è sostituita dalla misura di Lebesgue
Z
Z
Z
2
2
EM S (u, Γ) = λ |u0 (x) − u(x)| dx +
|∇u(x)| dx + ν ds
(1)
Ω
ΩrΓ
Γ
dove u0 (x) rappresenta l’immagine da trattare e ν e λ sono due parametri positivi del problema.
Il principale limite alla trattazione numerica del problema di Mumford-Shah risiede nell’impossibilità di derivare in uno spazio funzionale adeguato le equazioni di Eulero-Lagrange per la
minimizzazione del funzionale dell’energia e per tale motivo sono state proposte in letteratura delle
soluzioni alternative al problema in questione. Per una trattazione dettagliata dei metodi numerici
3
Segmentazione di immagini region-based
e, in particolare, dei metodi a elementi finiti per l’approssimazione del funzionale di Mumford-Shah
si rimanda a [BC00]. Nei paragrafi seguenti si presentano invece differenti modelli region-based per
la segmentazione di immagini. Per una questione di semplicità si assume che il dominio Ω venga
suddiviso in due sole regioni Ω1 e Ω2 tali che Ω1 ∪ Ω2 = Ω and Ω1 ∩ Ω2 = ∅, ma il modello può
essere generalizzato al caso di immagini multicanale in cui conseguentemente il numero di regioni
risulterà maggiore.
2.2
Il modello di Chan-Vese
Il modello proposto da Chan e Vese in [CV01] è il più semplice modello di segmentazione di immagini region-based che tratteremo. Esso si basa sulla costruzione di un funzionale che tenga in
considerazione lo scostamento tra l’intensità dell’immagine nei vari pixel e il valore medio della
stessa all’interno della regione in analisi:
Z
Z
Z
E(Γ) = λ
(u0 (x) − µ1 )2 dx + λ
(u0 (x) − µ2 )2 dx + ν ds
(2)
Ω1
Ω2
Γ
dove µ1 e µ2 rappresentano i valori medi di u0 (x) in Ω1 e Ω2 e Γ la frontiera tra le due aree.
Si introduce ora una funzione di livello Φ(x) tale che Φ(x) ≥ 0 se x ∈ Ω1 e Φ(x) < 0 se x ∈ Ω2
e si identificano pertanto le due regioni in analisi mediante la funzione di Heaviside H(Φ):
(
1 Φ(x) ≥ 0
H(Φ(x)) =
0 Φ(x) < 0
I valori medi µ1 e µ2 assumono pertanto la forma seguente:
R
R
u0 (x)H(Φ(x))dx
u0 (x)(1 − H(Φ(x)))dx
ΩR
µ1 =
µ2 = Ω R
Ω H(Φ(x))dx
Ω (1 − H(Φ(x)))dx
In questo contesto, la curva di livello zero di Φ(x) delinea il contorno Γ che separa le regioni e
il funzionale in (2) può essere espresso come
Z ³
´
E(Φ(x)) =
λH(Φ(x))(u0 (x) − µ1 )2 + λ(1 − H(Φ(x)))(u0 (x) − µ2 )2 + ν|∇H(Φ(x))| dx (3)
Ω
dove il gradiente della funzione di Heaviside è da intendersi nel senso delle distribuzioni.
2.3
Il modello con le statistiche regionali
La principale questione non risolta dal modello di Chan-Vese è dunque la modellazione dell’interno
delle regioni Ω1 e Ω2 . Un possibile raffinamento del modello proposto può consistere in una modifica del funzionale dell’energia che tenga conto anche della distribuzione dei valori dell’intensità
all’interno delle regioni individuate dall’algoritmo di segmentazione.
L’idea descritta in [Bro05] propone di costruire un modello che massimizzi la probabilità a
posteriori P (M |D) che il modello M sia attendibile, data l’informazione in D. In particolare, il
modello M è rappresentato dall’appartenenza di un pixel a una delle regioni Ω1 o Ω2 mentre il dato
D è associato all’intensità dell’immagine iniziale u0 (x). Dalla regola di Bayes si ottiene quindi per
i = 1, 2:
P (x ∈ Ωi | u0 (x) = s) =
P (u0 (x) = s | x ∈ Ωi )P (x ∈ Ωi )
pi (s)P (x ∈ Ωi )
=
P (u0 (x) = s)
P (u0 (x) = s)
(4)
In questa espressione P (x ∈ Ωi ) rappresenta la probabilità a priori che un punto appartenga alla
regione Ωi , P (u0 (x) = s) è la probabilità che un pixel assuma valore s mentre pi (s) = P (u0 (x) =
4
Segmentazione di immagini region-based
s | x ∈ Ωi ) è la densità di probabilità nella regione in analisi, ossia la distribuzione dei valori delle
intensità all’interno di Ωi .
Si osserva che la probabilità a priori che un pixel assuma un determinato valore di intensità è
indipendente dalla scelta della regione di appartenenza dello stesso; pertanto la massimizzazione di
(4) coincide con la massimizzazione del numeratore della frazione e dunque con la minimizzazione
di
F (Γ) = −P (u0 (x) = s | x ∈ Ωi )P (x ∈ Ωi ) = −pi (s)P (x ∈ Ωi )
Supponendo che i valori delle intensità nei singoli punti siano indipendenti - ipotesi ammissibile
in mancanza di interdipendenze note tra i i pixel - le probabilità pi (s) possono essere considerate
come realizzazioni indipendenti e identicamente distribuite di un processo stocastico in Ωi . Il termine
P (x ∈ Ωi ) permette di considerare dei vincoli su quale strategia di trattamento dei dati sia preferibile
imporre a priori; la scelta di tale termine risulta spesso determinante ai fini della costruzione del
modello. Una delle possibilità descritte in [CRD07] prevede un approccio di tipo geometrico in base
a cui siano favorite le segmentazioni in cui la lunghezza del contorno sia minima. Pertanto si ha
P (x ∈ Ωi ) ∝ e−ν|Γ| = e−ν
R
Γ
ds
e, in base a queste considerazioni, si può riformulare il precedente funzionale come
R
Y
Y
F (Γ) = −
p1 (u0 (x))
p2 (u0 (x))e−ν Γ ds
x∈Ω1
x∈Ω2
e ottenere il funzionale dell’energia basato sulle statistiche regionali
Z
Z
Z
E(Γ) = −
log p1 (u0 (x))dx −
log p2 (u0 (x))dx + ν ds
Ω1
Ω2
(5)
Γ
Introducendo la funzione di level-set Φ(x) come per il modello di Chan-Vese, si ricava la formulazione
finale del funzionale
Z ³
´
E(Φ(x)) =
− H(Φ(x)) log p1 (u0 (x)) − (1 − H(Φ(x))) log p2 (u0 (x)) + ν|∇H(Φ(x))| dx (6)
Ω
dove p1 (u0 (x)) e p2 (u0 (x)) assumono forme diverse a seconda delle statistiche considerate.
Per ulteriori dettagli sui modelli per il trattamento delle immagini basati su approcci di tipo
bayesiano e sulle formulazioni level-set associate si rimanda a [CRD07] .
2.3.1
Distribuzione di probabilità gaussiana
Una possibile scelta per la modellazione della densità di probabilità all’interno delle regioni Ωi
consiste in una classica distribuzione gaussiana univariata di media µi e varianza σi2 .
In particolare, nel caso in esame con due sole regioni Ω1 e Ω2 , si possono costruire i parametri
delle due distribuzioni di probabilità a partire dai dati dell’intensità iniziale dell’immagine nei pixel
calcolando i seguenti parametri.
R
R
u0 (x)(1 − H(Φ(x)))dx
u0 (x)H(Φ(x))dx
ΩR
µ2 = Ω R
µ1 =
H(Φ(x))dx
(1 − H(Φ(x)))dx
sR Ω
sR Ω
(7)
− µ1 )2 H(Φ(x))dx
(u0 (x) − µ2 )2 (1 − H(Φ(x)))dx
Ω (u0 (x)
Ω
R
R
σ1 =
σ2 =
Ω H(Φ(x))dx
Ω (1 − H(Φ(x)))dx
Una volta effettuata la stima dei parametri, è immediata la costruzione delle funzioni di densità
di probabilità
(s−µi )2
−
1
2
pi (s) = √
e 2σi
(8)
2πσi
5
Segmentazione di immagini region-based
È interessante osservare come il modello di Chan-Vese sia a tutti gli effetti un caso particolare del
modello basato sulle statistiche regionali in cui si considera un’approssimazione di tipo costante a
tratti su Ω1 e Ω2 con funzioni che risultano
√ rispettivamente pari ai valori attesi µ1 e µ2 . In tal
caso, fissato il valore delle varianze σi = 0.5, si costruisce un modello in cui l’appartenenza alle
singole regioni è equiprobabile e pertanto l’espressione della distribuzione log-normale log pi (u0 (x))
1
si riduce a log(π − 2 ) − (u0 (x) − µi )2 che, sostituita in (6), riporta all’equazione (3).
2.3.2
Distribuzione di probabilità ricostruita mediante una stima non-parametrica
Sebbene il modello gaussiano presentato nel paragrafo precedente fornisca sicuramente un buon
punto di partenza per il trattamento di immagini con regioni non uniformi, potrebbe rivelarsi non
adeguato a individuare i contorni di aree di figure che presentano dei pattern di distribuzione
particolari.
Un classico esempio è rappresentato dalla segmentazione di un’immagine contenente una zebra: un
modello basato esclusivamente sulla media rileverebbe una distribuzione pressocché uniforme di grigi
in entrambe le regioni e, analogamente, un modello gaussiano individuerebbe due aree con intensità
pari a un valore medio di grigio con varianza estremamente elevata.
In letteratura si propone pertanto di eseguire una stima non-parametrica della funzione densità
di probabilità per l’immagine in analisi. In particolare, invece di calcolare i parametri di una funzione
di densità parametrica, si esegue una stima di Parzen per la densità.
Innanzitutto si costruisce l’istogramma discreto dei valori di intensità dei pixel nelle diverse regioni
dell’immagine e in seguito si procede a uno smoothing tramite un kernel gaussiano Kσh di deviazione
standard σh :
(
R
1
dx
1 , se u0 (x) = s
{u
(x)=s}
0
Ω
pi (s) = Kσh ∗ i R
1{u0 (x)=s} =
(9)
0 , altrimenti
Ωi dx
In generale, le stime non-parametriche sono in grado di catturare più dettagli delle regioni che
analizzano rispetto ai metodi basati su stime parametriche. Tuttavia, i metodi che riconoscono molti
dettagli rischiano di generare un eccessivo livello di particolarità che riduce l’utilità del modello nella
sua globalità.
La scelta del valore della deviazione standard risulta pertanto critica ai fini del modello da costruire:
se la deviazione standard σh è piccola, la stima della densità di probabilità risulta estremamente
accurata ma non tiene in considerazione le incertezze legate al rumore sui dati; parallelamente,
grandi valori di σh introducono un’incertezza tale da ridurre l’utilità dell’informazione contenuta
nei restanti dati. Per dettagli sulla taratura del parametro di smoothing si rimanda all’ampia
trattazione dei modelli basati sulle statistiche regionali in [Bro05].
2.4
Formulazione mediante level-set
I funzionali dell’energia (3) e (6) possono essere scritti nella seguente forma generalizzata
Z ³
´
E(Φ(x)) =
H(Φ(x))α(x) + (1 − H(Φ(x)))β(x) + ν|∇H(Φ(x))| dx
(10)
Ω
Si è pertanto ricondotto il problema della segmentazione delle immagini a un problema di calcolo
delle variazioni che consiste nella minimizzazione del funzionale (10).
L’equazione di Eulero-Lagrange associata al funzionale dell’energia assume quindi la forma
"
#
∇Φ(x)
δ(Φ(x)) β(x) − α(x) + νdiv
=0
|∇Φ(x)|
6
Approssimazione numerica
Riformulando quest’equazione attraverso una parametrizzazione con una variabile temporale artificiale t, si può costruire un metodo di discesa di tipo gradiente che si formalizza nel seguente problema
evolutivo
#
"
∇Φ(x, t)
∂t Φ(x, t) = δ(Φ(x, t)) β(x) − α(x) + νdiv
(11)
|∇Φ(x, t)|
Si ottiene quindi la formulazione finale del problema differenziale per la segmentazione delle immagini mediante la funzione level-set Φ(x, t):

∇Φ(x, t)
∂t Φ(x, t)


− νdiv
= G(u0 (x))
Ω × (0, T ]


δ(Φ(x,
t))
|∇Φ(x,
t)|


δ(Φ(x, t)) ∂Φ(x, t)
(12)
=0
∂Ω × (0, T ]


|∇Φ(x,
t)|
∂n




Φ(x, 0) = Φ0 (x)
Ω
dove Φ0 rappresenta la curva scelta come condizione iniziale e G(u0 (x)) la generica forma del termine
forzante che varierà a seconda del modello considerato. Nello specifico, una possibile inizializzazione
potrebbe assumere la forma
(
dist(x, Γ)
, x interno a Γ
Φ0 (x) =
−dist(x, Γ) , x esterno a Γ
mentre per il termine forzante si avrà
(
λ[(u0 (x) − µ2 )2 − (u0 (x) − µ1 )2 ]
G(u0 (x)) =
log p1 (u0 (x)) − log p2 (u0 (x))
, Chan-Vese
, Statistiche regionali
(13)
dove i valori di µi e pi (u0 (x)) sono quelli ricavati in precedenza.
Come si può osservare dalle formulazioni dei funzionali E(Φ(x)) viste nei paragrafi precedenti,
l’evoluzione della curva di livello in equazione (11) è spinta dalla differenza dei valori medi dell’intensià in Ω1 e Ω2 per (3) e dalla scarto tra le densità di probabilità tra le due regioni in (6).
Il risultato di questa equazione consisterà quindi nella suddivisione del dominio in due regioni con
differenti valori medi di intensità u0 (x) per il modello di Chan-Vese e con diverse densità di probabilità per i modelli basati su statistiche regionali.
Per approfondire i metodi di level-set, si rimanda a [Qua08] e [OF02].
3
Approssimazione numerica
A partire dai funzionali di Chan-Vese e delle statistiche regionali presentati nei paragrafi 2.2 e
2.3, si procede alla discretizzazione del problema (12) mediante elementi finiti e alla successiva
presentazione di un algoritmo di tipo adattativo.
3.1
Regolarizzazione numerica delle funzioni
La trattazione numerica dei funzionali (3) e (6) presenta alcuni problemi dovuti alla discontinuità
e alla non limitatezza delle funzioni H(Φ(x)) e δ(Φ(x)) . Seguendo quanto suggerito in [CV01], si
esegue una regolarizzazione di queste funzioni per renderne possibile la gestione dal punto di vista
numerico. In generale, si richiede che la regolarizzazione della funzione di Heaviside sia tale che
• limΦ→−∞ Hρ (Φ) = 0
• limΦ→+∞ Hρ (Φ) = 1
7
Approssimazione numerica
• Hρ (0) = 0.5
Dato un parametro di regolarizzazione ρ > 0, si costruiscono, a partire dall’arcotangente, le seguenti
funzioni:
³ Φ ´´
2
1
ρ
1³
1 + arctan
δρ (Φ) =
Hρ (Φ) =
2
2
π
ρ
π ρ + Φ2
Di conseguenza i funzionali (3) e (6) assumono le forme regolarizzate
Z ³
´
Eρ (Φ(x)) =
(14)
Hρ (Φ(x))(u0 (x) − µ1 )2 + (1 − Hρ (Φ(x)))(u0 (x) − µ2 )2 + νδρ (Φ(x))|∇Φ(x)| dx
ZΩ ³
´
Eρ (Φ(x)) =
− Hρ (Φ(x)) log p1 (u0 (x)) − (1 − Hρ (Φ(x))) log p2 (u0 (x)) + νδρ (Φ(x))|∇Φ(x)| dx (15)
Ω
Al fine di eliminare i problemi associati alla presenza del termine |∇Φ(x)| al denominatore di (11)
quando il gradiente della soluzione si annulla, si procede sostituendo tale blocco con il termine
penalizzato
p
Q² (Φ(x)) = ²2 + |∇Φ(x)|2 , ² ∈ (0, 1)
e quindi il problema regolarizzato diventa

∂t Φ(x, t)
∇Φ(x, t)


− νdiv
= G(u0 (x))


Q² (Φ(x, t))

 δρ (Φ(x, t))
δρ (Φ(x, t)) ∂Φ(x, t)
=0



Q
∂n
² (Φ(x, t))



Φ(x, 0) = Φ0 (x)
3.2
Ω × (0, T ]
(16)
∂Ω × (0, T ]
Ω
Formulazione discreta
Procediamo ora alla discretizzazione del problema (16) mediante elementi finiti P1 e definiamo quindi
sulla triangolazione Th lo spazio a elementi finiti associato
Xh = {ϕ ∈ C 0 (Ω) | ϕ ∈ P1 (S) ∀S ∈ Th }
La formulazione semidiscreta del problema consiste quindi nel trovare ∀t Φh = Φh (t) ∈ Xh tale che
Z
Z
Z
∇Φh
∂t Φh
ϕh dx + ν
· ∇ϕh dx =
G(u0 (x))ϕh dx
∀ϕh ∈ Xh
(17)
Ω Q² (Φh )
Ω
Ω δρ (Φh )
Per procedere alla discretizzazione temporale del problema, si considera un passo di avanzamento
temporale τ tale che, detto il tempo finale T , si abbia T = M τ . Per ogni istante temporale tm si
definisce pertanto
m
Φm
tm = mτ , m = 0, . . . , M
h = Φh (x, t ) ,
e si sceglie uno schema di Eulero implicito per la discretizzazione in tempo e un approccio semiimplicito per la gestione dei termini non lineari.
Il problema discreto diventa quindi trovare ∀m = 1, . . . , M una funzione Φm
h ∈ Xh tale che
1
τ
Z
Ω
m−1
Φm
h − Φh
ϕh dx + ν
δρ (Φhm−1 )
Z
Ω
∇Φm
h
m−1 · ∇ϕh dx =
Q² (Φh )
Z
Ω
Gm−1 (u0 (x))ϕh dx
∀ϕh ∈ Xh
(18)
dove Φ0h = Φ0,h = Ih Φ0 è un’approssimazione adeguata della curva iniziale, nello specifico l’interpolante di Φ0 (x) sulla griglia a elementi finiti iniziale T0 .
8
Approssimazione numerica
Infine si presenta il problema algebrico associato la cui costruzione è immediata per ogni passo
temporale a partire da quanto illustrato:
(M + τ νK)Φm = M Φm−1 + τ Gm−1
,
m = 1, . . . , M
(19)
dove M = [Mij ] è una matrice di massa generalizzata, K = [Kij ] è una matrice di rigidezza
generalizzata e Gm−1 = [Gi ] è il termine forzante.
Z
Z
Z
ϕj ϕi
∇ϕj ∇ϕi
Mij =
dx
,
K
=
dx
,
G
=
Gm−1 (u0 (x))ϕi dx
ij
i
m)
m)
δ
(Φ
Q
(Φ
²
Ω ρ
Ω
Ω
h
h
3.3
Criterio di arresto
Il problema in analisi non è originariamente un problema dipendente dal tempo pertanto risulta di
fondamentale importanza individuare un criterio per arrestare l’evoluzione della curva di livello che
non sia fissare a priori un tempo finale T .
L’idea adottata consiste nel cercare una soluzione stazionaria del problema evolutivo (16), cioè una
soluzione tale che tra un istante temporale e il successivo la variazione della stessa risulti inferiore
a una data tolleranza tol² .
Per tale motivo, procediamo al confronto di una grandezza caratteristica sm
h (x) associata alla
segmentazione approssimante i bordi dell’immagine in istanti temporali consecutivi e calcoliamo
m−1
l’errore rm = ksm
(x)k2L2 (Ω) .
h (x) − sh
m
In dettaglio, sh (x) è la valutazione della grandezza che presenteremo in seguito sulla triangolazione
Thm all’istante temporale tm , mentre sm−1
(x) rappresenta l’interpolazione su Thm della grandezza
h
calcolata sullo spazio a elementi finiti all’istante temporale precedente. L’interpolazione si rende
necessaria in presenza di metodi di adattazione di griglia che verranno presentati nella sezione
successiva in quanto tra un istante temporale e il successivo la mesh viene modificata generando
quindi una variazione nello spazio a elementi finiti associato.
Nel caso dell’approssimazione costante a tratti del funzionale di Chan-Vese (3), sm
h (x) assume
il ruolo di segmentazione approssimante l’immagine; si procede quindi al calcolo dei valori medi di
u0 (x) nelle due regioni Ω1 e Ω2 e per l’istante temporale tm si costruisce
(
m
µm
1 , Φ (x) ≥ 0
sm
(x)
=
(20)
h
m
µm
2 , Φ (x) < 0
Tale approccio non fornisce tuttavia sufficienti informazioni per quanto riguarda i modelli presentati
nel paragrafo 2.3 in quanto la media dell’intensità non rappresenta una discriminante univoca per
la determinazione della regione di appartenenza di un dato pixel. Si considerano pertanto le densità
di probabilità p1 (u0 (x)) e p2 (u0 (x)) associate rispettivamente alle regioni Ω1 e Ω2 e si costruiscono
le seguenti funzioni
fmax (x) = max(p1 (u0 (x)), p2 (u0 (x)))
fmin (x) = min(p1 (u0 (x)), p2 (u0 (x)))
In base alla stessa idea presentata in (20), per i modelli basati sulle statistiche regionali si può
definire in questo caso
m
m
sm
(21)
h (x) = fmax (x) − fmin (x)
Il criterio d’arresto dell’evoluzione della soluzione è pertanto costituito dal rispetto della tolleranza tol² da parte dall’errore rm . In generale, come si osserva in [Fri03], se l’adattazione di griglia
ammette anche procedure di deraffinamento, la soluzione può essere considerata stazionaria solo in
seguito a ripetute iterazioni in cui rm < tol² . La concomitante aggiunta e rimozione di elementi della
triangolazione a ogni passo temporale può infatti generare delle oscillazioni del valore dell’errore; in
9
Adattività di griglia
tale circostanza, il rispetto della tolleranza a un dato istante temporale non garantisce la stazionarietà della soluzione che potrebbe essere mesh-dependent e quindi potrebbe violare la stessa condizione
a un istante temporale successivo. Tuttavia, dal momento che in questo lavoro si considerano solo
procedure di raffinamento di griglia, è sufficiente arrestare l’evoluzione del problema una volta che
rm risulta inferiore alla tolleranza tol² .
3.4
Una considerazione sulla funzione di level-set
Dalla letteratura è noto che, anche se si inizializza la curva di livello Φh (x) con il valore della distanza
del punto in analisi dalla curva Γ0 , l’evoluzione del problema fa sı̀ che la soluzione Φh (x) smetta
immediatamente di essere una funzione distanza rispetto alla curva di livello zero.
Per evitare che la curva di livello assuma valori troppo smussati o troppo ripidi, classicamente si
procede a una reinizializzazione a ogni istante temporale di Φh (x) imponendo il valore della funzione
uguale alla distanza con segno dist(x, Γ) del punto x dalla curva Γ ([OF02]).
In [Fri03], si osserva tuttavia che questa situazione non si presenta nel problema in questione
per cui la reinizializzazione risulta superflua. Al contrario, potrebbero invece comparire dei picchi
nel valore assoluto della curva di livello, facilmente risolvibili mediante un riscalamento di Φh (x)
ogniqualvolta |Φh (x)| superasse una valore di soglia fissato a priori.
4
Adattività di griglia
L’accuratezza della soluzione calcolata mediante gli algoritmi presentati nelle sezioni precedenti
dipende fortemente dalla precisione della griglia su cui si approssima il problema. Tuttavia, è
possibile che in un’immagine siano presenti ampie regioni uniformi che non richedono un elevato
livello di dettaglio. L’utilizzo di griglie di calcolo lasche in queste zone e più fini in prossimità di
aree a forte gradiente può ridurre notevolmente i tempi di calcolo delle simulazioni.
In questa sezione si introducono alcune tecniche per l’adattazione di griglia, sia per quanto riguarda la mesh iniziale, sia per quanto riguarda la mesh a ogni istante temporale del problema.
In generale, in letteratura ([D9̈6a], [D9̈6b]) sono presenti lavori che trattano sia le tecniche di raffinamento e sia quelle di deraffinamento; in questo progetto ci siamo focalizzati solo sulle prime
in quanto il deraffinamento, sebbene interessante da un punto di vista algoritmico, presenta una
complessità implementativa notevole non sempre ripagata da un effettivo guadagno in termini di
costo di calcolo. Le tecniche di deraffinamento, infatti, richiedono la memorizzazione della struttura
della mesh e delle informazioni associate agli elementi gerarchicamente annidati durante le varie
iterazioni degli algoritmi e spesso risulta pertanto più conveniente procedere alla rigenerazione di
una nuova mesh piuttosto che rimuovere alcuni elementi e verificare la conformità della griglia cosı̀
ottenuta. Per dettagli sulle tecniche di deraffinamento per problemi di trattamento delle immagini
si rimanda a [BM96].
Il procedimento seguito in questo progetto prevede di partire da una griglia iniziale estremamente
lasca e di eseguire solo raffinamenti. Nello specifico, si è implementato un algoritmo a due passi eseguibili anche singolarmente - che prevede innanzitutto il raffinamento locale della mesh in modo
da ottenere una descrizione accurata dell’immagine iniziale u0 (x) e successivamente il miglioramento
della griglia a ogni istante temporale a seconda dell’informazione fornita da un apposito stimatore.
Nei paragrafi seguenti, si presentano quindi gli stimatori proposti in [Fri03] per il problema di
segmentazione in analisi, l’algoritmo Guaranteed Error Reduction Strategy per marcare gli elementi
da raffinare e differenti tecniche per infittire i triangoli della griglia.
10
Adattività di griglia
4.1
Stimatori dell’errore
L’algoritmo di adattazione di griglia proposto si basa su una coppia di stimatori globali-locali. Lo
stimatore globale stabilisce se la mesh deve essere raffinata - e ciò si verifica quando il valore dello
stimatore supera una tolleranza fissata a priori - mentre lo stimatore locale indica quali elementi
della triangolazione devono essere infittiti.
In generale, come sostenuto in [Fri09], per immagini prive di rumore è sufficiente procedere con
l’adattazione di griglia iniziale per ottenere una triangolazione soddisfacente anche per le successive
iterazioni del solutore; in presenza di disturbi, tuttavia, non si può considerare l’ipotesi sull’immagine
iniziale adeguata a costruire una griglia accurata in grado di cogliere l’evoluzione temporale della
curva di livello anche in istanti temporali successivi.
In seguito si presentano pertanto i due stimatori e i due algoritmi di raffinamento associati.
4.1.1
Stimatore dell’errore iniziale
Il primo passo dell’algoritmo di adattazione di griglia prevede la costruzione di una triangolazione
che migliori la rappresentazione del dato iniziale u0 (x). Si costruisce pertanto il seguente stimatore
locale dell’errore iniziale in norma L2
η0 (S) = ku0 (x) − Ih u0 (x)kL2 (S)
(22)
dove Ih u0 (x) rappresenta l’interpolaziome dell’immagine iniziale u0 (x) sullo spazio a elementi finiti
costruito sulla triangolazione Th .
Da ciò si ricava lo stimatore globale dell’errore di approssimazione iniziale
X
η0 (S)
(23)
ηini (Th ) =
S∈Th
che misura l’errore di interpolazione della soluzione iniziale sulla griglia discreta in norma L2 .
È opportuno osservare che l’ipotesi iniziale sulla forma dell’interfaccia Γ che separa le due regioni
Ω1 e Ω2 può essere estremamente lontana dal risultato finale dell’algoritmo di segmentazione. Per
tale motivo, la curva di livello Φh (x) può variare in maniera considerevole durante le iterazioni
iniziali dell’algoritmo rendendo la mesh iniziale non adatta a una rappresentazione accurata della
soluzione. In tale circostanza si può procedere a un ulteriore passo di adattazione in base alle
informazioni ricavate dalla stima dell’errore calcolato a ogni passo temporale.
4.1.2
Stimatore dell’errore residuale a ogni passo temporale
L’adattività di griglia a ogni istante temporale è realizzata a partire dall’informazione fornita da uno
stimatore dell’errore di tipo residuale. Seguendo le indicazioni in [SBS05] si è costruito il seguente
stimatore locale
°
2
ηS (Φm
h (x))
=
°2
°
(x)
− Ihm Φm−1
°
h
+
−
G(u
(x))
°
0
° 2
τ δρ (Φm
(x))
h
L (S)
°t
|°2
°
m
X °
X
° ∇Φh (x) °
2 3
+
C
h
°
°
1
S
° Q² (Φm
h (x)) ° 2
° Φm (x)
°
C02 h4S ° h
°
C12 h3S
e⊂∂S∩Ω
L (e)
e⊂∂S∩∂Ω
°
°2
° ∇Φm (x)
°
°
°
h
·
n
°
°
m
° Q² (Φh (x)) ° 2
(24)
L (e)
Il primo termine misura il residuo sull’elemento S in norma L2 : esso tiene conto della derivata
temporale, in cui la soluzione al passo precedente viene interpolata sulla griglia al tempo corrente,
e il termine associato alla forzante; il blocco associato alla divergenza del gradiente della soluzione
11
Adattività di griglia
scompare in quanto l’approssimazione numerica è stata eseguita mediante elementi finiti P1 .
Mentre il primo termine stima la bontà a livello locale della soluzione discreta Φh (x) rispetto alla
soluzione Φ(x) del problema, il secondo termine valuta invece il salto del gradiente sui lati interni alla
triangolazione tenendo quindi in considerazione il cambiamento di ∇Φh (x) elemento per elemento
mentre l’ultimo termine stima l’effetto di una discrepanza del dato di bordo sull’errore (n rappresenta
la normale al bordo ∂Ω).
Lo stimatore globale a ogni istante temporale può quindi essere facilmente ricavato come segue
X
2
2
η(Φm
ηS (Φm
(25)
h (x)) =
h (x))
S∈Th
4.2
Strategie di marcamento: Guaranteed Error Reduction Strategy
In questo paragrafo si descrive la strategia di marcamento utilizzata per selezionare gli elementi
della triangolazione da raffinare e si presentano infine gli algoritmi di raffinamento implementati e
testati.
In [D9̈6a] Dörfler descrive una strategia che garantisce la riduzione dell’errore di approssimazione
sotto una data tolleranza per un problema di Poisson con forzante non lineare. Morin, Nochetto
e Siebert hanno poi approfondito lo studio della strategia GERS presentando in [MNS99] un risultato di convergenza nel caso di problemi ellittici approssimati su una griglia non precedentemente
adattata.
Il metodo si basa sull’ipotesi che tutti i lati di un triangolo marcato per il raffinamento vengano
divisi almeno una volta. In dettaglio, si proporranno in seguito differenti possibilità di suddivisione,
basate sulla divisione regolare o sulla bisezione.
L’idea alla base della strategia GERS consiste nel raffinare gli elementi all’interno di un dato sottoinsieme della triangolazione di partenza in modo che sia rispettata la seguente condizione sullo
stimatore dell’errore:
X
2
ηS (Φ(x))2 ≥ (1 − θ∗ )2 ηT2h
(26)
ηA
=
h
S∈Ah
Fulcro centrale del metodo è pertanto l’individuazione del sottoinsieme Ah ⊆ Th che verifichi il
suddetto vincolo per un valore fissato di θ∗ ∈ (0, 1). In particolare, si vuole determinare il sottoinsieme ottimale A∗h che è il più piccolo tra i vari sottoinsiemi costruibili a partire dalla triangolazione
data.
Un procedimento elementare per costruire A∗h consiste nell’ordinare gli errori locali ηS (Φ(x)) in
base al loro valore assoluto e poi marcare per il raffinamento gli elementi a partire da quello con
errore massimo, fino a rispettare la condizione (26).
Questo approccio non è tuttavia efficiente in quanto il migliore algoritmo di ordinamento implementabile avrà comunque una complessità di almeno Θ(|Th | log |Th |) dove |Th | è il numero di elementi
della triangolazione. Si propone pertanto un algoritmo di complessità Θ(|Th |) per l’identificazione
di A∗h . Dopo aver calcolato ηS (Φ(x)) ∀S ∈ Th , si considera un parametro γ ∈ (0, 1) e si esegue la
seguente procedura iterativa.
Procedimento di identificazione del più piccolo sottoinsieme di elementi da marcare per il raffinamento.
sum = 0 . 0 ;
ω = 1.0 ;
ηmax = maxS∈Th ηS (Φ(x)) ;
w h i l e ( sum < (1 − θ∗ )2 ηT2 h ) do
ω =ω−γ;
f o r ( t u t t i e l e m e n t i S ∈ Th )
i f ( S non marcato p e r i l r a f f i n a m e n t o )
i f ( ηS (Φ(x)) > ωηmax )
Marca S come “da r a f f i n a r e ”;
sum = sum + ηS (Φ(x))2 ;
12
Adattività di griglia
Il procedimento descritto termina sicuramente in un tempo finito in quanto ω raggiungerà alla
fine il valore nullo. Si osserva che il costo computazionale dell’algoritmo presentato resta contenuto
in quanto gli errori ηS (Φ(x)) sono stati precedentemente calcolati per tutti gli elementi S della
triangolazione.
Ai fini della costruzione del sottoinsieme Ah , infine, risulta critica la scelta del parametro γ: ci si
aspetta che valori piccoli di γ generino una buona approssimazione del sottoinsieme ottimale A∗h e
si rimanda a [D9̈6a] per dettagli sulla taratura di tale parametro.
Si presenta infine il seguente risultato descritto in [D9̈6a], in base a cui l’errore si riduce a ogni
iterazione dell’algoritmo di un fattore κ ∈ (0, 1) dipendente dal valore del parametro θ∗ e dai dati
del problema.
kη h+1 (Φ(x))k ≤ κkη h (Φ(x))k
4.2.1
Algoritmi di adattazione di griglia basati su GERS
Con gli stimatori presentati nel paragrafo 4.1 e la strategia di selezione degli elementi da raffinare
descritta nel paragrafo 4.2, è possibile formalizzare i procedimenti da seguire per adattare la mesh
all’istante iniziale e a ogni iterazione del solutore a elementi finiti illustrato in precedenza.
Adattazione iniziale Sono date un’immagine iniziale u0 (x), una mesh iniziale Th0 e una tolleranza
iniziale tolini .
Procedimento di adattazione della griglia iniziale da ripetere iterativamente fino a verificare {3}.
{1}
{2}
{3}
{4}
{5}
{6}
C a l c o l a r e l ’ i n t e r p o l a n t e Ihm u0 (x) su Thm ;
C a l c o l a r e η0 (S) p e r o g n i e l e m e n t o S ∈ Thm e ηini ;
Se ηini < tolini , t o l l e r a n z a s o d d i s f a t t a ;
A l t r i m e n t i , s e l e z i o n a r e g l i e l e m e n t i da r a f f i n a r e usando i l GERS;
Raffinare g l i elementi ;
Ritornare a {1}.
m
Adattazione a ogni istante temporale Sono date la soluzione Φm
h (x) al tempo t , una mesh
Thm e una tolleranza tol.
Procedimento di adattazione della griglia eseguito al più una volta ogni istante temporale.
(1)
(2)
(3)
(4)
(5)
C a l c o l a r e Φm+1
(x) su Thm ;
h
m+1
C a l c o l a r e ηS (Φh (x)) p e r o g n i e l e m e n t o S ∈ Thm e η(Φm+1
(x)) ;
h
Se η < tol , t o l l e r a n z a s o d d i s f a t t a ;
A l t r i m e n t i , s e l e z i o n a r e g l i e l e m e n t i da r a f f i n a r e usando i l GERS;
Raffinare g l i elementi .
Data la differente natura degli stimatori considerati nei due procedimenti di adattazione di griglia
e visto il differente numero di volte che tali algoritmi vengono eseguiti, si è deciso di adottare due
diversi valori di tolleranza tolini e tol, come proposto in [Fri03].
4.3
Strategie di raffinamento
Le condizioni enunciate nel paragrafo precedente per la validità della procedura GERS richiedono
metodi di infittimento della mesh particolari. Tra questi, se ne sono selezionati e confrontati tre
come suggerito da Mitchell in [Mit87].
In particolare, nei paragrafi seguenti si illustrano il procedimento di raffinamento Red Green Blue
che fa parte della famiglia dei metodi di divisione regolare e due strategie di infittimento basate
sulla bisezione, l’algoritmo Longest Edge bisection e quello Newest Vertex bisection.
13
Adattività di griglia
La differenza sostanziale tra queste famiglie di metodi consiste nella diversa modalità di divisione
degli elementi, essendo il primo un metodo che costruisce a partire da un triangolo quattro triangoli
simili mentre gli altri due procedono per bisezioni successive generando classi di equivalenza di
triangoli solo dopo un numero maggiore di raffinamenti.
Obiettivo primario di questa sezione è pertanto presentare le basi algoritmiche di questi tre metodi
evidenziandone pregi e difetti, mentre nella sezione 6 si procederà a un’analisi dei risultati forniti
da questi metodi con un approccio più quantitativo.
4.3.1
Raffinamento Red Green Blue
Il raffinamento Red Green Blue ([Ver96]) fa parte della famiglia dei metodi delle divisioni regolari
per l’adattazione di griglia. L’idea alla base di questa procedura prevede che ogni elemento marcato
per essere raffinato venga suddiviso in quattro triangoli analoghi a quello di partenza collegando i
punti medi dei lati; questo approccio rispetta il vincolo richiesto dalla strategia GERS di suddividere
ogni lato dei triangoli marcati per il raffinamento.
L’algoritmo Red Green Blue prevede tre differenti tipi di azioni sugli elementi della triangolazione
(Fig. 1):
• Raffinamento rosso: collegamento dei punti medi dei lati del triangolo;
• Bisezione blu: una prima bisezione del triangolo in modo che suddivida il lato più lungo;
una seconda bisezione, eseguita sul sottotriangolo contenente il nodo pendente, mediante il
collegamento di quest’ultimo al punto medio del lato più lungo del triangolo di partenza;
• Bisezione verde: bisezione del triangolo.
Fig. 1: Possibili strategie di raffinamento regolare: raffinamento rosso, bisezione blu e bisezione verde.
La strategia di adattazione di griglia basata sulla divisione regolare consiste in un primo passo di
marcamento dei triangoli da raffinare tramite la strategia GERS e in un successivo momento in cui
viene imposta la conformità della triangolazione analizzando gli elementi vicini a quelli da infittire:
nello specifico, se un triangolo presenta un nodo pendente su almeno un lato, l’algoritmo marcherà
per il raffinamento anche il lato più lungo.
Il principale lato negativo di questo metodo è rappresentato dall’elevato numero di nodi pendenti
che introduce sui lati. Pertanto, una volta terminata la fase di individuazione dei triangoli da
raffinare, si procede all’effettivo infittimento degli stessi mediante la seguente strategia:
• Un triangolo marcato dalla strategia GERS subisce un raffinamento rosso;
• Un triangolo con un nodo pendente subisce una bisezione verde;
• Un triangolo con due nodi pendenti sui lati subisce una bisezione blu;
• Un triangolo con tre nodi pendenti subisce un raffinamento rosso;
14
Adattività di griglia
• Un triangolo che presenta un nodo pendente su un lato già precedentemente sottoposto a
bisezione, subisce una bisezione blu.
Questa procedura è segnalata da Verfürth [Ver96] come la migliore disponibile in letteratura tra
le strategie di raffinamento regolare in quanto il numero di elementi inseriti per mantenere la
compatibilità della mesh risulta minimo.
4.3.2
Bisezione Longest Edge
Gli algoritmi di bisezione prevedono il raffinamento degli elementi della triangolazione mediante il
collegamento di un vertice al punto medio del lato opposto. Per un’introduzione teorica ai metodi
di raffinamento di mesh a elementi finiti basati sulla bisezione di triangoli e tetraedri, si rimanda a
[GGR07] e [HKK10].
In questo progetto si è affrontato il tema dell’adattazione di griglia attraverso la strategia di
riduzione dell’errore descritta in precedenza; per soddisfare la richiesta di suddividere tutti i lati
dell’elemento da raffinare, si procede dunque a una tripla bisezione: si collega il vertice di riferimento del triangolo con il punto medio del lato opposto e si ripete la stessa operazione per i due
sottotriangoli cosı̀ ottenuti.
Risulta pertanto di fondamentale importanza la scelta dei vertici di riferimento all’interno della
triangolazione. Nella mesh iniziale, si sceglie come riferimento per ogni triangolo il lato più lungo e
si marca il vertice opposto come vertice di riferimento. Una volta effettuata la prima bisezione, il
vertice di riferimento dei due sottotriangoli è fissato al valore dell’ultimo nodo inserito.
In [Riv84], Rivara propone di raffinare con una doppia bisezione tutti gli elementi marcati per
l’infittimento e di imporre successivamente la conformità della triangolazione raffinando i triangoli
che presentano nodi pendenti sui lati. Questo procedimento potrebbe generare nuove incompatibilità
o non risolvere quelle esistenti, pertanto si propone di ripetere iterativamente l’algoritmo fino a
eliminare tutti i nodi pendenti.
Dal momento che procede con la divisione iterativa del lato più lungo all’interno del triangolo,
l’algoritmo di bisezione Longest Edge dovrebbe impedire agli angoli di diventare troppo piccoli
deformando eccessivamente gli elementi della griglia. Nel lavoro citato, si dimostra inoltre come
questo processo termini in un numero finito di passi, generando una triangolazione regolare in cui
il più piccolo angolo ottenibile sia pari alla metà del più piccolo angolo presente nella mesh iniziale
T0 .
In questo lavoro, si procede in maniera leggermente differente, accorpando la strategia di marcamento GERS dei triangoli da raffinare con il procedimento per imporre la compatibilità della griglia
cosı̀ ottenuta. Durante il procedimento di individuazione dei triangoli da infittire, si memorizzano
gli elementi confinanti che si procede poi ad analizzare iterativamente: se un triangolo presenta un
nodo pendente su almeno un lato, l’algoritmo marcherà per il raffinamento anche il lato di riferimento.
Alla fine di questo controllo, si è quindi costruita una struttura che memorizza gli elementi da trattare e i lati suddividere per ogni triangolo. A seconda del numero di lati evidenziati, si procede
quindi a una, due o tre bisezioni (Fig. 2) e si aggiorna infine la triangolazione individuando per ogni
elemento il vertice di riferimento, opposto al lato più lungo.
4.3.3
Bisezione Newest Vertex
L’algoritmo di bisezione Newest Vertex si basa sulla stessa idea dell’algoritmo di bisezione Longest
Edge ma cambia la strategia con cui vengono selezionati i lati di riferimento per i singoli elementi.
È necessario innanzitutto selezionare un vertice di riferimento per ogni triangolo all’interno della
griglia iniziale; per comodità si è scelto di selezionare il vertice opposto al lato più lungo di ciascun
elemento ottenendo in tale maniera la stessa mesh iniziale utilizzata per l’implementazione della
15
Adattività di griglia
Fig. 2: Possibili strategie di bisezione per raffinare un triangolo suddividendo uno, due o tre lati.
bisezione Longest Edge.
Come il precedente metodo di bisezione, anche la bisezione Newest Vertex prevede il collegamento del
vertice di riferimento al punto medio del lato opposto e, per soddisfare la condizione dell’algoritmo
GERS di suddivisione di tutti i lati del triangolo segnato per il raffinamento, deve essere eseguito
tre volte consecutive per ogni triangolo da infittire.
Dopo aver collegato il vertice con il punto medio del lato opposto, si identifica il nodo inserito
più recentemente nella triangolazione come vertice di riferimento per i due sottotriangoli appena
creati.
Questo approccio assicura che un angolo non venga mai diviso più di una volta impedendo pertanto che si assottigli troppo generando elementi estremamente deformati. In particolare Sewell ha
dimostrato che tutti i triangoli generati mediante questo algoritmo rientrano in quattro classi di
similitudine (Fig. 3) e che quindi i valori degli angoli sono limitati nell’intervallo [0, π] ([Sew72]).
Nel lavoro sopracitato, si garantisce la conformità della mesh suddividendo solo i triangoli i cui
Fig. 3: Quattro classi di similitudine generate dalla bisezione Newest Vertex. Immagine tratta da [Mit87].
vertici di riferimento sono opposti allo stesso lato; un’altra possibilità è rappresentata dal procedimento illustrato in [Mit87] tramite un algoritmo ricorsivo che effettua delle bisezioni sull’elemento
opposto al vertice di riferimento fino a quando si incontra una coppia compatibile evitando in questa
maniera delle procedure di imposizione della conformità della mesh mediante la gestione dei nodi
isolati.
In questo progetto si è preferito seguire un approccio analogo a quello presentato per il metodo
di bisezione Longest Edge, andando cioè a individuare a priori i triangoli da raffinare e quelli
da trattare per imporre la compatibilità della griglia: come illustrato nel paragrafo precedente,
durante il processo di individuazione dei triangoli da raffinare tramite GERS, si tiene traccia degli
elementi adiacenti per poi procedere iterativamente a verificare se il lato di riferimento (opposto
all’ultimo vertice inserito) è marcato per essere suddiviso, ogniqualvolta un altro lato presenti un
nodo pendente. Questa strategia permette quindi di imporre la conformità della griglia prima di
raffinarla e procedere in seguito a una, due o tre bisezioni come in figura 2 a seconda del numero di
lati da dividere all’interno di ogni triangolo.
È importante sottolineare come i due metodi di bisezione siano tra loro molto simili e come in
alcuni casi le griglie che essi generano possano risultare identiche. È infatti immediato osservare che
se l’ultimo vertice inserito è sempre opposto al lato più lungo del triangolo, l’output sarà lo stesso.
16
Implementazione del codice C++
Parallelamente, la mesh iniziale da cui si parte con l’algoritmo di raffinamento rappresenta un vincolo
particolarmente stringente: nello specifico, la scelta di una triangolazione iniziale Longest Edge per
l’algoritmo Newest Vertex aumenta la probabilità che la griglia finale sia la stessa o quantomeno
estremamente simile.
5
Implementazione del codice C++
Nei paragrafi successivi si presenta la strategia di implementazione seguita per lo sviluppo di questo
progetto. Per i dettagli delle singole classi e dei vari metodi si rimanda alla documentazione Doxygen
associata al progetto, in questa sezione ci si limita a descrivere le principali scelte algoritmiche
effettuare.
5.1
GAS Framework
Questo progetto è stato sviluppato a partire da una libreria implementata in C++ per lo sviluppo di
solutori a elementi finiti per problemi differenziali, la GasFramework, per i cui dettagli si rimanda a
[FP10].
Innanzitutto si è inclusa la libreria per il trattamento delle immagini CImg ([CIM]) per facilitare
l’acquisizione dei dati iniziali associati alle figure da trattare e implementare una struttura di base per
la gestione delle tematiche associate al trattamento delle immagini. Si è inoltre ampliato il wrapper
per la libreria di geometria computazionale CGAL ([CGA]) all’interno della classe triangulation,
aumentandone le funzionalità per permettere di affrontare i temi dell’adattazione di griglia che si
sono presentati nella sezione precedente.
La classe problem contiene - come nella versione originale delle GasFramework - la formulazione
del problema e la costruzione del sistema algebrico. L’implementazione della forzante del problema
è stata generalizzata mediante la classe Force e la object factory associata ForceFactory.
Il calcolo delle stime a posteriori e le procedure di marcamento dei triangoli da raffinare, sono
implementati nella classe posterior, mentre le strategie di adattazione della mesh e le routine di
infittimento del singolo triangolo sono racchiuse rispettivamente nelle classi AdaptMesh e RefineMethod; nello specifico, la costruzione del metodo di raffinamento richiesto in base ai dati forniti
dall’utente è affidata alla object factory RefinementFactory.
Infine, la visualizzazione della soluzione è affidata alla classe svg che esporta i dati nel formato
svg; per questa operazione, si è fatto riferimento alla classe già implementata nella libreria GasFramework, estendendo alcune funzionalità per permettere il salvataggio della soluzione iniziale, della
griglia di calcolo o della soluzione a ogni istante temporale.
Si è poi creato un namespace per racchiudere tutte le procedure e gli oggetti associati al problema
di segmentazione dell’immagine e lo si è nominato img_seg. Si segnala la bibliografia essenziale
consultata durante lo sviluppo del progetto: [Str00], [Yan01] e [Ale01].
5.2
Il corpo del programma: main.cpp
Il programma implementato legge da file i dati del problema mediante la funzione readData e
costruisce di conseguenza le strutture necessarie per la sua soluzione. In particolare, le informazioni
essenziali per la risoluzione del problema sono contenute nella classe triangulation che gestisce la
griglia di calcolo e nella classe problem che racchiude la formulazione dei funzionali, l’assemblamento
delle matrici per il problema algebrico e la sua soluzione. A questi oggetti, sono inoltre collegate
le classi initial_image e initial_curve che maneggiano i dati iniziali e la classe Force con le
rispettive classi derivate per la formulazione dei termini forzanti associati ai vari modelli descritti.
Le tecniche di adattazione di griglia, sono infine trattate dalle classi posterior, RefineMethod e
AdaptMesh: la prima è incaricata della costruzione degli stimatori dell’errore e della strategia di
17
Implementazione del codice C++
marcamento Guaranteed Error Reduction Strategy; la seconda - con le classi derivate - si occupa di
infittire gli elementi da raffinare e garantire la conformità della mesh cosı̀ ottenuta; la terza coordina
l’azione delle precedenti classi e incarna quindi la procedura di adattazione nella sua totalità.
L’esportazione della soluzione è infine affidata alla classe svg che genera un output scrivendo su un
file Scalable Vector Graphics la triangolazione, la soluzione e il bordo della segmentazione a seconda
di quando i metodi di tale classe vengono invocati.
Di seguito, si riporta il flusso del codice main.cpp nel caso in cui tutte le procedure sopra
descritte vengano eseguite. Si sottolinea che le procedure di adattazione di griglia non sono necessariamente eseguite e che il solutore potrebbe essere chiamato direttamente su una griglia iniziale
senza raffinamento saltando i passi {6}, {7}, {8}, {9c}, {9d} e {9e}.
Flusso del main
{1}
{2}
{3}
{4}
{5}
{6}
{7}
{8}
{9}
5.3
L e t t u r a da f i l e d e i d a t i d e l problema ;
Creazione d e l l a t r i a n g o l a z i o n e ;
C r e a z i o n e d e l l ’ immagine i n i z i a l e e d e l l a curva i n i z i a l e ;
C r e a z i o n e d e l problema ( i n c l u d e c r e a z i o n e d e l l a f o r z a n t e ) ;
Stampa d e l l ’ immagine i n i z i a l e i n fo rm ato svg ;
Creazione d e l l o stimatore dell ’ e r r o r e di i n t e r p o l a z i o n e i n i z i a l e ;
Adattazione di g r i g l i a i n i z i a l e ;
Stampa d e l l a g r i g l i a a d a t t a t a i n f or m ato svg ;
Ciclo temporale
{ a } S o l u z i o n e d e l problema ;
{b} Stampa d e l l a s o l u z i o n e i n f ormato svg ;
{c} Creazione d e l l o stimatore d e l l ’ e r r o r e r e s i d u a l e a ogni passo temporale ;
{d} A d a t t a z i o n e d i g r i g l i a a o g n i i s t a n t e ;
{ e } Stampa d e l l a g r i g l i a a d a t t a t a i n f or mat o svg .
Lettura dati da input
La lettura dei dati da input è gestita dalla funzione readData, di cui si riporta il prototipo.
La funzione riceve come parametri la stringa inputpath che contiene il percorso per il file contenente i dati del problema, le stringhe path e res in cui vengono salvati rispettivamente i percorsi
dell’immagine da processare e della cartella in cui salvare i risultati e le struct pb_data e ref_data
che memorizzano i parametri del problema e i parametri per la procedura di adattazione di griglia.
Codice 1: Prototipo della funzione readData
1
2
void readData ( const std :: string & inputpath , std :: string & path , std :: string & res ,
struct pb_data & pb , struct ref_data & rf );
Si riporta un estratto della funzione in cui si legge il file al percorso inputpath e si gestiscono
eventuali eccezioni per poi passare alla lettura di uno degli elementi del file di configurazione. Per
queste azioni ci si è appoggiati alla libreria libconfig per la gestione dei file di configurazione e
si è creato un oggetto libconfig::Config. In particolare, i metodi di tale classe permettono di
leggere un file (metodo readFile), recuperare le informazioni associate all’inizio dello stesso (metodo
getRoot) e ricercare una data stringa per poi memorizzarla in una variabile data dall’utente (metodo
lookupValue).
Codice 2: Estratto della funzione readData
1
2
3
4
libconfig :: Config cfg ;
// Read file
try { cfg . readFile ( inputpath . c_str ()); }
catch ( const libconfig :: FileIOException & fioex ) {
18
Implementazione del codice C++
5
6
7
8
9
10
11
12
13
14
15
16
17
throw " I / O error while reading file . " ;
}
catch ( const libconfig :: ParseException & pex ) {
std :: cerr << " Parse error at " << pex . getFile () << " : " << pex . getLine ()
<< " - " << pex . getError () << std :: endl ;
throw " Abort " ;
}
const libconfig :: Setting & root = cfg . getRoot ();
// Look for " file_path "
if ( root . lookupValue ( " file_path " , path )) {
std :: cout < < " File path : " << path << std :: endl << std :: endl ;
}
else { throw " No ’ file_path ’ setting in configuration file . " ; }
5.4
Gestione della mesh: la classe triangulation
La classe triangulation racchiude e gestisce le informazioni sulla geometria della griglia di calcolo.
In questo lavoro si è effettuata una generalizzazione di quanto presentato in [FP10], estendendo le
funzionalità della classe per la mappatura e l’accesso agli elementi adiacenti a una data faccia, in
maniera da rendere possibili le tecniche di adattazione di griglia precedentemenete descritte.
In particolare, si è arricchita la classe face_info con l’informazione associata al lato di riferimento
del triangolo e si sono creati due contenitori nel private della classe triangulation per la memorizzazione delle liste delle coordinate dei vertici degli elementi precedentemente bisecati e raffinati,
rispettivamente bisected_ e refined_.
Codice 3: Membri privati della classe triangulation contenenti gli elementi bisecati e raffinati
1
2
3
4
// Container that stores the vertices of bisected triangles
std :: vector < std :: list < std :: pair < double , double > > > bisected_ ;
// Container that stores the vertices of refined triangles
std :: vector < std :: list < std :: pair < double , double > > > refined_ ;
Il costruttore della classe riceve pertanto in ingresso la lista dei punti di bordo - nel nostro caso
sotto forma di iteratori - e un parametro che determina il diametro massimo dei triangoli della mesh.
Durante la procedura di numerazione delle facce della triangolazione, il codice individua inoltre il
lato di lunghezza massima e ne memorizza l’indice locale all’interno del campo ref_idx. Essendo
la classe triangulation un wrapper della triangolazione implementata nella libreria CGAL, non si
entra in ulteriori dettagli implementativi e si rimanda a [FP10] e [CGA] per eventuali informazioni
aggiuntive.
Codice 4: Costruttore della classe triangulation
1
2
template < typename iterator_ >
triangulation ( iterator_ begin , iterator_ end , double h = 0.);
Ci si limita a descrivere brevemente le operazioni eseguite dai metodi per il raffinamento di griglia,
focalizzandoci sulla versione per triangoli da bisecare, essendo il trattamento dei triangoli da suddividere in maniera regolare tramite l’algoritmo di raffinamento Red Green Blue del tutto analogo.
Il metodo insert_biFace riceve in input una lista contentente le coordinate dei vertici di un triangolo e inserisce l’elemento associato nel vettore che memorizza le facce precedentemente bisecate;
analogamente, il metodo remove_biFace esegue una ricerca dell’elemento da rimuovere e, in caso
sia presente, lo elimina. Infine, il metodo is_bisected si occupa di eseguire una ricerca all’interno
del vettore bisected_ e restituisce l’iteratore alla posizione corrispondente alla faccia in analisi se
questa è presente o alla coda del vettore se non viene trovata. Dal momento che non è a priori noto
quale sia il primo vertice nella numerazione locale della faccia, l’algoritmo di ricerca verifica tutte
19
Implementazione del codice C++
le possibili combinazioni, ruotando il triangolo a partire dall’assunto che le etichette locali vengono
assegnate in ordine antiorario, comportamento di default in CGAL.
Codice 5: Metodi della classe triangulation per la gestione delle facce bisecate
1
2
3
4
5
6
7
// Insert a face in the vector of previously bisected faces
void insert_biFace ( const std :: list < std :: pair < double , double > > & lst );
// Remove a face from the vector of previously bisected faces
void remove_biFace ( face_iterator_t & it );
// Verify if a given face is contained in the vector of previously bisected faces
std :: vector < std :: list < std :: pair < double , double > > >:: iterator
is_bisected ( face_iterator_t & it , int & idx );
Una volta individuati i lati da inserire e rimuovere per il procedimento di raffinamento di griglia
stabilito, i metodi insertEdge e removeEdge si occupano dell’effettivo inserimento del lato all’interno della mesh come vincolo per la triangolazione e, rispettivamente, dell’eliminazione del vincolo
corrispondente. Il metodo insertEdge riceve come parametri di ingresso due coppie di coordinate
associate agli estremi del lato della triangolazione da inserire mentre il metodo removeEdge procede
all’eliminazione del lato i-eismo nella numerazione della faccia in analisi.
Codice 6: Metodi della classe triangulation per l’inserimento e la rimozione di lati nella mesh
1
2
3
4
5
// Insert an edge as a constraint in the triangulation
void insertEdge ( const std :: pair < double , double > & left ,
const std :: pair < double , double > & right );
// Remove an edge from the triangulation
void removeEdge ( face_iterator_t & it , const int & i );
Infine, l’implementazione della classe si conclude con i metodi di update che, dapprima, ciclano sui
nodi della mesh per eseguirne la rinumerazione e in seguito ripetono lo stesso procedimento sulle
facce assegnando una nuova etichetta e aggiornando il lato di riferimento in base all’algoritmo di
raffinamento associato: sono pertanto presenti le tre varianti per gli algoritmi Regular Division,
Longest Edge e Newest Vertex.
Codice 7: Metodi della classe triangulation per l’aggiornamento delle informazioni sulla griglia
1
2
3
4
5
6
// Update triangulation after RegularDivision refinement
void update_dataRD ();
// Update triangulation after LongestEdge refinement
void update_dataLE ();
// Update triangulation after NewestVertex refinement
void update_dataNV ();
Si osserva che i metodi di adattazione di griglia descritti hanno bisogno di poter accedere alle
informazioni della classe triangulation per procedere al calcolo degli stimatori dell’errore e all’infittimento della griglia di calcolo: per tale motivo, le classi associate sono dichiarate friend della
classe triangulation.
Codice 8: Classi friend della classe triangulation
1
2
3
4
5
// Allow the estimators to access the information about the triangulation
friend class posterior ;
// Allow refinement method to access and modify the triangulation
friend class RefineMethod ;
friend class AdaptMesh ;
Per quanto riguarda poi le classi annidate definite all’interno della classe triangulation, si è lavorato principalmente sulla classe face che implementa un wrapper della faccia definita nell’architettura
di CGAL. In particolare, si sono conservate tutte le operazioni di lettura precedentemente implementate in [FP10], tra cui quella dell’indice associato alla faccia, al lato di riferimento, ai vertici
20
Implementazione del codice C++
della stessa nonché quella delle coordinate di tutti gli estremi. In aggiunta si sono implementati dei
metodi che permettono di recuperare i dati collegati agli elementi confinanti: di seguito si riportano
i metodi per ottenere l’indice della faccia opposta a un dato lato, l’indice e le coordinate dell’i-esimo
vertice della faccia opposta a un dato lato e l’indice del vertice opposto al lato che divide un triangolo
dal suo vicino.
Codice 9: Estratto dei metodi della classe face per l’accesso alle informazioni sugli elementi confinanti
1
2
3
4
5
6
7
8
9
10
11
12
13
// Return the index of the neighbor opposite to the i - th edge
int neighbor ( int const & i ) const ;
// Return the index of the j - th node of the neighbor opposite to the i - th edge
int neighbor_idx ( int const & i , int const & j ) const ;
// Return the first coordinate of the j - th node of the neighbor opposite to
// the i - th edge
double neighbor_x ( int const & i , int const & j ) const ;
// Return the second coordinate of the j - th node of the neighbor opposite to
// the i - th edge
double neighbor_y ( int const & i , int const & j ) const ;
// Return the index of the vertex opposite to the i - th edge within
// the i - th neighbor
int loc_mirror_vertex ( int const & i ) const ;
5.5
Le informazioni iniziali: le classi initial_image e initial_curve
Come anticipato, per la gestione delle immagini si è inclusa la libreria CImg che permette di acquisire e trattare il dato iniziale in maniera efficiente. Nello specifico, si è implementata la classe
initial_image che riceve in ingresso un oggetto CImg della CImg Library e le sue dimensioni.
Per ricondursi alla griglia di calcolo costruita dalla classe triangulation, si è implementato il metodo Trestrict che memorizza nel vettore vec i valori dell’immagine iniziale nei soli nodi della griglia
di calcolo. Inoltre, per permettere l’integrazione sulle facce della mesh utilizzando i nodi di Gauss,
si è costruito un metodo che a partire dai valori nodali dell’immagine restituisce un’approssimante
lineare valutabile e si è corredato questo wrapper con l’overloading dell’operatore di accesso ().
Codice 10: Estratto dei metodi della classe initial_image
1
2
3
4
5
6
7
8
// Constructor
inline initial_image ( const cimg_library :: CImg < double > & img , double const w ,
double const h ): img_ ( img ) , w_ ( w ) , h_ ( h ) {}
// Restrict CImg to the computational mesh
void Trestrict ( triangulation const & cdt_ , Eigen :: VectorXd & vec ) const ;
// Return a function built from linear approximation of the nodal values of
// the initial image
inline integral_function fun () { return integral_function (* this ); }
L’equazione (16) che descrive l’evoluzione della funzione di level-set necessita di una condizione
iniziale per essere chiusa. La classe initial_curve si occupa appunto della costruzione di una
curva di livello da utilizzare come dato iniziale per il problema di level-set che si è descritto in
precedenza. In particolare, il costruttore riceve in ingresso una referenza costante alla triangolazione
e le dimensioni dell’immagine ed inizializza il vettore phi_ in modo che assuma il valore della distanza
con segno da un cerchio iniziale di raggio dato.
Gli altri metodi della classe si limitano a restituire all’esterno le informazioni dei membri privati.
Codice 11: Estratto dei metodi della classe initial_curve
1
2
3
4
// Constructor
initial_curve ( triangulation const & cdt_ , double const w , double const h );
// Export level - set initial vector
void vectorize ( Eigen :: VectorXd & vec ) const { vec = phi_ ; }
21
Implementazione del codice C++
5
6
7
8
// Return image width
inline double w () const { return w_ ; }
// Return image height
inline double h () const { return h_ ; }
5.6
Il termine forzante: le classi Force e ForceFactory
La forzante del problema assume forme diverse a seconda del modello che si sta considerando. Si
è pertanto introdotta una classe astratta Force e tre specifiche ChanVeseForce, RegionGaussianForce e RegionNonParametricForce. La costruzione dell’oggetto adeguato al problema in analisi
è quindi delegata a una object factory. In questo paragrafo, si riportano i dettagli di alcune classi
ausiliarie implementate per facilitare la gestione delle procedure di integrazione dei coefficienti del
problema e le specifiche delle varie formulazioni del termine forzante.
Le classi integral_argument e int_arg_mod
Le due classi in questione si occupano della costruzione di una funzione approssimante sulle facce della triangolazione a partire da un vettore di valori nodali. In particolare, la classe integral_argument
costruisce un’approssimazione lineare a tratti mentre int_arg_mod realizza un’approssimazione
costante a tratti.
Codice 12: Prototipo dei costruttori delle approssimanti lineari e costanti a tratti
1
2
3
4
// Constructor for piecewise linear approximating function
integral_argument ( Eigen :: VectorXd & vec , triangulation :: face_iterator_t & it );
// Constructor for piecewise constant approximating function
int_arg_mod ( Eigen :: VectorXd & vec , triangulation :: face_iterator_t & it );
Entrambe le classi sono poi caratterizzate dalla presenza di una classe annidata integral_function
che eredita la sua struttura pubblicamente da gas::functional::function< 2, · >, cioè da una
funzione di due variabili. Di seguito, si riporta il prototipo dell’oggetto annidato nella classe integral_argument mentre la corrispondente classe collegata a int_arg_mod può essere facilmente
ottenuta sostituendo il tipo ricevuto in ingresso dal costruttore.
Codice 13: Prototipo della classe integral_function annidata in integral_argument
1
2
3
4
5
6
7
8
9
10
11
class integral_function : public gas :: functional :: function <2 , integral_function >
{
public :
// Constructor
inline integral_function ( integral_argument const & F ): F_ ( F ) {};
// Operator at
double operator () ( double const & x , double const & y ) const ;
private :
// Integral argument object
integral_argument const & F_ ;
};
Infine, nella classe esterna è presente il metodo fun che restituisce l’oggetto integral_function
associato alla classe in analisi:
Codice 14: Prototipo dei costruttori delle approssimanti lineari e costanti a tratti
1
inline integral_function fun () { return integral_function (* this ); }
22
Implementazione del codice C++
La forzante Force
La generica forma del termine forzante tiene in considerazione la triangolazione su cui si esegue
l’approssimazione, la soluzione al precedente istante temporale, l’immagine iniziale e un parametro
di regolarizzazione ρ. Si osserva che non è possibile istanziare un oggetto di tipo Force in quanto
tre suoi metodi sono dichiarati virtual.
Fig. 4: Ereditarietà della classe Force.
Codice 15: Prototipo della classe astratta Force
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Force
{
public :
// Constructor
Force ( triangulation const & cdt , Eigen :: VectorXd & x_old ,
Eigen :: VectorXd & u0 , double rho ):
cdtF_ ( cdt ) , x_oldF_ ( x_old ) , u0F_ ( u0 ) , rhoF_ ( rho ) {};
// Virtual destructor
virtual ~ Force () {};
// Virtual method that returns the force term as a vector
virtual Eigen :: VectorXd & vector () = 0;
// Virtual method that returns a criterion to evaluate segmentation
// in region 1
virtual Eigen :: VectorXd & p1 () = 0;
// Virtual method that returns a criterion to evaluate segmentation
// in region 2
virtual Eigen :: VectorXd & p2 () = 0;
protected :
// Triangulation
triangulation const & cdtF_ ;
// Solution at previous time step
Eigen :: VectorXd x_oldF_ ;
// Restriction of the initial image to the triangulation
Eigen :: VectorXd u0F_ ;
// Parameter rho
double rhoF_ ;
private :
};
La forzante ChanVeseForce
La forzante associata al modello di Chan-Vese richiede la conoscenza del valore medio dell’intensità
nelle due regioni in cui si suddivide l’immagine, memorizzata nelle variabili mu1_ e mu2_; inoltre i
vettori p1_ e p2_ salvano la probabilità associata alle due regioni Ω1 e Ω2 e il valore finale della
forzante viene incluso nel vettore force_.
Codice 16: Membri privati della classe ChanVeseForce
1
2
3
4
// Vector containing force term
Eigen :: VectorXd force_ ;
// Vector containing the criterion related to region 1 to stop the evolution when
// the solution is " stationary "
23
Implementazione del codice C++
5
6
7
8
9
10
11
12
Eigen :: VectorXd p1_ ;
// Vector containing the criterion related to region 2 to stop the evolution when
// the solution is " stationary "
Eigen :: VectorXd p2_ ;
// Mean value of intensity over region 1
double mu1_ ;
// Mean value of intensity over region 2
double mu2_ ;
Il costruttore determina i valori medi presentati nel paragrafo 2.2 mediante la seguente routine:
Codice 17: Estratto del cstruttore della classe ChanVeseForce
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Loop over the faces to compute the integrals
for ( iterator_f it ( cdtF_ . face_begin ()); it != cdtF_ . face_end (); ++ it ) {
// Build the linear approximating functions from the previous vectors
integral_argument c0_num_fun ( c0_num_vec , it );
integral_argument c0_den_fun ( c0_den_vec , it );
integral_argument c1_num_fun ( c1_num_vec , it );
integral_argument c1_den_fun ( c1_den_vec , it );
s1 (* it ) , s2 (* it ) , s3 (* it ) , s4 (* it );
double const r1 ( s1 . integrate ( c0_num_fun . fun ()));
c0_num += r1 ;
double const r2 ( s2 . integrate ( c0_den_fun . fun ()));
c0_den += r2 ;
double const r3 ( s3 . integrate ( c1_num_fun . fun ()));
c1_num += r3 ;
double const r4 ( s4 . integrate ( c1_den_fun . fun ()));
c1_den += r4 ;
}
// Store the mean value of intensity in region 1 and 2
mu1_ = c0_num / c0_den ;
mu2_ = c1_num / c1_den ;
e il metodo vector viene sovrascritto in questa maniera
Codice 18: Implementazione del metodo ChanVeseForce::vector
1
2
3
4
5
6
Eigen :: VectorXd & ChanVeseForce :: vector () {
# define right_handCV (a , b ) (((( u0F_ ). cwise () - a )*(( u0F_ ). cwise () - a )) ((( u0F_ ). cwise () - b )*(( u0F_ ). cwise () - b )))
force_ = right_handCV ( mu2_ , mu1_ );
return force_ ;
}
La forzante RegionGaussianForce
Il termine forzante che si costruisce a partire dal modello basato sulle statistiche regionali con
densità di probabilità gaussiana, presenta un’interfaccia analoga a quella del modello di ChanVese, ad eccezione di due membri privati aggiuntivi che rappresentano l’informazione associata alle
deviazioni standard sig1_ e sig2_ rispettivamente in Ω1 e Ω2 .
Il calcolo dei valori medi all’interno delle regioni è lo stesso precedentemente mostrato per il caso
del modello di Chan-Vese; mediante un ciclo sulle facce, suddividendo l’integrale sul dominio nella
somma degli integrali sui singoli elementi della triangolazione, si può in maniera analoga dedurre il
valore delle deviazioni standard.
Il costruttore di questa classe può essere quindi ricavato estendendo in maniera elementare il costruttore già descritto per la forzante di Chan-Vese calcolando i due parametri aggiuntivi necessari
per la costruzione delle funzioni densità di probabilità p1 e p2 .
24
Implementazione del codice C++
Ci si limita pertanto a riportare l’implementazione del metodo vector che, costruisce il funzionale associato alla forzante come differenza delle log-distribuzioni di probabilità nelle regioni
Ωi .
Codice 19: Implementazione del metodo RegionGaussianForce::vector
1
2
3
4
5
6
7
8
9
10
Eigen :: VectorXd & RegionGaussianForce :: vector () {
// Compute the log - distributions from probability densities
Eigen :: VectorXd lp1 , lp2 ;
lp1 . setZero ( cdtF_ . nodes ());
lp2 . setZero ( cdtF_ . nodes ());
ln ( p1_ , lp1 );
ln ( p2_ , lp2 );
force_ = lp1 - lp2 ;
return force_ ;
}
La forzante RegionNonParametricForce
In questo caso, il termine forzante si ricava mediante una stima non-parametrica della densità di
probabilità quindi l’interfaccia privata della classe viene modificata come segue
Codice 20: Membri privati della classe RegionNonParametricForce
1
2
3
4
5
6
7
8
9
10
// Vector containing force term
Eigen :: VectorXd force_ ;
// Initial image
initial_image img_inF_ ;
// Vector containing the criterion related to region 1 to stop the evolution when
// the solution is " stationary "
Eigen :: VectorXd p1_ ;
// Vector containing the criterion related to region 2 to stop the evolution when
// the solution is " stationary "
Eigen :: VectorXd p2_ ;
Il costruttore legge i valori delle intensità di grigio dal vettore u0F_ e costruisce l’istogramma
discreto per mappare nella scala dei grigi la distribuzione dell’informazione che l’immagine iniziale
racchiude nei singoli pixel ed esegue infine uno smothing gaussiano.
Codice 21: Estratto del costruttore della classe RegionNonParametricForce
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
for ( iterator_n it ( cdtF_ . nodes_begin ()); it != cdtF_ . nodes_end (); ++ it ) {
// Rescale intensity
double v = u0F_ ( it - > i ())*255.0;
int res = int (( v - 0.0) * in . binValue ());
if ( res >= in . count_bins ()) { -- res ; }
// Identify which region the pixel belongs to
if ( x_oldF_ ( it - > i ()) > 0) {
inVal [ res ] += 1;
areaIN += 1;
}
else {
outVal [ res ] += 1;
areaOUT += 1;
}
}
double areaIN_inv = areaIN != 0 ? double (1) / double ( areaIN ) : 1;
double areaOUT_inv = areaOUT != 0 ? double (1) / double ( areaOUT ) : 1;
for ( int i = 0; i < 256; ++ i ) {
inVal [ i ] *= areaIN_inv ;
outVal [ i ] *= areaOUT_inv ;
25
Implementazione del codice C++
21
22
23
24
25
26
27
}
// Store the vector using an histogram object
in . set_values ( inVal );
out . set_values ( outVal );
// Gaussian smoothing
in . gaussK_smooth ( sigma );
out . gaussK_smooth ( sigma );
Per tenere in considerazione queste informazioni si è implementata la classe histogram che
memorizza i valori minimo e massimo presenti all’interno dell’istogramma e il numero di barre in
cui lo spazio dei valori viene suddiviso; i metodi associati permettono poi di individuare la barra
cui un dato valore appartiene e di calcolare la corrispondente probabilità, oltre che di effettuare uno
smoothing mediante un kernel gaussiano. Il metodo vector presenta infine la stessa implementazione
del caso precedente e il funzionale associato alla forzante viene espresso tramite la differenza delle
log-distribuzioni di probabilità.
La object factory ForceFactory
Si conclude questo paragrafo con la descrizione del metodo create della object factory ForceFactory
che permette la costruzione runtime del termine forzante associato al modello in analisi tramite
un’istanziazione dell’oggetto corretto a seconda di un parametro passato dall’utente.
Codice 22: Implementazione del metodo ForceFactory::create
1
2
3
4
5
6
7
8
9
10
11
Force * ForceFactory :: create () {
// Depending on the parameter type_ , calls a different constructor
if ( type_ == 0) { return new ChanVeseForce ( cdtF_ , x_oldF_ , u0F_ , rhoF_ ); }
else if ( type_ == 1) {
return new Regi onGaussianForce ( cdtF_ , x_oldF_ , u0F_ , rhoF_ );
}
else if ( type_ == 2) {
return new Reg ionNo nP ara m etri c Forc e ( cdtF_ , x_oldF_ , img_inF_ , u0F_ , rhoF_ );
}
return 0;
}
5.7
Formulazione del problema: la classe problem
La formulazione del problema differenziale è affidata alla classe problem che memorizza una referenza
costante alla triangolazione e tutti gli oggetti necessari per la costruzione delle forme variazionali,
del problema algebrico e per la corretta gestione degli output.
Codice 23: Membri privati della classe problem
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Mesh
triangulation const & cdt_ ;
// Solution at current time step
Eigen :: VectorXd x_ ;
// Solution at previous time step
Eigen :: VectorXd x_old_ ;
// Initial curve
initial_curve curve_ ;
// Initial image
initial_image img_in_ ;
// Restriction of the initial curve to the triangulation
Eigen :: VectorXd u_0_ ;
// Struct that contains the problem data
const struct pb_data & data_ ;
26
Implementazione del codice C++
15
16
17
18
19
20
21
22
// Elementi non nulli
int no_zeros_ ;
// Boolean variable " Problem already solved "
bool solved ;
// Boolean variable " Problem is not at first time step "
bool modified ;
// Boolean variable " Refinement step "
bool refinement ;
Per quanto riguarda i metodi implementati, la clsse problem presenta le seguenti funzionalità:
• Costruttore e distruttore;
• Metodi per accedere ai parametri del problema;
• Metodi per accedere alla soluzione al tempo corrente e precedente e all’immagine iniziale;
• Metodo per risolvere il problema;
• Metodo per calcolare il criterio di arresto;
• Metodi per aggiornare le informazioni associate al problema;
• Metodi per interpolare la soluzione.
Il costruttore riceve quindi in input la curva e l’immagine iniziali e la struct contenente i dati
forniti in ingresso dall’utente; dopo aver inizializzato le grandezze, procede quindi alla restrizione
dell’immagine iniziale e della curva al tempo zero sulla griglia di calcolo discreta.
Codice 24: Costruttore della classe problem
1
2
3
4
5
6
7
8
9
10
problem :: problem ( triangulation const & cdt , initial_curve & phi , initial_image & u0 ,
const struct pb_data & data ):
cdt_ ( cdt ) , curve_ ( phi ) , img_in_ ( u0 ) , data_ ( data ) , no_zeros_ (0 u ) , solved ( false ) ,
modified ( false ) , refinement ( false )
{
u_0_ . setZero ( cdt_ . nodes ());
img_in_ . Trestrict ( cdt_ , u_0_ );
x_old_ . setZero ( cdt_ . nodes ());
phi . vectorize ( x_old_ );
}
Il metodo solve rappresenta il cuore della classe problem in quanto racchiude le definizioni delle
forme variazionali che compaiono nel problema, le direttive per la costruzione della corrispondente
versione algebrica e la sua risoluzione.
Codice 25: Estratto del metodo problem::solve() per la definizione delle forme variazionali
1
2
3
4
5
6
7
8
9
10
11
12
13
// Using derivatives from gas
using gas :: functional :: dx ;
using gas :: functional :: dy ;
// Functions
# define delta_rho (r , z ) (( r * onesn ). cwise ()/( M_PI *( r * r + ( z * z ). cwise ())))
# define q_eps2 (e , g ) ( e * e + ( g * g ). cwise ())
// Generalized mass matrix
# define generalized_mass (u , v ) (( u * v )* coef_f1 . fun ())
// Generalized stiffness matrix
# define gener alized_stiffness (u , v ) (( dx ( u )* dx ( v ) + dy ( u )* dy ( v ))* coef_f2 . fun ())
// Generalized source
# define source ( v ) ( coef_f4 . fun ()* v )
// Generalized mass matrix for previous time step
27
Implementazione del codice C++
14
15
16
17
18
19
20
21
22
23
# define old_mass ( v ) ( coef_f3 . fun ()* v )
// Create force term
ForceFactory * myFactory = new ForceFactory ( this - > data (). model , cdt_ , x_old_ ,
img_in_ , u_0_ , this - > rho ());
Force * force_t = myFactory - > create ();
// Coefficients for the equation
coef_v1 =( onesn . cwise ()/( delta_rho ( this - > rho () , x_old_ )));
coef_v2 =( ones . cwise ()/ qe );
coef_v3 = x_old_ . cwise ()* coef_v1 ;
coef_v4 = force_t - > vector ();
Si procede quindi alla valutazione nei nodi di Gauss delle forme variazionali e dei coefficienti
precedentemente definiti e al conseguente assemblamento delle strutture algebriche del problema.
Codice 26: Estratto del metodo problem::solve() per l’assemblamento dei blocchi della matrice
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// Algebraic structures to solve the problem
Eigen :: DynamicSparseMatrix < double > Mtmp ( cdt_ . nodes () , cdt_ . nodes ());
Eigen :: DynamicSparseMatrix < double > Atmp ( cdt_ . nodes () , cdt_ . nodes ());
Eigen :: VectorXd b ;
integrator_t s ;
// Loop over the faces
for ( iterator_f it ( cdt_ . face_begin ()); it != cdt_ . face_end (); ++ it ) {
s (* it );
element_t e (* it );
// Approximating the coefficients with linear functions to evaluate them in
// Gauss points
integral_argument coef_f1 ( coef_v1 , it );
int_arg_mod coef_f2 ( coef_v2 , it );
integral_argument coef_f3 ( coef_v3 , it );
integral_argument coef_f4 ( coef_v4 , it );
// Generalized mass matrix
gas_rangeu (i , N ) {
gas_rangeu (j , N ) {
int const ii ( it - > i ( i ));
int const jj ( it - > i ( j ));
double const r ( s . integrate ( generalized_mass ( e . b ( j ) , e . b ( i ))));
Mtmp . coeffRef ( ii , jj ) += r ;
}
}
// Generalized stiffness matrix
gas_rangeu (i , N ) {
gas_rangeu (j , N ) {
int const ii ( it - > i ( i ));
int const jj ( it - > i ( j ));
double const r ( s . integrate ( ge neral iz ed_ s ti ff ness ( e . b ( j ) , e . b ( i ))));
r *= this - > mu ()* this - > tau ();
Atmp . coeffRef ( ii , jj ) += r ;
}
}
// Force term
gas_rangeu (i , N ) {
int const ii ( it - > i ( i ));
double const r1 ( s . integrate ( old_mass ( e . b ( i ))));
double r2 (0.0);
if ( this - > data (). model == 0) { // ChanVeseForce
r2 = this - > lambda ()* this - > tau ()*( s . integrate ( source ( e . b ( i ))));
}
else { // Regi onGaussianForce and R egion N o nP a r am et r i cF o r ce
r2 = this - > tau ()*( s . integrate ( source ( e . b ( i ))));
}
b . coeffRef ( ii ) += r1 ;
28
Implementazione del codice C++
b . coeffRef ( ii ) += r2 ;
47
}
48
49
}
Il calcolo della soluzione all’istante corrente è infine assegnata alla procedura implementata nella
Eigen Library che prevede la risoluzione del sistema algebrico mediante la fattorizzazione LU della
matrice in formato sparso costruita a partire dai blocchi precedenti.
Codice 27: Estratto del metodo problem::solve() per la fattorizzazione LU della matrice del problema
1
2
3
4
5
6
// Final matrix
Eigen :: SparseMatrix < double > K ( Mtmp + Atmp );
// Solve the algebraic problem using LU factorization for sparse matrix
Eigen :: SparseLU < Eigen :: SparseMatrix < double > , Eigen :: UmfPack > lu ( K );
gas_assert ( lu . succeeded ());
lu . solve (b , & x_ );
Come ampiamente descritto nel paragrafo 3.3, il problema di segmentazione delle immagini non
è tempo dipendente e pertanto risulta di fondamentale importanza la determinazione del tempo
finale in cui arrestare le simulazioni; il metodo, di cui si riporta il prototipo, calcola lo scostamento
in norma L2 dell’errore di segmentazione tra un istante temporale e il precedente utilizzando una
object factory per la costruzione del termine forzante e i suoi metodi p1 e p2 per l’estrazione delle
informazioni relative alle densità di probabilità nelle due regioni.
Codice 28: Metodo della classe problem per il calcolo del criterio di arresto
1
2
double stopping_res ();
s
Dal momento che le procedure di raffinamento della griglia generano una trasformazione della
triangolazione su cui è ambientato il problema, risulta di fondamentale importanza interpolare la
soluzione nei nuovi nodi della griglia cosı̀ generati. Per tale motivo, la procedura che si occupa di
identificare nuovi vertici da inserire all’interno di un triangolo, chiama la funzione interpolate che,
a partire dalla faccia in analisi e dalle coordinate del nuovo punto, ricava il valore della soluzione
al tempo corrente e al tempo precedente mediante un’interpolazione lineare. Una volta individuati,
il metodo updateInterpolator scorre tutti i nodi della triangolazione e opera una ricerca all’interno del vettore dei valori precedentemente interpolati, assegnando ai nodi inseriti durante l’ultimo
raffinamento le corrispondenti grandezze interpolate.
Codice 29: Metodi di interpolazione della classe problem
1
2
3
4
5
6
7
8
9
10
// Update the solution with newly interpolated values
void
updateInterpolator ( std :: vector < std :: pair < Eigen :: Vector2d , Eigen :: Vector2d > >& vec );
// Resize data structure to fit new dimensions
void InterpolationR esize ();
// Interpolate the solution in a new point (x , y )
void
interpolate ( triangulation :: face_iterator_t & it , double const & x , double const & y ,
std :: vector < std :: pair < Eigen :: Vector2d , Eigen :: Vector2d > >& vec ,
bool new_point ) const ;
Infine, si riportano i prototipi dei metodi incaricati di aggiornare le informazioni del problema in
seguito al raffinamento iniziale della griglia e al raffinamento a ogni istante temporale. In entrambe
le situazioni si procede a restringere l’immagine di partenza sulla griglia di calcolo discreta e, nel
primo caso, si ricalcola la restrizione della curva iniziale sulla triangolazione mentre nel secondo caso
si aggiorna la soluzione al passo precedente con la soluzione attuale.
29
Implementazione del codice C++
Codice 30: Metodi di aggiornamento della classe problem
1
2
3
4
// Update data after initial refinement
void init_update ();
// Update data after loop refinement or solver
void loop_update ();
5.8
Stimatori dell’errore: la classe posterior
Come anticipato, la classe posterior si occupa del calcolo degli stimatori dell’errore e della procedura di marcamento dei triangoli da raffinare. In particolare, il costruttore riceve in ingresso una
referenza al problema e una referenza costante alla struct ref data.
La classe è poi corredata dai metodi initial_error e loop_error che ricevono come parametro
un iteratore alla faccia e restituiscono rispettivamente l’errore di interpolazione iniziale e l’errore
residuale a ogni istante temporale sull’elemento S in formato double in base alle formule (22) e (24).
Codice 31: Estratto dei metodi della classe posterior
1
2
3
4
5
6
7
8
9
10
// Constructor
inline posterior ( problem & p , const struct ref_data & data_r ):
p_ ( p ) , data_r_ ( data_r ) {};
// Compute the initial error on a given face
double initial_error ( triangulation :: face_iterator_t & _it );
// Compute the residual error on a given face
double loop_error ( triangulation :: face_iterator_t & _it );
// GERS marking algorithm
void mark ( Eigen :: VectorXd & loc_err , double const & glob_err ,
std :: vector < bool > & marked );
Infine, il metodo mark esegue la procedura per marcare gli elementi da raffinare mediante la strategia
Guaranteed Error Reduction Strategy. I parametri di input sono la referenza a un vettore contenente
gli errori locali sui singoli elementi, l’errore globale e un vettore di bool di dimensione pari al numero
delle facce della triangolazione per memorizzare se un trangolo deve essere raffinato oppure no.
L’idea alla base del metodo di marcamento consiste nell’individuare un sottoinsieme degli elementi
della mesh di partenza in cui si concentra una data frazione dell’errore globale e raffinare solo
tali triangoli. Lo pseudocodice presentato nel paragrafo 4.2 è stato facilmente trasposto in C++ nel
metodo posterior::mark.
5.9
Algoritmi di adattazione di griglia: la classe AdaptMesh
La classe AdaptMesh si occupa di eseguire le strategie di adattazione iniziale e adattazione a ogni
istante temporale della griglia di calcolo. In particolare, il costruttore riceve in ingresso una referenza alla triangolazione e alla classe posterior per il trattamento dei dati e salva una copia
della mesh, dello stimatore e del problema ntra i suoi membri privati. Inoltre inizializza i contenitori to_be_inserted_ e to_be_removed_ destinati a memorizzare le informazioni sugli elementi da
inserire e rimuovere dalla triangolazione.
Codice 32: Membri privati della classe AdaptMesh
1
2
3
4
5
6
7
8
// Triangulation
triangulation & cdt_ ;
// Posterior object ( used for estimate and marking strategy )
posterior & stimator_ ;
// Problem
problem & p_ ;
// Vector that contains the triangles to be inserted
std :: vector < std :: list < std :: pair < double , double > > > to_be_inserted_ ;
30
Implementazione del codice C++
9
10
// Vector that contains the edges to be removed
std :: vector < std :: pair < bool , std :: list < int > > > to_be_removed_ ;
La procedura di raffinamento della griglia di calcolo si basa sull’idea di trattare i lati da inserire per
infittire i triangoli come vincoli all’interno della triangolazione corrente implementata dalla classe
triangulation. Per tale motivo, si è scelto di utilizzare due vettori ausiliari per memorizzare i
cambiamenti da effettuare nella mesh. Nel dettaglio, to_be_inserted_ è un vettore della Standard
Library i cui elementi sono list<pair<double,double> >, ognuno dei quali memorizza le coordinate dei tre vertici di un triangolo; per quanto riguarda invece il vettore to_be_removed_, esso
contiene un pair<bool,list<int> > dove il valore booleano indica se il triangolo di indice pari alla
posizione occupata presenta dei lati che devono essere rimossi e la lista contiene gli indici dei lati
da eliminare nella numerazione locale.
Entrambi i metodi restituiscono un pair<int,double>: il primo elemento rappresenta il numero
di triangoli inseriti dalla procedura di adattazione corrente e il secondo è l’errore globale prima
dell’esecuzione del raffinamento della griglia.
Codice 33: Estratto dei metodi della classe AdaptMesh per il raffinamento di griglia
1
2
3
4
5
6
// Constructor
AdaptMesh ( triangulation & cdt , posterior & stim );
// Initial adaptivity routine
std :: pair < int , double > initial_adapt ();
// Adaptivity routine at every time step
std :: pair < int , double > loop_adapt ();
Si riporta lo schema della procedura di raffinamento implementata per l’adattazione iniziale e l’adattazione a ogni istante temporale. In dettaglio, i due algoritmi sono del tutto equivalenti ad
eccezione degli stimatori che assumono le forme (22) e (24). Inoltre, il passo {3f} viene eseguito solo
durante il ciclo temporale in quanto per il calcolo dello stimatore iniziale è sufficiente reinterpolare
l’immagine iniziale sulla griglia discreta in analisi.
Procedura di raffinamento della mesh
{1} C a l c o l o d e l l ’ e r r o r e l o c a l e ;
{2} C a l c o l o d e l l ’ e r r o r e g l o b a l e ;
{3} Se l ’ e r r o r e g l o b a l e s u p e r a l a t o l l e r a n z a
{ a } S t r a t e g i a d i marcamento GERS;
{b} C o s t r u z i o n e d i un o g g e t t o RefineMethod t r a m i t e una o b j e c t f a c t o r y ;
{ c } A p p l i c a z i o n e d e l l a s t r a t e g i a d i r a f f i n a m e n t o i n d i v i d u a n d o i l a t i da
i n s e r i r e e da r i m u o v e r e ;
{d} I n s e r i m e n t o e r i m o z i o n e d e i v i n c o l i n e l l a t r i a n g o l a z i o n e ;
{ e } Aggiornamento d e l l e i n f o r m a z i o n i d e l l a t r i a n g o l a z i o n e ;
{ f } I n t e r p o l a z i o n e d e l l e s o l u z i o n i n e i nuovi n o d i d e l l a g r i g l i a .
5.10
Tecniche di raffinamento: le classi RefineMethod e RefinementFactory
I metodi di infittimento dei triangoli descritti nella sezione precedente sono stati implementati come
specifiche di una classe astratta RefineMethod, da cui ereditano pubblicamente la loro interfaccia
La classe astratta RefineMethod
La classe astratta RefineMethod definisce la struttura base di un metodo di raffinamento di griglia,
specificato poi nelle classi RegularDivisionRefinement, LongestEdgeRefinement e NewestVertexRefinement. In particolare, la classe base memorizza le informazioni generiche associate al
31
Implementazione del codice C++
Fig. 5: Ereditarietà della classe RefineMethod.
problema e alla triangolazione e i dettagli relativi agli elementi marcati per il raffinamento TbR_ e
destinati a essere aggiunti (to_be_inserted_) e rimossi (te_be_removed_) dalla griglia.
I metodi della classe RefineMethod successivamente ereditati dalle tre specifiche implementano
la memorizzazione degli elementi da modificare e le strategie base di bisezione. In particolare, i
metodi mark_insertion e mark_removal aggiungono i lati da introdurre e cancellare dalla struttura della mesh nei contenitori to_be_inserted_ e te_be_removed_; i metodi one_bisection,
two_bisection e triple_bisection si occupano invece di bisecare una, due o tre volte una faccia.
I metodi apply e update sono invece dichiarati virtual e una loro specifica viene fornita in ognuna
delle classe derivate.
Codice 34: Prototipo della classe RefineMethod
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class RefineMethod
{
public :
typedef triangulation :: face_iterator face_iterator_t ;
// Constructor
inline RefineMethod ( problem const & p , triangulation & cdt , std :: vector < bool >& TbR ,
std :: vector < std :: list < std :: pair < double , double > > >& to_be_inserted ,
std :: vector < std :: pair < bool , std :: list < int > > >& to_be_removed ,
std :: vector < std :: pair < Eigen :: Vector2d , Eigen :: Vector2d > >& new_points ,
const bool init ):
p_ ( p ) , cdt_ ( cdt ) , TbR_ ( TbR ) , to_be_inserted_ ( to_be_inserted ) ,
to_be_removed_ ( to_be_removed ) , new_points_ ( new_points ) , init_ ( init ) {};
// Virtual method to apply the refinement
virtual void apply () = 0;
// Virtual method to update labelas and reference edges
virtual void update () = 0;
// Insert an edge in the vector of edges to be inserted during
// refinement procedure
void mark_insertion ( const std :: pair < double , double > & left ,
const std :: pair < double , double > & right );
// Mark edge to be removed
void mark_removal ( face_iterator_t & it , int const & i );
// Perform one bisection on the triangle
void one_bisection ( face_iterator_t & it , int const ref );
// Perform two bisections on the triangle
void two_bisection ( face_iterator_t & it , int const ref , int const idx );
// Perform three bisections on the triangle
void triple_bisection ( face_iterator_t & it , int const ref );
protected :
// Problem
problem const & p_ ;
// Triangulation
triangulation & cdt_ ;
// Vector that stores if a triangle has to be refined
std :: vector < bool > & TbR_ ;
// Vector of elements
std :: vector < std :: list < std :: pair < double , double > > > & to_be_inserted_ ;
// Vector of the edges to be removed
std :: vector < std :: pair < bool , std :: list < int > > > & to_be_removed_ ;
// Vector that interpolates the solution in the new points of the
32
Implementazione del codice C++
// triangulation
std :: vector < std :: pair < Eigen :: Vector2d , Eigen :: Vector2d > > & new_points_ ;
// Boolean variable to identify if the refinement is at the begininng or
// during evolution
const bool init_ ;
private :
41
42
43
44
45
46
47
};
Il raffinamento RegularDivisionRefinement
La procedura di infittimento dei triangoli basata sulla divisione regolare Red Green Blue è implementata nella classe RegularDivisionRefinement. In particolare, si è implementata una specifica per i
metodi apply e update che eseguono rispettivamente l’algoritmo di raffinamento e l’aggiornamento
delle informazioni sulla griglia di calcolo. L’implementazione dell’algoritmo a partire dalle direttive
descritte nel paragrafo 4.3.1 non presenta particolarità degne di nota e ci si limita a riportare il
prototipo dei diversi metodi per la suddivisione di un elemento in base alla notazione in letteratura:
Codice 35: Estratto dei metodi della classe RegularDivisionRefinement
1
2
3
4
5
6
7
8
9
10
// Performs red refinement over the triangle
void red_refinement ( face_iterator_t & it );
// Performs green bisection over the face
inline void green_refinement ( face_iterator_t & it ) {
this - > one_bisection ( it , it - > reference_edge ());
}
// Performs blue bisection over the face
inline void blue_refinement ( face_iterator_t & it , int const idx ) {
this - > two_bisection ( it , it - > reference_edge () , idx );
}
La bisezione LongestEdgeRefinement e NewestVertexRefinement
I due algoritmi di raffinamento basati sulla bisezione sono tra di loro del tutto analoghi in quanto si
fondano sull’idea di bisecare una, due o tre volte il triangolo a seconda di quale scenario tra quelli
descritti nella sezione precedente si viene a determinare. Poiché i tre metodi per bisecare gli elementi
sono nativi della classe RefineMethod, essi sono ereditati dalle classi LongestEdgeRefinement e
NewestVertexRefinement che presentano pertanto esclusivamente l’implementazione dei metodi
apply e update.
Il codice è ricavabile dalle procedure descritte nei paragrafi 4.3.2 e 4.3.3 e varia sostanzialmente solo
nella scelta del lato di riferimento del triangolo per la bisezione e, di conseguenza, nella procedura
di aggiornamento dell’informazione associata alla griglia.
La object factory RefinementFactory
In modo analogo a quanto descritto per istanziare il termine forzante, si è implementata una object
factory per la costruzione runtime del metodo di raffinamento di griglia richiesto dall’utente. Si
riporta di seguito l’implementazione del metodo create che restituisce un oggetto RefineMethod
che varia in base al parametro passato alla factory. Questo approccio permette di generalizzare
la costruzione degli oggetti all’interno del codice senza rendere necessaria una ricompilazione dei
sorgenti a seconda del caso trattato.
Codice 36: Implementazione del metodo RefinementFactory::create
1
2
RefineMethod * RefinementFactory :: create () {
// Based on the parameter type_ calls a different constructor
33
Implementazione del codice C++
if ( type_ == 0) {
return new Reg u lar Di v is io n Ref i ne m e nt ( p_ , cdt_ , TbR_ , to_be_inserted_ ,
to_be_removed_ , new_points_ , init_ );
}
else if ( type_ == 1) {
return new LongestEdgeRefinem ent ( p_ , cdt_ , TbR_ , to_be_inserted_ ,
to_be_removed_ , new_points_ , init_ );
}
else if ( type_ == 2) {
return new Ne we stVerte xRe fi ne ment ( p_ , cdt_ , TbR_ , to_be_inserted_ ,
to_be_removed_ , new_points_ , init_ );
}
return 0;
3
4
5
6
7
8
9
10
11
12
13
14
15
16
}
5.11
Visualizzazione dei dati: la classe svg
Per l’esportazione dei dati, ci si è basati sulla classe svg originariamente implementata nelle GasFramework adattandone le funzionalità al problema in analisi. La struttura della classe è rimasta
sostanzialmente invariata: il costruttore riceve in ingresso una referenza al problema trattato e
inizializza le dimensioni dell’immagine di output.
Codice 37: Estratto dei metodi della classe svg per l’esportazione della soluzione
1
2
3
4
5
6
// Constructor
svg ( problem & p ): p_ ( p ) , x_ (1024 u ) , y_ (768 u ) {};
// Convert numerical solution to color and then to string
std :: string to_string ();
// Operator << overloading
friend std :: ostream & operator < < ( std :: ostream & out , svg & text );
Le principali novità sono riscontrabili nel metodo to_string che, a seconda della fase del problema in cui viene richiamata la procedura di esportazione dei risultati, costruisce un’immagine con
caratteristiche differenti. In particolare, trattandosi di un problema di tipo evolutivo, si procede
all’individuazione della grandezza da esportare in base ad apposite flag che permettono di scegliere
l’interpolazione dell’immagine iniziale per il primo grafico, l’interpolazione della curva iniziale per
il secondo caso e la soluzione del problema evolutivo per le restanti situazioni.
Codice 38: Identificazione dell’informazione da esportare a seconda della fase del problema in esecuzione
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// If problem hasn ’t been solved yet , we plot the initial image or the initial
// level - set curve
if (! p_ . already_solved ())
{
// The first time we plot the initial image restricted to the given triangulation
if (! p_ . not_original ())
{
r (0)= p_ . init_img_vec (i - > i (0));
r (1)= p_ . init_img_vec (i - > i (1));
r (2)= p_ . init_img_vec (i - > i (2));
}
else // We plot the initial level - set curve on the given mesh
{
r (0)= p_ . old_solution (i - > i (0));
r (1)= p_ . old_solution (i - > i (1));
r (2)= p_ . old_solution (i - > i (2));
}
}
else // we plot the solution at current time
34
Simulazioni numeriche
20
{
r (0)= p_ (i - > i (0));
r (1)= p_ (i - > i (1));
r (2)= p_ (i - > i (2));
21
22
23
24
}
Si illustrano infine gli ambienti creati all’interno dell’oggetto svg per l’esportazione delle informazioni associate a diversi aspetti del problema. L’ambiente solution raccoglie i valori nodali della
soluzione e calcola il valore medio della stessa sulle facce convertendolo in scala di grigi; l’ambiente
grid memorizza e stampa le informazioni legate ai lati della griglia di calcolo costruita e viene richiamato in seguito a ogni iterazione dell’algoritmo di raffinamento; infine, l’ambiente edges individua
i lati della triangolazione che approssimano la frontiera che separa le due regioni in analisi.
6
Simulazioni numeriche
In questa sezione si presentano i risultati numerici forniti dalle procedure precedentemente presentate
e implementate nel corso di questo lavoro. I vari algoritmi sono stati testati per immagini sintetiche
e immagini reali.
6.1
Parametri del problema
In tabella 1 si riportano i valori classici dei parametri, come da letteratura. In generale nelle
simulazioni numeriche si sono considerati tali valori, a meno di eccezioni che verranno segnalate.
Passo temporale τ
Parametro regolarizzazione ²
Parametro regolarizzazione ρ
Coefficiente ν
Coefficiente λ
Parametro GERS C0
Parametro GERS C1
Parametro GERS θ∗
Parametro GERS γ
Tolleranza raffinamento iniziale tolini
Tolleranza raffinamento iterativo tol²
Criterio di arresto tolr
0.1
10−6
1.0
0.001625625
255.0
0.01
0.495
0.5
0.01
0.05
0.01
0.0005
Tabella 1: Parametri del problema ricavati dalla letteratura analizzata, principalmente [Fri03].
6.2
Segmentazione di immagini con regioni uniformi
Nel primo caso test si verifica la capacità del classico algoritmo region-based di Chan-Vese di identificare due differenti aree all’interno di un’immagine con forte contrasto. A tal fine si è scelto di
testare il codice numerico su un’opera di Keith Haring, Radiant baby: lo stile dell’artista caratterizzato da ampie campiture uniformi di colori permette infatti di eseguire un test con un’immagine
reale e non sintetica che al tempo stesso non presenti criticità legate all’uniformità dei dati iniziali
come appunto richiesto dal modello di Chan-Vese.
Nello specifico, l’immagine considerata presenta una regione sulla tonalità del grigio che disegna
il contorno stilizzato di una figura di bambino a gattoni circondato da dei raggi di luce immerso in
uno sfondo nero uniforme.
35
Simulazioni numeriche
Si denominano le due regioni interna (grigia) ed esterna (nera) e si osserva che non sono connesse.
In letteratura, sono presenti numerosi riferimenti ai metodi edge-based che in circostanze analoghe
non riescono a individuare una parte dei contorni, in modo particolare quello all’interno della figura.
In figura 6 si presentano quindi differenti inizializzazioni della curva di livello - connesse e non
connesse - e le rispettive soluzioni iniziali e stazionarie. Un’effettiva verifica della stazionarietà della
soluzione ottenuta verrà proposta più avanti in questo paragrafo.
Nei quattro casi proposti, anche quando l’inizializzazione di Φ0 (x) è molto lontana dalla soluzione
del problema (Fig. 6j), si giunge in un numero limitato di iterazioni a una curva approssimante
veritiera.
m−1
In figura 7, si presenta infine l’evoluzione dell’errore rm = ksm
(x)k2L2 (Ω) dove sm
h (x) − sh
h (x)
2
assume la forma 20; questo errore rappresenta la variazione in norma L (Ω) tra la segmentazione al
passo tm e quella al passo precedente e può essere assunto come indicatore della stazionarietà del
metodo, una volta raggiunta una determinata tolleranza tolr .
Le quattro inizializzazioni considerate portano in un numero di iterazioni variabile da sei a dieci
a rispettare la tolleranza tolr = 5 · 10−4 . In particolare la curva in figura 6a genera una stima
iniziale imprecisa ma in poche iterazioni l’errore scende sotto la tolleranza fissata; parallelamente,
le inizializzazioni nelle figure 6g e 6j mappano l’immagine in base a informazioni locali associando ai
nodi della griglia valori iniziali con una minore varianza sul dominio e pertanto l’errore iniziale risulta
più contenuto; si osserva infine come una curva iniziale molto lontana dalla verità (Fig. 6d) possa in
alcune circostanze essere responsabile di forti variazioni iniziali nella segmentazione dell’immagine,
giungendo comunque a una situazione stazionaria sebbene con un numero di iterazioni maggiore.
Si osserva altresı̀ che a parità di tolleranza imposta, i bordi finali ottenuti dalla segmentazione
risultano meno regolari quando l’ipotesi sul dato iniziale è molto lontana dall’effettiva soluzione
finale (Fig. 6l).
In questo caso test, l’algoritmo di Chan-Vese si dimostra dunque estremamente efficace, come
ci si aspettava da quanto visto durante la sua trattazione teorica: tutti i bordi dell’immagine vengono riconosciuti soddisfacentemente e non si riscontrano sensibilità ai dati iniziali particolarmente
importanti.
6.3
Adattività di griglia
Uno dei limiti del programma a elementi finiti sviluppato a partire dal modello di Chan-Vese è
rappresentato dalla necessità di una griglia di calcolo estremamente raffinata per avere sufficienti
dettagli sull’immagine in analisi.
Per diminuire il costo computazionale si sono quindi implementati due algoritmi di adattazione di
griglia, basati sulla strategia Guaranteed Error Reduction Strategy descritta nella sezione precedente
con differenti tecniche di infittimento dei triangoli.
Innanzitutto, si confrontano i metodi di raffinamento Red Green Blue e di bisezione Longest
Edge e Newest Vertex. Dalla figura 8 si evince come tutti i metodi forniscano griglie adattate in
funzione dell’immagine da raffinare e la tabella 2 mostra come il numero di elementi totali generati
dalle tre procedure sia paragonabile.
In particolare, gli approcci di bisezione si dimostrano sostanzialmente equivalenti, sia per numero
di triangoli inseriti, sia per caratteristiche della griglia di calcolo che si ottiene. Osservando la griglia
ottenuta dal raffinamento Red Green Blue si possono invece notare delle discrepanze nella forma dei
nuovi elementi inseriti.
Sebbene sia la procedura di raffinamento regolare sia quelle di bisezione forniscano delle griglie di
calcolo competitive per quanto riguarda il numero degli elementi e quindi il risparmio di potenza
computazonale necessaria per processare le simulazioni, si ritiene preferibile l’approccio basato sulla
bisezione. In generale, in letteratura sono presenti svariate prove della regolarità che può essere
ottenuta mediante i metodi di bisezione per quanto riguarda l’ampiezza minima degli angoli dei
36
Simulazioni numeriche
(a) Curva di livello iniziale.
(b) Soluzione dopo un’iterazione.
(c) Soluzione stazionaria.
(d) Curva di livello iniziale.
(e) Soluzione dopo un’iterazione.
(f ) Soluzione stazionaria.
(g) Curva di livello iniziale.
(h) Soluzione dopo un’iterazione.
(i) Soluzione stazionaria.
(j) Curva di livello iniziale.
(k) Soluzione dopo un’iterazione.
(l) Soluzione stazionaria.
Fig. 6: Segmentazione dell’opera di Keith Haring Radiant Baby con il modello di Chan-Vese. Nella prima colonna si riporta la curva presa come dato iniziale Φ0 , nella seconda la soluzione dopo un’iterazione
dell’algoritmo e nella terza la corrispondente segmentazione finale.
37
Simulazioni numeriche
Evolutionary error in time
0.05
Error
Error
Error
Error
initialization
initialization
initialization
initialization
1
2
3
4
Evolutionary error sh
0.04
0.03
0.02
0.01
0
1
2
3
4
5
6
7
Time iteration [tau=0.1]
8
9
10
Fig. 7: Evoluzione dell’errore tra un istante temporale e il successivo per l’esecuzione del metodo di Chan-Vese
con le inizializzazioni proposte in figura 6.
Metodo utilizzato
Triangolazione uniforme
Raffinamento Red Green Blue I
Bisezione Longest Edge I
Bisezione Newest Vertex I
Bisezione Longest Edge II
Bisezione Longest Edge I+II
Numero di elementi
50029
10343
8188
8188
5202
12784
Tabella 2: Numero di elementi della triangolazione nei diversi casi esaminati. Con I si indica la procedura
di raffinamento iniziale e con II la procedura di raffinamento a ogni iterazione.
triangoli raffinati e le classi di equivalenza cui i nuovi elementi risultano appartenenti (si vedano i
risultati nei paragrafi 4.3.2 e 4.3.3). Inoltre, le tecniche di bisezione prevedono - a seconda di alcune
proprietà del triangolo in analisi - di procedere a una, due o tre bisezioni consecutive garantendo
quindi un’uniformità di trattamento per ogni elemento e sottoelemento della triangolazione; la
procedura Red Green Blue tende invece a inserire all’interno della mesh triangoli di diversa natura
a seconda che siano marcati per il raffinamento o infittiti per garantire la conformità della griglia.
Infine il numero di triangoli generati da questo raffinamento risulta estremamente inferiore rispetto
a quello della mesh uniforme testata e pertanto lo scopo dell’adattazione di griglia guidato dagli
stimatori descritti nella sezione precedente appare verificato.
Si considerano ora le diverse procedure di adattazione a uno e due passi nel caso l’infittimento
dei triangoli sia eseguito mediante la bisezione Longest Edge e le si confrontano con il caso di una
griglia uniforme che per una maggiore leggibilità è qui riportata con un numero di elementi inferiore
a quello reale (Fig. 9a).
In figura 9d si riporta la soluzione calcolata in corrispondenza della griglia ottenuta mediante adattazione iniziale. L’identificazione dei bordi dell’immagine risulta affidabile e il numero di
elementi della mesh estremamente inferiore a quello di una mesh uniforme (Tab. 2). Tuttavia, il raffinamento iniziale si basa sull’assunzione che l’immagine analizzata non presenti particolari disturbi
e quindi potrebbe essere a priori limitante.
Si introduce pertanto l’adattazione di griglia a ogni passo temporale in seguito alla risoluzione
38
Simulazioni numeriche
(a) Tre iterazioni Red Green Blue.
(b) Tre iterazioni Longest Edge.
(c) Tre iterazioni Newest Vertex.
(d) Nove iterazioni Red Green Blue.
(e) Nove iterazioni Longest Edge.
(f ) Nove iterazioni Newest Vertex.
Fig. 8: Confronto tra le griglie adattative dopo tre e dopo nove iterazioni delle procedure di adattazione
iniziale presentate.
39
Simulazioni numeriche
del problema. Le figure 9e e 9f mostrano rispettivamente la triangolazione e la soluzione ottenuta
fissando una soglia di tolleranza a priori. In questo caso la soluzione risulta meno precisa rispetto
al caso precedente e, al tempo stesso, la crescita del costo computazionale non sembra trascurabile.
Inoltre, l’adattazione a ogni istante temporale non garantisce un’effettiva riduzione dell’errore a ogni
infittimento (Fig. 11b) se la griglia di partenza non è sufficientemente precisa (si veda [D9̈6a]).
Si propone quindi un approccio a due passi in cui, a partire da una griglia lasca iniziale, si procede
a un raffinamento GERS basato su (23) e successivamente a ogni iterazione del solutore si esegue
un raffinamento guidato dall’informazione fornita in (25) come riportato in figura 10. I risultati
in questo caso sono decisamente più interessanti (Fig. 10c) e l’oscillazione dell’errore dovuto alla
sensibilità alla precisione della griglia risulta sostanzialmente annullata, come osservabile in figura
11c.
In [Fri03], tuttavia, si sottolinea come per la maggior parte delle immagini da processare l’adattazione a ogni istante temporale non sia indispensabile in quanto la riduzione dell’errore che apporta
il miglioramento della griglia a ogni iterazione del solutore non sembra ripagare il costo computazionale richiesto dal calcolo dello stimatore, dall’infittimento dei triangoli e dall’interpolazione della
soluzione sulla nuova mesh. L’osservazione dell’evoluzione degli stimatori dell’errore in figura 11
conferma questa idea per cui si ritiene in generale sufficiente limitare la procedura di adattazione
di griglia a un raffinamento iniziale fino a rispettare una data tolleranza fissata a priori.
6.4
Segmentazione di immagini con rumore
In questa sezione si presenta il risultato della segmentazione di un’immagine sintetica ottenuta introducendo un rumore gaussiano con deviazione standard differente in due regioni, una interna e
una esterna.
Si è partiti da una figura con una campitura uniforme di grigio e in un’area quadrata centrale si è inserito un rumore gaussiano con deviazione standard estremamente elevata. L’immagine cosı̀ costruita
presenta due regioni con stessa media ma varianza diversa rendendo pertanto la segmentazione con
funzioni costanti a tratti dell’algoritmo di Chan-Vese inefficace.
Il metodo basato sulle statistiche regionali prevede la costruzione di una funzione densità di
probabilità associata alle singole regioni in cui si sta suddividendo l’immagine. In tale maniera, per
ogni intensità si ottiene una differente probabilità di essere assegnati alla regione Ω1 o Ω2 , come ben
osservabile in figura 12.
Le densità di probabilità pi sono costruite a partire dalla stima dei parametri µi e σi descritti
in (7) e si osserva come effettivamente le due curve abbiano stessa media e varianza differente,
situazione consistente con la costruzione del problema. In particolare, la regione esterna presenta
una varianza estremamente piccola, dovuta in parte al classico errore presente nei segnali e in parte
all’approssimazione che si sta facendo del problema; alla regione interna sono invece associati valori
dell’intensità molto più variabili, come per l’appunto ci si aspetta vista la procedura seguita per
sintetizzare questa immagine.
In figura 13 si riporta infine la soluzione dell’algoritmo di segmentazione dopo 4 iterazioni e in
figura 13c si ha la sovrapposizione dell’immagine iniziale con il contorno cosı̀ ottenuto; il risultato
appare molto accurato, anche grazie alla finezza della griglia implementata per la simulazione.
In generale, l’approccio basato sulle statistiche gaussiane risulta sufficientemente generale per la
trattazione di immagini con regioni di intensità sostanzialmente uniforme in cui è stato introdotto
o è presente a priori un errore casuale. Ciò è possibile in quanto il rumore bianco che descrive il
generico disturbo su un segnale è modellizzabile come un processo stocastico gaussiano di media
nulla in cui le realizzazioni risultano indipendenti e identicamente distribuite, cioè con matrice di
covarianza diagonale.
Per tale motivo, considerare le densità di probabilità sulle regioni Ω1 e Ω2 distribuite come delle normali permette l’identificazione delle aree in cui l’immagine è suddivisa anche in presenza di disturbi
40
Simulazioni numeriche
(a) Griglia di calcolo non raffinata.
(b) Soluzione stazionaria.
(c) Griglia di calcolo raffinata inizialmente.
(d) Soluzione stazionaria.
(e) Griglia di calcolo raffinata a ogni passo.
(f ) Soluzione stazionaria.
Fig. 9: Griglie uniformi e griglie adattate per la segmentazione di immagini e corrispondente soluzioni
stazionarie.
41
Simulazioni numeriche
(a) Dopo il raffinamento inziale.
(b) Dopo il raffinamento iterativo.
(c) Soluzione stazionaria.
Fig. 10: Griglie adattate in seguito alla procedura completa di raffinamento a due passi per la segmentazione
di immagini e corrispondente soluzione stazionaria.
Global error estimate for initial adaptivity
Global error estimate for loop adaptivity
0.3
Global error estimate for two-steps adaptivity
0.04
0.3
Initial error estimate
Loop error estimate
0.035
0.25
0.25
0.15
0.2
0.025
Error estimate
Loop error estimate
Initial error estimate
0.03
0.2
0.02
0.15
0.015
0.1
0.1
0.01
0.05
0.05
0.005
0
0
2
4
6
8
10
12
Adaptivity iteration
14
16
(a) Stima dell’errore iniziale.
18
0
5
10
Adaptivity iteration
15
20
(b) Stima dell’errore a ogni passo.
5
10
15
Adaptivity iteration
20
25
(c) Stima di entrambi gli errori.
Fig. 11: Evoluzione delle stime dell’errore durante il processo di adattazione di griglia. Adattazione iniziale
(Fig. 11a), adattazione a ogni istante del processo evolutivo (Fig. 11b) e combinazione dei precedenti
procedimenti (Fig. 11c).
42
Simulazioni numeriche
Region probability
0.06
Region 1
Region 2
0.05
Region probability
0.04
0.03
0.02
0.01
0
0
50
100
150
Gray intensity
200
250
Fig. 12: Ricostruzione mediante la stima di Parzen della densità di probabilità nelle due regioni Ω1 e Ω2
dell’immagine con rumore gaussiano 13a.
(a) Immagine iniziale.
(b) Soluzione dopo 4 iterazioni.
(c) Ricostruzione del bordo.
Fig. 13: Segmentazione di un’immagine con rumore gaussiano di varianza diversa in due regioni.
sui dati iniziali. Al tempo stesso, va sottolineato che in caso l’errore non sia globale e gaussiano (i.e.
un errore localizzato in una sola area dell’immagine o un’interferenza in un determinato intervallo
di intensità) tale modello risulta inadeguato a segmentare le immagini. Parallelamente, il modello basato sulle statistiche gaussiane fallisce nell’identificare le diverse regioni nel caso in cui siano
presenti dei pattern spazialmente non uniformi per cui la densità di probabilità associata a Ω1 e
Ω2 non ha più la forma classica ma deve essere appositamente stimata, come vedremo nella sezione
seguente.
6.5
Segmentazione di immagini con pattern spazialmente non uniformi
Si presentano ora alcune applicazioni del modello di segmentazione basato sulla ricostruzione della
densità di probabilità tramite la stima di Parzen. Come il modello gaussiano della sezione precedente,
questo approccio prevede di assegnare un pixel a una regione piuttosto che a un’altra in base alla
distribuzione di probabilità associata alle intensità di grigio nell’immagine iniziale.
Il vantaggio di considerare nel funzionale (15) l’espressione (9) per la probabilità pi nella regione
43
Simulazioni numeriche
Ωi rispetto a (8) risiede nella maggiore generalità della prima formulazione che potrà pertanto
declinarsi anche nella forma di una distribuzione gaussiana.
Si sono pertanto selezionate delle immagini con pattern non uniformi nello spazio e con un’alternanza
di intensità di grigio estremamente differenti.
In natura questo tipo di texture sono osservabili nei manti di alcuni animali che sono quindi
protagonisti dei test di segmentazione nelle figure 14, 15, 17 e 18. Per tutte le prove si riportano
l’immagine originale, la soluzione del problema di segmentazione mediante level-set e la giustapposizione dell’immagine di partenza con il bordo individuato dall’algoritmo; infine si presentano i
risultati della stima della densità di probabilità a partire dall’istogramma discreto regolarizzato con
un kernel gaussiano e si commenteranno brevemente i risultati.
Tutte le simulazioni sono eseguite su griglie di calcolo molto raffinate già in partenza in quanto
l’obiettivo primario è testare l’affidabilità dell’algoritmo descritto anche in casi di regioni con una
variabilità spaziale molto elevata che si sarebbe persa con l’utilizzo di mesh lasche. Per tale motivo,
l’efficienza computazionale è stata parzialmente sacrificata a favore di una maggiore precisione del
dato iniziale da segmentare; l’utilizzo di procedure di adattazione di griglia risulta in questa circostanza più delicato vista la difficoltà di identificare una mesh iniziale sufficientemente raffinata a
livello locale per cogliere il passaggio da una regione a un’altra e al tempo stesso non eccessivamente
fina per impedire la perdita di globalità dell’informazione.
(a) Immagine iniziale.
(b) Soluzione dopo 6 iterazioni.
(c) Ricostruzione del bordo.
Fig. 14: Segmentazione dell’immagine di una coppia di giraffe con sfondo un cielo limpido.
(a) Immagine iniziale.
(b) Soluzione dopo 3 iterazioni.
(c) Ricostruzione del bordo.
Fig. 15: Segmentazione dell’immagine di una coppia di tigri albine su sfondo scuro.
I risultati delle figure 14 e 15 sono abbastanza accurati in quanto presentano due regioni netta44
Simulazioni numeriche
mente separate da un contrasto elevato. In particolare, la segmentazione delle due giraffe non risulta
del tutto esatta in quanto la parte inferiore dello sfondo presenta pixel con un’intensità assimilabile
a quella degli animali piuttosto che a quella del cielo. In questa circostanza, quindi, l’algoritmo si
è dimostrato in grado di identificare le due regioni con intensità differenti ma la natura dell’immagine risulterebbe più adatta a un approccio di segmentazione multiregione. Un risultato migliore si
osserva con la segmentazione delle tigri anche se ciò è per l’appunto imputabile al fatto che i due
animali sono albini e si stagliano pertanto in maniera netta sullo sfondo di colore scuro.
Queste considerazioni sono confermate dall’osservazione delle stime delle distribuzioni di probabilità in figura 16. In entrambi i casi, le densità presentano dei picchi in intervalli diversi della scala
dei grigi e una delle distribuzioni manifesta una varianza molto maggiore rispetto all’altra: ciò è
consistente con l’osservazione dei dati in quanto la regione Ω1 descrive l’area interna al contorno e
nelle immagini in analisi essa comprende il manto degli animali e quindi dei pattern non uniformi
di grigi.
Region probability
Region probability
0.04
0.02
Region 1
Region 2
Region 1
Region 2
0.035
0.015
Region probability
Region probability
0.03
0.025
0.02
0.015
0.01
0.01
0.005
0.005
0
0
0
50
100
150
Gray intensity
200
250
0
(a) Stima di Parzen per una coppia di giraffe.
50
100
150
Gray intensity
200
250
(b) Stima di Parzen per una coppia di tigri albine.
Fig. 16: Ricostruzione delle distribuzioni di probabilità mediante la stima di Parzen per la segmentazione
dell’immagine 14a (coppia di giraffe) e dell’immagine 15a (coppia di tigri albine). In blu la densità p1 associata
alla regione interna e in rosso la probabilità p2 associata alla zona esterna.
Infine, si è applicato il metodo basato sulla ricostruzione delle statistiche regionali mediante
la stima di Parzen a due casi in cui la definizione dei contorni risulta meno chiara. In dettaglio,
in figura 17 si ha un’immagine di un leopardo in cui il contrasto è estremamente basso e non
c’è una netta separazione tra il soggetto e lo sfondo. La segmentazione è comunque accettabile
e coglie i tratti fondamentali dell’immagine ma si evincono una serie di problematiche: lo scarso
contrasto tra il soggetto e lo sfondo, l’alternanza di luce e ombra, la presenza di un fondale non
uniforme rappresentato dalle sterpaglie della savana rendono la segmentazione basata solo su due
regioni difficoltosa e talvolta deficitaria come si osserva nei pressi del muso e delle zampe posteriori
dell’animale.
Per quanto riguarda la coppia di zebre in figura 18, l’immagine di partenza è di qualità molto
bassa e pertanto non ci si attende un risultato particolarmente preciso. La scarsa qualità del dato
iniziale e lo sfondo disturbato aumentano l’imprecisione della segmentazione che rimane comunque
abbastanza accettabile in quanto in grado di isolare i due animali da buona parte dello sfondo, fatta
eccezione per una regione grigia la cui media e la cui varianza risultano assimilabili al congiunto
delle zebre.
Le osservazioni precedenti riguardo ai problemi derivanti dalla segmentazione di queste immagini
(Figg. 17 e 18) sono confermate dallo studio dei grafici delle distribuzioni di probabilità ricostruite
45
Conclusioni
(a) Immagine iniziale.
(b) Soluzione dopo 7 iterazioni.
(c) Ricostruzione del bordo.
Fig. 17: Segmentazione dell’immagine di un leopardo nella savana con scarso contrasto.
(a) Immagine iniziale.
(b) Soluzione dopo 4 iterazioni.
(c) Ricostruzione del bordo.
Fig. 18: Segmentazione dell’immagine di una coppia di zebre in movimento con definizione molto bassa.
mediante la stima di Parzen. A differenza dei precedenti casi, non si possono infatti identificare
due intervalli della scala di grigio separati cui facciano riferimento i valori massimi delle densità e
al tempo stesso l’andamento della distribuzione presenta differenti picchi e pozzi.
In base a queste osservazioni si può supporre che un approccio mediante statistiche regionali locali
ricostruite tramite la stima di Parzen e quindi l’utilizzo di distribuzioni locali della probabilità (si
veda [Bro05]) potrebbero aumentare la precisione del metodo, che si valuta comunque positivamente.
7
Conclusioni
L’approccio a elementi finiti per problemi di trattamento delle immagini si è dimostrato di grande interesse in quanto permette di realizzare algoritmi numerici computazionalmente efficienti e
di implementare tecniche di adattazione di griglia in grado di migliorare ulteriormente i risultati
ottenuti.
In questo progetto si sono implementati tre differenti modelli region-based per la segmentazione
di immagini e si è verificata la loro validità in svariati casi. I modelli più semplici, benché adeguati
per il trattamento di immagini elementari, si sono dimostrati deficitari in presenza di rumore o
di particolari pattern spazialmente non omogenei. Tuttavia il modello basato sulla stima nonparametrica della densità di probabilità si è dimostrato un’ottima scelta ai fini dell’analisi anche di
immagini complesse.
Si è inoltre osservato che l’adattazione di griglia rappresenta una tematica estremamente utile
46
Conclusioni
Region probability
Region probability
0.014
0.02
Region 1
Region 2
Region 1
Region 2
0.012
0.015
Region probability
Region probability
0.01
0.008
0.006
0.01
0.004
0.005
0.002
0
0
0
50
100
150
Gray intensity
200
250
0
(a) Stima di Parzen per il leopardo nella savana.
50
100
150
Gray intensity
200
250
(b) Stima di Parzen per due zebre nell’erba.
Fig. 19: Ricostruzione delle distribuzioni di probabilità mediante la stima di Parzen per la segmentazione
dell’immagine con scarso contrasto 17a (leopardo nella savana) e dell’immagine di bassa qualità 18a (due
zebre nell’erba). In blu la densità p1 associata alla regione interna e in rosso la probabilità p2 associata alla
zona esterna.
per l’ottimizzazione degli algoritmi di segmentazione delle immagini in quanto il grado di definizione
della mesh necessario per una buona approssimazione della soluzione si è dimostrato estremamente
elevato rendendo il costo computazionale del codice enorme. Nella prospettiva di trattare immagini
multicanale e tridimensionali tale grado di definizione non risulta sostenibile e pertanto la strategia
di raffinamenti successivi di una griglia iniziale lasca si rende indispensabile.
Le tecniche analizzate per l’adattazione di griglia hanno permesso un considerevole abbattimento
delle risorse richieste garantendo al tempo stesso un’ottima precisione dell’approssimazione.
Parallelamente, restano aperte le questioni legate all’applicabilità di queste tecniche in presenza di
pattern spazialmente non uniformi. Un eccessivo livello di infittimento della griglia computazionale
può infatti generare problemi di overfitting dei dati e una conseguente impossibilità di segmentare
in maniera efficace le aree che racchiudono i pattern.
In generale, il codice implementato è facilmente generalizzabile a problemi di trattamento delle immagini più complessi e, da un punto di vista algoritmico, è configurato in maniera tale da
rendere possibile l’estrazione degli script legati all’adattazione di griglia. In particolare, i codici
per il marcamento degli elementi da raffinare e per l’infittimento della griglia potrebbero essere
estratti dall’implementazione che si è realizzata all’interno delle GasFramework per essere inclusi in
un’apposita libreria stand-alone per l’adattazione di griglia implementata in C++.
7.1
Possibili sviluppi
Un’immediata estensione del lavoro svolto consisterebbe nella generalizzazione degli algoritmi proposti al caso multidimensionale. Questo sviluppo renderebbe possibile la segmentazione di immagini
multicanale permettendo quindi l’individuazione non solo di regioni con differenti intensità di grigio
ma anche con varie tonalità, classicamente rosso verde e blu per le immagini RGB ([Fri09]).
Per quanto riguarda i modelli di segmentazione, invece, in [Bro05] Brox ha presentato una serie di
possibili miglioramenti dei modelli basati sulle statistiche regionali, introducendo dei parametri che
considerassero il valore locale delle distribuzioni di probabilità utilizzate.
Se si considera infine l’ottimizzazione del codice, i possibili sviluppi futuri di questo lavoro sono
estremamente ampi, soprattutto nell’ambito del calcolo ad alte prestazioni. In particolar modo,
le moderne tecniche di parallelizzazione potrebbero migliorare notevolmente le prestazioni degli
47
Riferimenti bibliografici
algoritmi descritti.
Il trattamento delle immagini risulta infatti un’applicazione principe per la parallelizzazione tramite
scheda grafica. Ripensare pertanto il codice per una parallelizzazione tramite CUDA permetterebbe
di sfruttare le enormi potenzialità di calcolo delle moderne schede NVIDIA; si rimanda a [MSH+ 13]
per il paradigma di programmazione da seguire per assemblare un codice a elementi finiti efficiente
su scheda grafica.
Da un altro lato, invece, si potrebbe pensare di sfruttare la potenza di calcolo di una rete di
calcolatori parallelizzando tramite Message Passing Interface il calcolo degli stimatori dell’errore
di approssimazione come proposto in [FSRS09]. Tuttavia le procedure di marcamento studiate
presentano un collo di bottiglia intrinseco alla possibilità di parallelizzare il codice a causa dello
step di individuazione degli elementi da raffinare per mantenere la compatiblità all’interno della
griglia.
Di conseguenza non sono chiari gli effettivi vantaggi di uno sviluppo in tale direzione che potrebbero
essere ovviati con l’utilizzo di differenti tecniche di adattazione di griglia. Tra queste si segnalano
gli approcci basati sulla ricostruzione del gradiente e sugli stimatori anisotropi [Pap11]. Si è invece
deciso di tralasciare e le tecniche di deraffinamento poiché, sebbene di grande interesse a livello
algoritmico, risultano nella realtà di scarsa utilità pratica in quanto i vantaggi che apportano non
sono sufficienti a bilanciare la notevole complessità implementativa e l’elevato costo computazionale
che richiede la memorizzazione e la gestione delle griglie gerarchicamente annidate dai processi di
raffinamento e deraffinamento.
Riferimenti bibliografici
[AK06]
G. Aubert and P. Kornprobst. Mathematical Problems in Image Processing: Partial
Differential Equations and the Calculus of Variations (Applied Mathematical Sciences).
Springer-Verlag New York, Inc., Secaucus, NJ, USA, 2006.
[Ale01]
A. Alexandrescu. Modern C++ design: generic programming and design patterns applied.
Addison-Wesley Longman Publishing Co., Inc., Boston, MA, USA, 2001.
[BC00]
B. Bourdin and A. Chambolle.
Implementation of an adaptive finite-element
approximation of the Mumford-Shah functional. Numer. Math., 85(4):609–646, 2000.
[BM96]
E. Bänsch and K. Mikula. A Coarsening Finite Element Strategy in Image Selective
Smoothing. Preprint. Albert-Ludwigs-Univ., Math. Fak., 1996.
[Bro05]
T. Brox. From pixels to regions: partial differential equations in image analysis. PhD thesis, Faculty of Mathematics and Computer Science. Saarland University, Saarbruecken,
April 2005.
[CGA]
CGAL - Computational Geometry Algorithms Library. http://www.cgal.org.
[CIM]
The
CImg
Library
C++
http://cimg.sourceforge.net/.
[CRD07]
D. Cremers, M. Rousson, and R. Deriche. A review of statistical approaches to level
set segmentation: Integrating color, texture, motion and shape. Int. J. Comput. Vision,
72(2):195–215, April 2007.
[CV01]
T.F. Chan and L.A. Vese. Active contours without edges. Image Processing, IEEE
Transactions on, 10(2):266–277, February 2001.
48
Template
Image
Processing
Toolkit.
Riferimenti bibliografici
[D9̈6a]
W. Dörfler. A convergent adaptive algorithm for Poisson’s equation. SIAM J. Numer.
Anal., 33(3):1106–1124, June 1996.
[D9̈6b]
W. Dörfler. A time and space adaptive algorithm for the linear time-dependent
Schrödinger equation. Numerische Mathematik, 73:419–448, 1996.
[FP10]
D. Ferrarese and M. Penati. GasFramework - Una libreria orientata ad oggetti per
la costruzione di risolutori per problemi differenziali con il metodo agli elementi finiti.
Technical report, Course of Advanced Programming for Scientific Computing. Politecnico
di Milano, Milan, 2010.
[Fri03]
J.M. Fried. Image segmentation using adaptive finite elements.
Mathematische Fakultät Freiburg, 2003.
[Fri09]
J.M. Fried. Multichannel image segmentation using adaptive finite elements. Comput.
Vis. Sci., 12(3):125–135, February 2009.
Technical report,
[FSRS09] L. Figueroa, M. Solar, M.C. Rivara, and M.C. Stelling. A parallel 2T-LE algorithm
refinement with MPI. CLEI Electron. J., 12(2), 2009.
[GGR07]
C. Gutierrez, F. Gutierrez, and M.C. Rivara. Complexity of the bisection method. Theor.
Comput. Sci., 382(2):131–138, 2007.
[HKK10]
A. Hannukainen, S. Korotov, and M. Křı́ek. On global and local mesh refinements by
a generalized conforming bisection algorithm. J. Comput. Appl. Math., 235(2):419–436,
November 2010.
[Mit87]
W.F. Mitchell. A Comparison of Adaptive Refinement Techniques for Elliptic Problems.
Technical report. Department of Computer Science, University of Illinois at UrbanaChampaign, 1987.
[MNS99]
P. Morin, R.H. Nochetto, and K.G. Siebert. Data oscillation and convergence of adaptive
FEM. SIAM J. Numer. Anal, 38:466–488, 1999.
[MS89]
D. Mumford and J. Shah. Optimal approximations by piecewise smooth functions and
associated variational problems. Communications on Pure and Applied Mathematics,
42(5):577–685, 1989.
[MSH+ 13] G.R. Markall, A. Slemmer, D.A. Ham, P.H.J. Kelly, C.D. Cantwell, and S.J. Sherwin. Finite element assembly strategies on multi-core and many-core architectures.
International Journal for Numerical Methods in Fluids, 71(1):80–97, 2013.
[OF02]
S.J. Osher and R.P. Fedkiw. Level Set Methods and Dynamic Implicit Surfaces. Springer,
1 edition, October 2002.
[Pap11]
N. Papucci. Adattazione anisotropa di griglia applicata alla segmentazione di immagini.
Master’s thesis, Department of Mathematics. Politecnico di Milano, Milan, 2011.
[Qua08]
A. Quarteroni. Modellistica Numerica Per Problemi Differenziali. Unitext. Springer
Milan, 2008.
[Riv84]
M.C. Rivara. Algorithms for refining triangular grids suitable for adaptive and multigrid
techniques. International Journal for Numerical Methods in Engineering, 20(4):745–756,
1984.
49
Riferimenti bibliografici
[SBS05]
A. Schmidt, T.J. Barth, and K.G. Siebert. Design of Adaptive Finite Element Software: The Finite Element Toolbox Alberta. Lecture Notes in Computational Science and
Engineering. Springer London, Limited, 2005.
[Sch10]
O. Scherzer. Handbook of Mathematical Methods in Imaging.
Springer, 2010.
[Sew72]
E.G. Sewell.
Automatic Generation of triangulations for piecewise polynomial
approximation. PhD thesis, Purdue University, West Lafayette, 1972.
[Str00]
B. Stroustrup. The C++ programming language. Addison-Wesley, 2000.
[Ver96]
R. Verfürth. A review of a posteriori error estimation and adaptive mesh-refinement
techniques. Advances in numerical mathematics. Wiley-Teubner, 1996.
[Yan01]
D. Yang. C++ and Object-Oriented Numeric Computing for Scientists and Engineers.
Springer New York, 2001.
50
Springer Reference.