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