Dentro de un nuevo post sobre visualización gráfica y tenologías relacionadas, vamos a hacer una pequeña reseña en relación a una potente herramienta de manipulacion de imágenes y diseño gráfico.
No es ni más ni menos que el entorno y lenguaje de programación llamado PROCESSING. Este lenguaje está basado en Java y nos permite, de una manera muy sencilla y potente, relaizar diferentes manipulaciones sobre imágenes, desarrollar elementos gráficos, visualizaciones, interacciones y demás temas relacionados con el diseño gráfico y la visualización de datos.
Esta es la URL oficial de PROCESSING, donde se pueden encontrar tutoriales, ejemplos, distribuibles, etc.
Es una herramienta muy potente usada ampliamente en diferentes sectores como el arte, la visualización de datos, el desarrollo de software o incluso es la base de otros lenguajes de programación y entornos de desarrollo como Arduino.
Los dos ejemplos que se muestran a continuación son sencillas pruebas de lo que se puede hacer con Processing a la hora de manipular y analizar imágenes. Pero con Processing se pueden hacer muchas más cosas, como crear formas, textos, imágenes en 2D y 3D, etc. Se recomienda encarecidamente consultar la web oficial y repasar los tutoriales y ejemplos que alli se encuentran.
1. API de Processing
Como hemos dicho antes, Processing está basado en Java, asi que funciona de forma similar. Proporciona una API y una serie de classes que nos permiten trabajar con imágenes de forma sencilla desde cualquier programa o entorno de desarrollo.
Estos son algunos de los elemento de Processing que veremos en los siguientes ejemplos:
- PApplet – es un applet Java del que extiende nuestra clase principal y que creará una ventana alrededor de nuestro contenido para una mejor visualización.
- PImage – representa una imagen.
- setUp() – inicializa la aplcicación.
- draw() – es invocado de forma recurrente para refrescar el contenido de la aplicación.
- loadImage() – carga una imagen en memoria.
- image() – dibuja una imagen en las coordenadas que le indiquemos.
2. Estructuras básicas
Los elementos básicos de los que debe constar una aplicación implementada con Processing serían los siguientes:
- El applet de Processing
1 2 3 4 5 6 7 8 |
/** * Main class. * * @author lagarcia */ public class JProcessing extends PApplet { ... } |
Una alternativa a extender el applet e invocar nuestra aplicación como tal es usar esto dentro del metodo main:
1 2 3 4 5 6 7 8 |
/** * Main method. * * @param args */ public static void main(String[] args) { PApplet.main(new String[] {"--present", "net.luisalbertogh.processing.JProcessing" }); } |
- El método setpUp() donde se inicializa nuestra aplicación
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
/** * @see processing.core.PApplet#setup() */ @Override public void setup() { /* Load images */ source = new PImage[1]; source[0] = loadImage("images/sunflower.jpg"); /* Destination */ destination = new PImage[2]; destination[0] = createImage(source[0].width, source[0].height, RGB); destination[1] = createImage(source[0].width, source[0].height, RGB); /* Set canvas size */ size(800, 400); } |
- El método draw() que se invoca de forma repetida mientras la aplicación esté activa
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
/** * @see processing.core.PApplet#draw() */ @Override public void draw() { /* Background color is black */ // background(0); /* Draw the image to the screen at coordinate (x,y) */ image(source[0], 0, 0); /* Transform original image into B&W */ toBlackAndWhite(source[0], destination[0], 200.0f); toPointill(source[0], 10); // Display the destination image(destination[0], source[0].width + 10, 0); } |
- Otros métodos de captura de eventos, como por ejemplo el siguiente
1 2 3 4 5 6 7 |
/** * @see processing.core.PApplet#mousePressed() */ @Override public void mousePressed() { ... } |
3. Manipulación de imágenes
Una vez tenemos las imagenes cargadas en memoria, podemos acceder a sus píxeles, a las propiedades de estos y usar la API de Processing para manipularlos.
Los píxeles se representan como un array unidimensional. Para acceder a un píxel basta con calcular el índice que le correspondería en ese array. Antes debemos cargar los píxeles de la imagen en cuestión usando la función loadPixels():
1 2 3 4 |
// Pixel location and color source.loadPixels(); int loc = x + y * source.width; int pix = source.pixels[loc]; |
El resultado es un valor numérico que representa al píxel (en función de su RGB, brillo, etc.).
Para modificarlo, podemos escribir un nuevo valor en el array. Despùés llamamos a la función updatePixels() para que vuelque los valores a la imagen de nuevo:
1 2 3 |
destination.pixels[loc] = c; // We changed the pixels in destination destination.updatePixels(); |
Existen una serie de métodos que podemos usar para determinar las diferentes propiedades de cada píxel, como son el rgb, el brillo, etc:
1 2 3 4 |
float bright = brightness(pix); float r = red(pix); float g = green(pix); float b = blue(pix); |
4. Dibujo
Aunque no forma parte de esta explicación, como se ha mencionado antes, Processing es una potente herramienta a la hora de realizar cualquier tipo de dibujo, diseño gráfico, etc. Por supuesto tiene un conjunto de funciones que nos permiten dibujar formas de manera sencilla:
1 2 3 |
/* Draw a ellipse */ fill(r, g, b, 100); ellipse(x + 20 + source.width * 2, y, pointillSize, pointillSize); |
Finalmente, con lo visto hasta aquí podríamos crear un par de funciones que modifiquen los píxeles de una imagen original para que, en un caso, los cambio a blanco o negro en función de su brillo, y en otro caso, los cambie por elipses de un tamaño determinado y del color del píxel. En este caso, en lugar de cambiar la imagen original, dibujamos dos nuevas imágenes. Puedes descargar el proyecto completo desde aqui.
El resultado sería algo como lo siguiente:
A la izquierda, la imagen original, en el centro en blanco y negro, y a la derecha, con elipses de colores.
¡¡Ahora a jugar con Processing!!