Università degli studi Roma Tre Facoltà di Ingegneria Elettronica Anno Accademico 2001/2002 “Codifica e presentazioni di immagini fisse per terminali mobili in reti a commutazione di pacchetto” Francesco Stabile Relatore: Ch.mo Prof. A. Neri Co-Relatore: Ing. F. Romano 2° Relatore: Ch.mo Prof. C. Palma PREFAZIONE La tesi è stata svolta presso le strutture ed i laboratori dalla Scuola di Formazione Superiore ELIS di Roma, nell'ambito del progetto “Servizi Multimediali” commissionato da Ericsson a “Vivai D’Impresa”. Le basi su cui poggia l’informatica sono sempre soggette a continui cambiamenti, ed inevitabili cedimenti dovuti alla rapidità con cui emergono le nuove necessità. Gli impulsi dati da Internet, dall’e-commerce e dai nuovi dispositivi digitali, in particolare dalla diffusione di cellulari e pda, tendono a mettere in evidenza le carenze di tecnologie consolidate da tempo. Resta da vedere se le innovazioni saranno così importanti da comportare l’abbandono di formati largamente diffusi e supportati quali lo JPEG ed il GIF. Questi attuali sistemi di codifica delle immagini hanno consentito l’incredibile sviluppo delle telecomunicazioni multimediali e di internet, ma già ci si scontra con i loro limiti nell’evoluzione dei nuovi scenari. Un breve ma significativo esempio: un codificatore “a perdita” di informazioni per compressione come JPEG, a bassi bit rate fornisce immagini di pessima qualità percettiva; non permette inoltre la definizione di zone dell’immagine più importanti. La soluzione a queste necessità ed altre necessità ha portato alla definizione dello standard JPEG 2000. Scopo del lavoro di ricerca e sperimentazione presentato in questa tesi è lo sviluppo di un software multipiattaforma per dispositivi wireless e portabili che consenta di sfruttare le caratteristiche di JPEG 2000. Nel corso del primo capitolo si descrivono brevemente gli standard attuali per le immagini fisse con particolar riguardo a JPEG, JPEG-LS, PNG e JBIG; si analizzeranno le limitazioni rispetto a JPEG 2000. Quest’ultimo verrà trattato ampiamente nel secondo capitolo, dove si affronteranno: la trasformata pag. II Wavelet per il supporto alla scalabilità della risoluzione; la codifica entropica, che grazie al “rate allocator”, permette una scalabilità del livello di dettaglio; il “packet partitioning” che ha lo scopo di migliorare la capacità di accesso diretto a regioni dell’ immagine. Nel terzo capitolo si descriveranno le peculiarità del linguaggio Java, analizzando soprattutto le piattaforme utilizzate in ambito mobile: Java 2 MicroEdition e Personal Java. Piattaforma, quest’ultima, che è stata utilizzata per lo sviluppo del software. Nel capitolo quattro si analizza l’architettura software alla base dello sviluppo: il compilatore ed il simulatore per piattaforme Symbian; inoltre verrà analizzato in dettaglio il processo che permette di creare i files di installazione, utili per testare l’applicativo sul Sony Ericsson P800, il terminale utilizzato per i effettuare i test finali dell’applicativo. Nel capitolo cinque si descrivono le scelte progettuali effettuate e le caratteristiche del visualizzatore realizzato. Verranno analizzate l’interfacce grafiche utilizzate e le soluzioni adottate per risolvere i problemi di compatibilità con la piattaforma Personal Java del P800. Nel capitolo 6 si analizzano alcune architetture implementate per lo streaming, in special modo quella utilizzante JPIP (JPEG 2000 Internet Protocol), protocollo di comunicazione interattivo per dati compressi in formato JPEG 2000. Tale protocollo è il più accreditato, tra tutti gli altri a diventare un vero e proprio standard di riferimento. Nel corso del capitolo, oltre ad una disamina tecnica di JPIP, è presente anche un’applicazione Java (lato client) che testa il meccanismo di richiesta scalabile secondo questo protocollo. Infine nel settimo capitolo si presentano le considerazioni conclusive, commentando i risultati ottenuti ed indicando eventuali sviluppi futuri. pag. III INDICE CAPITOLO 1 STANDARD ATTUALI PER LE IMMAGINI ............ 3 1.1 IMMAGINI FISSE ..................................................................................... 3 1.1.1 JPEG-LS................................................................................................ 4 1.1.2 PNG....................................................................................................... 5 1.1.3 JBIG E JBIG 2 ...................................................................................... 6 1.1.4 JPEG...................................................................................................... 6 CAPITOLO 2 LO STANDARD JPEG 2000........................................ 11 2.1 INTRODUZIONE..................................................................................... 11 2.2 CODIFICA ................................................................................................ 13 2.2.1 TRASFORMAZIONE DELLE COMPONENTI ............................... 13 2.2.2 TRASFORMATA WAVELET........................................................... 15 2.2.3 QUANTIZZAZIONE.......................................................................... 21 2.2.4 CODIFICA ENTROPICA .................................................................. 23 2.2.5 REGIONI DI INTERESSE................................................................ 27 2.3 DECODIFICA........................................................................................... 29 2.3.1 DECODIFICA ENTROPICA ............................................................. 30 2.3.2 DEQUANTIZZAZIONE .................................................................... 30 2.3.3 ANTITRASFORMATA WAVELET ................................................. 32 2.3.4 TRASFORMAZIONE INVERSA DELLE COMPONENTI ............. 33 CAPITOLO 3 JAVA, J2ME E PJAVA ................................................ 35 3.1 LINGUAGGIO JAVA .............................................................................. 35 3.1.1 INTRODUZIONE AL LINGUAGGIO .............................................. 35 3.1.2 UN LINGUAGGIO SEMPLICE ........................................................ 38 3.1.3 ARCHITETTURA NEUTRALE........................................................ 41 3.1.4 SICUREZZA IN JAVA ...................................................................... 44 3.2 J2ME (JAVA 2 MICROEDITION) ........................................................ 48 3.2.1 INTRODUZIONE............................................................................... 48 3.2.2 IL SETTORE EMBEDDED ............................................................... 49 3.2.3 L’ARCHITETTURA J2ME................................................................ 50 3.3 PJAVA (PERSONAL JAVA) .................................................................. 57 CAPITOLO 4 AMBIENTE DI SVILUPPO......................................... 58 4.1 INTRODUZIONE..................................................................................... 58 4.2 SOFTWARE UTILIZZATI..................................................................... 58 4.2.1 ECLIPSE IDE ..................................................................................... 58 4.2.2 UIQ SDK PER SYMBIAN................................................................. 59 pag. 1 4.3 PROCESSO DI SVILUPPO DI APPLICAZIONI JAVA PER PIATTAFORME SYMBIAN......................................................................... 60 4.3.1 INTRODUZIONE............................................................................... 60 4.3.2 SVILUPPO DEL CODICE JAVA...................................................... 62 4.3.3 SVILUPPO PER L’INTERFACCIA SYMBIAN .............................. 63 4.3.4 GENERAZIONE DEL FILE SYS...................................................... 68 4.4 SONY ERICSSON P800 .......................................................................... 69 CAPITOLO 5 REALIZZAZIONE DEL VISUALIZZATORE DI IMMAGINI JPEG 2000 ..................................................................................... 70 5.1 JPEG 2000 VIEWER................................................................................ 70 5.1.1 ANALISI REQUISITI ........................................................................ 70 5.1.2 SCELTE PROGETTUALI ................................................................. 71 5.1.3 INTERFACCIA GRAFICA................................................................ 71 5.1.4 VISUALIZZATORE .......................................................................... 73 CAPITOLO 6 ARCHITETTURE SCALBILI PER LO STREAMING DI IMMAGINI JPEG 2000................................................................................ 75 6.1 STREAMING SCALABILE UTILIZZANDO JPIP............................. 75 6.1.1 INTRODUZIONE............................................................................... 75 6.1.2 ARCHITETTURA PROPOSTA ........................................................ 76 6.1.3 SINTASSI DI RICHIESTA ................................................................ 76 6.1.4 SINTASSI DI REPLICA .................................................................... 78 6.1.5 JPIP HEADERS.................................................................................. 79 6.1.6 DESCRIZIONE DEL PROTOCOLLO INTERATTIVO JPIP-H ...... 80 6.1.7 DESCRIZIONE DEL PROTOCOLLO INTERATTIVO JPIP-HT.... 81 6.1.8 COMUNICAZIONI NON INTERATTIVE ....................................... 81 6.2 STREAMING SCALABILE UTILIZZANDO HTTP ......................... 82 6.2.1 ARCHITETTURA .............................................................................. 82 6.2.2 SAMPLE SESSION............................................................................ 84 6.2.3 STRUTTURA DEL CODESTREAM JPEG2000 .............................. 87 6.2.4 STRUTTURA DELL’ INDEX FILE.................................................. 88 6.3 LE DUE ARCHITETTURE A CONFRONTO ..................................... 89 CAPITOLO 7 CONCLUSIONI E SVILUPPI FUTURI..................... 90 APPENDICE A: LISTATO DEL MAIN DI JP2VIEW................................. 92 APPENDICE B: LISTATO RELATIVO ALL’ IMPLEMENTAZIONE DEL CLIENT JPIP-H IN JAVA ....................................................................... 97 BIBLIOGRAFIA............................................................................................... 100 pag. 2 CAPITOLO 1 STANDARD ATTUALI PER LE IMMAGINI In questi ultimi anni stiamo assistendo ad una grande diffusione di dispositivi quali fotocamere digitali, smart phone dotati di fotocamere on-board, pda. In questi apparati più che in altri, si ha l’esigenza di avere un formato che consenta di memorizzare immagini occupando poca memoria ma che nello stesso tempo permette una visualizzazione con qualità percettive soddisfacenti. Con particolar riguardo al campo della telefonia mobile, un formato più “snello”, consente un utilizzo inferiore della risorsa di banda nel caso di trasmissione o ricezione di un’ immagine. In genere per le immagini ci sono due tipi differenti di compressione o codifica: “lossless” (senza perdita) o “lossy” (con perdita). Nel primo caso si è in grado di restituire, al termine della decompressione, un'immagine esattamente uguale all'originale, com'era prima che venisse compressa. Nel secondo caso non si può assicurare una reversibilità assoluta, infatti l’immagine ricostruita è una versione simile a quella di partenza ma non identica. 1.1 IMMAGINI FISSE Ci sono differenti formati per rappresentare le immagini, dipende dal programma utilizzato per salvarle; i più conosciuti sono bmp (Bitmap Image for Windows o OS/2), tiff (Tagged Image File Format), tag (Truevision Targa), gif (CompuServe Graphics Interchange), i quali codificano l’immagine normalmente o talvolta utilizzano tecniche di compressione con perdita. Dipendentemente dal formato, un’immagine è rappresentata da un set di tre matrici di numeri, dove ogni numero indica l’ampiezza della caratteristica del pixel, per esempio l’ampiezza pag. del 3 colore (es. rosso, verde o blu). Ogni numero è compreso in un intervallo tra 0 e 255, oppure è normalizzato tra 0 ed 1, e mostra l’ampiezza della scala di grigio dell’intensità del pixel, dove zero è nero e 255 è bianco. Questo numero è rappresentato da un set di otto bit ( a seconda del formato anche 12, 16 o 24 bit). Queste tre matrici potrebbero rappresentare i tre colori principali nello spazio dei colori, normalmente rosso verde e blu per RGB, o è possibile trasformarli in YCbCr o YUV dove vengono rappresentate le caratteristiche di luminanza e crominanza dell’ immagine. Ci sono molti standard che cercano di ridurre in modi differenti le dimensioni delle immagini utilizzando diversi metodi e proprietà. Quelli più importanti e più efficienti per compressioni lossy e lossless sono JPEG che utilizza la trasformata DCT, e JPEG 2000 che verrà descritto nel capitolo 2; tratterò brevemente anche differenti tecniche come JPEG-LS , PNG, JBIG. 1.1.1 JPEG-LS JPEG-LS è uno standard introdotto per le codifiche lossless di immagini fisse ed è stato proposto da ISO/ITU-T recentemente. Prevede anche una compressione “quasi” lossless per incrementare il tasso di compressione dell’immagine. Il sistema base descritto nella Parte 1 dello standard, utilizza tecniche di predizione adattativa; la compressione “quasi” lossles è ottenuta invece, fissando un errore di campionamento massimo. Nella Parte 2 vengono introdotte alcune estensioni, ed in particolare un algoritmo di codifica aritmetica, questo algoritmo è utilizzato per raggiungere alti tassi di compressione lossless utilizzando una codifica con bassa pag. 4 complessità computazionale. JPEG-LS comunque non prevede supporti per la scalabilità, per la resistenza agli errori e caratteristiche simili. 1.1.2 PNG Il formato PNG (Portable Network Graphics), è stato sviluppato da 3WC per la codifica di immagini fisse è importante perché nasce in contrapposizione al Gif come un formato grafico compresso e del tutto gratuito. Vedremo ora alcune le sue caratteristiche: • Compressione: utilizza esclusivamente compressione lossless, non esistono opzioni per salvarlo in un formato non compresso oppure in modalità lossy. • Controllo Errore: utilizza un sisteme chiamato chiamato CRC-32, ovvero “cyclic redundancy check” (controllo di ridondanza ciclico) a 32 bit, che associa valori di controllo ad ogni blocco di dati ed è in grado di rilevare immediatamente qualsiasi corruzione delle informazioni salvate o trasmesse via Internet. • Supporto milioni di colori: Le immagini PNG supportano la modalità RGB. E’ bene sottolineare che non supportano la modalità CMYK o YUV. Altre caratteristiche supportate sono il canale alfa e l’interlacciamento. pag. 5 1.1.3 JBIG E JBIG 2 JBIG (Joint Bi.level Image Export Group), come JPEG, è un gruppo di esperti nominati da enti di standardizzazione nazionali, per lavorare alla produzione di standard per immagini bi-livello; in questi anni hanno prodotto due standard: JBIG e JBIG2. Possono anche essere utilizzati per la codifica di immagini a scala di grigio ed a colori con un numero limitato di bit per pixel. La prima versione dello standard è stata sviluppata molto tempo fa; recentemente i tecnici stanno lavorando per completare la nuova versione, JBIG2 che ha vantaggi significativi: migliore nella compressione, prestazioni migliori anche nella decodifica, supporta anche le tecniche lossless e lossy. 1.1.4 JPEG E’ uno standard flessibile che definisce una serie di possibili elaborazioni da eseguire sulle immagini, che possono essere saltate. In pratica non viene specificato come si deve fare la compressione ma solo quali regole devono essere rispettate dai dati compressi per poter poi ottenere una corretta decompressione. Segue una breve analisi dei passi da eseguire per la codifica; si tratta di una procedura più o meno standard che viene maggiormente utilizzata. Conversione di spazio monocromatico L’immagine originale viene convertita dallo spazio cromatico RGB a quello YIQ( o YUV). Il formato YUV consiste in 3 piani di colore: luminanza (Y) e due componenti di crominanza (U e V). Questa separazione, anche se non necessaria, permette una migliore compressione. Infatti, sfruttando un fenomeno noto nel campo video, è possibile ridurre le dimensioni dell'immagine YUV con una pag. 6 leggera riduzione della qualità applicando un sotto campionamento (decimazione) delle componenti cromatiche e mantenendo intatte le informazioni sulla luminosità. Se questa fase viene saltata, la successiva processerà l'immagine in RGB invece che in YUV. Analisi in Frequenza (DCT) L'elaborazione chiave del JPEG è sicuramente la DCT, Discrete Cosine Transform nella sua versione bidimensionale (2D). La DCT è una trasformata che in generale fa passare il segnale dal dominio del tempo al dominio della frequenza. Si tratta di una versione in campo reale della FFT che invece è in campo complesso. I coefficienti che ne derivano rappresentano le ampiezze di quei segnali armonici (coseno) che sommati ricostruiscono il segnale. Nel JPEG viene usata la versione bidimensionale della DCT ed in questo caso non si parla di tempo e frequenza ma di spazio e frequenze spaziali. Per poter essere processata, l'immagine viene divisa in piani (tre piani cromatici R-G-B o Y-U-V a seconda dallo stadio precedente) e all'interno di ogni piano viene di nuovo suddivisa in blocchi di 8x8 pixel. Il blocco di 8x8 pixel nel dominio dello “spazio” viene trasformato in un blocco di 8x8 coefficienti nel dominio della frequenza spaziale. In questo blocco avremo i coefficienti in alto a sinistra che rappresentano le basse frequenze spaziali mentre quelli via via in basso a destra rappresentano le alte frequenze spaziali ossia i dettagli dell'immagine. In particolare il primo coefficiente del blocco trasformato rappresenta la media dei valori del blocco 8x8 originario. pag. 7 Quantizzazione In questa fase avviene l'eliminazione delle informazioni visive meno importanti. Ciò si realizza moltiplicando la matrice 8x8 di coefficienti in frequenza per una “quantization table”. La tabella contiene valori tra zero ed uno, quelli più bassi si trovano in corrispondenza delle alte frequenze mentre quelli più alti in corrispondeza delle basse frequenze. I valori così ottenuti vengono arrotondati all'intero più vicino, in questo modo i coefficienti meno significativi tendono ad azzerarsi mentre rimangono i coefficienti relativi ai contributi informativi più importanti. Essendo già piccoli, i valori in alta frequenza vengono molto spesso arrotondati a zero. Il risultato è la concentrazione di pochi coefficienti diversi da zero in alto a sinistra e zero tutti gli altri. Quando in un file JPEG si sceglie il fattore di compressione, in realtà si sceglie un fattore di scala sui valori della “quantization table”. Più i valori sono bassi e maggiore è il numero di coefficienti che si azzerano con conseguente riduzione del numero di coefficienti significativi. Questo processo ovviamente cancella informazioni via via più importanti e porta ad un progressivo deterioramento della qualità dell'immagine compressa. Codifica entropica Una volta eliminati i dettagli meno importanti grazie alla DCT e alla quantizzazione, è necessario adottare una serie di tecniche entropiche per ridurre la quantità di memoria necessaria per trasmettere le restanti informazioni significative. Tra i restanti coefficienti è importante separare la componente continua (DC) dalla componente variabile (AC). La componente AC viene analizzata tramite una lettura a Zig-Zag. La lettura a Zig Zag rende adiacenti il più possibile i coefficienti uguali a zero e permette un’ottimale rappresentazione dei dati tramite Run Lenght Encoding (RLE). Si tratta di una semplice tecnica di pag. 8 compressione applicata ai componenti AC. Il vettore 1x64 risultante dalla lettura a Zig-Zag contiene molti zero in sequenza, per questo si rappresenta il vettore tramite coppie (skip, value), dove “skip” è il numero di valori uguali a zero e “value” è il successivo valore diverso da zero. La coppia (0,0) viene considerata come segnale di fine sequenza. Sul valore DC di ciascun blocco viene invece applicata una tecnica detta DPCM. In pratica esistendo generalmente una relazione statistica nelle immagini tra le componenti DC di blocchi adiacenti, è possibile codificare la componente DC di un blocco come differenza rispetto al valore del blocco precedente. Questo stratagemma consente una ulteriore riduzione dello spazio occupato dai dati. L'ultima codifica entropica applicata ai dati è la classica codifica a lunghezza di codice variabile. In pratica i dati vengono suddivisi in “parole” (stringhe di bit), viene analizzata la frequenza statistica di ciascuna parola e ognuna viene ricodificata con un codice a lunghezza variabile in funzione della frequenza di apparizione. Un codice corto per le parole che appaiono frequentemente e via via codici più lunghi per quelle meno frequenti. Complessivamente il numero di bit necessari per rappresentare i dati si riduce consistentemente. Decompressione JPEG è un codec simmetrico per sua natura quindi l'elaborazione necessaria per la decompressione è l'esatto inverso di quella necessaria per la compressione. Sui dati compressi si applica la decompressione Huffman, i dati risultanti servono per la ricostruzione blocco dopo blocco delle componenti DC e AC, quindi i coefficienti vengono moltiplicati per una tabella di quantizzazione inversa. il blocco 8x8 risultante viene sottoposto ad una DCT inversa e a questo punto, a seconda delle impostazioni del file si è già ottenuto l'immagine RGB oppure si deve effettuare la conversione da YUV a RGB. pag. 9 Verso JPEG 2000 JPEG è uno standard abbastanza semplice nella sua essenza, eppure l'importanza che ha assunto nel mondo dell'informatica è stata ed è tutt'oggi veramente enorme. Tutto questo non sarebbe stato possibile senza l'impegno del Joint Picture Expert Group. Ma come per tante altre tecnologie è arrivata l’ora di essere sostituito. Siamo infatti ormai vicinissimi alla completa definizione del nuovo standard JPEG 2000 che promette maggiori prestazioni in compressione, maggiore qualità e maggiori funzionalità nell'ambito della sicurezza. Nel corso del secondo capitolo si parlerà in modo dettagliato di JPEG2000. . pag. 10 CAPITOLO 2 LO STANDARD JPEG 2000 2.1 INTRODUZIONE JPEG 2000 è il nuovo standard di compressione di immagini statiche destinato al Web e alla distribuzione su PDA, cellulari, PC, televisioni,ecc. Esso costituisce l'evoluzione del famosissimo formato JPEG e, anche se dotato di caratteristiche fortemente innovative, JPEG 2000 non mira, almeno nel breve termine, a sostituire JPEG, piuttosto ci si attende una "transizione", durante la quale il nuovo standard integrerà ed amplierà le funzionalità offerte da JPEG. Le caratteristiche salienti di JPEG 2000 sono: • Consente sia la compressione con perdita d’informazione (lossy), sia quella senza perdita (lossless). • E' un sistema di codifica unico, in grado di trattare in modo efficace immagini provenienti da sorgenti diverse, con diverse necessità di compressione. • Produce immagini con qualità visive migliori, specialmente a bassi bitrate, rispetto a quelle ottenibili con JPEG, grazie alle proprietà della trasformata Wavelet. • Permette di modificare ed, eventualmente, di decodificare regioni qualsiasi dell'immagine, operando direttamente sui dati in forma compressa. • Può generare immagini compresse scalabili sia in risoluzione sia in livello di dettaglio, lasciando all’utilizzatore la libertà di scegliere quanta informazione e quali parti dell'immagine utilizzare per la decompressione. • Introduce il concetto di ”Region of Interest” (ROI, Regione di Interesse) di un'immagine. pag. 11 • E' in grado di raggiungere notevoli tassi di compressione con qualità ancora accettabili. Lo standard JPEG 2000 è suddiviso principalmente in sei documenti distinti, chiamati Part nella terminologia dello standard. Part 1 - Definisce i requisiti minimi che un decoder deve avere. Serve a creare un insieme di opzioni di base per le quali sia garantita la massima interoperabilità tra le diverse realizzazioni. Part 2 - Si occupa di applicazioni più specifiche, che forniscono incrementi prestazionali, specialmente in particolari ambiti applicativi. Un’ immagine codificata con queste estensioni potrebbe non essere interpretata correttamente da decoder conformi soltanto alle specifiche della Part I. Part 3 - Riguarda "Motion JPEG 2000", uno standard video digitale di elevata qualità. Part 4 - Indica le regole di conformità allo standard JPEG 2000. Part 5 - E' costituito da modelli software che possono essere usati come riferimento nello sviluppo di prodotti JPEG 2000. Part 6 - Contiene una serie di specifiche per l'impiego di JPEG 2000 in ambiti quali fac-simile numerico ed editoria elettronica (fax-like). Nella Part 1 dello standard viene specificato soltanto l'algoritmo di decodifica e il formato dei dati compressi. Per l’algoritmo di codifica non c’è nessun vincolo di implementazione, ma ovviamente deve essere compatibile con l'algoritmo di decodifica. Nella Part 9 dello standard, che non è ancorà disponibile, si parla di Jpip (Jpeg 2000 internet protocol) che è un protocollo di comunicazione interattivo per lo scambio di dati compressi con JPEG 2000 . JPEG 2000 è un formato a multirisoluzione. L'immagine viene memorizzata in numerose risoluzioni in un unico file, senza ridondanza dei dati e può essere trasmessa o decodificata a una risoluzione adatta al dispositivo su cui viene visualizzata. In questo modo l'immagine viene compressa una sola volta ad una pag. 12 risoluzione sufficientemente alta per poi essere decodificata in diverse risoluzioni. La caratteristica di multirisoluzione è dovuta all'utilizzo della cosiddetta Discrete Wavelet Transform (DWT), che permette una codifica lossy o lossless a seconda del tipo di trasformata utilizzata. 2.2 CODIFICA La codifica di un’immagine può essere scomposta logicamente in una successione di trasformazioni applicate sequenzialmente. Lo schema a blocchi è il seguente: Immagine Originale Trasformazione delle componenti Trasformata Wavelet Quantizzazione Codifica Entropica Immagine compressa Figura 1: Codifica JPEG 2000 (schema a blocchi) 2.2.1 TRASFORMAZIONE DELLE COMPONENTI Il primo passo dell’algoritmo consiste nel suddividere l’immagine da codificare in regioni rettangolari disgiunte dette “tile”. Le dimensioni di tutte le tile sono uguali, fatta eccezione per le regioni che si trovano ai bordi dell’immagine,che possono essere più piccole. Per motivi di efficienza computazionale, le loro dimensioni devono essere potenze di due. Nel caso in cui un’immagine abbia più di una componente, solitamente rosso, verde e blu, si applica una trasformazione puntuale di decorrelazione per ogni campione di ogni componente. Possono essere impiegati due tipi di trasformazione: pag. 13 • YCrCb. Analoga alla trasformazione utilizzata in JPEG, viene impiegata in codifiche lossy. La trasformazione da RGB ad YCrCb può essere espresse dalle seguenti equazioni poste in forma matriciale: 0.587 0.114 R Y 0.299 C = − 0.16875 − 0.33126 0.5 G B − 0.41869 − 0.08131 B C R 0.5 • RCT – Reversible Component Transform. Fornisce una decorrelazione analoga alla precedente, ma consente la ricostruzione senza perdite dell’immagine; ovviamente è impiegata in codifiche lossless. In modo analogo alla trasformazione irreversibile, possiamo esprimere la RCT mediante la seguente uguaglianza matriciale: R + 2G + B 4 R −G B −G Y R U = R V R Le trasformazioni successive sono applicate in modo indipendente ad ogni tile di ciascun componente; così facendo è possibile estrarre e ricomporre anche singole parti dell’immagine originale, realizzando una prima tipologia di accesso diretto. Più avanti vedremo un ulteriore raffinamento del metodo di accesso diretto, perchè se dipindesse esclusivamente dal tiling, potrebbe rivelarsi troppo grossolano e di scarsa utilità. E’ anche possibile diminuire notevolmente la pag. 14 memoria necessaria per la compressione elaborando separatamente le varie parti dell’immagine. Infine l’indipendenza della codifica rispetto a componenti e tile permette applicare tutte le trasformazioni a ciascuna tile per ogni componente. Perciò in seguito si farà riferimento ad immagini formate da una singola tile e da un’unica componente. Si potrà fare un ragionamente analogo anche per casi più complessi, con immagini multi-tile e multi-componente. 2.2.2 TRASFORMATA WAVELET Mediante una trasformata Wavelet discreta (DWT) ogni tile viene scomposta in più livelli di risoluzione. Ciascun livello di decomposizione contiene un certo numero di sottobande, formate da coefficienti che esprimono le caratteristiche dell’immagine in termine di frequenze spaziali orizzontali e verticali. Sono previsti tre diversi tipi di decomposizione. Nella decomposizione usata normalmente da JPEG 2000, l’immagine viene scomposta tramite i seguenti filtri: • LL: deriva dall’applicazione di un filtro passo-basso separabile all’immagine originale, seguito da un sottocampionamento in entrambe le direzioni. Quindi LL è una versione a bassa risoluzione dell’immagine originale. • HL: filtro passa-alto orizzontale, addolcisce i picchi orientati orizzontalmente, in modo da corrispondere maggiormente all’orientazione verticale. • LH: simile ad HL ma con direzioni opposte • HH: corrisponde principalmente a caratteristiche orientate in diagonale La sottobanda LL, viene successivamente e reiteratamente scomposta in livelli di decomposizione successivi come mostrato in figura 2. pag. 15 Figura 2: Suddivisione in sottobande Nelle seguenti immagini possiamo osservare i vari livelli di decomposizione che si susseguono. Al primo livello, l’immagine originale è stata scomposta i quattro sotto-immagini (con risoluzione ¼ dell’ originale) che rappresentano il contenuto in bassa frequenza (figura 3) .Al secondo livello di decomposizione si opera sul contenuto in bassa frequenza generato al primo livello. Vengono quindi generate altre sottobande di dettaglio e nuova sottobanda di bassa frequenza (figura 4). Si procede per livelli successivi generando sempre più sottobande di dettaglio (figura 5). I dettagli generati ai livelli successivi di decomposizione sono più importanti ai fini percettivi di quelli generati ai primi livelli. In figura 6 possiamo notare quali sottobande sono necessarie per ricostruire il livello di risoluzione desiderato. pag. 16 Figura 3: Decomposizione in sottobande, primo livello Figura 4: Decomposizione in sottobande, secondo livello pag. 17 Figura 5: Decomposizione in sottobande, terzo livello Figura 6: Livelli di decomposizione in sottobande pag. 18 La trasformata DWT può essere reversibile o irreversibile. La trasformazione irreversibile utilizzata generalmente da JPEG 2000 impiega il filtro Daubechies 9/7, a valori reali (R). I coefficienti dei filtri di analisi, passa-alto e passa-basso, sono riportati nella tabella 1. La trasformazione reversibile viene solitamente realizzata mediante un filtro 5/3, a valori in Z. La caratteristica di questo tipo di filtro è permettere una ricostruzione completa e senza perdite in fase di sintesi. I coefficienti dei filtri di analisi, passa-alto e passa-basso, sono riportati in tabella 2. I termini analisi e sintesi indicano rispettivamente la fase codifica e decodifica. 9/7 – Analisi i h L (i ) h H (i ) 0 0.6029490182 1.1150870524 ±1 0.2668641184 -0.5912717631 ±2 -0.0782232665 -0.0575435262 ±3 -0.0168641184 0.0912717631 ±4 0.02674875741 TABELLA 1 : CODIFICA JPEG2000. FILTRO DAUBECHIES 9/7 DI ANALISI pag. 19 5/3 – Analisi i h L (i ) h H (i ) 0 3/4 1 ±1 1/4 -1/2 ±2 -1/8 TABELLA 2:CODIFICA JPEG 2000. FILTRO 5/3 DI ANALISI Lo standard permette di realizzare la trasformata Wavelet in due modi differenti. Il più tradizionale fa uso dell’implementazione a banco di filtri; il secondo si basa sul lifting scheme. Sinteticamente, il filtraggio basato sul lifting scheme consiste in una serie di operazioni elementari, dette passi di lifting, mediante le quali: • il valore dei campioni di posto pari è aggiornato in base ad una media pesata dei campioni di posto dispari (prediction); • il valore dei campioni di posto dispari è aggiornato in base ad una media pesata dei campioni dei nuovi campioni di posto pari (update). Il filtraggio basato sul lifting scheme, per la trasformazione reversibile 5/3, è espresso dalle seguenti relazioni (analisi): x 2 n + x 2 n + 2 − 1 y 2 n +1 = x 2 n +1 − 2 + y 2 n +1 − 2 y y 2 n = x 2 n + 2 n −1 4 pag. 20 Dove x (n ) è l’estensione simmetrica periodica del segnale x (n ) . I campioni da trasformare devono essere estesi ai bordi dell’immagine a prescindere dal tipo di trasformazione utilizzata. Lo standard prescrive l’utilizzo di un’estensione simmetrica e periodicache è necessaria per garantire la consistenza delle operazioni di filtraggio effettuate in prossimità dei bordi dell’immagine. Il numero di campioni che dovranno essere prodotti per estensione simmetrica dipende strettamente dalla lunghezza del supporto e dal tipo di filtro utilizzato. 2.2.3 QUANTIZZAZIONE Nel processo di quantizzazione i coefficienti prodotti dalla trasformata Wavelet vengono discretizzati, con conseguente riduzione della precisione. JPEG 2000 utilizza una quantizzazione scalare ed uniforme. Il quantizzatore scalare è un processo che associa ad ogni valore scalare d’ingresso un valore scalare in uscita, mediante opportuni intervalli di quantizzazione chiamati soglie. L’aggetttivo uniforme definisce un quantizzatore scalare che ha tutti gli intervalli di quantizzazione uguali, con ampiezza pari a ∆ bi . Per la quantizzazione scalare uniforme ∆ bi è detto passo di quantizzazione. La quantizzazione, raggruppando in intervalli discreti la dinamica continua dell’ingresso, è un processo che porta alla perdita di informazione. Unica eccezione è il caso di coefficienti interi prodotti, ad esempio, con Wavelet 5/3 e passo di quantizzazione unitario: qui la quantizzazione può avvenire senza alcuna perdita di informazione. Possiamo avere quindi due possibili condizioni: pag. 21 • Codifica lossless. Wavelet 5/3 e assenza di ogni tipo di quantizzazione. Per “aggirare” il passo di quantizzazione è sufficiente imporre ∆ bi = 1. • Codifica lossy. Si utilizza una Wavelet 9/7, o comunque non intera. Ad ogni coefficiente a bi ( x, y ) ∈ R della sottobanda bi è associato il valore quantizzato q bi (x, y ) mediante la relazione: a b ( x, y ) i q bi ( x, y ) = χ ∆ bi dove χ = sgn(a bi ( x, y )) , mentre ∆ bi è definito rispetto alla dinamica Rb della sottobanda corrente, all’esponente ε bi ed alla mantissa µ bi , secondo quanto segue: ∆ bb = 2 Rbi − − ε B I µ BI 1 + 11 2 La scelta del passo di quantizzazione non è imposta dallo standard , si tratta di un parametro modificabile in fase di codifica, fa parte dei “gradi di libertà” disponibili nella realizzazione di un encoder. I coefficienti quantizzati qbi ( x, y ) sono numeri interi con segno, anche nel caso in cui i campioni dell’immagine iniziale ne risultino privi. Per il corretto funzionamente del blocco di codifica entropica è essenziale che i coefficienti quantizzati devono essere sempre rappresentati in modulo e segno. Prima della codifica entropica ogni sottobanda quantizzata può essere sottoposta ad una ulteriore suddivisione facoltativa, detta “packet partitioning”. Ogni sottobanda viene suddivisa in una serie di rettangoli analogamente a quanto fatto per il tiling. La differenza sono le dimensioni dei rettangoli, generalmente minori rispetto a quelle delle tile. Quest’operazione ha lo scopo di migliorare le capacità di accesso diretto a regioni dell’immagine, fornendo un metodo di accesso migliore rispetto a quella ottenuta con l’utilizzo pag. 22 delle sole tile. Applicando questa ripartizione è anche possibile aumentare la resistenza alla propagazione degli errori nello stream codificato e ridurre la memoria necessaria, localizzando l’elaborazione a livello di pacchetto piuttosto che processare intere sottobande. Se non si utilizza il packet partitioning è sufficiente impostare le dimensioni dei pacchetti a valori maggiori di quelle dell’immagine originale. Lo standard, in realtà, suggerirebbe di utilizzare 2 ^15 come altezza e larghezza di ogni pacchetto, in quanto questo valore rappresenta la dimensione massima consentita per ogni tile. 2.2.4 CODIFICA ENTROPICA Ogni sottobanda è suddivisa in blocchi rettangolari disgiunti di uguali dimensioni detti codeblock. Se si utilizza il packet partitioning, invece che ad ogni sottobanda, la suddivisione in codeblock è applicata ad ogni pacchetto. L’area di ciascun codeblock non può superare i 4096 coefficienti; per di più la larghezza e l’altezza dei blocchi devono essere potenze di due. La caratteristica principale dei codeblock è di poter essere codificati in modo indipendentemente gli uni dagli altri, utilizzando un algoritmo chiamato EBCOT(Embedded Block Coding with Optimized Truncation). La codifica entropica di JPEG 2000 si basa essenzialmente sulla compressione aritmetica adattativa. Stimare la distribuzione di probabilità del simbolo da codificare equivale a ricercare il valore del suo contesto: è lecito attendersi l’esistenza una forte correlazione tra ogni campione e l’insieme dei suoi vicini. Per enfatizzare questo legame si utilizza un particolare tipo di codificatore detto bit-plane coder. Si definisce bit-plane p-esimo l’insieme dei bit di peso 2 p di tutti i coefficienti appartenenti ad un determinato codeblock Bi. Il bit-plane sarà indicato con Vpi. Un bit-plane coder è un processo che codifica l’informazione esplorando un bit-plane alla volta, a partire da quello più pag. 23 significativo. A ciascun bit del bit-plane, si applicano, in sequenza, i tre passi di codifica: 1. “Significance propagation pass”. 2. “Magnitude refinement pass”. 3. “Cleanup pass”. E’ importante sottolineare che ciascun campione del bit-plane è codificato in uno soltanto dei tre passi di codifica sopra indicati. Durante ogni passo, la visita dei campioni avviene secondo un ordine particolare, visibile in figura 7: Figura 7: Ordine di scansione all’interno di un code-block Il contesto fornito al codificatore aritmetico viene formato in modo differente a seconda del passo di codifica che si si sta utilizzando. Durante il Significance propagation pass si effettua una stima della probabilità che il coefficiente corrente diventi significativo durante l’attuale passo di codifica. Per questo si utilizza come contesto lo stato di significatività degli otto immediati vicini (vedi figura 8). I coefficienti ai quali è applicato questo passo hanno tutti il contesto diverso da zero e sono non ancora significativi. In caso di contesto nullo, il coefficiente è tralasciato, rimandando la sua codifica ai passi successivi. Se il coefficiente diventa significativo, oltre al bit appartenente al bit-plane, occorre codificare anche il valore del suo segno. pag. 24 D0 V0 D1 H0 X H1 D2 V1 D3 Figura 8: Maschera di creazione del contesto Ai campioni già significativi, con eccezione di quelli che lo sono appena diventati durante il Significance propagation pass, si applica il Magnitude refinement pass. Per determinare il contesto si utilizzeranno solo le informazioni di stato del campione corrente. Tutti i campioni che non sono stati codificati nel Significance propagation pass e nel Magnitude refinement pass sono elaborati durante il Cleanup pass. Qui il contesto è valutato in modo analogo a quanto fatto nel Significance propagation pass. Una volta terminato questo passo, sicuramente tutto il bit-plane corrente sarà stato codificato. Il processo fin qui descritto può essere iterato passando al bit-plane successivo, partendo dal Significance propagation pass sino ad esaurire tutti i bit-plane del blocco. Completando la panoramica sulle funzionalità di EBCOT è bene presentare le capacità di rate allocation fornite dall’algoritmo. Come accennato nel paragrafo 2.1, JPEG 2000 costruisce immagini compresse scalabili sia in risoluzione che in distorsione. Il supporto alla scalabilità rispetto alla risoluzione è fornito dalla trasformata Wavelet. Per quanto concerne la possibilità di decodificare le immagini a diversi valori di dettaglio occorre un supporto esplicito in fase di codifica entropica. Per comprendere la suddivisione delle funzionalità all’interno di EBCOT si può fare riferimento alla figura 9. La parte di codifica entropica vera e propria (bitplane coder e codifica aritmetica) è contenuta all’interno del blocco B1. In B2 si colloca la sezione relativa al “rate allocator”, il cui scopo è creare uno stream che pag. 25 permetta la scalabilità a livello di dettaglio. I dati compressi, provenienti da ogni code-block, vengono distribuiti tra uno o più quality layer. Ogni singolo layer è un insieme di informazioni codificate che aumenta monotonicamente la qualità dell’immagine. EBCOT Blocchi di una sottobanda B1 Codifica di basso levello(singoli codeblock) Blocco compresso B2 Rate-Allocation Creazione del layer bitstream completo Figura 9: Schema a blocchi di EBCOT Utilizzando un’opportuna segnalazione all’interno dello stream si possono identificare i layer, garantendo la capacità di accesso diretto. Volendo raggiungere un certo livello di dettaglio è sufficiente decodificare un numero di layer ridotto, scartando tutti i rimanenti. Nel caso l’utente desideri migliorare la qualità dell’immagine ricevuta sarà sufficiente decodificare i layer rimasti,aggiungendo i nuovi contributi a quanto già precedentemente estratto. In JPEG 2000 la scalabilità offerta dal B2 di EBCOT è chiamata SNR scalability. pag. 26 2.2.5 REGIONI DI INTERESSE Una delle novità introdotte in JPEG 2000 è la possibilità di enfatizzare l’importanza di alcune regioni dell’immagine, privilegiando la codifica dei coefficienti appartenenti a queste zone. L’uso delle ROI (Region of Interest) risulta particolarmente indicato per la codifica di immagini nelle quali alcune parti siano più importanti di quanto le circonda. Il sistema utilizzato da JPEG 2000, è chiamato Maxshift Method. Con questo metodo le regioni di interesse vengono trattate in modo che: • l’informazione appartenente alla ROI venga codificata da EBCOT prima rispetto a quella appartenente allo sfondo; • l’informazione appartenente alla ROI appaia, nello stream codificato, • prima di quella appartenente allo sfondo. La prima condizione è ottenuta effettuando uno scalamento verso destra di tutti i coefficienti che non appartengono alle zone di interesse. Questo scalamento lascia i bit-plane più significativi occupati soltanto da valori che appartengono alla regione di interesse. La codifica entropica, partendo sempre dal piano più significativo, elaborerà per primi i coefficienti delle ROI, realizzando quanto espresso nella prima condizione. Il coefficiente di scalamento Ks deve essere scelto in modo che: KS ≥ max{q BG ( x, y )} min{q ROI ( x, y ) | q ROI ( x, y ) ≠ 0} pag. 27 dove: • q BG ( x, y ) è il generico campione quantizzato, di coordinate (x,y), che non fa parte di nessuna ROI ; • q ROI ( x, y ) è il generico campione quantizzato, di coordinate (x;y), che appartiene almeno ad una ROI. Il numero di spostamenti verso destra da applicare alla rappresentazione binaria di ogni campione appartenente al background (insieme dei coefficienti q BG ( x, y ) ) può essere espresso come: s = [log 2 K S ] A questo punto occorre capire quali informazioni risultino utili, in fase di decodifica, per la corretta interpretazione delle regioni di interesse. Si potrebbe immaginare che, oltre al valore dello scalamento, sia necessaria anche un’indicazione della forma della ROI, una sorta di “mappa” che identifichi quali campioni vi appartengano e quali no. In realtà un indiscutibile pregio di Maxshift è la capacità di interpretare correttamente i campioni ricevuti senza bisogno di conoscere la ROI mask. E’ sufficiente che il valore di s, calcolato dal codificatore, venga incluso esplicitamente nello stream; il decoder, basandosi sul fatto che q ROI ( x , y ) ≥ 2 S ≥ q BG ( x, y ) ∀( x, y ) pag. 28 confronta ogni q(x,y) con il valore di 2 S . Se il campione è minore del coefficiente di scalamento viene semplicemente scalato verso sinistra di s posizioni, in quanto appartenente al background. In caso contrario significa che fa parte di una ROI e quindi non necessita di alcun scalamento. La seconda condizione non è trattata in alcun modo dal Maxshift Method; la sua realizzazione è affidata all’unità che si occupa di costruire lo stream. Spetterà a quest’ultima ordinare le informazioni opportunamente, facendo in modo che i coefficienti appartenenti alle regioni di interesse compaiano all’inizio dello stream. Naturalmente sarà necessario esplicitare, all’interno dell’immagine codificata, la posizione spaziale della regione di interesse, per consentire una corretta ricostruzione da parte del decoder. 2.3 DECODIFICA In modo analogo a quanto realizzato nella presentazione del processo di codifica, anche la decodifica pu`o essere scomposta in una sequenza di trasformazioni. In figura 10 si riporta lo schema a blocchi: In ogni blocco si realizza la funzione inversa rispetto a quanto descritto per la codifica. Pertanto si presenteranno soltanto le differenze significative rispetto alla codifica. Immagine compressa Decodifica entropica Dequantizza zione Trasformata Wavelet inversa Trasformazione inversa delle Immagine ricostruita componenti Figura 10: Decodifica JPEG 2000 (schema a blocchi) pag. 29 2.3.1 DECODIFICA ENTROPICA Costituisce il duale del blocco presentato nel paragrafo 2.2.4. La principale differenza rispetto alla codifica è nel blocco B2 che, in questo caso, non si occupa di effettuare la rate allocation, quanto di interpretare correttamente la suddivisione in layer, fornendo le capacità di SNR scalability. 2.3.2 DEQUANTIZZAZIONE Per effettuare la dequantizzazione dei coefficienti a bi ( x , y ) , appartenenti alla sottobanda bi , si deve conoscere il valore del passo di quantizzazione ∆ bi . In realtà il passo di quantizzazione può essere calcolato, noti la dinamica dei valori Rbi , l’esponente ε bi e la mantissa µ bi , mediante la relazione: ∆ bi = 2 Rbi −ε bi µ 1 + 11bi 2 La mantissa e l’esponente possono: • essere segnalati, per ogni sottobanda, in modo esplicito all’interno dello stream; • essere segnalati solo per la sottobanda LL. In questo caso si parla di quantizzazione implicita. pag. 30 Usando la quantizzazione implicita è possibile ricavare, noti i valori di ε 0 e µ 0 della banda LL, i valori di ε bi e µ bi , per la generica sottobanda bi . Per l’esponente si ha: ε b = ε 0 + nsd b − nsd 0 i dove nsd bi i è il numero di livelli di decomposizione che intercorrono tra l’immagine originale e la sottobanda bi, mentre nsd 0 è il numero di livelli di decomposizione per LL. La mantissa, invece, rimane la stessa utilizzata per la sottobanda LL, cioè: µb = µo i Il valore del coefficiente ricostruito a bi può essere determinato, a seconda del tipo di filtro, come: a bi ( x, y ) = q bi ( x, y ).∆ bi per trasformazioni irreversbili a bi ( x, y ) = q bi ( x, y ) per trasformazioni reversibili Per completezza va ricordato che, a causa delle caratteristiche di JPEG2000, si potrebbe scegliere, nel corso della decodifica entropica, di non ricostruire tutti i bit-plane, effettuando il troncamento dello stream. Questo aumenterebbe, di fatto, pag. 31 il passo di quantizzazione utilizzato. Pertanto le relazioni di dequantizzazione precedenti dovrebbero essere ritoccate, per tener conto di questa eventualità. 2.3.3 ANTITRASFORMATA WAVELET In questo passo è applicata la trasformata Wavelet inversa (IDWT). Valgono tutte le considerazioni presentate nel paragrafo 2.2.2. In particolare, in caso di implementazione a banco di filtri, le tabelle seguenti riportano i coefficienti per i filtri di sintesi passa-basso e passa-alto. 9/7 – Sintesi i g L (i ) g H (i ) 0 1.1150870524 0.6029490182 ±1 0.5912717631 -0.2668641184 ±2 -0.0575435262 -0.0782232665 ±3 -0.091717631 0.0168641184 ±4 0 TABELLA 3 0.02674875741 : DECODIFICA JPEG2000. FILTRO DI SINTESI 9/7 pag. 32 5/3 – Sintesi i g L (i ) g H (i ) 0 1 3/4 ±1 -1/2 1/4 ±2 0 -1/8 TABELLA 4 : DECODIFICA JPEG2000. FILTRO DI SINTESI 5/3 Qualora si utilizzi un’implementazione lifting scheme, per la sintesi è sufficiente invertire tutti i passi e le operazioni fatte in analisi. 2.3.4 TRASFORMAZIONE INVERSA DELLE COMPONENTI Questo passo è semplicemente l’inversione delle trasformazioni applicate nel primo blocco del processo di codifica. Innanzitutto occorre ricostruire le componenti originali, qualora sia stata utilizzata una decomposizione in tile. Questo assemblaggio non presenta, in genere, particolari difficoltà. Va comunque ricordato che, anche se non è stata esplicitamente menzionata nella descrizione della codifica, l’unità che assembla lo stream ha la facoltà di spezzettare le tile nelle cosddette tile-part. Questi pezzi di tile possono essere anche non contigui all’interno dello stream. Sarà cura di chi decodifica l’immagine provvedere al necessario riordino. Una volta ricostruite completamente le singole componenti, sarà possibile tornare alle componenti originali invertendo la trasformazione di decorrelazione applicata in codifica. Anche in questo caso esistono due possibilità distinte: pag. 33 • Inverse ICT(Irreversibile Component Trasform). Le equazioni descrivono la trasformazione YCrCb a RGB possono essere espresse come: 0 0.114 Y R 1 G = 1 − 0.34413 − 0.71414 C b 1.772 0 Cr B 1 • Inverse RCT(Deversible Component Transform). Le equazini che descrivono la trasformazione d YUV a RGB sono: U + V r R = Vr + Yr − r 4 U + V r G = Yr − r 4 U + V r B = U r + Yr − r 4 pag. 34 CAPITOLO 3 JAVA, J2ME E PJAVA 3.1 LINGUAGGIO JAVA 3.1.1 INTRODUZIONE AL LINGUAGGIO Java ha avuto origine come parte di un progetto di ricerca volto a sviluppare programmi per dispositivi elettronici, lo scopo era quello di un software di sviluppo piccolo, riusabile, portabile e distribuito. Il risultato è stato un linguaggio che si è dimostrato ideale per lo sviluppo di applicazioni sicure e distribuite. Adatto allo sviluppo di applicazioni in diversi ambienti, dal network al World Wide Web ed in questi ultimi anni si è avvicinato anche al mondo della telefonia mobile con notevoli risultati. La crescita di Internet e del World Wide Web sta portando ad un modo completamente nuovo di distribuire il software. In un ambiente eterogeneo e multipiattaforme come il network, lo schema tradizionale di rilascio, correzione e aggiornamento di un software non si sta dimostrando molto valido. Java con le sue caratteristiche di essere ad architettura neutrale, portabile, adattabile dinamicamente, si adatta a questo nuovo scenario meglio di altri linguaggi. Rivoluzione poratata da Java Java ha un ambiente di sviluppo orientato agli oggetti molto efficiente. Progettato per creare software molto affidabile, fornisce ampi controlli in fase di compilazione, seguito da ulteriori controlli in fase di esecuzione. Il linguaggio pag. 35 rappresenta una guida ai programmatori verso l’abitudine a produrre programmi affidabili: gestione automatica della memoria, nessun puntatore da gestire. Java è nato per operare in ambiente distribuito, questo significa che la sicurezza è di grande importanza. E’ stata dedicata particolare attenzione alla sicurezza sia a livello di linguaggio sia a livello di sistema run-time. Java permette di costruire applicazioni che possono difficilmente essere invase da altre applicazioni. Java supporta applicazioni che devono essere capaci di eseguirsi su architetture hardware diverse e sistemi operativi diversi. Per risolvere questa diversificazione, il compilatore Java genera i bytecode, un formato di codice intermedio tra il codice ad alto livello e quello macchina, progettati per essere efficientemente trasportati su piattaforme hardware e software diverse. Per quanto riguarda la portabilità, Java precisa e specifica la grandezza dei tipi di dati e il comportamento degli operatori aritmetici, i programmi sono gli stessi su ogni piattaforma, non ci sono incompatibilità di tipi di dati attraverso diverse architetture hardware e software. L’ambiente architettura neutrale e linguaggio portabile e conosciuto come Java Virtual Machine(JVM), esse sono le specifiche di una macchina astratta per la quale il compilatore Java genera il codice. Le prestazioni sono sempre da considerare: Java ottiene ottime prestazioni adottando uno schema attraverso il quale l’interprete può eseguire i bytecode alla massima velocità senza necessariamente controllare l’ambiente run-time. Una applicazione automatica, garbage collector, eseguita in background assicura con elevata probabilità che la memoria richiesta sia disponibile. Applicazioni particolari o applicazioni che richiedono grossa potenza di calcolo possono essere scritte in codice nativo della macchina ed interfacciate con l’ambiente Java. L’interprete Java può eseguire i bytecode Java su ogni macchina alla quale l’interprete e il sistema run-time è stato portato. Le moderne applicazioni di rete come i browser del World Wide Web, hanno bisogno di fare più cose contemporaneamente, in Java si utilizza il multithreading che dà i mezzi per costruire applicazioni con più attività concorrenti, infatti questo è detto anche programmazione concorrente. La libreria del linguaggio contiene la classe Thread pag. 36 ed il sistema run-time fornisce i monitor e condizioni di lock. A livello di libreria, in più, il sistema Java è stato scritto per essere sicuro nella gestione del multithreading: delle funzionalità provvedono che le librerie siano disponibili senza conflitti tra thread concorrenti in esecuzione. La compilazione avviene con controlli severi; le classi sono collegate solo quando occorre, nuovi moduli di codice possono essere collegati a richiesta da una varietà di sorgenti, anche da sorgenti disponibili attraverso la rete ciò permette l’aggiornamento trasparente di applicazioni. Il sistema base di Java La società che ha sviluppato il linguaggio, Sun Microsystems, ha incluso nel Java Developer’s Kit (JDK) una libreria di classi e metodi utili per creare applicazioni multi-piattaforma, questa libreria è organizzata in package di cui i più importanti sono: • java.lang: Una collezione di tipi base che sono sempre importate in ogni unità compilata. Qui si trova la dichiarazione di Object (la radice della gerarchia delle classi) e Class, oltre ai thread, eccezioni, include i tipi di dati primitivi e una varietà di classi fondamentali. • java.io: Package che fornisce le classi per gestire l’input e l’output streams per leggere e scrivere dati da file, stringhe, e qualunque altra sorgente (es. network). • java.util: Package che contiene un insieme di utility, include una generica struttura dati, manipolazione delle stringhe, proprietà di sistema, generatore di numeri casuali ed altro. • java.net: Package che fornisce le classi per il supporto in rete, sockets TCP, sockets UDP, indirizzo IP, URL. pag. 37 • java.awt:Package che fornisce un integrato set di classi per il controllo di interfacce grafiche utente come windows, dialog boxes, buttons, checkboxes, menus. • java.applet:Package che permette la creazione applets attraverso la classe Applet. 3.1.2 UN LINGUAGGIO SEMPLICE Java rappresenta la creazione di un linguaggio sufficientemente comprensivo, tale da indirizzare una ampia varietà di sviluppatori di applicazioni software verso di esso. Mentre mantiene un aspetto simile al C/C++, Java acquista in semplicità dalla rimozione sistematica di alcuni particolari di quest’ultimo; Java segue il C/C++ fino ad un certo punto, ciò porta i benefici di essere familiare a molti programmatori, ma si diversifica per altre cose, al di la dei tipi di dati primitivi discussi qui di seguito, tutto è un oggetto. Anche i tipi di dati primitivi possono essere incapsulati in oggetti. Ci sono solo tre gruppi di tipi primitivi di dati: numerico, booleano e array. I tipi di dati numerico intero sono byte a 8-bit, short a 16-bit, int a 32-bit e long a 64-bit. In Java non c’è lo specificatore unsigned per i tipi di dato intero. I tipi di dati numerico reale sono float a 32-bit e double a 64-bit. I valori letterali in virgola mobile sono considerati double per default; bisogna eseguire esplicitamente il “cast” a float se si desidera assegnarli a variabili float. Il tipo di dato carattere di Java si sposta dai linguaggio tradizionali. Il tipo char di Java è definito come carattere Unicode a 16-bit. Il set di caratteri Unicode sono valori a 16-bit senza segno definiti nell’intervallo 0 - 65535, adottando lo standard Unicode per questo tipo di dati le applicazioni Java sono disponibili per supportare caratteri internazionali. pag. 38 Java ha il tipo di dato boolean come tipo primitivo, una variabile Java boolean può assumere i valori true o false, il tipo boolean e distinto dagli altri tipi primitivi e non può essere convertito in altro tipo numerico. Gli array sono implementati in modo particolare in Java, nonostante essi possono essere allocati come degli oggetti e sono dotati di variabili istanza non c’è una classe Array. La classe Array è costruita automaticamente durante l’esecuzione dei programmi. Inoltre, per ogni tipo primitivo e per ogni oggetto esiste una sottoclasse implicita Array che ne rappresenta gli array. Un oggetto array si comporta come se appartenesse a una classe, anche se questa non esiste propriamente. Nel linguaggio Java le stringhe sono degli oggetti. Ci sono due tipi di oggetti stringa: la classe String per oggetti stringa di sola lettura (immutabili), la classe StringBuffer per oggetti stringa che possono essere manipolati. Anche se in Java le stringhe sono degli oggetti veri e propri, il linguaggio prevede una sintassi conveniente a trattare le stringhe come se fossero dei tipi primitivi, così quando un letterale stringa compare in un programma, Java crea automaticamente un’istanza della classe String con il valore indicato, inoltre sono comprese nel linguaggio alcune facilitazioni sintattiche per aiutare i programmatori a fare comuni operazioni sulle stringhe, concatenazioni di oggetti String, conversione da altri tipi possono essere fatte esplicitamente, ma Java effettua gran parte di queste operazioni automaticamente per permettere una maggiore chiarezza nella scrittura dei programmi. pag. 39 Gestione della memoria Il linguaggio Java rimuove completamente il compito di gestione della memoria da parte del programmatore. Un’automatica garbage collection fa parte integrante del sistema run-time di Java, mentre c’è un operatore “new” per allocare un oggetto in memoria, non c’è nessun operatore esplicito per "liberare" la memoria allocata. Una volta allocato un oggetto, il sistema run-time mantiene il controllo dello stato dell’oggetto recuperando la memoria quando non è più in uso e rendendola libera per usi futuri. Il modello della gestione della memoria di Java si basa su oggetti e riferimenti ad essi, Java non ha puntatori, tutti i riferimenti alla memoria allocata, in pratica tutti i riferimenti a oggetti, sono "memorizzati" dal sistema Java, quando un oggetto non ha più riferimenti questa è candidato per la garbage collection. Il garbage collector di Java ottiene alte prestazioni sfruttando il comportamento degli utenti che interagendo con le applicazioni software hanno molte pause naturali, il sistema run-time di Java sfrutta queste pause per eseguire il garbage collector in un thread a bassa priorità, il garbage collector riunisce e compatta la memoria non più in uso, aumentando la probabilità che adeguate risorse di memoria siano disponibile quando occorrono. Infine Java è semplice grazie anche alle sue dimensioni contenute; l’interprete Java occupa circa 40 kByte di memoria RAM, escludendo il supporto per il multithreading e le librerie standard, che richiedono altri 175 kByte. La semplicità di Java consente di risparmiare tempo nella codifica e nella ricerca degli errori, dedicandosi maggiormente all’analisi dei problemi e delle soluzioni e alla soddisfazione del cliente. Tale semplicità, inoltre, consente ai programmi Java di funzionare su computer che offrono modelli di memoria limitata, la quantità di memoria dell’interprete Java con le sue librerie standard risulta insignificante se confrontata con altri ambienti e linguaggi di programmazione. In seguito vedremo la versione pag. 40 dell’interprete progettata per i telefoni cellulari ed esamineremo le diversità tra le versioni attualmente in uso. . 3.1.3 ARCHITETTURA NEUTRALE Con la fenomenale crescita del network, le applicazioni, devono poter migrare facilmente su un’ampia varietà di architetture hardware e di sistemi operativi, e devono operare con diverse interfacce grafiche user. Le applicazioni devono poter essere eseguite da ovunque sul network senza precedente conoscenza della piattaforma hardware e software, se i programmatori sviluppano software per una specifica piattaforma, la distribuzione di codice binario attraverso il network che è eterogeneo diventa impossibile. La soluzione che il sistema Java adotta per risolvere il problema della distribuzione del codice è un formato di codice binario indipendente dall’architettura hardware e software, il formato di questo codice binario è ad architettura neutrale. Se il sistema run-time di Java viene reso disponibile per determinate (o tutte) piattaforme hardware e software, le applicazioni scritte in Java possono essere eseguite senza il bisogno di eseguire il "porting" su quelle piattaforme. pag. 41 Bytecode Il compilatore Java non genera codice macchina nel senso di istruzioni hardware native, piuttosto genera i bytecode: un codice macchina indipendente, per un’ipotetica macchina che è implementata dal sistema run-time di Java (interprete). I bytecode Java sono stati progettati per essere facilmente interpretati su ogni macchina e per poter essere tradotti dinamicamente in codice macchina nativo. L’approccio ad architettura neutrale si dimostra utile non solo nelle applicazioni di rete, ma anche nella distribuzione del software su singoli sistemi soprattutto oggigiorno dove il mercato dei computer si diversifica per architetture hardware e software. Usando Java, con l’Abstrach Windows Toolkit, la stessa versione di applicazione può eseguirsi su tutte le piattaforme per le quali e stato reso disponibile l’interprete Java ed avrà l’aspetto interfaccia grafica tipico di quella piattaforma. Portabilità Il fatto che Java sia neutro rispetto all’architettura contribuisce notevolmente alla portabilità del linguaggio; ma c’è di più, esso specifica la dimensione di tutti i tipi di dati primitivi, per cui il comportamento di questi sarà lo stesso su tutte le piattaforme: byte 8 bit, complemento a due short 16 bit, complemento a due int 32 bi, complemento a due long 64 bit, complemento a due float 32 bit, standard IEEE 754 per i numeri in virgola mobile double 64 bit, standard IEEE 754 per i numeri in virgola mobile pag. 42 char 16 bit, carattere Unicode Queste scelte sono state fatte tenendo presente le architetture dei microprocessori moderni, che essenzialmente condividono queste caratteristiche. Lo stesso ambiente Java è facilmente portabile su nuove architetture e sistemi operativi, in compilatore Java è scritto anch’esso in linguaggio Java, mentre il sistema run-time di Java è scritto in ANSI C con un chiaro riguardo alla portabilità nel senso che non ci sono implementazioni dipendenti. Robustezza Java è stato progettato per sviluppare software che deve essere robusto, affidabile e sicuro. Il compilatore Java effettua particolari e severi controlli in fase di compilazione, in modo che ogni errore possa essere individuato prima che il programma sia eseguito, in Java tutte le dichiarazioni di tipo devono essere esplicite e non supporta lo stile delle dichiarazioni implicite. Il linker di Java ripete tutte le operazioni di controllo di tipo per evitare incongruenze di interfaccia o di metodi. Dinamicità La nozione di una fase separata di "link" dopo la compilazione è assente nell’ambiente Java. Il "linking", che è il processo di caricamento di una nuova classe, è un processo incrementale e leggero. La natura interpretata e portabile del linguaggio Java produce un sistema altamente dinamico ed estensibile dinamicamente. Le classi sono collegate solo quando richiesto e possono provenire dal file system locale così come dalla rete, inoltre il codice viene verificato prima di essere passato all’interprete per l’esecuzione. E’ possibile aggiungere nuovi metodi e variabili di istanza ad una classe, senza dover ricompilare l’intera applicazione. pag. 43 Il compilatore Java non compila utilizzando riferimenti a valori numerici, esso passa riferimenti simbolici al verificatore di bytecode e all’interprete. L’interprete Java esegue la risoluzione dei nomi quando la classe viene effettivamente collegata, quando il nome è risolto, il riferimento viene scritto come un offset numerico permettendo all’interprete Java di procedere velocemente. La posizione di un oggetto in memoria non è stabilita dal compilatore ma è definita dall’interprete in fase di esecuzione. Nuove variabili istanze e nuovi metodi possono essere aggiunti a una classe senza il bisogno di ricompilare l’intera applicazione. Rappresentazione Run-time Una classe Java possiede una rappresentazione di run-time che consente ai programmatori di eseguire la ricerca sul tipo di una classe e di linkare dinamicamente la classe in base al risultato della ricerca. Tali rappresentazioni run-time consentono, inoltre, di controllare i cast della struttura di dati durante la compilazione così come durante l’esecuzione, fornendo così un ulteriore controllo sugli errori. 3.1.4 SICUREZZA IN JAVA L’ambiente Java fa la supposizione che nulla sia di fiducia, così il compilatore e il sistema run-time implementano dei strati di difesa contro il codice che può essere potenzialmente pericoloso. Uno dei primi livelli di difesa del compilatore Java è il modello di allocazione della memoria. Prima di tutto, le decisioni in riguardo all’allocazione della pag. 44 memoria non sono prese dal compilatore del linguaggio Java, piuttosto sono rinviate al sistema run-time che è diverso a secondo della piattaforma hardware e software su cui il sistema Java è in esecuzione. Secondariamente, Java non ha più il concetto esplicito di puntatore, il codice Java compilato ha dei riferimenti alla memoria simbolici, riferimenti che sono risolti in indirizzi di memoria reali in fase di esecuzione, dall’interprete Java. I programmatori Java non devono più preoccuparsi di eventuali riferimenti fuori dalla memoria, anche perché l’allocazione della memoria e il modello dei riferimenti è completamente opaco al programmatore ed è controllato completamente da sistema run-time di Java. Processo verifica Bytecode Anche se il compilatore Java assicura che il codice Java non viola le regole di sicurezza, quando un’ applicazione importa dei pezzi di codice Java da qualunque parte, essa non sa se il codice segue le regole di sicurezza imposte da Java: il codice può non essere stato prodotto da un compilatore Java conosciuto. Il sistema run-time di Java parte dal presupposto di non avere fiducia nel codice e sottopone i bytecode a una serie di processi di verifica. I test vanno dalla semplice verifica che il formato del codice sia corretto, a tutta una serie di controlli che verificano se:non ci sono puntatori "falsi", on sono state violate le restrizioni di accesso. Più parti compongono il sistema run-time di Java, questo per garantire che il codice eseguito sia sicuro. Nella figura 11 è illustrato il flusso dei dati e i controlli del sistema Java, controlli fatti attraverso il compilatore Java, il caricatore di bytecode, il verificatore di bytecode e quindi l’interprete Java. pag. 45 Figura 11: Processo verifica bytecode Il verificatore dei bytecode esamina un bytecode per volta, costruendo informazioni riguardanti lo stato dei tipi e verifica che il tipo di ciascun parametro, argomento e risultato sia corretto. Sia il caricatore di bytecode che il verificatore non fanno nessuna assunzione sulla sorgente primaria del codice che può essere un sistema locale o il network; il verificatore assicura che il codice passato all’interprete Java sia in buono stato e può essere eseguito senza pericoli di interruzione. Quando il codice viene importato non è permessa l’esecuzione finché non ha superato tutti i testi del verificatore. Una volta che il verificatore ha terminato un importante numero di proprietà sono conosciute: pag. 46 • Non ci sono operandi nello stack in overflow o underflow. • I tipi e i parametri di tutte le istruzioni dei bytecode sono noti e sono corretti. • Accessi ai campi di un oggetto sono legali. Questi controlli che possono sembrare tormentosi, servono a fare in modo che quando il verificatore ha terminato il suo lavoro, l’interprete Java, sapendo che il codice è sicuro, possa procedere velocemente senza effettuare nessun ulteriore controllo. Il verificatore, quindi, rappresenta la parte cruciale del sistema di sicurezza di Java e il suo buon funzionamento dipende dalla corretta implementazione del sistema di esecuzione. Inizialmente solo la Sun Microsystems ha prodotto i sistemi di esecuzione di Java e questi sono sicuri. Successivamente altre società hanno stretto accordi con la Sun Microsystems è produrranno una loro versione dell’ambiente di esecuzione di Java. In futuro la Sun Microsystems ha intenzione di implementare tecniche di convalida per i sistemi di esecuzione, compilatori e così via, in modo da verificare la sicurezza e il corretto funzionamento. pag. 47 3.2 J2ME (JAVA 2 MICROEDITION) 3.2.1 INTRODUZIONE A distanza di sei anni dalla sua nascita, Java, con la Micro Edition, si è avvicinato al settore embedded, con la consapevolezza che la situazione di questo mercato è preparata ad un suo utilizzo. Sun Microsystem, insieme ad alcuni suoi partner e sviluppatori indipendenti ha creato nel tempo tutta una serie di moduli e API che estendono le caratteristiche standard di Java permettendo l'utilizzo del linguaggio anche nel settore embedded (PersonalJava, JavaPhone, JavaTV, Embedded Java). Non si trattava però di un progetto ben delineato e definito, ma di un insieme di moduli, tra loro poco correlati, studiati per rispondere a ben determinate esigenze. J2ME, al contrario, non è indirizzata a singole piattaforme ma, in generale, al settore embedded tutto, la sua architettura e le sue specifiche sono ben delineate ed è una versione standard di Java. Naturalmente il fatto che J2ME sia dedicata a tutto il mercato embedded non vuol certo dire che possa essere utilizzata con qualunque tipo di apparecchiatura e in qualunque contesto. La sua struttura modulare ed espandibile permette però di coprire una vasta gamma di piattaforme (apparati) embedded: dai PDA ai cellulari, dai navigatori satellitari a TV digitali con i vantaggi che il linguaggio Java può portare. Uno degli aspetti più interessanti e apprezzati di Java è sempre stato il suo alto grado di portabilità e il suo livello di astrazione dall'hardware, che J2ME mantiene ed estende ad un settore, quello dell'elettronica di consumo, davvero ricco di piattaforme differenti. pag. 48 3.2.2 IL SETTORE EMBEDDED La diffusione di palmari, agende elettroniche, cellulari, smartphone rappresenta ormai una realtà di mercato consolidata. Questi dispositivi vengono collocati in quello che viene definito mercato o settore dell'elettronica di consumo, vale a dire in tutti quegli apparati che ci circondando nella vita quotidiana. Si parla anche di mondo "embedded": non esiste una definizione precisa del termine, ma in genere indica i sistemi elettronici a microprocessore con un hardware dedicato, progettati per una determinata applicazione. Il significato del termine è quindi molto generico e riguarda dispositivi diversi, che si differenziano per complessità dell'hardware e del software, dimensioni e per le funzioni per le quali sono stati realizzati. L'idea di disporre di tecnologie integrate con l'ambiente che ci circonda (in inglese per l'appunto embedded) non è un fenomeno recente ma, è un processo iniziato parecchi anni orsono e interessa sempre più gli oggetti di uso comune. Certamente questo progressivo aumento nell' uso di apparati elettronici, e in particolare di dispositivi digitali, non riguarda solo l'elettronica di consumo e l'ambito domestico, ma è un processo che coinvolge gran parte della realtà che ci circonda. L'elettronica di consumo è forse il settore dove la tecnologia e il suo reale potenziale sono più visibili e alla portata di tutti. Dal punto di vista della programmazione questa eterogeneità del settore embedded fa nascere l'esigenza di dover disporre di strumenti (API, tool di sviluppo) dedicati per ogni singola piattaforma hardware. Sembra difficile quindi poter mantenere quel proverbiale "Write once, run anywhere" che da sempre accompagna il linguaggio Java. Se consideriamo il mondo della telefonia mobile e pensiamo di sviluppare un applicativo per questi apparati, si dovrebbe scrivere un applicazione per ogni marca di telfonino e magari per ogni versione della stessa marca. Di fronte ad un simile scenario, è evidente come un'unica soluzione Java, intesa coma una Virtual Machine e un insieme di API, non sarebbe stata una scelta ottimale: avrebbe comportato da un lato l'esclusione di tutti quei dispositivi con requisiti inferiori a quelli minimi richiesti, dall'altro non avrebbe permesso il pieno sfruttamento degli pag. 49 apparati più dotati. Occorreva quindi un'architettura scalabile e modulare che permettesse di coprire il più possibile la gamma dei dispositivi del settore sfruttandone le differenti caratteristiche: J2ME . 3.2.3 L’ARCHITETTURA J2ME Queste caratteristiche di modularità e scalabilità vengono garantite in J2ME attraverso i concetti di Configurazioni e Profili: J2ME PROFILE J2ME CONFIGURATION Virtual Machine Host Operating System Figura 12: Architettura J2ME pag. 50 • Configurazioni: definiscono le caratteristiche di base del linguaggio Java e della Virtual Machine per un insieme di dispositivi, forniscono un insieme di librerie di base comuni per più categorie di apparati ( Cellulari, PDA, smartphone); • Profili: si basano su una configurazione, ne estendono le funzionalità di base, fornendo API addizionali e tools di sviluppo per una particolare categoria di dispositivi (es PDA). Le applicazioni scritte per un dato profilo sono portabili ed eseguibili su qualunque dispositivo che lo supporti; Le configurazioni, quindi, sono dirette ad un insieme di apparati e non ad una specifica categoria, mentre i profili sono qualcosa di più "legato" al dispositivo stesso. Prendendo come riferimento l'intero settore embedded si può dire che, mentre le configurazioni eseguono un "raggruppamento" dei dispositivi in senso orizzontale, i profili lo fanno in senso verticale. Figura 13: Configurazioni e profili di J2ME Va detto che sebbene i profili siano diretti ad una particolare segmento di mercato il loro utilizzo è in realtà flessibile: un dispositivo può supportare più profili, spetta quindi al programmatore la scelta di utilizzare quello più idoneo e pag. 51 conveniente per la realizzazione di una data applicazione. Talvolta infatti un profilo, seppure dedicato ad una categoria di dispositivi differente può risultare particolarmente adatto alla realizzazione di un applicativo (utilizzo "orientato all'applicazione"), quando è invece necessario utilizzare le caratteristiche proprie dei vari dispositivi è necessario ricorrere al profilo dedicato (utilizzo "orientato al dispositivo"). Occorre aggiungere che, per le loro caratteristiche intrinseche alcuni profili risultano più "device-specific" (es. PDA Profile, MID Profile) di altri (es. Foundation Profile, PersonalProfile, RMI Profile) che possiamo definire invece "application-specific". Configurazioni Abbiamo detto che le configurazioni raggruppano i dispositivi "orizzontalmente" cioè, stabiliscono le caratteristiche della macchina virtuale e forniscono librerie di base per un insieme di dispositivi con risorse hardware simili. Le attuali configurazioni previste da J2ME suddividono il variegato settore embedded in due grandi segmenti. In base alle caratteristiche dei vari apparati possiamo distinguere: • dispositivi a uso esclusivamente personale, mobili, che necessitano di connessioni a reti in modo non continuo. In questa categoria rientrano i telefoni cellulari, i PDA, cioè tutti quei dispositivi caratterizzate da un'interfaccia utente molto semplice, ridotte disponibilità di memoria (tipicamente 128 Kbyte - 2 Mbyte), limitate capacità di elaborazione, basso consumo di energia e connessioni a reti di telecomunicazione di tipo wireless con modesta larghezza di banda. • dispositivi condivisi, fissi, che necessitano di connessioni a reti in modo continuo. Tipici esempi di apparecchiature che rientrano in questa categoria sono Web TV, telefoni capaci di accedere ad Internet, ricevitori televisivi satellitari, communicator, sistemi di navigazione per auto. Tutti questi dispositivi, essendo installati in modo fisso, non risentono dei tipici pag. 52 problemi dei dispositivi portatili (o addirittura tascabili) come peso, dimensioni e consumi di energia e possono, quindi, contare su dotazioni di memoria anche notevoli (da 2 Mbyte a 32 MByte), processori veloci e performanti e connessioni continue a reti con ampia larghezza di banda. Naturalmente, il confine che separa queste due categorie non può essere netto. Tuttavia questa suddivisione è importante soprattutto per tenere in considerazione alcune caratteristiche hardware fondamentali del dispositivo sul quale il software dovrà funzionare quali: • la potenza di calcolo e la disponibilità di memoria; • la presenza o meno di un display e le sue caratteristiche; • le tipologie di connessione a reti di TLC; E' proprio sulla base di questi parametri che J2ME ha implementato le sue due attuali configurazioni, ossia: • CLDC (Connected Limited Device Configuration) - è dedicata a dispositivi con memoria minima di circa 128 Kbyte (RAM e ROM), processori (RISC/CISC) a 16-32 bit, basso consumo e funzionamento tipicamente a batteria, connessioni non continue a reti eterogenee, sovente di tipo wireless con larghezza di banda limitata (spesso uguale o inferiore a 9600 bps). La Virtual Machine prevista per questa configurazione è la KVM (Kilobyte Virtual Machine), ossia una macchina virtuale Java studiata per apparecchi con memoria totale dell'ordine dei Kbyte (il core della KVM occupa da 32 a 80 Kbyte). pag. 53 • CDC (Connected Device Configuration) - è studiata per dispositivi hardware che dispongono di almeno 2 MByte totali di memoria (RAM e ROM), processori a 32 bit, di connessioni persistenti a reti TLC con ampia larghezza di banda e interfacce grafiche anche molto sofisticate. La virtual Machine utilizzata per questa configurazione è la CVM (C Virtual Machine). La CLDC può essere considerata come una versione "alleggerita" della CDC. Abbiamo visto che le configurazioni forniscono un set minimo di librerie di base: alcune di queste costituiscono un sottoinsieme di package della Standard Edition altri, sono state realizzate appositamente per la Micro Edition (figura 14). Figura 14: J2ME e Standard Edition Tutto ciò è molto importante: ci dice che in realtà J2ME altro non è che Java "ridisegnato" per piattaforme "non convenzionali", con un set minimo di API ridotto e con alcune Java Extension dedicate, adatto a funzionare in apparati tra loro molto differenti e anche con risorse limitate. Questo garantisce la compatibilità "verso l'alto", con le versioni "maggiori". Il bytecode della J2ME, infatti, resta perfettamente compatibile con quello delle altre versioni. Nonostante alcune limitazioni e differenze rispetto alla Standard Edition, dovute al target di pag. 54 dispositivi a cui è diretta, J2ME mantiene inalterate tutte le caratteristiche principali del linguaggio, non si tratta quindi di una versione "speciale" per dispositivi particolari ma di Java "puro". Profili Le funzionalità di base garantite dalle configurazioni però non bastano per realizzare un applicativo. Prendiamo ad esempio un cellulare. La configurazione dedicata è la CLDC: questa ci mette a disposizione un insieme di librerie di base che permettono di "utilizzare Java" (java.lang), di gestire gli stream di I/O (java.io), ci fornisce tutta una serie di classi di utilità (java.util) e ci da la possibilità di comunicare(javax.microedition.io). Manca però l'iterazione con l'utente, le interfacce grafiche, il salvataggio dei dati. A tutto questo è chiamato a rispondere il profilo. Esso si occupa di gestire tutte quelle problematiche più strettamente collegate al tipo di apparato, come la gestione dei dispositivi di Input/Output, il salvataggio persistente dei dati nelle memorie non volatili, la realizzazione di UI e fornisce tools di sviluppo per una determinata categoria di dispositivi (in questo caso i cellulari). I profili si suddividono in base alla configurazione per la quale sono stati realizzati. Quelli attualmente previsti (non tutti attualmente disponibili) sono: pag. 55 per la configurazione CDC: • Foundation Profile: profilo per dispositivi che richiedono una completa implementazione della Java Virtual Machine e includono quindi tutte le API della J2 Standard Edition; • RMI Profile: questo tipo di profilo, come indicato dal nome, include un sottoinsieme di API RMI (Remote Method Invocation) della J2SE, in modo da garantire un certo grado di interoperatività con altre virtual machine sia J2ME sia J2SE; • Personal Profile: è l'estensione dell'ambiente PersonalJava di Sun e garantisce una piena compatibilità con le applicazione sviluppate con le versioni 1.1.X e 1.2.X di PersonalJava Application Enviroment. per configurazione CLDC: • PDAP: il Personal Digital Assistant Profile è il profilo dedicato ai dispositivi palmari. Tali apparati devono avere almeno 512 Kbyte di memoria (RAM + ROM) e un display con risoluzione totale di almeno 20.000 pixel; • MIDP: il MIDP (Mobile Information Device Profile) è un profilo dedicato a dispositivi wireless come telefoni cellulari, smartphone e pager; Un vantaggio derivante dalla scalabilità e modularità di J2ME, è la possibilità di implementare nuovi profili senza modificare le configurazioni di base esistenti con una notevole riduzione dei tempi di sviluppo e dei costi di realizzazione. pag. 56 3.3 PJAVA (PERSONAL JAVA) Personal Java è un sottoinsieme della piattaforma Java dedicato all’utilizzo di dispositivi mobili come PDA e periferiche elettroniche di consumo come set-topboxes e smart phones. È stato progettato per essere compatibile verso l’alto con la piattaforma Java, ma è in grado di operare in ambienti con risorse limitate.Il linguaggio comprende una JVM (PersonalJava Virtual Machine), AWT (Abstract Windows Toolkit), la maggior parte degli applet, JavaBeans, le funzionalità di gestione dell’I/O, il supporto di rete e vari package di utilità propri del linguaggio Java. Possono essere aggiunte ulteriori API (PersonalJava Application Programming Interface) opzionali per esigenze specifiche come il package java.rmi. Essendo il set di API ridotto è possibile attraverso il JavaCheck [Sun], un tool messo a disposizione degli sviluppatori, eseguire un’analisi statica del codice Java per determinare la compatibilità con la piattaforma Personal Java. Per ulteriori informazioni sulle specifiche e le classi compatibili è possibile consultare il sito della Sun. E’ importante evidenziare che in futuro non sentiremo più parlare di Personal Java ma di Personal Profile, che come accennato nel precedente paragrafo è un profilo della configurazione CDC. pag. 57 CAPITOLO 4 AMBIENTE DI SVILUPPO 4.1 INTRODUZIONE Nel corso di questo capitolo descriverò i software utilizzati e la loro configurazione; spiegherò in modo dettagliato il processo di sviluppo di applicazioni Java per dispositivi che utilizzano il sistema operativo Symbian. 4.2 SOFTWARE UTILIZZATI 4.2.1 ECLIPSE IDE Per la compilazione del codice sorgente in bytecode ho utilizzato Eclipse IDE (Integrated Development Environment), che è un software open source fruibile sul sito eclipse.org. Il progetto fu lanciato originariamente a Novembre del 2001, quando IBM donò 40 milioni di dollari di codice sorgente a Websphere Studio Workbench e formò Eclipse Consortium con l’intento di continuare lo sviluppo del tool. L’installazione è molto semplice, è indispensabile che sul calcolatore sia presente la Java virtual machine, disponibile sul sito della Sun. pag. 58 4.2.2 UIQ SDK PER SYMBIAN E’ un kit di sviluppo che consentre di creare applicazioni per dispositivi che utilizzano il sistema operativo Symbian. I linguaggi di programmazione supportati sono Java e C++. In particolare per questo lavoro di tesi ho utilizzato due tools del kit e precisamente: • AifBuilder: consente di generare i file di installazione • Epoc: il simulatore vero e proprio, permette di effettuare test e debugging delle applicazioni. Il software è fruibile sul sito www.symbian.com. Attualmente è disponibile solo la versione per sistemi operativi Windows. Il kit è stato da me utilizzato con le seguenti versioni: Win98, Win Xp, Win 2000. L’installazione è molto semplice, l’unica cosa necessaria ed indispensabile è il settaggio delle variabili d’ambiente, e precisamente: _epoc_drive_j = C:\Symbian|Uiq_70\epoc32\java epoctool = \Symbian\Uiq_70\ pag. 59 4.3 PROCESSO DI SVILUPPO DI APPLICAZIONI JAVA PER PIATTAFORME SYMBIAN 4.3.1 INTRODUZIONE Il processo di sviluppo di un’applicazione per piattaforme Symbian si può dividere in tre passi principali: • Sviluppo del codice Java e relativo test preliminare con un qualsiasi ide, Eclipse nel mio caso. • Creare i file per sviluppare l’ applicazione su interfacce Symbian usando il tool AIF Builder. • Comprimere tutti gli elementi dell’applicazione in un unico file finale. Il seguente diagramma di flusso descrive sinteticamente tutti i passi da eseguire per lo sviluppo, is seguito verrano analizzati in modo dettagliato. pag. 60 Sviluppo codice Java Creare un unico file JAR Sviluppo file supporto(es. suoni) Test dalla Dos-shell Sviluppo file installazione con Aifbuilder Creare il file di supporto *.pkg Test con il simulatore: epoc Con makesis costruire il file d’installazione Test del packge di installazione con epoc Rilascio Applicazione Figura 15: Diagramma di flusso del processo di sviluppo pag. 61 4.3.2 SVILUPPO DEL CODICE JAVA Lo sviluppo di un’applicazione Java per piattaforme Symbian inizia con la stesura del codice sorgente e relativa compilazione per ottenere il file *.class. In genere in un progetto otteniamo diversi file compilati che possono essere compressi in un unico file con estensione *.jar. A questo punto si può fare un primo test utilizzando il simulatore. In seguito farò riferimento ad un file di esempio che chiamerò “test” che per comodità sarà lo stesso nome della funzione “main”. Supporrò che il simulatore sia stato installato nel drive “C:\” del calcolatore. Seguono sinteticamente gli step da effettuare per il test: • Dalla seguente directory: C:\Symbian\UIQ_70\epoc32\java Creare una sottodirecotory (es.test), contenente il file “test.jar”. • Per la simulazione, da una dos-shell portarsi in C:\Symbian\UIQ_70\epoc32\release\winscw\udeb\ Digitare il comando: pjava_g –cp j:\test\test.jar test dove test e il nome della funzione “Main” contenuta in test.jar A questo punto si aprirà una finestra del simulatore e dal “Launcher” situato nel menù bar superiore si puo lanciare l’applicazione,eseguendo “Task List”: pag. 62 Figura 16: Task list del simulatore 4.3.3 SVILUPPO PER L’INTERFACCIA SYMBIAN Aif Builder (Application Tab) Per sviluppare i file di installazione si utilizza AIF Builder, per lanciarlo, da: C:\Symbian\UIQ_70\epoc32\tools\ digitare aifbuilder. Una volta aperto, dal menù sono disponibili le comuni opzioni come creare una nuovo progetto o aprirne uno esistente. Selezionando “new project” la prima cosa da fare e completare i campi in “Application” come segue: pag. 63 Figura 17: Aifbuilder Application tab Per default il linguaggio di programmazione selezionato è il C++. Scegliendo Java comparirà un ulteriore campo: “Java Command Line Text”. Adesso è possibile scrivere i seguenti dettagli: • “Application Name”, dovrebbe essere il nome data al file del programma, test in questo caso caso. • “Application Uid”, inidica l’identificativo dell’applicazione, possono essere richiesti direttamente a Symbian (inviando una e-mail al seguente indirizzo:[email protected]) se si intendono sviluppare applicazioni. • “Java Command Line Text”, è una versione semplificata del comando utilizzato per il test del programma da dos, “-cp test.jar test”. pag. 64 Aif Builder (UI Variants) Il passo successivo è completare i dettagli nel tab “UI Variants” come segue: Figura 18: Aifbuilder UI variants tab pag. 65 dove: • “mbm”: nel caso si vuole creare un icona associata al programma va indicato il percosro della locazione della stessa. • “location to generate file”: dobbiamo indicare la directory dove il programma genererà i file, per comodità conviene creare una sottodirectory in: C:\Symbian\UIQ_70\epoc32\winscw\c\system\Apps\ Il nome della sottodirectory è test; dovrà contenere il file test.jar • “temporary files”: indicare la directory dove il programma memorizzerà file temporanei. Aif Builder (Caption) Nell’ultimo tab “Caption” possiamo selezionare i linguaggi che la nostra applicazione supporta. Questo settaggio è opzionale. pag. 66 Generazione dei files d’installazione Una volta digitate tutte le informazioni necessarie, il bottone “Generate” in basso a destra creerà i file necessari. In questa versione del simulatore c’è un bug, infatti il bottone generate dà un errore, per ovviare bisogna procedere nel modo seguente: • Dal menu file salvare il progetto nella seguente directory: C:\Symbian\UIQ_70\epoc32\Wins\c\system\apps\test\ Il file salvato sarà test.aifb • Aprire una finestra dos e digitare: devices –setdefault@UIQ_70:com.symbian.UIQ • Sempre dalla shell dos digitare il comando : aifbuilder –generate C:\Symbian\UIQ_70\epoc32\Wins\c\system\apps\test\test.aifb • Nella directory su indicata verranno generati i seguenti file: 1. test.aif 2. test.app 3. test.txt In realtà per sviluppare i file si può scegliere una qualsiasi locazione, però quella su indicata e proprio la directory dove si installano i file di applicazione per il simulatore. Infatti adesso basterà lanciare il simulatore tramite il comando epoc, e vedremo che sul “desktop” del simulatore ci sarà un icona relativa alla nostra applicazione “test”. pag. 67 4.3.4 GENERAZIONE DEL FILE SYS Per creare il file finale, che verrà installato sul terminale c’è un ulteriore passo da effettuare. Bisogna creare un file di testo e nominarlo test.pkg che dovrà contenere le seguenti informazioni: #{"Minimal application"},(0x10008ACE),1,0,0 (0x101F617B), 2, 0, 0, {"UIQ20ProductID"} ;Application Files " test.app"-"!:\system\apps\test\test.app" " test.aif"-"!:\system\apps\test\test.aif" " test.jar"-"!:\system\apps\test\test.jar" " test.txt"-"!:\system\apps\test\test.txt" Dovrà essere memorizzato nella directory di installazione creata precedentemente: C:\Symbian\UIQ_70\epoc32\Wins\c\system\apps\test\. Tramite l’utility makesis sempre da dos, possiamo digitare il seguente comando dalla directory su indicata: “makesis test.pkg” verrà creato un file “test.SYS” che può essere installato sul telefonino. Ovviamente è consigliabile fare un test di quest’ultimo file anche sul simulatore. Per l’installazione, dal file manager del simulatore o del telefonino, basta cliccare sul file *.SIS e partirà l’installzione guidata dell’ applicazione. pag. 68 4.4 SONY ERICSSON P800 Il P800 è un terminale GSM triband 900/1800/1900 mhz, con compatibilità GPRS di classe 8, quindi 4+1, dotato del sistema operativo Symbian OS 7, di un processore ARM 920 da 200 Mhz e di 12 Mb di RAM. E’ stato utilizzato per effettuare i test finali del software sviluppato. Il P800 è uno smart phone ricchissimo di funzioni tipiche dei più avanzati palmari, è dotato di caratteristiche telefoniche tali da renderlo un piccolo ufficio mobile. Le dimensioni del P800 sono innegabilmente generose, dato il tipo di terminale e dato soprattutto l'enorme display, uno dei più grandi in commercio nel settore della telefonia mobile. Proprio lo schermo, che misura 208x320 pixels, molto simille a quelle di un Pocket Pc. Esternamente il P800 ha come simbolo assoluto l'enorme display. La presenza del flip attivo, che in ogni caso può essere rimosso, non nasconde la grandezza del medesimo, dove i 4096 colori appaiono molto chiari. Il fatto che lo stesso sia touch screen conferisce un ulteriore tocco di professionalità al terminale, dotandolo di uno strumento importantissimo per la fruizione dei suoi servizi e delle funzionalità. L'interazione del P800 con il PC è pressoché totale. La presenza del protocollo SyncML permette la sincronizzazione totale delle informazioni contenute nel computer sul P800 e vice versa. La compatibilità delle stesse è praticamente perfetta, anche in remoto. Da far notare che la professionalità del P800 viene aumentata dalla presenza di un pratico lettore prodotto da Stellent che consentirà di visionare i file Word, Excel, PowerPoint, Adobe Acrobat, e decine di altri file. Il programma consente la sola visualizzazione degli stessi e non la loro editazione. La connessione col PC o con eventuali PDA, può avvenire tramite IrDA, Bluetooth, cavo seriale o USB. Completano il quadro delle funzioni professionali il client e-mail POP3 ed IMAP4, la compatibilità J2ME, Personal Java e C++, il browser Internet che supporta il WAP 2.0, l'HTML 3.2, l'xHTMl,cHTML. pag. 69 CAPITOLO 5 REALIZZAZIONE DEL VISUALIZZATORE DI IMMAGINI JPEG 2000 Nel corso di questo capitolo descriverò l’analisi dei requisiti e delle scelte progettuali che hanno portato alla realizzazione del software: “JP2View”. 5.1 JPEG 2000 VIEWER 5.1.1 ANALISI REQUISITI Il software è stato progettato per l’utilizzo su dispositivi differenti: da quelli che utilizzano il sistema operativo Symbian, largamente diffuso nei telefoni cellulari, a sistemi operativi utilizzati dai palmari per esempio; ovviamente devono essere dotati di “Personal Virtual Machine”. Per la realizzazione ho fatto riferimento al package jj2000.j2k, che è fornito dagli stessi ideatori dello standard a supporto dello sviluppo; questo comporta due grandi vantaggi: • E’ open source, il che faciliterà la diffusione e la penetrazione dello standard JPEG 2000. • Supporta pienamente tutte le caratteristiche di JPEG 2000 perché è stato sviluppato con riferimento alla parte 1 dello standard. pag. 70 5.1.2 SCELTE PROGETTUALI La piattaforma di sviluppo utilizzata è Personal Java che rispetta pienamente le caratteristiche di portabilità e riusabilità. Questo è molto importante perché ci permette di soddisfare uno dei requisiti posti dall’analisi che prevede la possibilità di realizzare un applicativo multipiattaforma. Infatti Pjava tra gli ambienti di sviluppo multipiattaforma è sicuramente il più diffuso ed è anche quello che permette uan maggiore contiguità con l’ambiente Java per Personal Computer. Per l’interfaccia grafica la scelta è inevitabilmente ricaduta sul package AWT di Personal Java che ha permesso di elevare la fruibilità del software rendendolo fortemente user-friendly. Fondamentale è stato l’utilizzo del File Dialog che permette di interagire con il file sistem del dispositivo; questo permette l’accesso al file indipendentemente dalla zona di memoria utilizzata per memorizzarlo. Per quanto riguarda i pattern di programmazione e l’infrastruttura software, si è scelto di mantenere un’architettura il più possibile aperta e modulare, utilizzabile quindi per sviluppi futuri. 5.1.3 INTERFACCIA GRAFICA L’interfaccia grafica è stata sviluppata mediante l’utilizzo del package AWT di Pjava. In AWT tutti gli oggetti grafici di interfaccia sono chiamati componenti e derivano da una classe chiamata “Component”. I componenti si dividono in due categorie: i “Container” che fanno da contenitori per altri componenti; e i “Controlli”che forniscono tutte le funzionalità necessarie per realizzare l’interfaccia grafica. La classe “Main” del software ha un contenitore che deriva dalla classe Frame, a sua volta derivata da “Window”, che è il capostipite di tutti gli oggetti di tipo window, cioè quelli che fungono da “contenitori principali”, quindi non contenuti da altri oggetti. Una volta creato il Frame principale, ho pag. 71 definito le variabili della classe che contengono i riferimenti ai componenti che verranno visualizzati al suo interno ed in paricolare: un “MenuBar” che serve da contenitore dei menù; due “MenuItem” dotati di label che generano un “Action Event” quando sono selezionati. La classe ActionEvent serve per associare ad ogni item del menu un evento(es. apri file, chiudi). In particolare ci sono due menù, per scegliere il file e la risoluzione rispettivamente. Figura 19: JP2View (1) pag. 72 5.1.4 VISUALIZZATORE Per visualizzare l’immagine, è stata indispensabile una fase di studio e di debug per ottimizzare e rendere il package jj2000.j2k interpretabile dalla virtual machine del dispositivo. In particolare la classe CmdLnDecoder, è stata istanziata passando come parametro il Frame di riferimento della classe Main su descritta. Inoltre nella classe Decoder è stato opportunamente inserito un nuovo costruttore di classe contenente il riferimento al Frame prima citato e cioè: public Decoder (ParameterList pl, Frame mywin, boolean status){ this(pl,null) win=mywin; } Queste soluzioni sono state fondamentali per risolvere i problemi di compatibilità con la piattaforma Personal Java del P800, senza i quali non avremmo ottenuto una corretta visualizzazione dell’immagine. Il software sviluppato consente di visualizzare immagine compressa secondo lo standard JPEG 2000 a diverse risoluzioni. Se le dimensioni dell’ immagine sono più grandi di quelle dello schermo, è possibile effettuare lo scrolling tramite due barre laterali di scorrimento o semplicemente utilizzando la touch pen ,puntando l’immagine e scorrendola per visualizzare la regione di interesse. Questo perché il buffer video e maggiore delle dimensioni fisiche. pag. 73 Figura 20: JP2View (2) pag. 74 CAPITOLO 6 ARCHITETTURE SCALBILI PER LO STREAMING DI IMMAGINI JPEG 2000 In questo capitolo descriverò due possibili architetture utilizzabili per il trasporto su reti wireless, wired e mobili in genere, di immagini compresse secondo lo standard JPEG 2000. Particolar riguardo sarà dato a quella utilizante il protocollo JPIP (vedi paragrafo 6.1) che fra breve diventerà uno standard. Di quest’ ultimo proporrò l‘implementazione sviluppata per un client utilizzabile su piattaforme Personal Java. L’altra utilizza semplicemente il protocollo http (vedi paragrafo 6.2). 6.1 STREAMING SCALABILE UTILIZZANDO JPIP 6.1.1 INTRODUZIONE Jpip, JPEG 2000 Internet Protocol, è un protocollo di comunicazione interattivo per dati compressi secondo lo standard JPEG 2000. In realtà, è bene specificare che esistono due versioni di questo protocollo: “jpip-h” e “jpip-ht”. Il primo utilizza HTTP/1.1 (RFC 2616) per tutte le sue segnalazioni. Il secondo usa esattamente la stessa sintassi di richiesta e replica di “jpip-h”, anche in questo caso trasportata su http. Comunque, i dati relativi all’ immagine che devono essere consegnati al client sono trasportati su un canale TCP ausiliario(infati la t in “jpip-ht” sta per TCP). Questo ha il vantaggio di permettere al server di ricevere informazioni di segnalazione da parte del client che riguardano la ricezione dei dati. In questo modo il server può stimare meglio le condizioni della pag. 75 rete e di ottimizzare la trasmissione per sfruttare al meglio la banda a disposizione. 6.1.2 ARCHITETTURA PROPOSTA Il protocollo JPIP permette di riutilizzare tutte le infrastrutture di rete senza apportare alcuna modifica, si poggia sul protocollo HTTP 1.1. Con riferimento allo pila OSI, essendo un’ estensione di HTTP, è collocato sempre nello strato applicativo. L’architettura proposta è una struttura client-server, nei quali è implementato il protocollo JPIP. In seguito vedremo in dettaglio tutte le interazioni tra client e server analizzando la sintassi in modo particolare. Questo protocollo è stato da me sperimentato utilizzando una versione demo di un software (Kakadu Software) che comprende un client ed un server, ed è stato sviluppato da David Taubman. Per questo lavoro ho utilizzato il Kdu server. Infine nell’appendice B è disponibile il listato di una mia implementazione sperimentale per un client, sviluppata con tecnologia l’attuale carenza di documentazione, l’applicazione Personal Java. Data potrà essere ottimizzata quando Jpip diventerà standard ufficiale e sarà quindi disponibili documenti che lo descrivono in modo dettagliato. Quest’ ultimo modulo può essere facilmente integrato nel visualizzatore descritto nel capitolo precedente. 6.1.3 SINTASSI DI RICHIESTA Le comunicazioni tra client e server consistono in una o più richieste GET fatte dal client. Il server risponde da ogni richiesta con una replica, e opzionalmente con dati dell’ immagine in forma compressa. Le interrogazioni da parte del client pag. 76 sono conformi alle specifiche HTTP/1.1 per le richieste GET, e potrebbero essere fatte indirettamente da un proxy intermedio, il che potrebbe avere un diverso impatto sulla tempestività di ripsosta da parte del server. Queste interazioni tra client e server utilizzano come strato di trasporto sottostante TCP, una connessione persistente è preferibile, come specificato da HTTP/1.1. Non è lecito chiudere la connessione TCP tra diverse richieste di una stessa sessione, in tal caso questo provocherà un utilizzo delle risorse di rete inefficiente. Come specificato in “HTTP/1.1”, un client può inviare le richieste in sequenza, ciò significa che nuove richieste possono essere fatte prima che una replica ad una richiesta precedente è stata ricevuta. Tutte le richieste consistono in un singolo paragrafo http, la cui prima linea( la linea di richiesta) ha la forma seguente: GET<risorsa>?<query> http/1.1 Dove <risorsa> è una stringa utilizzata per indicare il servizio richiesto al server. Secondo lo standard http dovrebbe essere della forma seguente: http://<host name>/ Il resto della stringa <resource> potrebbe identificare in modo specifico l’immagine richiesta. La stringa <query> può contenere nessuna o più campi di richiesta separati dal carattere speciale “&” . Alcuni esempi di questi campi sono: pag. 77 R=<Rx,Ry> Rx e Ry sono due interi che rappresentano la risoluzione dell’immagine richiesta. Le immagini sono disponibili in un numero limitato di risoluzioni, il server selezionerà la più piccola che si avvicina a quella richiesta. O=<Px, Py> Utilizzato nel caso in cui si è interessati in una particolare regione d’interesse dell’immagine. Indica l’offset dall’ origine dell’immagine(angolo superiore sinistro). Px, Py sono due numeri interi. S=<Sx,Sy> Usato per specificare le dimensioni dell’immagine. Sx ed Sy sono due interi. B=<spec> Potrebbe essere usato per diminuire la quantità di dati che il server manda in risposta a questa richiesta. Se non è presente il server manda dati fino a quando tutti i dati sono stati inviati o è interrotto per l’arivo di una nuova richiesta. La stringa<spec> è un intero che indica i bytes. SID=<Session-ID> La stringa Session-ID è inviata dal server durante l’instaurazione della connessione e serve da identificativo per la stessa. 6.1.4 SINTASSI DI REPLICA Per ogni richiesta del client il server deve rispondere con una replica conforme alle specifiche HTTP/1.1. Tale replica deve iniziare con una linea detta “status” che può essere seguita da linee di “header” addizionali. La linea “status” ha la forma seguente: “HTTP/1.1 <status code> <explanation>” La stringa <explanation> è arbitraria, ma è un’ informazione utile all’utente per effetturare procedure di risoluzione di errori. La stringa <status code> deve essere conforme alle specifiche di HTTP/1.1; seguono alcuni esempi: pag. 78 • 200(OK) • 202(Accepted) • 400(Bad Request) • 404(Not Found) 6.1.5 JPIP HEADERS Il protocollo HTTP permette alle applicazione di definire propri hedear. Jpip definisce tre “request header” che che non sono ancora parte delle specifiche HTTP. Questi sono: • Max-Quality Header: potrebbe essere usato dal client per informare il server sul limite massimo della qualità dell’ immagine a cui è interessato. • Cache-Contents Header: potrebbe essere usato dal client per informare il server sugli elementi che ha nella sua cache. Può essere utile al client se ha ricevuto porzioni di un’immagine compressa durante una sessione precedente; piuttosto che specificare gli elementi dell’ immagine da richiedere, spesso è più efficiente che il client informi il server sugli elementi contenuti nella sua cache. • Cache-Needs Header: ha un ruolo complementare a Cache-Contents su descritto, e serve quindi per richiedere al server gli elementi che mancano. pag. 79 6.1.6 DESCRIZIONE DEL PROTOCOLLO INTERATTIVO JPIP-H Come già accennato questo protocollo è costruito interamente da un piccolo set delle specifiche HTTP/1.1. Tutte le richieste sono GET http, che seguono la sintassi riportata nel paragrafo 5.1.2. In risposta ad ogni richiesta, il server manda repliche nella forma di “http paragraph”. La struttura è l’interpretazione di quest’ultimi è descritta in 5.1.3. Segue ora la sequenza dell’ interazioni tra client (che indicherò con C) e server(S) a partire dall’instaurazione della connessione: (I) C—S GET http://10.50.5.27/image.jp2 HTTP/1.1\r\n Host: 10.50.5.27\r\n Accept: Session/jpip-h\r\n JPIP-Client-Location: in Italy??, [GMT+2:00] at 10.50.5.11\r\n\r\n (II) S—C HTTP/1.1 OK\r\n Server: Kakadu JPIP Server v3.4\r\n Cache-Control: no-cache\r\n Content-Type: session/jpip-h\r\n Content-Lenght : 127\r\n\r\n (III) S—C Session-Host : 10.50.5.27 :80\r\n Session-Resource: jpip-h\r\n Session-ID: 033C38BE48119CB1\r\n Image-ID : 78DE342FFF3423BEFA22232\r\n (IV) C—S GET http://10.50.5.27/jpip-h?SID=033C38BE48119CB1&R=0,0&B=2000 HTTP/1.1\r\n Host: 10.50.5.27\r\n\r\n (V) S—C HTTP/1.1 200 OK\r\n Cache-Control: no-cache\r\n Transfer-Encoding: chunked\r\n Content-Type: image/jpip-stream\r\n\r\n Data (4 bytes) (VI) S—C Data (201 bytes) (VII) C—S GET http://10.50.5.27/jpiph?SID=033C38BE48119CB1&R=640,480&O=0,0&S=640,457&B=2000 HTTP/1.1\r\n Host: 10.50.5.27\r\n\r\n (VIII) S—C HTTP/1.1 200 OK\r\n Cache-Control: no-cache\r\n Transfer-Encoding: chunked\r\n Content-Type: image/jpip-stream\r\n\r\n Data (5 bytes) pag. 80 Se non si specifica nessuna opzione(R,B,S) nella seconda richiesta, l’immagine verrà inviata interamente alla massima risoluzione e dimensione disponibile sul server. 6.1.7 DESCRIZIONE DEL PROTOCOLLO INTERATTIVO JPIP-HT Il protocollo “jpip-ht” è identico a “jpip-h”, la differenza sta nel fatto che il server manda i dati di ritorno su un canale TCP separato. Infatti le prime tre richieste sono identiche a quelle di jpip-h, ovviamente Jpip-ht comparirà nella stringa Session-Resource. Per completare la connesione con il server, il client deve aprire un’ulteriore connessione TCP specificando l’indirizzo dell’ host è un altro numero della porta sul quale resterà in ascolto per ricevere i dati. 6.1.8 COMUNICAZIONI NON INTERATTIVE Vediamo ora come è possibile accedere alle immagini JPEG 2000 in modo non interattivo, con un’unica richiesta da parte del client. Consideriamo la seguente richiesta http. GET http://10.50.5.27/image.jp2?R,512,640!O=0,0!S=512,640 HTTP/1.1\r\n Host: 10.50.5.27\r\n Accept: Session/jpip-stream La risposta del sever dovrebbe essere identica a quella fornita da jpip-h: HTTP/1.1 200 OK\r\n Resolution: 512,640 Size: 512,640 Transfer-Encoding: chuncked Content .Type: image/jpip-stream pag. 81 6.2 STREAMING SCALABILE UTILIZZANDO HTTP Nel corso di questo paragravo descriverò un’architettura scalabile [8] per lo streaming di immagini JPEG 2000, utilizzando il protocollo http. L’utilizzo di http per la trasmissione di immagini JPEG 2000 faciliterà il suo sviluppo, per la grande disponibilità ed accessibilità di web server che fanno uso di questo protocollo. Perciò le immagini JPEG2000 possono contenute da web server e possono essere trasmesse ad applicazioni client. La soluzione proposta è scalabile. I client possono visualizzare un’immagine a diverse risoluzioni e livelli di qualità. I motivi di utilizzare il protocollo HTTP per lo streaming di immagini JPEG2000 sono: • Facilità di sviluppo: attualmente le immagini JPEG e GIF sono memorizzate su web server. L’utilizzo di web server(HTTP) invece di un server proprietario faciliterà lo sviluppo. • HTTP supporta le connessioni affidabili. Alcune delle proprietà di questa architettura sono: l’utilizzo del protocollo http, e di una caratteristiche di “byte range” per gestire la scalabilità, l’utilizzo di un “index file” (il file JPEG 2000 viene indicizzato) in modo da permettere al client di richiedere al web server solo alcune parti del file. 6.2.1 ARCHITETTURA Il web server contiene una pagina web che include “thumbnail”(preview di piccole dimensioni) delle immagini JPEG 2000 (in formato JPEG o GIF) o i nomi delle stesse. Ognuna ha un link ad un index file utilizzato dal client per effettuare richieste http. Questo file è scaricato dal web server e passato all’applicazione JPEG 2000 del client quando l’utente clicca sul thumbnail o sul nome dell’immagine. pag. 82 Figura 21: Architettura di streaming utilizzando http L’applicazione client manda richieste HTTP al web server, utilizzando le informazioni contenute nell’index file per il download della versione dell’immagine alla più bassa risoluzione disponibile. Per fare ciò si utilizza “byte ranges”, è una caratteristiche del protocollo HTTP/1.1. Questa versione dell’immagine JPEG 2000 a bassa risoluzione sarà decodificata e visualizzata nel browser JPEG2000 del client. L’utente può interagire utilizzando lo zoom, selezionare uan regione di interesse utilizzando il mouse; vengono cercati i corrispondenti “byte ranges” basati sull’index file ed appropriate richieste HTTP vengono inviate al server. Nel prossimo paragrafo vedrremo in dettaglio i passi su indicati con la sintassi di richiesta. pag. 83 6.2.2 SAMPLE SESSION Vedremo ora la sintassi di richiesta (da parte del client) e di risposta(da parte del server) per lo streaming di immagini JPEG 2000, seguendo i passi su indicati. I risulati sono stati ottenuti utilizzando un web server e comandi http lato client. Il server web utilizzato è Apache. 1. Richiesta dell’ index file Richiesta del Client (Web Browser) al Server : GET /index_files/index1.idx HTTP/1.1 Host: www.host1.com CRLF Risposta del Server al Client (Web Browser): HTTP/1.1 200 OK Date: Fri, 07 Jul 2000 22:45:48 GMT Server: Apache/1.3.12 (Win32) Last-Modified: Fri, 07 Jul 2000 22:45:28 GMT ETag: "0-56-39665d88" Accept-Ranges: bytes Content-Length: 86 Content-Type: text/jpeg2000_index_file CRLF http://www.imageshost.com/image_files/image1 _jpeg2000.jp2 [main_header_data].. [tile-part_header_data].. [packet_header_data].. 2. L’ index file contiene informazioni sulla corrispondente immagine JPEG2000. L’header dei dati è analizzato dal client per trovare qulae parte dello strema JPEG2000 corrisponde alla più bassa risoluzione dell’ immagine. Per esempio possiamo assumere che l’immagine è codificata come una singola tile e che la versione a più bassa risoluzione è codificata nell’ intervallo di byte [120,1068]. pag. 84 3. Richiesta dell’ immaginie JPEG2000 alla risoluzione più bassa: Richiesta del Client al Server : GET /image_files/image1_jpeg2000.jp2 HTTP/1.1 Host: www.imageshost.com Range: bytes=120-1068 CRLF Risposta del Server al Client : HTTP/1.1 206 Partial Content Date: Fri, 07 Jul 2000 22:38:58 GMT Server: Apache/1.3.12 (Win32) Last-Modified: Wed, 05 Jul 2000 19:04:01 GMT ETag: "0-fffb-396386a1" Accept-Ranges: bytes Content-Length: 949 Content-Range: bytes 120-1068/65531 Content-Type: image/jpeg2000 CRLF [image data bytes 120-1068] 4. Decodifica e visualizzazione dell’immagine a più bassa risoluzione nel browser del client. 5. L’utente, per esempio, seleziona dall’immagine a bassa risoluzione un regione di interesse con coordinate dell’angolo superiore sinistro a (100,180) e con dimensioni di larghezza e altezza di 200 e 500 rispettivamente. Dall’ index file vengono individuati gli intervalli di byte necessari per la parte di immagini di interesse,per esempio :120-168, 175-200, 205-300, 345-346, 400-500, 555-666,667-800, 900-1000, 25002567, 2890-3056, 5678-9000, 10000-12004,12050-12060, 15600-15605, 17000-17001, 17005-17010,17050-17060, 17800-17905, 20000-20005. pag. 85 Richiesta Client- Server : GET /image_files/image1_jpeg2000.jp2 HTTP/1.1 Host: www.imageshost.com Range: bytes=120-168,175-200,205-300,345-346, 400-500,555-666,667-800,900-1000,2500-2567, 2890-3056,5678-9000,10000-12004,12050-12060, 15600-15605,17000-17001,17005-17010, 17050-17060,17800-17905,20000-20005 CRLF Risposta Server- Client : HTTP/1.1 206 Partial Content Date: Tue, 01 Aug 2000 16:38:35 GMT Server: Apache/1.3.12 (Win32) Last-Modified: Wed, 05 Jul 2000 19:04:01 GMT ETag: "0-fffb-396386a1" Accept-Ranges: bytes Content-Length: 8350 Content-Type: multipart/byteranges; boundary=3986fd0b22d CRLF CRLF --3986fd0b22d Content-type: image/jpeg2000 Content-range: bytes 120-168/65531 CRLF [image data bytes 120-168] --3986fd0b22d Content-type: image/jpeg2000 Content-range: bytes 175-200/65531 CRLF [image data bytes 175-200] --3986fd0b22d … --3986fd0b22d Content-type: image/jpeg2000 Content-range: bytes 20000-20005/65531 CRLF [image data bytes 20000-20005] --3986fd0b22d-- 6. Decodifica delle risposte del web server e visualizzazione dell’ immagine richiesta nel browser del client. pag. 86 6.2.3 STRUTTURA DEL CODESTREAM JPEG2000 Nel capitolo 2 abbiamo visto in dettaglio tutti i passi per codificare un immagine secondo lo standard JPEG 2000, in breve: suddivisione in tile, sottobande, codeblock, quest’ ultimi infine distribuiti su diversi “quality layer”. I dati che rappresentano una specifica tile, layer o componetne sono memorizzati nel codestream in un segmento continuo chiamato packet. Ci sono due tipi di headers (figura 22). Il Main header situato all’inizio del codestream, ed il tile.part headers situati all’inizio di ogni tile-part. Nel main header ci sono informazioni che riguardano l’immagine non compressa come la larghezza, altezza, dimensioni delle tile, numero di componenti, e bit rate di ogni componente. Inoltre contiene informazioni che riguardano lo stile di codifica di default (COD), (es. livelli di decomposizione, ordine di progressione, numero di layers,dimensione dei codeblock, filtri wavelet utilizzati), la quantizzazione di default(QCD), così come informazioni opzionali che riguardano le regioni di interesse, una lista della lunghezza dei pacchetti (PLM), la lunghezza di ogni tile part nello strema (TLM). Il main header è seguito da uno o più tile part (ognuno include un header e i dati). Il tile part header contiene informazioni simili a quelle contenute nel main header. La lunghezza del main header, di ogni tile part header, e la lunghezza di ogni tile part, si possono ottenere facilmente dal main header o dal tile part header. In più, la lunghezza di ogni pacchetto può essere ottenuta dal main header o derivata dal packet header posizionati nel main headero niel codestream. Un index file può essere generato per memorizzare queste informazioni analizzando il codestream header, includendo il main headers, tile part headers e packet headers. L’index fiel può essere utilizzato per facilitare l’accesso a particolari porzioni del codestream. pag. 87 Tile-part 1 Main Header Tile-part N Tile-part bitstream Header Packet 11 Packet Tile-part bitstream Header 1m1 Packet N1 Packet NmN Figura 22: Struttura del codestream 6.2.4 STRUTTURA DELL’ INDEX FILE Come già accenato precedentemente l’index file serve per facilitare l’accesso da parte del clien ta zone di interesse dell’ immagine. Può essere memorizzato sul server insieme allo stream JPEG2000 sia come file separato che come metadato all’ interno dello strema JPEG2000. Generalmente l’index file è formato dagli header del codestream ed in particolare da: • main header • tile part header • packet header • tile part • packet. pag. 88 6.3 LE DUE ARCHITETTURE A CONFRONTO Entrambe sfruttano la scalabilità che è propria di JPEG 2000, permettendo quindi una scalabilità dell’immagine progressiva per risoluzione o per livelli di qualità. Per fare questo nell’architettura utilizzante JPIP è definita una sintassi che definisce le regole di interazione tra client e server, quindi il client può richiedere direttamente l’immagine alla risoluzione e qualità desiderata. In quella utilizzante HTTP, il client dovrà prima scaricare il file indice, poi richiedere l’immagine alla più bassa risoluzione disponibile ed infine potrà interagire per l’eventuale richiesta di zone di interesse. In quest’ultimo scenario, si ha una ridondanza di dati, dovuta al fatto che il file indice contiene informazioni contenute nel file dell’immagine JPEG2000; inoltre non si presta bene per applicazioni riguardanti la telefonia mobile; l’utente è costretto al download del file indice e dell’immagine alla più bassa risoluzione. Una caratteristica da sottolineare del protocollo JPIP, è la possibilità da parte del client di informare il server sugli elementi dell’ immagine che già possiede; pensiamo al caso in cui si interrompa accidentalmente la sessione di comunicazione, è utilissimo per il client poter fare il “resume” del file. L’unica peculiarità dell’architettura adottante http, è l’utilizzo di comuni web server per la memorizzazione di immagini. pag. 89 CAPITOLO 7 CONCLUSIONI E SVILUPPI FUTURI L’obiettivo di questo progetto è lo sviluppo di un’applicativo per dispositivi mobili, basato su tecnologie innovative che si stanno diffondendo nel settore, che permette la visualizzazione ed il download di immagini fisse. L’utilizzo del linguaggio di programmazione Java ha garantito la modularità e la riusablità del software realizzato, su tutti gli apparati dotati di piattaforma Personal Java. Lo standard di compressione JPEG 2000 si presta bene per essere utilizzato in applicazioni Internet e wireless in genere. Basti pensare che il guadagno percentuale in termini di efficienza rispetto al suo predecessore(JPEG), si aggira intorno al 20%; esaminando la situazione da un punto di vista sistemico ci si rende conto facilmente del notevole risparmio di banda. Per non parlare dello streaming, sarà infatti possibile interrompere la ricezione di un’immagine ed ottenere in ogni caso un file visualizzabile anche se incompleto. Nel capitolo precedente abbiamo analizzato due possibili architetture di streaming; quella basata su JPIP è più efficiente, bisognerà però attendere che questo protocollo diventi uno standard. Invece un incognita relativa alla diffusione di JPEG 2000 è rappresentata dallo schema di amministrazione delle licenze che verrà adottato. Si ipotizza un doppio binario, una versione di base del formato liberamente utilizzabile ed una versione avanzata, strutturata su tecnologie proprietarie, il cui utilizzo sarà vincolato alla negoziazione di una licenza. Per quanto riguarda gli sviluppi futuri di questo progetto è da considerare l’implementazione del software per dispositivi provvisti di piattaforma J2ME (vedi paragrafo 3.2) in modo da estenderlo anche a questa categoria di apparati che hanno memoria (Ram e Rom) limitata. Per l’integrazione in browsers si puo’ prevedere lo sviluppo di un plug-in utilizzando il linguaggio Java, molto adatto per questa tipologia di applicazioni. pag. 90 Un’applicazione di sicuro interesse anche per l’impatto diretto sul mercato dei servizi multimediali mobili è l’estensione di JPEG 2000 alla piattaforma utilizzata per il servizio di MMS di multimedia messaging su reti cellulari di seconda e terza generazione, con la possibilità di ottimizzare l’archiviazione di immagini su dispositivi mobili, la trasmissione ottimizzata, con un guadagno in termini di efficienza e usabilità di uno dei servizi più promettenti e delle sue future estensioni. pag. 91 APPENDICE A: LISTATO DEL MAIN DI JP2VIEW import import import import import java.awt.*; java.awt.event.*; jj2000.j2k.*; jj2000.disp.*; jj2000.j2k.decoder.CmdLnDecoder; /*Questa classe gestisce il Frame principale dell’ applicazione*/ class Main extends Frame implements ActionListener, ItemListener { String mem; String img,openimg; CheckboxMenuItem u; CheckboxMenuItem v; CheckboxMenuItem x; CheckboxMenuItem y; CheckboxMenuItem z; Main() { /*crea la menu bar contenente i menu item "File" e "Resolution"*/ super("JP2K Viewer"); MenuBar mb = new MenuBar(); Menu m = new Menu("File"); Menu n = new Menu("Resolution"); MenuItem mi; mi = new MenuItem("Open"); mi.addActionListener(this); m.add(mi); mi = new MenuItem("Open_url"); //mi.disable(); mi.addActionListener(this); m.add(mi); m.add("-"); mi = new MenuItem("Close"); mi.addActionListener(this); m.add(mi); mb.add(m); u = new CheckboxMenuItem("Res1"); u.addItemListener(this); n.add(u); v = new CheckboxMenuItem("Res2"); v.addItemListener(this); n.add(v); x = new CheckboxMenuItem("Res3"); x.addItemListener(this); n.add(x); y = new CheckboxMenuItem("Res4"); y.addItemListener(this); n.add(y); pag. 92 z = new CheckboxMenuItem("Res5"); z.addItemListener(this); n.add(z); mb.add(n); /* Associa le menu bar al frame*/ setMenuBar(mb); resize(208, 320); show(); } /*Questa classe gestisce gli eventi associati al menu "File"*/ public void actionPerformed(ActionEvent evt) { String cmd = evt.getActionCommand(); if (cmd.equalsIgnoreCase("Open")) { if (mem!=null){ /* Istanzia un oggetto FileDialog che serve per interagire con il file system del dispositivo*/ FileDialog f = new FileDialog(Main.this, "Openfile"); f.show(); String[] a; /*img contiene il nome dell'immagine e * della directory che la contiene */ img = f.getDirectory() + f.getFile(); if (f.getFile() != null) { a = new String[] { "-res", mem, "-i", f.getDirectory() + f.getFile()}; /* Chiamata alla funzione decoder passando il Frame di riferimento come parametro e la stringa * contenente il nome e la risoluzione dell' immagine scelta */ CmdLnDecoder cm = new CmdLnDecoder(a, (Frame) Main.this); } } else { MessageDialog a=new MessageDialog(this,"Error","Choose Resolution",true); a.show(); } } else if (cmd.equalsIgnoreCase("Open_url")) { Scegli s=new Scegli(this,"file","",true); s.show(); openimg=s.MemFile(); System.out.println(openimg); } else if (cmd.equalsIgnoreCase("Close")) { System.exit(0); } } pag. 93 /*Questa classe gestisce gli eventi associati al menù "Resolution"*/ public void itemStateChanged(ItemEvent e) { if ((e.getItem() == "Res1") && (e.getStateChange() == ItemEvent.SELECTED)) { mem = "1"; v.disable(); x.disable(); y.disable(); z.disable(); String[] a; a = new String[] { "-res", mem, "-debug", "-i", img}; if (img != null) {CmdLnDecoder cm = new CmdLnDecoder(a, (Frame) Main.this); } } else if ( (e.getItem() == "Res1") && (e.getStateChange() == ItemEvent.DESELECTED)) { v.enable(); x.enable(); y.enable(); z.enable(); } else if ( (e.getItem() == "Res2") && (e.getStateChange() == ItemEvent.SELECTED)) { mem = "2"; u.disable(); x.disable(); y.disable(); z.disable(); String[] a; a = new String[] { "-res", mem, "-debug", "-i", img}; if (img != null) CmdLnDecoder(a, (Frame) Main.this); {CmdLnDecoder cm = new } pag. 94 } else if ( (e.getItem() == "Res2") && (e.getStateChange() == ItemEvent.DESELECTED)) { u.enable(); x.enable(); y.enable(); z.enable(); } else if ( (e.getItem() == "Res3") && (e.getStateChange() == ItemEvent.SELECTED)) { mem = "3"; u.disable(); v.disable(); y.disable(); z.disable(); String[] a; a = new String[] { "-res", mem, "-debug", "-i", img}; if (img != null) CmdLnDecoder(a, (Frame) Main.this); {CmdLnDecoder cm = new } } else if ( (e.getItem() == "Res3") && (e.getStateChange() == ItemEvent.DESELECTED)) { u.enable(); v.enable(); y.enable(); z.enable(); } else if ( (e.getItem() == "Res4") && (e.getStateChange() == ItemEvent.SELECTED)) { mem = "4"; u.disable(); v.disable(); x.disable(); z.disable(); pag. 95 String[] a; a = new String[] { "-res", mem, "-debug", "-i", img}; if (img != null) CmdLnDecoder(a, (Frame) Main.this); {CmdLnDecoder cm = new } } else if ( (e.getItem() == "Res4") && (e.getStateChange() == ItemEvent.DESELECTED)) { u.enable(); v.enable(); x.enable(); z.enable(); } else if ( (e.getItem() == "Res5") && (e.getStateChange() == ItemEvent.SELECTED)) { mem = "5"; u.disable(); v.disable(); x.disable(); y.disable(); String[] a; a = new String[] { "-res", mem, "-debug", "-i", img}; if (img != null) CmdLnDecoder(a, (Frame) Main.this); {CmdLnDecoder cm = new } } else if ( (e.getItem() == "Res5") && (e.getStateChange() == ItemEvent.DESELECTED)) { u.enable(); v.enable(); x.enable(); y.enable(); } } pag. 96 static public void main(String[] args) { new Main(); } } APPENDICE B: LISTATO RELATIVO ALL’ IMPLEMENTAZIONE DEL CLIENT JPIP-H IN JAVA import java.lang.reflect.Array; import java.net.*; import java.io.*; public class JpipClient { public JpipClient(String textURL) throws IOException { Socket socket = null; pag. 97 dissectURL(textURL); socket = connect(); try { getPage(); } finally { socket.close(); } } protected String host, file; protected int port; protected Socket socket; protected void dissectURL(String textURL) throws MalformedURLException { URL url = new URL(textURL); host = url.getHost(); port = url.getPort(); if (port == -1) port = 80; //file = url.getFile (); } protected BufferedReader in; protected DataOutputStream out; protected Socket connect() throws IOException { System.err.println("Connessione a " + host + ":" + port + "..."); socket = new Socket(host, port); System.err.println("Connessione avvenuta."); BufferedOutputStream buffOut = new BufferedOutputStream(socket.getOutputStream()); out = new DataOutputStream(buffOut); in = new BufferedReader(new InputStreamReader(socket.getInputStream())); return socket; } public int readChunkSize(BufferedReader in) throws IOException { String size = in.readLine(); System.out.println("#" + size); return Integer.parseInt(size, 16); } public byte[] readChunkData(InputStream in, int size) throws IOException { int cont = 0; byte data[]; data = new byte[size]; System.out.println(in.read(data, 0, size)); in.read(); in.read(); return data; } pag. 98 protected void getPage() throws IOException { out.writeBytes("GET http://10.50.5.11/out2.jp2 HTTP/1.1\r\n"); out.writeBytes("Host: 10.50.5.11\r\n"); out.writeBytes("Accept: session/jpip-h\r\n"); out.writeBytes( "JPIP-Client-Location: in Italy??, [GMT+2:00], at 127.0.0.1\r\n\r\n"); out.flush(); FileOutputStream image = new FileOutputStream("prova.jp2"); System.err.println("Ricezione dati..."); String input, sid; sid = ""; int countline = 0; while (((input = in.readLine()) != null)) { int pos = input.indexOf("Session-Id:"); if (pos >= 0) { sid = input.substring(pos + 12); out.writeBytes( "GET http://10.50.5.11/JPIP?SID=" + sid + "&R=768,512 HTTP/1.1\r\n"); out.writeBytes("Host: 10.50.5.11\r\n\r\n"); out.flush(); int cont = 0; while (cont < 9) { System.out.println("#" + in.readLine()); cont++; } //inizia la lettura dei chunk int size; int ctot = 0; while ((size = readChunkSize(in)) > 0) { System.out.println(size); image.write(readChunkData(socket.getInputStream(), size)); System.out.println("@#@"); ctot++; } System.out.println("Numero di chunk = " + ctot); } countline++; } image.close(); } public void chunkdecode() { } pag. 99 public static void main(String args[]) throws IOException { try { String in = "http://10.50.5.11"; new JpipClient(in); } catch (IOException ex) { ex.printStackTrace(); } System.out.println("exit"); } } BIBLIOGRAFIA 1. “The JPEG-2000 Still Image Compression Standard”; Michael D. Adams 01/09/2000. 2. JPEG 2000 Part I Final Committee Draft Version 1.0 ISO/IEC JTC1/SC29 WG1, JPEG 2000 Editor Martin Boliek, Co-editors Charilaos Christopoulos, and Eric Majani 11 April 2000. 3. JPEG2000 Part V FCD Editor Faouzi Kossentini (University of BritishColumbia); Co-editor Joel Askelof (Ericsson); Co-editor Michael Adams (University of British Columbia). 4. C. Christopoulos JPEG 2000 Verification Model 6.0 SC29WG1 N1575, January 2000. 5. “High performance scalable image compression with EBCOT,” IEEE Trans. on Image Processing; D. Taubman. pag.100 6. “Proposal and Implementation of JPIP (Jpeg2000 Internet Protocol) in Kakadu v3.3”; David Taubman, 9 August, 2002. 7. “Architecture, Philosophy and Performance of JPIP: Internet Protocol Standard for JPEG2000”; David Taubman and Rober Prandolini. 8. “Scalable Streaming of JPEG2000 Images using Hypertext Transfer Protocol Sachin Deshpande”; Wenjun Zeng. 9. Fielding R., Gettys J., Mogul J. C., Frystyk H., Masinter L.,Leach P., Berners-Lee T., “Hyper-text Transfer Protocol --HTTP 1.1,” RFC 2616, June 1999. 10. “An analytical study of JPEG 2000 functionalities”; Diego Santa-Cruz and Touradj Ebrahimi. 11. “Region of Interest Coding in JPEG2000 for interactive client/server applications”; Diego Santa Cruz, Touradj Ebrahimi, Mathias Larsson, Joel Askelöf and Charilaos Cristopoulos. 12. “Image compression using the 2-D wavelet transform,” IEEE Trans. on Image Processing; A. S. Lewis and G. Knowles 13. “Image codingusing wavelet transform,” IEEE Trans. on Image Processing; M. Antonini, M. Barlaud, P. Mathieu, and I. Daubechies 14. ISO/IEC, ISO/IEC 14492-1, “Lossy/lossless coding of bi-level images”. 15. “The JPEG still picture compression standard”; G. K. Wallace 16. “Java on Mobile Device” ; Bjørg Lyngstad. 17. McMahon, P., GSS-API authentication method for SOCKS version 5, Request for Comments 1961, Internet Engineering Task Force. 1996. 18. “The lifting scheme: A construction of second generation wavelets”; W. Sweldens pag.101 19. “Manuale Pratico di Java” P. Aiello, L. Bettini, L. Dozio, A. Gini, A. Giovannini, M. Manzini, M. Molino, G. Morello, G. Pulit, S. Rossigni, N. Venditti. (www.mokabyte.it) 20. “Thinking in Java”; Bruce Eckel 21. “Java Mattone dopo Mattone”; Massimilaino Tarquini, Alessandro Ligi 22. “Java in a nutshell”; Flanagan 23. “Java 2 unleashed “; Jaworski Ringraziamenti Ringrazio il Prof. Alessandro Neri per la collaborazione e la disponibilità durante lo svolgimento di questa tesi. Un sentito grazie va all’Ing. Francesco Romano per il supporto durante questo progetto nonostante i suoi numerosi impegni. Ringrazio l’Elis per le infrastrutture messe a disposizione nel corso del progetto Vivai d’Impresa. Un grazie particolare va Massimo Rofi, che a condiviso i momenti cruciali del progetto. Ringrazio di cuore la mia famiglia, che mi ha dato sostegno e affetto durante il corso dei miei studi, e senza la quale non sarei arrivato fin qui. Ringrazio tutti i miei amici per essermi stati “vicini” in questi anni. pag.102 pag.103