MokaByte Numero 09 - Giugno 1997 |
|||
|
Elaborazione delle immagini |
||
di |
Puntata numero 6 | ||
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 |
||
|