Capitolo 6

Descrizione della struttura dati.

 

 

 

In questo capitolo verrà descritta la struttura dati dell’applicazione per la gestione delle cartelle cliniche, che abbiamo pensato di suddividere in più settori, a seconda del campo d’azione:

Nell’appendice C ci sarà un’approfondimento sui requisiti hardware e software e sulle modalità di installazione dell’applicazione.

 

 

6.1 Sturttura dati per l’algoritmo di segmentazione.

Come precedentemente accennato possiamo schematizzare l’algoritmo di segmentazione nei seguenti passi:

  1. Inizializzazione dell’immagine segmentata.
  2. Creazione del Quad-Tree.
  3. Fino a quando l’immagine segmetata non è del tutto colorata:
    1. Creazione del percorso ottimo dalla radice alle foglie.
    2. Scelta del nodo campione appartenente al percorso ottimo.
    3. Colorazione dei nodi ‘simili’ a quello campione.

Le classi trattate in questo paragrafo sono:

La classe Pix.

La classe Pix è la struttura dati che descrive un’immagine, infatti ogni oggetto appartenente a questa classe possiede un array di interi mono-dimensionale di lunghezza 256x256. C’è praticamente una corrispondenza uno a uno tra ogni elemento dell’array ed ogni pixel dell’immagine. Per controllare il pixel di coordinate (x,y) basterà fare riferimento all’elemento (y*256+x)-esimo dell’array.

A completare la classe c’è un intero che tiene conto dei pixel ancora da colorare e che viene inizializzato a 256x256. Questo campo servirà successivamente per bloccare l’algoritmo che, come accennato nel capitolo 2, si ferma quando i pixels da colorare sono meno di 3000. Riassumendo: i campi della classe Pix sono:

La classe QTnode.

Il secondo passo dell’algoritmo consiste nel creare il Quad-Tree. La classe QTnode rappresenta la struttura dati di quest’albero. Ogni oggetto di questa classe rappresenta un nodo del Quad-Tree e gli attributi sono:

La classe PercorsoOttimo

Il passo 3.1 dell’algoritmo prevede la creazione del percorso ottimo dalla radice ad una foglia dell’albero, scegliendo ad ogni passo il figlio, non appartenente alla lista BClosed, con minor grado di entropia rispetto al padre. Ricordando che la lunghezza del percorso dalla radice alle foglie è 7, è stata creata la classe PercorsoOttimo con i seguenti campi:

La classe Istogramma.

Il calcolo della funzione di valutazione del grado di entropia di un nodo rispetto ad un altro è stato affidata alla classe Istogramma, i cui oggetti presa in input una porzione d’immagine creano un’istogramma, ne calcolano i picchi e confrontano questi risultati con un altro oggetto di tipo Istogramma ritornando la funzione di valutazione. I campi della classe sono:

Le classe Colori.

Per quanto riguarda i colori da usare durante la segmentazione è stata creata una classe i cui oggetti contengono un’array di colori ed un puntatore al colore corrente aggiornato ad ogni passo dell’algoritmo. Quindi i campi della classe Colori sono:

La classe BMP.

E’ stata creata inoltre una classe con soli metodi statici per importare immagini di formato bitmap in oggetti di tipo Pix. Questa è la classe BMP che successivamente nell’applicazione si interesserà anche della scrittura dei file grafici sempre di formato bitmap.

La classe Segmentazione.

L’implementazione vera e propria dell’algoritmo di segmentazione avviene tramite la classe Segmentazione, la quale ha i seguenti campi:

Metodi fondamentali.

Dopo aver enunciato la struttura dati delle classi che concorrono alla realizzazione dell’algoritmo di segmentazione passiamo ad analizzare i metodi più importanti implementati nelle varie classi per l’effettiva esecuzione.

Innanzitutto possiamo notare che il passo 1 dell’algoritmo avviene con la semplice inizializzazione di un oggetto di tipo Pix tramite il suo costruttore, impostando il campo whitePix a 256x256 ed ogni oggetto dell’array pix[ ] a 0.

Viene inoltre creato un altro oggetto di tipo Pix importando l’immagine input tramite il metodo contenuto nella classe Pix, detArray(Image im), che grazie all’aiuto della classe BMP, riempie l’array pix[ ] con il colore dei relativi pixels dell’immagine im. Questi due oggetti di tipo Pix rappresenteranno rispettivamente i campi outPix e inPix dell’oggetto di tipo Segmentazione.

Il metodo ricorsivo creaQT(int liv) della classe QTnode creerà il Quad-Tree. Il valore di defalut dei campi sarà fv=0, BClosed = false. I valori degli altri campi sarà assegnato dal metodo stesso in base alla posizione del nodo. La radice dell’albero rappresentarà il campo QT dell’oggetto di tipo Segmentazione.

Il metodo che sceglie il nodo campione è nodoCampione(Pix _inp, QTnode _rad) della classe PercorsoOttimo. Questo metodo presi in input un oggetto di tipo Pix ed uno di tipo QTnode rappresentanti rispettivamente l’immagine input e la radice del Quad-Tree, restituisce, grazie ai metodi della stessa classe creaPercorsoOttimo(Pix _inp, QTnode _rad) e cercaNodoCampione( ), il puntatore al nodo campione nel quale il campo fv conterrà il grado di entropia rispetto al padre.

A questo punto possiamo parlare dei metodi della classe di tipo Istogramma, i quali sono stati creati esclusivamente per il calcolo della funzione di valutazione. I metodi più importanti sono: creaIstogramma(Pix _p, int _x, int _y, int _dim,boolean isCol[]) e calcolaFv(Istogramma I1, Istigramma I2).

Il primo ha input una porzione dell’immagine rappresentata dall’oggetto Pix p, che inizia nel punto (_x,_y) ed ha dimensione dim e, tramite vari metodi, tra cui creaPicchi(), riempie i vari campi dell’oggetto di tipo Istogramma. Inizialmente riempie il campo isto[ ], incrementando il valore dell’elemento i-esimo se, durante la scansione dei pixels non colorati nella porzione d’immagine, la tonalità di grigio di un singolo pixel e appunto i. Il valore di ogni elemento di isto[] diventa successivamente il rapporto tra il valore stesso e l’area della zona trattata. Alla fine tramite creaPicchi(), vengono calcolati le posizioni ed i valori dei vari picchi. Una volta riempiti i campi, il metodo calcola_fv(Istogramma i1, Istogramma i2) calcola la funzione di similitudine tra due istogrammi in base all’intersezione dei picchi dei due oggetti i1 e i2.

Scelto il nodo campione l’ultimo passo dell’algoritmo è quello di ricerca dei nodi simili nello stesso livello e dell’eventuale colorazione. Questo avviene tramite il metodo colora(Pix _pix, Colori _col, QTnode _node) della classe QTnode, il quale crea l’istogramma per il nodo campione e poi chiama a seconda del tipo di ricerca i metodi nodiConnessi(QTnode _nc, Istogramma _i1, float _err) o nodiNonConnessi(QTnode _nc, Istogramma _i1, float _err) della classe Segmentazione.

 

6.2 Struttura dati per l’estrazione del contorno, l’approssimazione poligonale e ricavo delle caratteristiche geo-morfologiche.

Il processo di estrazione del contorno da una forma e della sua approssimazione poligonale è immediatamente successivo a quello della segmentazione dell’immagine. Infatti l’input di tale algoritmo è rappresentato da un’immagine segmentata e dalle coordinate del punto interno alla forma di cui si vuole estrarre il contorno.

Le classi che concorrono alla realizzazione di tale processo sono:

La classe Contorno.

La classe più importante per questo secondo passo è rappresentata dalla classe Contorno che ha i seguenti campi:

La classe Features.

Una volta definito il contorno approssimato possiamo estrarre da esso le caratteristiche geo-morfologiche e la classe Features si occupa, appunto, di questo processo. Infatti i campi di questa classe sono:

Metodi Fondamentali.

Una volta ottenuta l’immagine segmentata e il punto all’interno della forma di cui vogliamo il contorno, seguendo i passi dell’algoritmo descritto nel secondo capitolo, tramite il metodo della classe Contorno, primoPunto(Pix img), ci ricaviamo il primo punto del contorno.

Successivamente il metodo estrazione(Pix img), grazie all’apporto di check8Conn(int dir, Pix pixels, int col) e cercaProssimo(Contorno curr, Contorno pred, Pix img), crea la catena di punti che formano il contorno della forma.

Dopo la creazione del contorno, col metodo sceltaErrore(int _num) decidiamo, in riferimento alla tabella 1.5, quale errore di approssimazione scegliere. Dopodicchè, col metodo della classe Contorno, approssimazionePoligonale(double err), applichiamo l’algoritmo del capitolo 1 ed otteniamo il contorno approssimato.

Questo contorno viene poi successivamente passato alla class Features che tramite l’applicazione degli algoritmi già precedentemente descritti calcola le caratteristiche geo-morfologiche della forma.

 

6.3 Struttura dati per la gestione delle Virtual Images e delle queries by icons.

Le classi che durante l’esecuzione dell’applicazione che gestiscono le virtual images e le queries by icons sono essenzialmente tre:

La classe Rettangolo.

Visto il particolare modo di calcolare le relazioni spaziali, gli oggetti possono essere visti come rettangoli all’interno dell’immagine. Abbiamo pensato, allora, di creare una classe i cui oggetti fossero dei veri e proprio rettangoli. I campi sono:

La classe QueryCanvas.

Questa classe estende la classe Canvas contenuta in una della librerie standard di Java. Il motivo di tale estensione è dovuto al fatto che abbiamo bisogno di poter inviare a video delle immagini che sono, praticamente, le anomalie e gli oggetti canonici del referto. A questa classe è lasciato il compito di gestire la creazione della virtual image nella finestra Contorno e della query by icons nella finestra PerparazioneQuery. Il numero di oggetti da noi permessi nella virtual image sono sei compresa l’anomalia, quindi, tutti i campi array della classe sono di dimensione sei. I campi della classe QueryCanvas sono:

La classe DatiQuery.

Questa classe si occupa di prendere i dati da un oggetto di tipo QueryCanvas e gestire le virtual images in fase di interrogazione del database. Si propone quindi di salvare la virtual image nel database e di gestire l’esecuzione delle queries. I campi della classe sono:

 

 

Metodi principali.

Durante la creazione di una virtual image dobbiamo avere la possibilità di inserire l’anomalia e gli oggetti canonici nelle posizioni desiderate. Questo avviene nelle finestre Contorno e PreparazioneQuery per mezzo di alcuni metodi della classe QueryCanvas per l’immissione e la cancellazione di un oggetto dalla scena pittorica. Questi metodi sono:

public void caricaAnomalia(String nomefile,String _desc,int _sizex,int

_sizey,int _viid)

public void caricaCanonico(String nomefile,String _desc,int _sizex,int

_sizey, int _viid)

public void caricaContorno(Contorno _Cont,String _desc, int _viid)

public void remove(int i)

Grazie ai metodi per la gestione del mouse che mette a disposizione la libreria di Java, possiamo controllare lo spostamento degli oggetti, andando a modificare i campi locX e locY. Inoltre, i metodi della classe Rettangolo relX(int pos1, int dim1, int pos2, int dim2) e relY(int pos1, int dim1, int pos2, int dim2) computano le relazioni spaziali dell’anomalia rispetto all’oggetto spostato.

Prima dell’esecuzione della query creata, viene creato un oggetto di tipo DatiQuery in cui vengono immessi tutti i dati recuperati in fase di preparazione. Quindi nella finestra EsecuzioneQuery impostiamo tutti i valori relativi alla query quali i margini inferiori e superiori delle caratteristiche geo-morfologiche ed i margini d’errore per i metodi di ricerca e poi alla pressione del bottone Esegui viene applicato il metodo calcola() della classe DatiQuery, il quale prima svuota la tabella Risultati e poi a seconda del valore di mode vengono applicati gli algoritmi descritti nei paragrafi 2.5 e 3.3.

 

6.4 Struttura dati per la gestione dell’interfacciamento tra Java ed Oracle.

Le versione 1.1.x di Java mette a disposizione dell’utente un package particolare (java.sql) che gestisce l’interfacciamento di Java a un qualsiasi DBMS. I passi necessari per la connessione e l’interrogazione sono:

Creazione di un oggetto di tipo Connection.

Avviene tramite due istruzioni che nel nostro caso sono:

Class.forName ("oracle.jdbc.driver.OracleDriver");

Connection Conn = DriverManager.getConnection( "jdbc:oracle:oci8:@",

"utente","password" );

In questo modo abbiamo creato la connessione al database Oracle 8 tramite l’user chiamato utente.

Creazione di un oggetto di tipo Statement.

L’oggetto di tipo Statement permette di interrogare il database tramite i metodi executeQuery ed executeUpdate, e mettere il risultato della query in un oggetto di tipo ResultSet. In effetti esso è l’equivalente del CURSOR di Oracle.

L’istruzione per creare un oggetto di tipo Statement è

Statement stmt = conn.createStatement();

Dove conn è un oggetto di tipo Connection.

Interrogazione del database tramite i metodi executeQuery o executeUpdate.

Come detto prima l’interfaccia Statement mette a disposizione due metodi che ritornano o l’insieme di dati ricercato o il numero di record manipolati:

ResultSet executeQuery(String arg)

int executeUpdate(String arg)

Il primo crea automaticamente un oggetto di tipo ResultSet.

Recupero del risultato in un oggetto di tipo ResultSet tramite i metodi getInt, getDouble, getDate, ecc…

A questo punto non dobbiamo far altro che scorrere l’insieme di record e recuperare le informazioni ricercate. Il metodo dell’interfaccia ResultSet per scorrere i record è:

boolean next()

il quale ritorna il valore true se esiste un prossimo record, false altrimenti. Da notare che un metodo per posizionarsi sul record precedente non esiste, almeno nella versione 1.1.x di Java. Quindi lo scorrimento dei record può avvenire solo verso il basso.

Per recuperare le informazioni contenute nell’oggetto di ResultSet possiamo applicare vari metodi a seconda del tipo di informazione quali:

int getInt(String campo)

double getDouble(String campo)

String getString(String campo)

ed altri ancora per tutti i tipi di dati.

La classe SQL.

Nel nostro caso abbiamo pensato di gestire il tutto tramite la classe SQL i cui campi sono:

conn: un oggetto di tipo Connection che viene inizializzato alla partenza dell’applicazione.

stmt: un oggetto di tipo Statement.

rset: un oggetto di tipo ResultSet.

I metodi principali.

I metodi principali di questa classe sono senza dubbio executeQuery(String arg) e executeUpdate(Connection conn, String arg) che di fatto interrogano il database a seconda che si tratti di una lettura o di una scrittura. Il metodo Connessione ( ) crea la connessione al database. Altri metodi importanti usati durante l’esecuzione dell’applicazione sono quelli riguardanti il recupero di informazioni dal campo rset, quali getInt(String campo),getDouble(String campo) e così via. Vi sono infine 52 metodi statici numerati che sono di fatto le vere e proprie query che l’applicazione lancia durante la sua esecuzione. Ognuno di questi metodi ha come input i valori variabili della query, crea così la stringa contenente la query e lancia uno dei due metodi sopra citati per l’interrogazione vera e propria del database.

Home