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.