appendice a: .listato del main di jp2view

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