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
