El API 2D de Java implementa un nuevo modelo de imagen que permite la manipulación de imágenes de resolución fija almacenadas en memoria. La clase BufferedImage es una nueva clase Image en el paquete java.awt.image, que puede usarse para manipular datos de una imagen recuperados desde un archivo o una URL. Por ejemplo, se puede usar un BufferedImage para implementar doble búfer . Las clases BufferedImage y BufferedImageOp también permiten realizar una gran
variedad de operaciones de filtrado de imágenes como blur o sharpen. El modelo de imagen productor/consumidor proporcionado en las versiones anteriores del JDK se mantiene por razones de compatibilidad.
39.1 El modelo de imágenes de modo inmediato y el BufferedImage
El modelo de imágenes en "modo inmediato" permite manipular y mostrar imágenes de pixeles mapeados cuyos datos están almacenados en memoria. Es posible acceder a los datos de la imagen en una gran variedad de formatos y usar varios tipos de operaciones de filtrado para manipular los datos.
BufferedImage es la clase clave del API del modo-inmediato. Esta clase maneja una imagen en memoria y proporciona métodos para almacenar, interpretar y dibujar cada dato de pixel. Un BufferedImage puede ser renderizado en un contexto Graphics o en un contexto Graphics2D.
Un BufferedImage es esencialmente un Image con un búfer de datos accesible. Un BufferedImage tiene un ColorModel y un Ráster de los
datos de la imagen. Figura 66 Clase BufferedImage (Sun Microsystems)
El ColorModel proporciona una interpretación de color de los datos de los píxeles de la imagen. El Ráster representa las coordenadas rectangulares de la imagen, mantiene los datos de la imagen en memoria, y proporciona un mecanismo para crear múltiples subimagenes de un sólo búfer de imagen. El Ráster también proporciona métodos para acceder a píxeles específicos dentro de la imagen.
39.2 Filtrado de un BufferedImage
El API Java 2D define varias operaciones de filtrado para objetos BufferedImage. Cada operación de proceso de imágenes está incluida en una clase que implementa la interfaz BufferedImageOp. La manipulación de imágenes se realiza en el método filter. La clase BufferedImageOp en el API Java 2D soporta:
Transformación afin Escalado
Modificación de Aspecto
Combinación Linear de Bandas
Conversión de color Convolución.
Para filtrar un BufferedImage usando una de las clases de operación de imagense debe:
1. Constuir una instancia de una de las clases BufferedImageOp:
AffineTransformOp, BandCombineOp, ColorConvertOp, ConvolveOp, LookupOp , o RescaleOp.
2. Llamar al método de operación filter, pasando el BufferedImage que se desea filtrar y el BufferedImage donde se quiere almacenar el resultado. El siguiente ejemplo, tomado del Tutorial de Java 2D de Sun Microsystems ilustra el uso de cuatro operaciones de filtrado de imagenes: low-pass, sharpen, lookup, y rescale. Se hicieron algunas modificaciones, pues se presentaban errores al cargar los archivos de imagen. El resultado de la ejecución es la pantalla que se muestra en la Figura 67.
El filtro sharpen se realiza usando un ConvolveOp. Convolución es el proceso de hacer más pesado el valor de cada pixel en una imagen con los valores de los pixeles vecinos. La mayoría de los algoritmos de filtrado espacial están basados en las operaciones de convolución.
Para construir y aplicar este tipo de filtrado al BufferedImage, este ejemplo usa un código similar al del siguiente fragmento.
public static final float[] SHARPEN3x3 = { 0.f, -1.f, 0.f,
-1.f, 5.0f, -1.f, 0.f, -1.f, 0.f};
BufferedImage dstbimg = new BufferedImage(iw,ih,BufferedImage.TYPE_INT_RGB); Kernel kernel = new Kernel(3,3,SHARPEN3x3);
ConvolveOp cop = new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null); cop.filter(srcbimg,dstbimg);
Figura 67 Ejemplo de tratamiento de imágenes
El objeto Kernel define matemáticamente cómo se ve afectada la salida de cada pixel en su área inmediata. La definición del Kernel determina el resultado del filtro. El código completo de la aplicación, que contiene los cuatro filtros aplicados a las imágenes es el siguiente:
/*
* Ejemplo de aplicación de filtros utilizando BufferedImage y BufferedImageOp. * Versión modificada del ejemplo del Tutorial de Java 2D de Sun Microsystems. */ import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.awt.image.*; import java.awt.geom.AffineTransform; import java.awt.font.TextLayout;
import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.awt.event.WindowAdapter; import java.applet.*;
import java.net.URL;
public class ImageOps extends Applet {
private BufferedImage vectorbi[];
public static final float[] SHARPEN3x3_3 = { 0.f, -1.f, 0.f,
-1.f, 5.f, -1.f, 0.f, -1.f, 0.f};
public void init() {
setBackground(Color.white);
vectorbi = new BufferedImage[4];
String nombresimg[] = { "canocristales01.jpg", "canocristales01.jpg", "rioapaporis01.jpg", "rioapaporis01.jpg"};
for ( int i = 0; i < vectorbi.length; i++ ) {
//Obtiene la imagen a partir del nombre de archivo correspondiente Image imagen = Toolkit.getDefaultToolkit().getImage(nombresimg[i]);
/* El objeto MediaTracker, optimiza el proceso de cargado de la imagen al bloquear la tarea hasta que
* la imagen esté totalmente cargada, con lo que se elimina el parpadeo que se produce al ir * presentándose en la pantalla partes de esa imagen que no está totalmente cargada. */
try {
MediaTracker tracker = new MediaTracker(this); tracker.addImage(imagen, 0);
tracker.waitForID(0); }
catch ( Exception e ) { }
int ialto = imagen.getHeight(this);
vectorbi[i] = new BufferedImage(iancho, ialto, BufferedImage.TYPE_INT_RGB);
//Crea un contexto gráfico a partir de la imagen Graphics2D completa = vectorbi[i].createGraphics(); //Dibuja en el contexto la imagen
completa.drawImage(imagen,0,0,this); }
}
public void paint(Graphics g) { Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RÉNDER_QUALITY);
//Tamaño del applet para calcular los tamaños de imagen y sus posiciones int anchoap = getSize().width;
int altoap = getSize().height;
g2.setColor(Color.black);
float[][] datos = {{0.1f, 0.1f, 0.1f, // Matriz del Filtro 0.1f, 0.2f, 0.1f,
0.1f, 0.1f, 0.1f}, SHARPEN3x3_3};
String Descrip[] = { "Convolve LowPass", "Convolve Sharpen", "LookupOp", "RescaleOp"};
for ( int i = 0; i < vectorbi.length; i++ ) { int iancho = vectorbi[i].getWidth(this); int ialto = vectorbi[i].getHeight(this); int x = 0, y = 0;
AffineTransform transformacion = new AffineTransform();
transformacion.scale((anchoap-14)/2.0/iancho, (altoap-34)/2.0/ialto);
BufferedImage biorig = new BufferedImage(iancho,ialto,BufferedImage.TYPE_INT_RGB); //BufferedImage original
switch ( i ) { case 0 :
case 1 : x = i==0?5:anchoap/2+3; y = 15; Kernel kernel = new Kernel(3,3,datos[i]); ConvolveOp cop = new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP,
null);
cop.filter(vectorbi[i],biorig);
bitrans = new AffineTransformOp(transformacion, AffineTransformOp.TYPE_NEAREST_NEIGHBOR); break;
case 2 : x = 5; y = altoap/2+15; byte chlut[] = new byte[256]; for ( int j=0;j<200 ;j++ ) chlut[j]=(byte)(256-j);
ByteLookupTable blut=new ByteLookupTable(0,chlut); LookupOp lop = new LookupOp(blut, null);
lop.filter(vectorbi[i],biorig);
bitrans = new AffineTransformOp(transformacion,AffineTransformOp.TYPE_BILINEAR); break;
case 3 : x = anchoap/2+3; y = altoap/2+15; RescaleOp rop = new RescaleOp(1.1f,20.0f, null); rop.filter(vectorbi[i],biorig);
bitrans = new AffineTransformOp(transformacion,AffineTransformOp.TYPE_BILINEAR); }
//Dibuja la imagen en el contexto gráfico en la correspondiente x e y. g2.drawImage(biorig,bitrans,x,y);
//Dubija el texto
TextLayout texto = new TextLayout(Descrip[i], g2.getFont(),g2.getFontRenderContext()); texto.draw(g2, (float) x, (float) y-4);
} }
public static void main(String s[]) {
JFrame f = new JFrame("Tratamiento de imágenes"); f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {System.exit(0);} });
Applet elapplet = new ImageOps();
f.getContentPane().add("Center", elapplet); elapplet.init(); f.pack(); f.setSize(new Dimension(610,450)); f.show(); } }