MokaByte Numero 03 - Dicembre 1996

 
Introduzione alla elaborazione 
delle immagini in Java
di Massimo Carli
 

 



link articolo oopIl mese precedente abbiamo visto quello che sarà il programma del corso ed abbiamo iniziato a vedere i modelli di scomposizione dei colori delle immagini RGB e HSB. Questo mese vedremo come si carica un'immagine e come si utilizza creando qualche piccola animazione.

link articolo oopCome visto le immagini non sono altro che un insieme di colori posizionati in maniera opportuna. Questo concetto sembra stupido ma è alla base della gestione delle immagini in Java che ci accingiamo ad affrontare. Java, attraverso il JDK, permette la gestione di due tipi di file immagini, GIF e JPEG, anche se, come vedremo nella prossima puntata, sara' possibile elaborare altri formati. Il GIF (Graphics Interchange Format) è di proprietà della Compuserve. Essa utilizza un metodo di compressione che permette di rappresentare immagini grandi in file piccoli (LZW). Il JPEG (Joint Photographic Experts Group) è uno standard internazionale utilizzato principalmente per materiale fotografico. Il metodo di compressione del JPEG utilizza la coseno-trasformata discreta (DCT) per rimuovere quello che l'occhio umano non riesce a vedere in maniera da ridurre il numero dei dati memorizzati. Si dice che la coseno-trasformata è "lossy" in quanto con essa, si ha perdita di informazione. Il metodo LZW si dice "lossless" cioè senza perdite. La DCT rimuove quei particolari che non sono visibili all'occhio umano per cui non si ha necessita' di memorizzazione. Il metodo LZW (Lempel Ziv Welch) consiste nella creazione di quello che si chiama un alfabeto esteso. Esso contiene, oltre ad i caratteri normali, anche le sequenze di caratteri. Per ognuno di questi si utilizzano almeno 9 bit da cui l'alfabeto sarà di almeno 2^9 caratteri. Il metodo LZW si basa su di un principio di memorizzazione delle sequenze di caratteri come nuovi caratteri. Ad ogni ripresentazione della sequenza viene associato un solo carattere. Ad esempio la sequenza AAABBAACCAACC permetterà la creazione di caratteri corrispondenti alla tripla A, alle doppia A, B e C da cui si avranno 6 caratteri di 9 bit invece di 13 da 8, con un risparmio di 13*8-6*9=54 bit che rappresenta quasi il 50%. Il metodo JPEG sfrutta una caratteristica dell'occhio umano. Abbiamo visto come le immagini (o meglio i colori che le compongono) possano essere scomposte in varie componenti. Se scomponiamo una immagine nelle componenti YCrCb cioè in una componente di luminanza (Y) e nelle due di crominanza (Cr e Cb) si ha un fatto particolare (questa scomposizione è quella che si ha nella trasmissioni televisive PAL). L'occhio umano è, infatti, più sensibile alle variazioni di luminosità rispetto a quelle di colore. Il metodo di compressione JPEG non fa altro che diminuire la risoluzione delle componenti di crominanza introducendo delle perdite ma prive di importanza. E' un modo per non memorizzare le cose che non ci interessano perchè non visibili. Per quello che riguarda le componenti YCrCb sono legate da regole molto semplici a quelle RGB.

link articolo oopDopo questa breve digressione sulle tecniche di compressione delle immagini torniamo a Java. Come prima cosa diciamo che una immagine è caratterizzata da un oggetto di tipo Image del package java.awt. Per caricare una immagine si possono utilizzare i seguenti due metodi: link articolo oopIl primo metodo può essere utilizzato solamente in un applet in quanto è ereditato dalla classe java.applet.Applet da cui ogni applet deriva. Il secondo metodo può essere utilizzato sia negli applet che nelle applicazioni. Consiglio di utilizzare il primo metodo negli applet ed il secondo nelle applicazioni. Infatti, in un applet è inutile, salvo applicazioni particolari che richiedano un utilizzo del Toolkit, ricorrere a tale metodo. Nelle applicazioni consiglio il secondo metodo anche perchè è l'unico possibile!!. Un cosa importante è il fatto che i metodi getImage ritornano subito senza attendere il caricamento dell'immagine corrispondente. Per caricare l'immagine bisogna visualizzarla. Questo procedimento di gestione delle immagini è stato fatto per non appesantire la memoria del calcolatore. In un applet, per esempio, è inutile caricare un'immagine se non la si visualizza. La classe Applet fornisce due versioni per il metodo getImage le cui firme sono: link articolo oopIl secondo metodo è il piu' conveniente se si utilizzano immagini residenti sul proprio server. E' cosa utile e ormai diffusa mettere le immagini in un subdirectory \images nel directory in cui vi è la pagina HTML oppure la classe .class. Nel caso in cui si mettano le immagini relativamente al direttorio in cui vi è il codice dell'applet si utilizza il metodo getCodeBase(), altrimenti nel caso in cui il riferimento sia la pagina HTML che contiene l'applet si utilizza il metodo getDocumentBase(). Questi metodi appartengono alla classe java.applet.Applet e forniscono l'URL corrispondente, rispettivamente, al file .class ed al file .htm o .html. Nel caso si mettano le immagini nella subdirectory \images del codice si scriverà: Analogamente nel caso del getDocumentBase() se il subdirectory è relativo a quello in cui c'è il file HTML: Anche la classe java.awt.Toolkit dispone i due metodi getImage(): Notiamo subito come qui sia possibile caricare l'immagine a partire dal nome del file dell'immagine. Questo nel caso dell'applet, non è possibile per i noti motivi di sicurezza in quanto attraverso il solo nome si esprime solo un file locale.

Una volta che è stato instanziato un oggetto Image non resta che visualizzare l'immagine corrispondente. Per fare questo si utilizza il seguente metodo della classe Graphics:

link articolo oopLa variabile immagine è ovviamente l'oggetto Image da visualizzare. Le variabili intere x e y rappresentano le coordinate del vertice superiore sinistro dell'immagine nel sistema costituito dal componente che dovrà contenere l'immagine stessa. Una cosa importante è rappresentata dall'ultimo parametro this. Esso è riferito ad un oggetto ImageObserver. Sappiamo che ImageObserver è una interfaccia del package java.awt.image la quale è implementata da ogni oggetto che deriva dalla classe Component tra cui l'Applet. Un oggetto definito di un tipo interfaccia rappresenta una qualunque instanza di una classe che implementa l'interfaccia stessa. Il this, a questo punto, risulta chiaro se il metodo drawImage è inserito nella paint() di un applet. L'interfaccia ImageObserver sarà esaminata nella prossima puntata, per ora vediamo velocemente le altre firme dei metodi drawImage della classe Graphics.

link articolo oopQuesta permette di specificare il colore di background

link articolo oopQueste permettono di specificare le dimensioni dell'immagine. Se queste ultime sono diverse da quelle originali, l'immagine viene automaticamente ingrandita o rimpicciolita. link articolo oopFino ad ora abbiamo imparato a creare un oggetto Image ed a visualizzarlo. Come detto l'immagine viene effettivamente caricata all'atto della visualizzazione e non alla creazione dell'oggetto Image. Talvolta vi è la necessità di caricare l'immagine prima di visualizzarla. Se volessimo conoscere, per esempio, le dimensioni dell'immagine dobbiamo prima caricarla e non vogliamo necessariamente visualizzarla per conoscere tali informazioni. Infatti, se usiamo i metodi getHeight() e getWidth() della classe Image solo dopo la dichiarazione dell'oggetto Image stesso, si ha -1 in entrambi i casi. Infatti per conoscere le dimensioni dell'immagine bisogna caricarla. Per fare questo il JDK mette a disposizione la classe java.awt.MediaTracker. Essa sfrutta un concetto che riguarda l'interfaccia ImageObserver che vedremo il prossimo mese. Questa classe permette di caricare l'immagine senza il bisogno di visualizzarla. Vediamo come si fa. Supponiamo di essere nella init() di un applet. Per prima cosa definiamo l'oggetto img di tipo Image. Supponiamo di leggere il file figura.jpg Ora si definisce l'oggetto MediaTracker il quale ha come argomento un oggetto di tipo Component come è un Applet. La cosa importante, come vedremo, è il fatto che un Component implementa la interfaccia ImageObserver. Si ha quindi: Il this è relativo all'applet che è un Component e quindi implementa ImageObserver. Attraverso questa istruzione diciamo all'oggetto MediaTracker di considerare l'immagine img. Ad essa viene associato anche un valore intero che sarà una specie di identificatore dell'immagine nel MediaTracker. Per caricare effettivamente l'immagine basterà scrivere: Il metodo waitForID(0) permette di attendere fino a che l'immagine associata al valore 0 sia completamente caricata. Nel caso di più immagini basterà associare diversi valori per ciascuna ed attendere che ciascuna sia caricata. Per fare questo si puo' utilizzare anche il metodo waitForAll() che attende che tutte le immagini registrate nel MediaTracker siano caricate.

link articolo oopOra vediamo un esempio completo di un applet che visualizza delle animazioni. Questo applet è molto semplice e permette il caricamento di 3 immagini e la visualizzazione alternata delle stesse. Notiamo che attraverso l'utilizzo del MediaTracker tutte le immagini saranno disponibili contemporaneamente in quanto saranno visualizzate solo se tutte e tre caricate.Il risultato ottenibile con questo semplice applet lo si può osservare nella animazione in funzione accanto al titolo. Il codice Java invece è qui.

link articolo oopPer questo mese abbiamo detto abbastanza. Il prossimo parleremo delle famose interfaccie relative alla gestione delle immagini vedendo cosa fa effettivamente il MediaTracker e incontraremo quella che è la gestione dei modelli cioè la classe ColorModel.
 
  

 

MokaByte rivista web su Java

MokaByte ricerca nuovi collaboratori
Chi volesse mettersi in contatto con noi può farlo scrivendo a mokainfo@mokabyte.it