Implementando la carga dinámica de clases en Processing

Introducción

La carga dinámica de clases es una característica muy conocida de los lenguajes orientados a objetos que permite crear instancias de clases con solo conocer su nombre.  Esto permite a los desarrolladores crear aplicaciones que utilicen clases que sean creadas después de la aplicación sin necesidad de recompilar la misma.  También es muy útil cuando se desea generalizar la creación de un conjunto de objetos mediante el patrón de fábrica.

Gracias a la reflexión en Java y otros lenguajes como C# es muy fácil de implementar, sin embargo en Processing (derivado de Java) su implementación fue un poco mas elaborada ya que las clases que implementa el usuario son en realidad clases internas del proyecto desarrollado.

Explicación

En Processing cuando se crea un Proyecto y en él se implementan las clases A, B y C, y este se compila, internamente se crea un archivo de código fuente Java conteniendo a este código de la siguiente manera.

import processing.core.*;

// Imports del usuario

// Otros imports

public class Proyecto extends PApplet {

    class A
    {
        // ...
    }

    class B
    {
        // ...
    }

    class C
    {
        // ...
    }

    static public void main(String args[]) {
        PApplet.main(new String[] { "--bgcolor=#DFDFDF", "Proyecto" });
    }
}

Implementación

Para realizar la implementación de una versión simple del patrón de fábrica utilizando la carga dinámica de clases en Processing se supondrá que existe la clase Padre de la cual se derivan las clases Hijo1 e Hijo2 del proyecto Demostracion.

Crear un objeto donde se almacenará la instancia a crearse de manera dinámica.

Padre instance = null;

Recuperar la referencia al cargador de clases del sistema.

ClassLoader classLoader = ClassLoader.getSystemClassLoader();

Cargar la referencia de la clase a partir de su nombre (className), en este caso podría ser Hijo1 o Hijo2.  Recuerde que esta clase es realmente interna a la clase Demostracion.

Class loadedClass = classLoader.loadClass("Demostracion$" + className);

Obtener una referencia al constructor que se desea utilizar para crear la instancia.  En este caso se eligió un constructor que recibe dos parámetros: una cadena y un real.  Nótese además que todos los constructores reciben una referencia al applet de Processing, en este caso de Demostracion (que hereda a su vez de PApplet).

java.lang.reflect.Constructor constructor = loadedClass.getConstructor(new Class[] {PApplet.class, String.class, float.class});

Crear la nueva instancia utilizando el constructor obtenido en el paso anterior.  Al método newInstance se le especifican los valores de los parámetros que serán suplidos en el constructor.

instance = (Padre) constructor.newInstance(new Object[] {applet, str, flt});

Nótese como el primero de ellos, applet, hace referencia al applet de Processing que se está ejecutando.  Al no encontrar una forma mas elegante para obtener esta referencia al applet, declarar una variable global para almacenarlo.

PApplet applet;

Y obtener su valor en el procedimiento setup.

void setup()
{
    // ...

    applet = this;

    // ...
}

En el código mostrado anteriormente se deberán manejar las excepciones ClassNotFoundException y ClassCastException para poder llevar a feliz término su ejecución.

Enlaces

Detectando movimiento con Processing utilizando Flob

Introducción

Movement detection with Flob
Movement detection with Flob

En la búsqueda de como detectar movimiento en las imágenes recibidas de una cámara web utilizando un sketch de Processing para ser utilizado como medio para la interacción entre el usuario y el computador, he iniciado el día de hoy algunas pruebas con la librería Flob que facilita este proceso.

Hasta el momento se ha utilizado la característica para calcular la imagen de movimiento y la facilidad para determinar las áreas de movimiento (blobs) agrupándolas según su cercanía.

El sketch de demostración se desarrolló utilizando Processing 2 (alpha 4) por lo que se descargó la versión de desarrollo de la librería.

Enlaces

Explotando burbujas con Processing utilizando GSVideo y Minim

Introducción

Demostración de GSVideo_Bubbles
Demostración de GSVideo_Bubbles

Este sketch combina la técnica de detección de movimiento descrita anteriormente para crear un ambiente interactivo simple que consiste en explotar las burbujas que aparecen al azar sobre la imagen del usuario.  Adicionalmente utiliza la librería Minim para reproducir un efecto sonoro al explotar las burbujas.

Changelog

  • 20120509: actualización de GSVideo al paquete de Video estándar.  Requiere Processing 2.0 (2a5).
  • 20120223: versión inicial, procedimental basada en GSVideo.  Incluye reproducción de un sonido utilizando Minim.

Enlaces

Instalando javacvPro para manipular OpenCV 2.3 desde Processing en GNU/Linux Ubuntu y Mint

Introducción

Después de experimentar con la versión 2.1 en Processing que era provistra por ubaa.net he decidido probar con otra diferente ya que esta al parecer ha dejado de ser actualizada.

En este caso estoy probando javacvPro la cual se ve muy prometedora a pesar de que su sitio web y documentación se encuentra escrito en francés.

Requisitos

Es necesario contar los paquetes de la librería OpenCV 2.3 instalados.

Instalación

Descargar la versión mas reciente de la librería desde el siguiente enlace.  Ver la sección Téléchargement.

http://www.mon-club-elec.fr/pmwiki_reference_lib_javacvPro/pmwiki.php

En este caso se utilizó la versión 0.3 disponible actualmente.

Descomprimir el paquete recién descargado.

$ unzip javacvPro-0.3.zip

Crear el directorio para las librerías de Processing (si este aún no existe).

$ mkdir -p ~/sketchbook/libraries

Ubicar la librería en esta ubicación para que esté disponible para Processing.

$ mv javacvPro ~/sketchbook/libraries

Enlaces

Detectando movimiento con GSVideo y Processing

Introducción

De manera análoga a como se realizó inicialmente con OpenCV, se realizó el proceso de detección de movimiento básico utilizando Processing y la librería de GSVideo instalada hace poco.  Esta librería se especializa en la captura, grabación y reproducción del video mas que en su procesamiento así que la manipulación de las imágenes se realiza manualmente con las facilidades que provee el lenguaje.

En términos generales el procedimiento se basa en capturar el video proveniente de la cámara web a través de una instancia de la clase GSCapture provista por la librería GSVideo.  Cada una de las imágenes obtenidas es comparada con la imagen inmediatamente anterior.  De cada uno de sus píxeles se toma el color en forma de tripleta RGB y se determina su cantidad de variación utilizando la fórmula de distancia (función dist en Processing).  De esta manera se determina si en ese punto específico sucedió movimiento o no y es traducido a un píxel de color blano o negro respectivamente.

Screenshot

Imagen de movmiento sobre el objetivo
Imagen de movmiento sobre el objetivo

Enlaces

Instalando GSVideo en Processing bajo GNU/Linux

Introducción

GSVideo es una librería para Processing desarrollada por Andrés Colubri y basada en GStreamer.  Esta librería permite reproducir videos (incluyendo a la cámara web), capturar imágenes y realizar grabaciones.  Su API sigue los lineamientos de la librería nativa (processing.video.*) la cual por estar basada en Apple QuickTime no se encuentra disponible en GNU/Linux.

Instalación

La instalación de la distribución binaria es muy simple y se describe a continuación.

Descargar la versión mas reciente de la librería desde la siguiente ubicación.

http://sourceforge.net/projects/gsvideo/files/gsvideo/

En este caso se obtuvo el archivo GSVideo-1.0.0-linux.zip.

Descomprimir el paqute con la distribución de la librería.

$ unzip GSVideo-1.0.0-linux.zip

Reubicar la librería en el lugar apropiado del sketchbook.

$ mv GSVideo ~/sketchbook/libraries/

Dependencias

Como se mencionó inicialmente esta librería depende de GStreamer para su funcionamiento.  Utilizando GNU/Linux Mint 12 no fue necesario instalar ningún paquete adicional para trabajar con la librería.

$ sudo aptitude search gstream | grep “^i”

i   bluez-gstreamer                 – Bluetooth GStreamer support               
i   gir1.2-gstreamer-0.10           – Description: GObject introspection data fo
i   gstreamer0.10-alsa              – GStreamer plugin for ALSA                 
i   gstreamer0.10-ffmpeg            – FFmpeg plugin for GStreamer               
i   gstreamer0.10-fluendo-mp3       – Fluendo mp3 decoder GStreamer plugin      
i   gstreamer0.10-gconf             – GStreamer plugin for getting the sink/sour
i   gstreamer0.10-nice              – ICE library (GStreamer plugin)            
i   gstreamer0.10-pitfdll           – GStreamer plugin for using MS Windows bina
i   gstreamer0.10-plugins-bad       – GStreamer plugins from the “bad” set      
i   gstreamer0.10-plugins-bad-multi – GStreamer plugins from the “bad” set (Mult
i   gstreamer0.10-plugins-base      – GStreamer plugins from the “base” set     
i   gstreamer0.10-plugins-base-apps – GStreamer helper programs from the “base”
i   gstreamer0.10-plugins-good      – GStreamer plugins from the “good” set     
i   gstreamer0.10-plugins-ugly      – GStreamer plugins from the “ugly” set     
i   gstreamer0.10-pulseaudio        – GStreamer plugin for PulseAudio           
i   gstreamer0.10-tools             – Tools for use with GStreamer              
i   gstreamer0.10-x                 – GStreamer plugins for X11 and Pango       
i   libgstreamer-plugins-base0.10-0 – GStreamer libraries from the “base” set   
i   libgstreamer0.10-0              – Core GStreamer libraries and elements

Demostración

El siguiente código se basa en el ejemplo GettingStartedCaptureLinux incluído en la distribución de la librería.

// Import the GSVideo library classes

import codeanticode.gsvideo.*;

// GSVideo capture object reference

GSCapture cam;

void setup()
{
  size(640, 480);

  // Create the GSVideo capture object with the capture's resolution

  cam = new GSCapture(this, 640, 480);

  // Begin the video capture process

  cam.start();

  // Retrieve the video resolutions available

  println("Supported video resolutions: ");
  int[][] res = cam.resolutions();
  for (int i = 0; i < res.length; i++)
  {
    println(res[i][0] + "x" + res[i][1]);
  }

  println();

  // Retrieve the video framerates available

  println("Supported video framerates: ");
  String[] fps = cam.framerates();
  for (int i = 0; i < fps.length; i++)
  {
    println(fps[i]);
  }
}

void stop()
{
  // Stop the GSVideo webcam capture

  cam.stop();

  // Stop the sketch

  this.stop();
}

void draw()
{
  // Check if there is a capture device available

  if (cam.available() == true)
  {
    // In this case, read an image from it

    cam.read();

    // Display it on the window

    image(cam, 0, 0);
  }
}

Enlaces

Detectando movimiento con OpenCV y Processing

Introducción

Una de las características que quería aprender a implementar con OpenCV era la detección de movimiento, esto me permitirá implementar formas de interacción interesantes (y bastante simples de generar) entre el usuario y el sketch a través de la cámara web.

En pocas palabras, la técnica para detectar movimiento que se describe a continuación se basa en generar una imagen monocromática con los píxeles que han cambiado entre dos cuadros del video.  Para hacer esto se siguen los pasos mostrados a continuación.

  1. Obtener una nueva imagen de la cámara web
  2. Invertir la imagen horizontalmente (opcional)
  3. Calcular la diferencia entre la imagen recién obtenida y la almacenada en memoria (recordada)
  4. Procesar la imagen para facilitar su manejo: convertirla a escala de grises, suavizar la imagen y reducir el ruido eliminando los valores demasiado altos o bajos.
  5. Mostrar la imagen de movimiento (opcional)
  6. Recordar la imagen actual para ser procesada nuevamente en la siguiente iteración

opencv.read();

opencv.flip(OpenCV.FLIP_HORIZONTAL);

opencv.absDiff();

opencv.convert(OpenCV.GRAY);

opencv.blur(OpenCV.BLUR, 3);

opencv.threshold(20);

image(opencv.image(), 0, 0);

opencv.remember(OpenCV.SOURCE, OpenCV.FLIP_HORIZONTAL); 

Para verificar si ha sucedido movimiento en una determinada área de la imagen se debe verificar si los píxeles que pertenecen a esa área se encuentran blancos (si hubo) o no.  Esta información se obtiene utilizando el método get(int x, int y) del objeto PImage el cual retorna el color del píxel seleccionado.  Por facilidad se recomienda que se obtenga el brillo (brightness(color)) de este píxel para su comparación.

El siguiente código revisa un área cuadrada de píxeles en búsqueda de movimiento en esa zona.

for(int px=x; px<x+size; px++)  
    for(int py=y; py<y+size; py++)     
        if (px < width && px > 0 && py < height && py > 0)       
            if (brightness(mImage.get(px, py)) > 127)
                count ++;

Screenshot

Imagen de movmiento sobre el objetivo (modo buffer)
Imagen de movmiento sobre el objetivo (modo buffer)

 

Enlaces

Poniendo sombreros a las personas con OpenCV y Processing

Introducción

Como una versión un poco mas elaborada de la publicación anterior he preparado este sketch que toma el flujo de video proveniente de la cámara web,  encuentra en él los rostros de las personas y les pone un sombrero.  Se incluyen diferentes sombreros que pueden cambiarse mediante el teclado.

q – terminar el sketch
a – mostrar/ocultar el rectángulo rojo alrededor de cada cara encontrada
s – mostrar/ocultar la máscara sobre cada cara encontrada
z/x – alternar entre las imágenes de máscaras disponibles

Screenshots

Demostración con varias personas en la misma imagen
Demostración con varias personas en la misma imagen
Demostración con la foto de un rostro
Demostración con la foto de un rostro

Agradecimientos para Martha, Diego y Jennifer Connerlly por participar como modelos para las fotos del artículo.

Acerca de la ubicación de los rostros

Hasta el momento el perfil que mejores resultados me ha dado para ubicar los rostros de las personas ha sido OpenCV.CASCADE_FRONTALFACE_ALT_TREE (haarcascade_frontalface_alt_tree.xml).  Este encuentra los rostros con el menor número de errores (identificación equivocada de un objeto inanimado como si fuera un rostro), sin embargo parece tener poca tolerancia a las variaciones de posición del rostro de las personas, si ellas agachan o rotan un poco la cara probablemente ya no serán renocidas.  Un trabajo a futuro consistirá en encontrar como mejorar estos resultados de la ubicación de rostros.

Estos son los perfiles de reconocimiento disponibles a través de OpenCV según la instalación que se realizó de la librería.

Los siguientes perfiles se encuentran disponibles como constantes asociadas a la clase OpenCV de Processing.

  public static final String CASCADE_FRONTALFACE_ALT_TREE
  public static final String CASCADE_FRONTALFACE_ALT
  public static final String CASCADE_FRONTALFACE_ALT2
  public static final String CASCADE_FRONTALFACE_DEFAULT
  public static final String CASCADE_PROFILEFACE
  public static final String CASCADE_FULLBODY
  public static final String CASCADE_LOWERBODY
  public static final String CASCADE_UPPERBODY

A continuación se listan todos los archivos XML de los perfiles de reconocimiento que también pueden ser utilizados directamente en Processing.

  haarcascade_eye_tree_eyeglasses.xml   
  haarcascade_frontalface_default.xml  
  haarcascade_mcs_eyepair_small.xml  
  haarcascade_mcs_upperbody.xml
  haarcascade_eye.xml                   
  haarcascade_fullbody.xml             
  haarcascade_mcs_lefteye.xml        
  haarcascade_profileface.xml
  haarcascade_frontalface_alt2.xml      
  haarcascade_lefteye_2splits.xml      
  haarcascade_mcs_mouth.xml          
  haarcascade_righteye_2splits.xml
  haarcascade_frontalface_alt_tree.xml  
  haarcascade_lowerbody.xml            
  haarcascade_mcs_nose.xml           
  haarcascade_upperbody.xml
  haarcascade_frontalface_alt.xml       
  haarcascade_mcs_eyepair_big.xml      
  haarcascade_mcs_righteye.xml 

Enlaces

Ubicando caras en una webcam con OpenCV y Processing

Introducción

En este sketch se aprovecha la agilidad para desarrollar en Processing con la facilidad que provee OpenCV para ubicar los rostros de las personas en el flujo de video proveniente de una cámara web.  En este caso el sketch mostrará una cara sonriente cuando se encuentra acompañado por una persona y una cara triste cuando está solo.

Screenshots

Sketch feliz
Sketch feliz

Sketch triste
Sketch triste

Enlaces