MokaByte Numero 09 - Giugno 1997

Foto
 

Elaborazione delle immagini

 

di  

Massimo Carli
Puntata numero 6 
 

 
 

 



Il mese scorso abbiamo introdotto i filtri in Java. Abbiamo visto quali sono le classi principali ed il loro utilizzo attraverso due semplici esempi. I filtri creati permettevano di creare immagini filtrate attraverso la semplice elaborazione di ciascun pixel della stessa indipendentemente da altri pixel vicini. I filtri più belli e significativi si ottengono, però, facendo si' che ogni pixel risenta di pixel che gli stanno vicino. In questa puntata vedremo un filtro di questo tipo


 
 

Cosa dobbiamo fare?

Il mese scorso avevamo creato filtri che estendevano la classe java.awt.image.RGBImageFilter ridefinendo il metodo filterRGB() che forniva l'elaborazione di ogni singolo pixel, mentre ora dovremo estendere direttamente la classe ImageFilter. Vediamo di creare un filtro che permette di dare l'effetto di profondità dell'immagine. Per fare questo bisogna che ciascun pixel conosca tutti i pixel che gli stanno attorno.
Il procedimento per la creazione del filtro è quello già spiegato nelle puntate precedenti e commentato nel listato che segue.

 

import  java.awt.image.*;                               // Per la gestione delle immagini

public class Profondita extends ImageFilter {

// Notiamo che il filtro estende la classe ImageFilter. Infatti ogni filtro in Java

// deve estendere tale classe.

        protected int width;                    // Larghezza dell'immagine

        protected int height;                   // Altezza dell'immagine

        protected int pixels[];         // Vettore dei pixel dell'immagine

        
        

        // Costruttore: Non deve fare nulla

        public Profondita(){

        }// fine costruttore

        // Dobbiamo definire le proprietà per questo filtro. Noi indichiamo che esso debba

        // elaborare ogni pixel dell'immagine. Basterà allora ridefinire in quel modo 

        // il metodo setHints. Ricordoche la variabile consumer appartiene alla classe

        // ImageFilter  e rappresenta la sua funzione di Producer appunto.

        public void setHints(int hints){

                consumer.setHints(hints & ~ImageConsumer.COMPLETESCANLINES);

        }// fine setHints


        

        // Ora dobbiamo definire le dimensioni dell'immagine filtrata che saranno, nel nostro

        // caso identiche a quelle dell'immagine originale. Per cui:

        public void setDimensions(int width, int height){

                this.width = width;                     // Salviamo le dimensioni nelle nostre

                this.height= height;                    // variabili locali

                this.pixels = new int[width*height];    // Creiamo il vettore di pixel vuoto

                consumer.setDimensions(width,height);   // Settiamo le dim del super    

        }// fine setDimension

        


        // Ora mettiamo il metodo di elaborazione dei pixel. Prendiamo la seguente versione:

        public void setPixels(  int x, int y, int width, int height,ColorModel model,

                                byte[] pixels, int offset, int scansize){

                // Dobbiamo percorrere tutti i pixel dell'immagine

                for (int i=0;i<height;i++){

                        for (int j=0;j<width;j++){

                                // Prendiamo il byte meno significativo dell'immagine originale

                                int pixel= pixels[i*scansize+offset+j]&0xff;

                                // Otteniamo il corrispondente valore RGB

                                this.pixels[(y+i)*width+x+j]= model.getRGB(pixel);

                        }// fine for j

                }// fine for i

        }// fine setPixels                                                                                              


        

        // Dobbiamo definire anche il seguente metodo che differisce dal primo solo

        // perche' il vettore di pixel ora è di interi. Questo perche' non si conosce se

        // il modello utilizzato è il modello RGB per cui lo trattiamo come un normale 

        // modello indicizzato.

        

        public void setPixels(  int x, int y, int width, int height,ColorModel model,

                                int[] pixels, int offset, int scansize){

                // Dobbiamo percorrere tutti i pixel dell'immagine

                for (int i=0;i<height;i++){

                        for (int j=0;j<width;j++){

                                // Ora lo prendiamo tutto

                                int pixel= pixels[i*scansize+offset+j];

                                // Otteniamo il corrispondente valore RGB

                                this.pixels[(y+i)*width+x+j]= model.getRGB(pixel);

                        }// fine for j

                }// fine for i

        }// fine setPixels                      

        
        

        // Quando l'elaborazione dell'immagine è conclusa viene automaticamente

        // chiamato il seguente metodo. ne approfittiamo allora per elaborare 

        // effettivamente l'immagine originale. Questo sarà fatto dal metodo 

        // performEffect() che sarà caratteristico del filtro.

        public void imageComplete(int status){

                // Elaboriamo l'immagine

                performEffect();

                // Forniamo i pixel al producer

                deliverPixels();

                //richiamiamo il metodo del super

                super.imageComplete(status);            

        }// fine imageComplete

        

        



        protected void deliverPixels(){

                consumer.setPixels(0,0,this.width,this.height,ColorModel.getRGBdefault(),this.pixels,

                0,this.width);

        }// fine deliverPixels

        

        

        // Ora dobbiamo elaborare l'immagine.

        public void performEffect(){

                // Definiamo il vettore dei pixel dell'immagine filtrata

                int newPixels[] = new int[width*height];

                // Ora scorriamo tutta l'immagine e calcoliamo il valore corrispondente

                // a ciascun pixel. Qui vi è il problema del fatto che per calcolare il

                // valore relativo alla profondità bisogna conoscere tutti i pixel attorno

                // al pixel da calcolare per cui per i pixel sui bordi bisogna fare attenzione.

                // Intanto, pensiamo agli altri

                for (int y=1; y<height-1;y++ ){

                        int lineOffset= y*width;

                        for (int x=1;x<width-1; x++){

                                int pointOffset = lineOffset+x;

                                int redSum              = 0;

                                int greenSum    = 0;

                                int blueSum             = 0;

                                // Procediamo al calcolo applicando la matrice

                                redSum -= 2*((pixels[pointOffset-width-1]  16)&0xff);

                                greenSum -= 2*((pixels[pointOffset-width-1]  8)&0xff);

                                blueSum -= 2*pixels[pointOffset-width-1]&0xff;

                                

                                redSum -= ((pixels[pointOffset-width]  16)&0xff);

                                greenSum -= ((pixels[pointOffset-width]  8)&0xff);

                                blueSum -= pixels[pointOffset-width]&0xff;                              

                                

                                redSum -= ((pixels[pointOffset-1]  16)&0xff);

                                greenSum -= ((pixels[pointOffset-1]  8)&0xff);

                                blueSum -= pixels[pointOffset-1]&0xff;                                  

                                

                                redSum += 2*((pixels[pointOffset+width+1]  16)&0xff);

                                greenSum += 2*((pixels[pointOffset+width+1]  8)&0xff);

                                blueSum += 2*(pixels[pointOffset+width+1]&0xff);                        

                                

                                redSum += ((pixels[pointOffset+width]  16)&0xff);

                                greenSum += ((pixels[pointOffset+width]  8)&0xff);

                                blueSum += pixels[pointOffset+width]&0xff;      

                                

                                redSum += ((pixels[pointOffset+1]  16)&0xff);

                                greenSum += ((pixels[pointOffset+1]  8)&0xff);

                                blueSum -= pixels[pointOffset+1]&0xff;                          

                                // e poi normalizzando i valori

                                

                                redSum =3;

                                greenSum =3;

                                blueSum =3;

                                

                                // Li aggiungiamo al grigio medio

                                

                                redSum += 0x7f;

                                greenSum += 0x7f;

                                blueSum += 0x7f;

                                

                                // Controlliamo che i valori ottenuti siamo nei limiti

                                

                                if (redSum<0) redSum=0;

                                if (redSum255) redSum =255;

                                if (greenSum<0) greenSum=0;

                                if (greenSum255) greenSum =255;

                                if (blueSum<0) blueSum=0;

                                if (blueSum255) blueSum =255;                                                           

                                

                                // Calcoliamo poi il valore effettivo come il massimo delle

                                // tre componenti

                                

                                int gray= Math.max(greenSum,Math.max(blueSum,redSum));

                                

                                // Otteniamo poi il valore finale facendolo cadere all'interno delle

                                // gradazioni di grigio moltiplicandolo per il valore 0x010101

                                

                                newPixels[pointOffset] = 0xff000000 + 0x010101 *gray;

                        }// fine for x                  

                }// fine for y

                this.pixels = newPixels;

        }// fine performEffect




}// fine classe Profondita


Il Procedimento seguito si può riassumere in poche righe. Per calcolare il valore corrispondente ad un pixel, abbiamo preso tutti i pixel attorno ed abbiamo applicato loro una matrice. Nel nostro caso la matrice è :


-2  -1   0

-1   0   1

 0   1   2

Abbiamo poi normalizzato i valori ottenuti e preso il massimo delle tre componenti RGB. Poi abbiamo aggiunto il valore ottenuto al valore grigio medio e controllato che questo restasse nei limiti. Il gioco è fatto.

Comunque indipendentemente dal procedimento utilizzato la creazione di filtri segue lo schema esposto. 
Le uniche differenze si possono limitare alla definizione del metodo performEffect() e questo ha il solo limite della fantasia del programmatore.

Il codice dell'applet è il seguente :









*********************************************************************

*    Class   : AppletComponentFilter                                *

*********************************************************************

*                                                                   *

* Questa classe permette la verifica di un filtro ComponentFilter   *

*                                                                   *

*********************************************************************

* Author:Massimo Carli     Environment:JDK1.1.1    Date:18/02/1997  *

* Version: 0.01                                                     *

*********************************************************************/


// Importiamo le classi che ci servono

import  java.awt.*;               // Per gli oggetti grafici

import  java.awt.image.*;         // Per le immagini e filtri

import  java.applet.*;            // Perche' e' un applet

import  Profondita;               // Filtro che utilizziamo


public class AppletProfFilter extends Applet implements Runnable {

        

        Image   img_prova;        // Immagine di prova

        Image   img_fine;         // Versione rossa

        AppletContext ac ;        // AppletContext

        Thread  runner;           // Thread dell'applet

        boolean loaded;



        public void init(){

                // Acquisiamo il costento dell'applet

                ac = getAppletContext();

                ac.showStatus("Loading Image...");

                loaded= false;

                // Prima cosa carichiamo l'immagine di prova

                MediaTracker tracker = new MediaTracker(this);

                img_prova= getImage(getCodeBase(),"provaimg.gif");

                tracker.addImage(img_prova,0);

                try{

                                tracker.waitForID(0);           

                }catch(InterruptedException e) {}               

                ac.showStatus("Image loaded!!");

                // Creazione dell'immagine con le componenti rosse

                Profondita prof = new Profondita();             // Filtro utilizzato

                FilteredImageSource fis = new FilteredImageSource(img_prova.getSource(),prof);

    img_fine            = createImage(fis);

    tracker.addImage(img_fine,0);

                try{

                                tracker.waitForAll();                                           

                }catch(InterruptedException e) {}                       

                loaded = true;  // Immagini caricate

                ac.showStatus("Image parsed!!");

        }// fine init

        

        

        public void paint(Graphics g){

                if (img_prova!=null)

                        g.drawImage(img_prova,0,0,this);

                if (loaded){

                        g.drawImage(img_fine,100,0,this);



                }       

                

        }

        

        

        public void update(Graphics g){

                paint(g);

        }// fine update

        

        

        public void run(){

                repaint();

        }// fine run





        public void start(){

                if (runner==null){

                        runner = new Thread(this);

                        runner.start();

                }       

        }       // fine start

        

        public void stop(){

                if (runner!=null){

                        runner.stop();

                        runner=null;

                }

        }       // fine stop





}// fine AppletComponentFilter

 

 

MokaByte rivista web su Java

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