Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Sistemas Interactivos1
Sistemas Interactivos1
interactivos en el arte
Emiliano Causa, Diego Alberti
ISBN 978-987-3706-08-0
ISBN: 978-987-3706-08-0
La Universidad Virtual de Quilmes de la Universidad Nacional de Quilmes se reserva la facultad de disponer de esta
obra, publicarla, traducirla, adaptarla o autorizar su traducción y reproducción en cualquier forma, total o parcial-
mente, por medios electrónicos o mecánicos, incluyendo fotocopias, grabación magnetofónica y cualquier sistema
de almacenamiento de información. Por consiguiente, nadie tiene facultad de ejercitar los derechos precitados sin
permiso escrito del editor.
2
Íconos
Leer con atención Para ampliar
Son afirmaciones, conceptos o definiciones Extiende la explicación a distintos casos o
destacadas y sustanciales que aportan cla- textos como podrían ser los periodísticos o
ves para la comprensión del tema que se de otras fuentes.
desarrolla.
Actividades
Para reflexionar Son ejercicios, investigaciones, encuestas,
Propone un diálogo con el material a tra- elaboración de cuadros, gráficos, resolu-
vés de preguntas, planteamiento de pro- ción de guías de estudio, etcétera.
blemas, confrontaciones del tema con la
realidad, ejemplos o cuestionamientos que
alienten la autorreflexión. Audio
Fragmentos de discursos, entrevistas, re-
gistro oral del profesor explicando algún
Texto aparte tema, etcétera.
Contiene citas de autor, pasajes que contex-
tualicen el desarrollo temático, estudio de
casos, notas periodísticas, comentarios para Audiovisual
formular aclaraciones o profundizaciones. Videos, documentales, conferencias, frag-
mentos de películas, entrevistas, grabacio-
nes, etcétera.
Pastilla
Incorpora informaciones breves, comple-
mentarias o aclaratorias de algún término o Recurso web
frase del texto principal. El subrayado indica Links a sitios o páginas web que resulten
los términos a propósito de los cuales se in- una referencia dentro del campo discipli-
cluye esa información asociada en el margen. nario.
Lectura recomendada
Ejemplo Bibliografía que no se considera obligato-
Se utiliza para ilustrar una definición o una ria y a la que se puede recurrir para ampliar
afirmación del texto principal, con el objetivo o profundizar algún tema.
de que se puedan fijar mejor los conceptos.
Línea de tiempo
Código Se utiliza para comprender visualmente
Incorpora al material un determminado una sucesión cronológica de hechos.
lenguaje de programación.
3
4
Índice
Los autores 7
Introducción 8
Objetivos del curso 8
Unidad 1 Unidad 3
Sistemas interactivos en el arte Visión por computadora
1.1. Introducción 9 3.1. Introducción 58
1.2. Arquitectura de un sistema interactivo 10 3.2. Captura óptica de movimiento 58
1.2.1. Instalaciones interactivas 12 3.2.1. Captura por substracción de movimiento 58
1.2.2. Hardware y software de los sistemas 3.3. Captura devideo en Processing 61
interactivos en el arte: lo físico y lo virtual 14 3.4. La substracción de video 62
1.2.3. Los campos de acción: sensado, 3.5. Cómo se programa con la captura de movimiento 64
reproducción, generación y control 15 3.5.1. Los métodos principales 67
1.2.4. Lenguajes de programación y herramientas 3.5.2. Calibrar el umbral 68
de control 16 3.5.3. Monitoreo del resultado 68
1.2.5. Taxonomía del software de los sistemas 3.5.4. A la búsqueda del movimiento 69
interactivos en el arte 17 3.6. Revisión de la clase PMoCap 71
1.2.6. Sistemas operativos 25 3.7. Aplicación de la captura de movimiento 75
1.2.7. Sistemas ópticos de sensado de posición
y silueta 25
1.2.8. Protocolos de comunicación 31 Unidad 4
1.2.9. Hardware de los sistemas interactivos: Detección de contenido e interfaces tangibles
sistemas de control y sensado físico 33 4.1. Detección de Blobs 81
4.1.1. La librería BlobDetection 83
4.1.2. Un ejemplo con detección de Blobs 84
Unidad 2 4.2. Sistema de detección de patrones para
Imagen y sonido en tiempo real la implementación de una mesa reactiva 95
2.1. Introducción 35 4.2.1. El protocolo TUIO 98
2.2. La computadora personal como herramienta 35 4.2.2. El funcionamiento del ejemplo 102
2.3. Herramientas de software 38 4.3. Breve comentario final 111
2.3.1. Pure Data (PD) 38
2.3.2. Processing 40
2.4. Ejemplo de aplicación 41 Referencias bibliográfícas 113
2.5. Generación de imagen a partir de Processing y
datos por osc 53
5
6
Los autores
Emiliano Causa
Nació en La Plata (Argentina) en 1970. Artista multimedia e Ingeniero en Sistemas de Infor-
mación (Universidad Tecnológica Nacional). Integrante fundador del grupo Proyecto Biopus
(www.biopus.com.ar). Fue Coordinador del MediaLab del Centro Cultural de España en Bue-
nos Aires. Ha sido Coordinador de la Dirección de Investigación y Posgrado en el Área de Artes
Multimediales del IUNA. Es profesor de Arte Multimedia e Informática Aplicada en la Licen-
ciatura de Artes Multimediales del IUNA. Profesor de Tecnología Multimedia de la carrera de
Diseño Mutimedial de la Facultad de Bellas Artes de la Universidad Nacional de La Plata.
Dirige el Laboratorio de Experimentación EmmeLab de la Facultad de Bellas Artes UNLP. Es profesor de Sistemas Diná-
micos II de la Maestría en Artes Electrónicas de la UNTREF. Dicta el Taller de Investigación y Realización de Objetos II en
la Especialización de Teatro de Objetos del Área de Artes Dramáticas del IUNA. Trabaja en investigaciones relaciona-
das con Bioarte, Realidad Aumentada y Arte Generativo. Fue docente de Informática en las carreras de Composición
Musical y Cine en la UNLP y de Inteligencia Artificial en Ingeniería de Sistemas en la UTN.
Actualmente trabaja en investigación y dicta cursos de posgrado relacionados al arte interactivo y nuevas interfaces
(sensores y control de video y sonido en tiempo real). Se dedica al arte interactivo, al arte generativo, a la construcción
de instalaciones interactivas y a la aplicación de la informática al arte en general.
Diego Alberti
Estudió Diseño de Imagen y Sonido y Diseño Gráfico (FADU, UBA). Realizó diversos cursos
de posgrado y talleres relacionados con arte y nuevas tecnologías. Actualmente produce
música electrónica, videos, software y piezas de arte digital.
Expuso su obra en Museo Macro de Rosario, Museo Municipal de Bellas Artes Juan B. Castag-
nino de Rosario, Museo de Arte Contemporáneo de Bahía Blanca, Centro Cultural Recoleta,
Centro Cultural San Martín, Centro Cultural de España en Buenos Aires, Museo Municipal de
Arte Ángel María de Rosa de Junín, Espacio Fundación Telefónica de Buenos Aires, Museo de
Arte Moderno de Mendoza, medialabPrado Madrid y FILE Festival de San Pablo.
Es docente de la materia Informática Aplicada 2 Artes Multimediales en el Instituto Universitario Nacional de Arte y de
la materia Dibujo y Maqueta Cátedra Vitullo de la carrera de Diseño de Imagen y Sonido en Facultad de Arquitectura
Diseño y Urbanismo de la Universidad de Buenos Aires.
Dicta cursos sobre tecnología y nuevos medios en diversas instituciones del país: MBA/MAC de Bahía Blanca, Univer-
sidad Nacional de Noreste, Centro Cultural de España en Buenos Aires, Universidad de Palermo, Centro Cultural de
España Chile, Centro Cultural Balmaceda Arte Joven, Valparaíso, Chile.
Trabaja y reside en Buenos Aires, desarrolla diferentes proyectos artísticos relacionados con electrónica, nuevas tec-
nologías y software. Colabora con artistas como Carlos Trilnick, Gabriel Valansi, Marina de Caro, Juan Rey, Mariano
Giraud, Ernesto Romeo, Jorge Haro y otros.
7
Introducción
En este mateiral desarrollaremos los aspectos fundacionales de lo que consideramos elemental a cualquier obra de
arte interactiva que utilice tecnología. Nos referimos esencialmente al uso de software para lograr aplicaciones de
audio y sonido que permitan la generación en tiempo real de espacios sinestésicos donde imagen y sonido se com-
plementen de manera homogénea.
Utilizaremos los dispositivos más inmediatos con los que contamos en una computadora personal para realizar acti-
vidades donde la imagen y el sonido interactúen de forma dinámica.
Recurriremos a cámaras de video para detectar presencia o actividad dentro de una escena y con ella producir sonido
y como contraparte veremos cómo analizar audio proveniente de un micrófono para generar o modificar una imagen.
Consideramos que este acercamiento por demás elemental esconde un alto nivel de complejidad producto del avan-
ce constante de la tecnología puesta en juego y de la enorme cantidad de variables que aparecen involucradas en el
desarrollo de este tipo de aplicaciones; principalmente en lo relacionado a lo azaroso del mundo real y el comporta-
miento imprevisible de los individuos interactuantes. En ese sentido es que invitamos a los estudiantes a profundizar
cada uno de los temas abordados a partir de los conceptos fundamentales que aquí se presentan.
Esperamos que esta colección de recursos sirva como disparador para poder luego ahondar en la complejidad que el
tópico propone.
Los foros y la documentación relacionada con las aplicaciones utilizadas son nuestras fuentes de información, es por
eso que recomendamos a los estudiantes consultar sistemáticamente toda la información referida a los problemas
concretos en la web ya que es allí donde la información permanece actualizada.
Esperamos que disfruten la lectura de este material y que consigan completar con éxito las consignas y ejercicios que
aquí se presentan.
8
1. Sistemas interactivos en el arte
Objetivos
• Incorporar conocimientos introductorios sobre el campo de la multimedia aplicada fundamentalmente a los siste-
mas interactivos en tiempo real.
• Reflexionar sobre las cuestiones tecnológicas elementales que hacen a la particularidad de esta disciplina.
• Conocer las relaciones intrínsecas existentes entre software y hardware.
• Proyectar una obra tecnológica teniendo en cuenta los aspectos fundamentales generalmente desarrollados en
este campo.
1.1. Introducción
En esta unidad nos ubicaremos en el campo de la multimedia aplicada y focalizaremos en los sistemas interactivos en
tiempo real. Entendemos por “tiempo real” toda aquella actividad en donde el resultado del estímulo generado por
el espectador o interactor aparece reflejado en una consecuencia sinestésica más o menos instantánea. Llamamos
sinestésico a todo aquel fenómeno donde se ven involucrados diferentes sentidos a la vez.
La cibernética es una ciencia introducida en la década de 1940 por Norbert Wiener. Trata del estudio de los
sistemas autoregulados. La idea de interactividad está íntimamente relacionada con estos conceptos aun-
que de manera superadora. En general, lo que se espera de una obra interactiva es que de alguna manera
“dialogue” con el espectador y no sea solo una acción reactiva la que deviene frente al estímulo del público.
Por decirlo de otra forma, un sistema interactivo es capaz de “percibir” al público o ciertas acciones o particularidades
de este y generar una respuesta a través de dispositivos y medios de representación. Su característica central se vin-
cula con la capacidad de lograr inmediatez entre las acciones del público y la respuesta generada; lo que en nuestro
campo se llama “tiempo-real”. La generación de esta respuesta a través de medios de representación (como el sonido
y la imagen) en tiempo real, solo es posible (salvo raras excepciones) por medio del procesamiento de la información
mediante computadoras y por ende, las técnicas y herramientas para la producción de sistemas interactivos está
fuertemente ligada a la informática y a la electrónica.
Este material no intenta ser una definición sobre el arte interactivo en términos conceptuales, sino más
bien una descripción general de nuestro objeto de estudio, abordado desde un punto de vista tecnológico.
9
El campo de estudio en el que pretendemos involucrar-
nos es en extremo extenso por la diversidad de recur- Es la ley de Moore según la cual
sos y herramientas con las que contamos para producir “aproximadamente cada 18 me-
nuestras obras y por la profundidad necesaria con la ses se duplica el número de tran-
cual debemos abordar el aprendizaje de estos temas sistores en los circuitos integra-
para lograr superar las barreras técnicas que estos dis- dos”, es decir, que la tecnología
positivos o herramientas nos presentan. Si bien esto no crece a un paso exponencial.
debiera ser motivo de conflicto sucede que, además Ley de Moore: <http://www.com-
de ser un campo extenso, este se expande de manera puterhistory.org/semiconductor/
exponencial conforme avanza el desarrollo tecnológi- timeline/1965-Moore.html>
co tanto de las aplicaciones como de los dispositivos
informáticos. Esto a su vez trae aparejada la aparición
repentina de nuevos espacios de acción.
Es por esto que nos vemos obligados a tratar de lograr un enfoque metodológico y además conceptual respecto
de las disciplinas asociadas al uso de computadoras en el arte y los multimedios. Esta unidad en particular aparece
como una introducción a este cambiante mundo de acción en el cual los conocimientos sobre los dispositivos y he-
rramientas que lograremos aprender hoy, seguramente serán obsoletos el día de mañana. De ahí la importancia de
centrarnos en los conceptos fundamentales de esta disciplina.
10
Imagen 1.1. Formula for computer art
Es importante aclarar que todos los sensores con los que trabajamos convierten de alguna u otra forma
una magnitud o parámetro del mundo físico en una magnitud eléctrica. Estos sensores se encuentran ge-
neralmente conectados a una interfaz digitalizadora, es decir, un dispositivo preparado para comunicar
esas señales eléctricas producidas por el sensor en el “lenguaje” que la computadora puede comprender.
Esto es básicamente un conversor “analógico-digital”. Nuestro estudio estará fuertemente inclinado a
los sistemas de captura de sonido y ópticos ya que son los dispositivos más inmediatos y que mayores
prestaciones ofrecen al intentar capturar información en tiempo real de una escena o un dispositivo de
interfaz tangible.
Como material central en su trabajo, Jim Campbell utiliza la idea de memoria, por ese motivo hace hincapié dentro de
su esquema en el ítem Memory (invisible): memoria invisible.
La memoria es un componente fundamental de la computadora ya que permite que las operaciones puedan ser
realizadas. La otra parte que Campbell propone como relevante son los algoritmos. En nuestro caso, esos algoritmos
y esa memoria está mediatizada por softwares disponibles tanto como producto comercial o experimental o aquellos
que nosotros mismos podemos construir para desarrollar nuestras propias experiencias.
Por último, el esquema señala un controlador de salida: es cualquier dispositivo capaz de recibir instrucciones desde
un ordenador y a su vez traducir esa información en señales que pueden controlar diversos artefactos electrónicos o
resultados concretos esperados por tal o cual dispositivo. Campbell menciona “texto que se mueve”, “objetos moto-
rizados”, “generadores de viento”, etc. En general, nos limitaremos a artefactos capaces de producir sonido o imagen
(proyectores, pantallas, sistemas de sonido, etcétera).
11
En resumen, en cualquier dispositivo tecnológico multimedial podemos encontrar tres partes claramente definidas:
un subsistema de entrada de datos, un proceso ligado a operaciones informáticas (de automatización de datos) y un
subsistema de salida que devuelve al medio el resultado del proceso sobre los elementos de entrada.
Texto Aparte
Una vez allí son invitados a enviar un mensaje de texto a un número predetermi-
nado. Los textos enviados al sistema aparecen proyectados en las paredes de la
escenografía; a su vez, un sintetizador de voz los canta. En ese momento se inicia
12
una búsqueda en internet al respecto de esos mensajes que devuelve fragmentos
de oraciones extraídos de los resultados obtenidos. Para esa búsqueda se utilizan
diversos buscadores como Yahoo y Google. Toda la información tanto de los textos
ingresados, los números de los celulares participantes y las búsquedas devueltas
son almacenados en una base de datos. Junto a los textos enviados por los espec-
tadores comienzan a aparecer todos los resultados obtenidos en las búsquedas.
La imagen anterior (1.3.) es un esquema del sistema para tres pantallas: el software
recibe como input mensajes, búsquedas que realiza en la web y un control de joys-
tick para manipular la visualización en tiempo real. Como salida tenemos el sistema
de pantallas inmersivo y el sistema de sonido donde se reproduce la banda sonora
con el agregado de los textos cantados y las sonificaciones de los eventos.
13
1.2.2. Hardware y software de los sistemas interactivos en el arte: lo físico y lo virtual
En los sistemas interactivos aplicados al arte, uno de los puntos centrales de su objeto de estudio se centra en la rela-
ción que se construye entre el público y la obra entendido desde este punto de vista: las particularidades existentes
entre el cuerpo humano y el sistema interactivo. Cuando hablamos de comunicación entre el cuerpo y un sistema,
aparece el campo de la “interfaz”. Gui Bonsiepe describe la interfaz de la siguiente forma:
En primer lugar existe un usuario o agente social, que desea efectivamente cumplir
una acción. En segundo lugar se encuentra una tarea que él mismo quiere ejecutar
[...] En tercer lugar existe un utensilio o un artefacto del que necesita el agente
para llevar a término la acción [...] Y aquí aparece la cuestión de cómo se pueden
conectar, hasta formar una unidad, a tres elementos tan heterogéneos: el cuerpo
humano, el objetivo de una acción, un artefacto o una información en el ámbito de
la acción comunicativa. La conexión entre estos tres campos se produce a través
de una interfase. Se debe tener en cuenta que la interfase no es un objeto, sino un
espacio en el que se articula la interacción entre el cuerpo humano, la herramienta
(artefacto, entendido como objeto o como artefacto comunicativo) y objeto de la
acción. [...] la interfase vuelve accesible el carácter instrumental de los objetos y el
contenido comunicativo de la información. Transforma los objetos en productos;
transforma la simple existencia física (Vorhandenheit) en el sentido de Heidegger,
en disponibilidad (Zuhandenheit). (Bonsiepe, 1999: 17)
En el caso de los sistemas informáticos, esta interfaz puede dividirse en dos espacios, el de los dispositivos común-
mente considerados de entrada/salida (hardware) y el de las aplicaciones o programas que son mediadas a través de
estos dispositivos (software). Se puede considerar que en términos “físicos” el público se enfrenta a un conjunto de
dispositivos que generan representaciones, interfaz física, pero en términos “virtuales” las representaciones (produci-
das por estos dispositivos) constituyen una interfaz virtual.
Es común que en informática se contrapongan lo “físico” a lo “lógico”, entendiendo por “lógico” aquello que existe
representado como dato y que no tiene una manifestación física, tangible. En este sentido, lo virtual se inscribe en
el mundo de lo lógico. Estas distinciones puede parecer que no tienen importancia, a primera vista, pero en nuestra
práctica trabajar del lado virtual o físico de la interfaz puede acarrear diferentes tipos de problemas.
El trabajo con entidades virtuales (lógicas) se relaciona con problemáticas del tipo algorítmicas en donde se debe
decidir cuál es la mejor estrategia para procesar determinado tipo de información en pos de un resultado, es decir,
problemas en el campo de la informática. El trabajo con entidades físicas, se vincula con problemas mecánicos, elec-
tromagnéticos y otros, del orden de la física y la ingeniería, y sus estrategias muchas veces deben apuntar a lograr
limpiar el ruido intrínseco del mundo físico para poder extraer información de sus fenómenos.
Por último, controlar un motor mediante una interfaz de conversión DAC puede, muchas veces, estar estrictamente
ligada al campo de los problemas físicos: cálculos de corriente y potencia, estrés mecánico de los materiales, etcétera.
14
En relación con la cuestión de integración de software con dispositivos
electromecánicos resulta de vital importancia el campo de la robótica.
Una referencia invaluable es el sitio Robots Argentina donde puede en-
contrarse todo tipo de información respecto de sensores, actuadores y
dispositivos de control aplicados: <http://robots-argentina.com.ar/>
Más allá del problema de interfaz eléctrico que significa poder introducir en la computadora parámetros que repre-
sentan magnitudes del orden de lo físico, lo verdaderamente importante del asunto es poder relacionar esos paráme-
tros con valores de control sobre el dispositivo informático; esto es: mapeo.
Estas traducciones y modificaciones de los valores para acondicionar las magnitudes de un espacio a otro (de lo físico
a lo virtual) está dado por la inteligencia aplicada en las capas de software que median entre un valor y otro. Por eso,
gran parte de este estudio estará dedicado a los procesos que se efectúan en la conversión de valores, por ejemplo, la
velocidad de una mano que controla la intensidad con que se desplaza un objeto en la pantalla.
Muchas veces, este tipo de configuraciones implica el proceso de las señales para lograr describir tendencias en lugar
de valores instantáneos (cómo varía tal magnitud en vez de su valor instantáneo).
15
Infinidad de dispositivos controladores salieron al
mercado en los últimos años: perillas, botones, tecla- El protocolo MIDI es un estándar de
dos y los más diversos sistemas con control inundan comunicación para instrumentos
las vidrieras de las casas de videojuegos y electrónica. musicales electrónicos desarrolla-
Muchos de esos dispositivos pueden ser hackeados y do a finales de la década de 1970
empleados de maneras innovadoras en los proyectos. por los principales fabricantes de
Web cams, Controles MIDI, Wii Motes, Joysticks tradi- instrumentos electrónicos. Es prác-
cionales y en este último tiempo el Kinect de Microsoft ticamente ubicuo en todos los sis-
aparecen en todo tipo de instalaciones interactivas temas de producción musical. Se
muchas veces ocultos en escenografías especialmente pueden consultar más detalles so-
diseñadas. Aprenderemos cómo desarrollar nuestros bre MIDI en el sitio de la asociación
propios dispositivos de control a partir de estos y otros de fabricantes que utilizan la nor-
artefactos disponibles. ma: <http://www.midi.org>
Bien sabemos que la particular capacidad de una computadora es la de poder controlar de manera extremadamente
precisa y rápida una inmensidad de parámetros. Esto las coloca como herramientas fundamentales para producir
espacios inmersivos donde uno o varios performers u operadores pueden controlar diversos elementos en la escena.
Pero es también gracias a esas mismas características de precisión y velocidad que las computadoras pueden ser má-
quinas extremadamente útiles para generar entornos virtuales produciendo imagen y sonido de manera automática.
Estas dos grandes capacidades determinan, en consecuencia, tres grandes grupos de softwares: los que están pensa-
dos para controlar y/o modificar la reproducción de algún material multimedial preexistente, los que pueden generar
materiales visuales y sonoros sintéticos y los que pueden realizar ambas tareas a la vez.
Recordemos que si bien hay muchos programas que, por ejemplo, pueden producir imágenes o bocetos con mode-
los tridimensionales, secuencias de sonidos o cualquier otro tipo de imágenes en general, solo nos interesan los que
permiten ejecutar estos procesos en tiempo real.
Texto Aparte
16
1.2.5. Taxonomía del software de los sistemas interactivos en el arte
En relación con lo anterior, intentaremos ahora una discriminación bien estructural de los diversos sistemas de soft-
ware disponibles que utilizaremos, en función de cuál es el tipo de aproximación que el usuario puede tener. Esto se
traduce fundamentalmente en el grado de abstracción que tal o cual software permite a la hora de realizar nuevas
operaciones. Es decir, por un lado tenemos disponibles lenguajes completos de programación y entornos de desarro-
llo específicos para diseño y producción de arte visual y sonoro. Y en el otro extremo tendremos softwares prediseña-
dos, esto es: cerrados, cuya funcionalidad específica es provista por el creador y no admite la modificación sustancial
de las funciones originales (pero sí su manipulación).
Nos interesa, entonces, hacer una diferenciación de estas herramientas según un criterio arbitrario. Este criterio res-
ponde a los tipos de aplicaciones que consideramos más relevantes para nuestra disciplina. A lo largo del material
intentaremos describirlos:
• Sistemas de generación y control de sonido e imagen en tiempo real.
• Sistemas ópticos de captura de movimiento (MoCap).
• Sistemas de análisis de contenido (Blob Detection).
• Protocolos de comunicación (OSC).
Muchas de las herramientas de VJ (como vimos, VJ es la interpretación de imagen en tiempo real análogo a un DJ) se
encuadran dentro de la primera categoría, ya que son aplicaciones que permiten asociar la ejecución (reproducción)
de contenidos visuales (imágenes estáticas, videos o animaciones) a ciertos eventos, así como controlar la forma en
que se les aplica efectos a estos contenidos.
En el conjunto de las herramientas para reproducción y control en tiempo real, podemos encontrar algunas de las
aplicaciones para VJ como Arkaos, Resolume, VDMX y Modul8, entre otros.
17
En la imagen anterior (1.4.) se muestra la interfaz gráfica de Arkaos. Como puede verse, Arkaos se encuadra dentro de
la metáfora de la consola, ya que su interfaz gráfica está diseñada como un conjunto de paneles en los que el usuario
puede organizar el material (los videos, animaciones, efectos, etc.) y decidir a qué eventos los asocia, como sucede
en el panel inferior en el que aparece un teclado (con disposición de teclado de piano) en el que el usuario acomoda
los videos que ejecutará con cada tecla. También se pueden ver varios controles gráficos que imitan potenciómetros
lineales que servirán para configurar parámetros de la reproducción y de los efectos que se aplican durante la misma.
Texto Aparte
Arkaos
<http://www.arkaos.net>
Versión Actual: Grand VJ.
Resolume
<http://www.resolume.com/avenue/>
Versión Actual: 3.0
VDMX
<http://vidvox.net/>
Versión actual: 5
18
Modul8
<http://www.modul8.ch/>
Versión actual: 2.6.4
Modul8 es el más versátil de toda la línea ya que fue diseñado principalmente por
visualizadores, vjs y artistas visuales. Sus desarrolladores son Suizos. Modul8 inte-
gra todo un espacio escénico: múltiples pantallas, sonido y además la posibilidad
de integrar sistemas de control de luces con estándar DMX (un protocolo de comu-
nicación para iluminación escénica). Sus desarrolladores también crearon actual-
mente un software muy popular para diseño de mappings llamado “MadMapper”
(<www.madmapper.com>).
Ableton’s Live
<http://www.ableton.com/live-8>
Versión Actual: Ableton Live 8
Como se dijo anteriormente todas estas aplicaciones son cerradas, es decir que las
posibilidades de modificación corren solo por cuenta de sus desarrolladores.
También encontramos lenguajes de programación para contenidos multimediales que nos permitirán construir nues-
tras propias herramientas con funciones particulares. Estos lenguajes de programación permiten que el usuario defi-
na (muchas veces desde cero) cómo se orquesta la reproducción o generación de los contenidos.
En estos sistemas impera la lógica del algoritmo bajo dos modalidades: la primera corresponde a los entornos visua-
les en los que el usuario configura una red de objetos que se comunican entre sí procesando de diferentes formas la
información.
Este tipo de entornos de programación se denominan “visuales” ya que la codificación de la secuencia lógica de fun-
cionamiento se realiza a través de un entorno gráfico donde las funciones están representadas fundamentalmente
por objetos interconectables. Cada uno de estos objetos tiene una serie de conexiones preestablecidas que toma o
devuelve (según sea el caso) tipos de datos específicos.
Cada objeto en cuestión está preparado para resolver una tarea muy concreta, lo que permite una gran flexibilidad de
interconexión con el resto de los objetos con el fin de conseguir una funcionalidad más compleja.
19
Imagen 1.5. Interfaz de max/MSP
La imagen anterior (1.5.) muestra la interfaz gráfica de Max/MSP, un entorno de programación visual cuya evolución
tiene ya casi 25 años. Como puede verse en el esquema de la parte izquierda, el algoritmo está organizado en un
conjunto de objetos, representados por los cuadros con texto en su interior, que están conectados entre sí por ca-
bles, expresados por las líneas. La idea consiste en procesar información mediante su paso a través de los diferentes
objetos. En el caso de Max/MSP la información va de arriba hacia abajo, por ejemplo en la imagen, parte del proceso
se inicia en el objeto de arriba, que tiene el icono del micrófono, y termina en los objetos ubicados en la parte inferior
como el que tiene el dibujo del parlante.
Debido a que de un objeto pueden surgir varios “cables” y que otros objetos pueden aceptar varias conexiones como
entrada, este paradigma de programación suele privilegiar ciertas estrategias de proceso en paralelo. Además, por
su lógica de flujo, facilita la programación de aquellos algoritmos que pueden ser pensados como una secuencia de
procesos (o filtros) transformadores aplicados uno a continuación de otro, por ejemplo, en un flujo de sonido o video.
Por otra parte, se tiene cierta desventaja cuando se quiere programar estrategias que implican cambios de estados en
el tiempo, particularmente si estas llevan a complejos bucles de retroalimentación.
Dentro de este tipo de entornos de programación podemos nombrar a MaxMSP, Pure Data, VVVV, Quartz Composer,
entre otros.
20
Texto Aparte
De más está decir que ambos mundos (el visual y el sonoro) pueden convivir en la
misma aplicación con lo cual Max/MSP + jitter (o PD+Gem) pueden ser entornos
muy adecuados para resolver instalaciones en donde se requiere que el sonido y la
imagen funcionen de manera sincrónica.
VVVV
<http://vvvv.org/tiki-index.php>
Versión Actual: 40beta23
Quartz Composer
<http://en.wikipedia.org/wiki/Quartz_Composer>
Versión Actual: 4 (para Mac OS 10.6 Snow Leopard)
21
Si bien el Massachusetts Institute of Technology (MIT) tiene una larga tradición ligada a la producción de sistemas y
lenguajes para la enseñanza de la informática en distintos niveles, no es hasta la aparición de Processing que los ar-
tistas visuales o realizadores pudieron comenzar a desarrollar de manera prolija y sistemática sus trabajos utilizando
código de computadora. Processing es en principio un lenguaje de programación diseñado para poder bocetar de
manera rápida ideas visuales en un contexto informático. Es libre de ser distribuido y además es de código abierto, es
decir, que cualquiera que esté interesado puede acceder a su código fuente para ver cómo está internamente progra-
mado. Esto permite además optar por modificarlo o extenderlo según una necesidad particular.
Processing (abreviado P5) es en gran medida el origen de una serie de desarrollos que se fueron desencadenando
bajo este concepto para acercar a artistas en general, herramientas que hasta ese momento estaban solo disponibles
para aquellos que tuviesen los conocimientos necesarios para poder adentrarse en la informática y en la microelec-
trónica. La plataforma de hardware libre Arduino es otro ejemplo de ello.
En ese sentido P5 es el más “cuidado” de todos los lenguajes de programación asociados a las artes visuales. Existen
otros lenguajes con mayor desarrollo pero que se encuentran ligados a una función comercial particular, por ejemplo
AS3 Flash. Estos lenguajes fueron específicamente diseñados a partir de una necesidad técnica muy concreta: animar
la web. Processing a su vez fue creado con una intención didáctica, esa es la razón principal por la cual es de código
abierto.
22
Como referencia tanto histórica como específica del lenguaje Processing
recomendamos la lectura del libro Processing a Programming Handbook
editado por Mit Press con prólogo de Joe Maedea, quien fuera profesor
de Ben Fry y Casey Reas, iniciadores del proyecto Processing. P5 es re-
sultado de uno de sus trabajos en laboratorio de investigación en el MIT.
P5 es, como ya dijimos, un completo lenguaje de programación gráfica pero también un entorno de desarrollo por el
cual uno puede producir aplicaciones ejecutables (como cualquier otro programa en nuestro ordenador) a partir del
código escrito en lenguaje P5.
El proceso de confección de una aplicación consiste en generar el código con las instrucciones necesarias en lenguaje
comprensible por el ser humano para luego ser convertido (compilado) al lenguaje de la máquina. Processing resuel-
ve este proceso que suele ser en algunos casos muy complicado de manera automática y sencilla: basta apretar play
(run) para que nuestro programa corra en la pantalla.
El éxito de P5 fue tal que el número de usuarios globales del sistema creció exponencialmente año tras año y aún hoy
se mantiene la tendencia. En gran parte esto se debe al gran número de librerías que fueron apareciendo para sortear
diferentes problemas específicos de los usuarios de Processing.
Una librería es un paquete de software que se puede utilizar para resolver alguna cuestión específica como comuni-
carse con una cámara web sin necesidad de saber específicamente nada sobre la cámara. Otras librerías están des-
tinadas a solucionar, por ejemplo, problemas de orden gráfico, es decir, cómo se puede realizar una operación de
procesamiento visual de manera rápida y prolija. Hay en este momento varios cientos de librerías avaladas por los de-
sarrolladores de P5 que aceleran el proceso de creación de software visual para entornos interactivos. Veremos varias
de ellas a lo largo de este material y analizaremos un caso particular creado exclusivamente para resolver el problema
de la captura de movimiento en una escena.
Algunas de las librerías más utilizadas son “minim” para captura y análi-
sis de audio, “BlobDetection” para análisis de contenido en la imagen y
“Firmata” para interactuar con un dispositivo Arduino.
<http://processing.org/reference/libraries/>
Texto Aparte
23
Como referencia a otros lenguajes de programación y entornos para la computación gráfica interactiva mencionare-
mos solo dos:
openFrameworks: aparece como una alternativa a Processing para aquellos realizadores que además poseen un co-
nocimiento más avanzado en programación o que ya hicieron sus primeros pasos en Processing y ahora sus aplica-
ciones requieren de mayor rendimiento o del uso de piezas de software no disponibles en Java (que es el lenguaje
original sobre el cual está escrito Processing).
openFrameworks es también de código abierto y con una comunidad creciente muy efervescente que produce en
conjunto una cantidad de software increíblemente alta, versátil y ecléctica. Esto se realiza por medio de “addons” que
en definitiva serían el análogo a las librerías de Processing.
La diferencia fundamental entre openFrameworks y P5 es que openFrameworks está escrito en lenguaje C++ y no
tiene un editor de código asociado lo que hace el proceso de fabricación de aplicaciones un poco menos directo que
en Processing.
24
El otro framework que se ha puesto “de moda” en estos últimos tiempos es Cinder. Está también escrito en C++ y su
particularidad es que el core (núcleo), o sea, la rama central de código, está prolijamente mantenida por su creador
Andrew Bell. Cinder aparece como un entorno de desarrollo de aplicaciones gráficas pero orientado al segmento
profesional y se presenta como una librería de código organizada de manera lógica e intuitiva al realizador gráfico.
Andrew Bell
<http://drawnline.net/>
Cinder
<http://libcinder.org/>
Es también válido mencionar que en su origen el sistema operativo Mac Os Classic permitía a los desarrolladores generar
aplicaciones de manera muy sencilla. Había una clara intensión de la empresa Apple de producir no solo computadoras
sino de permitirle a sus usuarios que produzcan software compatible con su ordenador de la manera más cómoda e
inmediata posible. No es casual que la primera versión de max/MSP fue escrita para la computadora Macintosh.
Max/MSP
<http://en.wikipedia.org/wiki/Max_(software)>
Otra opción interesante en nuestro campo es GNU/Linux, un sistema operativo completo de código abierto y libre.
Por tratarse de un sistema operativo libre en la que una gran cantidad de desarrolladores globales se encuentran
trabajando cotidianamente, suelen aparecer soluciones muy inmediatas a problemas relacionados con nuevas inter-
faces o dispositivos.
La comunidad de software libre trabaja con ese tipo de software por lo tanto es más probable que desarrollos libres
provengan desde ese lugar más que desde un sistema cerrado. Por ese motivo es muy común hoy día encontrar sis-
temas GNU/Linux en gran cantidad de dispositivos multimedias como RaspberriPI y otros.
25
Las técnicas pueden ser aplicadas mediante la implementación de sus algoritmos en algún lenguaje de programación
como los anteriormente descritos, pero también existen herramientas de software que ya tienen resuelto el algorit-
mo y que solo deben ser puestos en funcionamiento para que brinden la información que deseamos obtener de la
escena.
Algunos ejemplos:
Los sistemas ópticos de sensado de posición y silueta han adquirido una gran relevancia en los sistemas interactivos,
dado que es un tipo de sensado más sencillo y eficaz. Es, además, menos costoso que el sensado por medios físicos
de los mismos fenómenos.
En el arte interactivo vinculado a la multimedia escénica (instalaciones interactivas, performance, etc.), este tipo de
sensado es de gran utilidad ya que nos permite captar el movimiento del cuerpo mediante la utilización de cámaras
de bajo costo como lo son las cámaras web.
26
Ejemplo de proceso de captura de movimiento:
Este tipo de sistemas también ha habilitado la construcción de pantallas sensibles al tacto, multitacto (es decir, que
pueden captar varios dedos a la vez). Por un lado, en formato de pantallas de acrílico, retroproyectada, como las crea-
das por Jefferson Han que aprovechan un fenómeno óptico llamado Reflexión total interna frustrada. Este fenómeno
permite que se ilumine un acrílico en los cantos cuando se apoya un dedo sobre la superficie del acrílico, la yema del
dedo se iluminará, luego una cámara captará dicha luz e interpretará la posición del dedo.
27
Imagen 1.11. Imagen de las pantallas sensibles al tacto multitacto hecha por Jefferson Han
El siguiente video muestra las imágenes de las pantallas sensibles al tacto multitacto hechas por Jefferson Han.
28
Imagen 1.12. Imagen de la obra Sensible
29
Imagen 1.14. Análisis de imagen realizado por el sistema de pantalla sensible al tacto con tela de Lycra
30
Una versión más refinada de los sistemas ópticos de captación
son aquellas que permiten interpretar determinados patrones
visuales en la imágenes provenientes de la cámara. Existen técni-
cas y algoritmos destinados a captar patrones bitonales (en blan-
co y negro) en la escena. La idea consiste en colocar a objetos físi-
cos etiquetas con patrones bitonales de forma que estos puedan
ser captados por el sistema y así poder deducir la ubicación del
objeto físico en la escena.
Esto es algo que a los músicos electrónicos les ocupa desde el origen mismo de la disciplina. Los grandes sistemas
modulares de la década de 1950 comunicaban información de un módulo a otro utilizando un sistema analógico
denominado control por voltaje. El control por voltaje permite que varios instrumentos musicales funcionen en con-
junto, de esa manera, un mismo teclado puede estar controlando a la vez un oscilador y disparando una envolvente
para generar la dinámica acorde con el sonido que se intenta sintetizar.
31
Imagen 1.16. Sintetizador Moog Modular
Esta idea es tomada luego por los fabricantes de instrumentos musicales digitales para desarrollar la norma de comunica-
ción MIDI que será la base de los protocolos y sistemas de comunicación con los cuales trabajaremos a lo largo del material.
El protocolo de comunicación MIDI nace a principios de la década de 1980. Desarrollado por varias empresas fabri-
cantes de equipos musicales. Propuesto inicialmente por Dave Smith de Sequencial Circuits, pretendía ser el estándar
de comunicación establecido para que cualquier instrumento musical electrónico digital pudiese comunicarse (com-
partir información) con otros sin importar el tipo de artefacto, modelo o fabricante.
De hecho, el estándar MIDI se impuso con tal fuerza que hasta el día de hoy (40 años más tarde) sigue vigente, sin
duda debido a la simpleza de su implementación.
Un “estándar” no es más que una “definición”. En el caso de los estándares de comunicación entre artefactos digitales
esa definición comprende esencialmente dos partes: una referida a la capa física, el “cómo” y otra referida a la capa
lógica, el “qué”.
En el caso del protocolo MIDI, el estándar prevé que la comunicación entre dispositivos esté soportada por una co-
nexión serial donde los artefactos intervinientes se encuentren aislados eléctricamente pero donde se mantenga un
vínculo de circuito cerrado entre el que recibe y envía. Es lo que se conoce como un loop de corriente.
El dispositivo que emite un mensaje MIDI envía una corriente eléctrica que modula un diodo infrarrojo en el receptor.
Esa variación es interpretada internamente por el receptor quien es el encargado de “descifrar” el mensaje con base
en un código binario establecido de antemano que en el caso de MIDI se transmite a una velocidad de 31250 bits por
segundos (o baudios).
En lo que concierne a la capa “lógica” del protocolo, se establecen mensajes donde la información efectivamente
transmitida es de 7 bits, esto representa valores de entre 0 y 127. La combinatoria de varias de estas “palabras” se es-
tablece también en el protocolo, el cual permite, por ejemplo, transmitir usando tres palabras: información respecto
de qué nota está siendo ejecutada en un teclado como también con qué valor de intensidad la tecla ha sido percutida
(siempre en valores que van de 0 a 127).
<http://es.wikipedia.org/wiki/MIDI>
32
En nuestro trabajo encontraremos que muchas veces se utilizan interfaces o dispositivos que responden a esta norma
como software que puede ser controlado a partir de este protocolo tanto desde un dispositivo interno como por otro
software. En este caso la capa física de conexión permanece virtualizada internamente en el sistema operativo.
Si bien la potencia y flexibilidad del protocolo MIDI es indiscutible, con el tiempo surgió la necesidad de mejorar ese
estándar de comunicación pensando ya en la conexión de diferentes instrumentos musicales (fundamentalmente
software que corra en un ordenador) ampliando en principio la posibilidad de mensajes que se pueden enviar, su
largo de palabra (se refiere a cuán grande es el rango de posibilidades que puede transmitir), la velocidad de transfe-
rencia y la implementación de mensajes específicos creados por el usuario final. Como respuesta a esta demanda es
que surge el protocolo OSC.
OSC (opensoundcontrol) es un protocolo cuyo estándar físico está basado en otro protocolo familiar a las compu-
tadoras que es TCP usado en las conexiones de red. OSC funciona sobre TCP utilizando paquetes en otro protocolo
denominado UDP. Este último permite despachar mensajes desde un servidor y todos los clientes que escuchen a ese
servidor en un puerto determinado pueden recibir el mensaje.
Web de OSC
<http://opensoundcontrol.org/introduction-osc>
Los mensajes se establecen según tipos de datos específicos: números enteros, decimales, cadenas de caracteres o
arreglos que incluyen listas de algunos de ellos. El protocolo también prevé que los mensajes lleven una especie de
“encabezado” que identifique el tipo de mensaje, de manera que quien lo reciba, conociendo de antemano la especi-
ficación para ese tipo de mensaje, pueda entenderlo y utilizarlo en consecuencia.
Un microcontrolador es una pequeña computadora integrada en una pastilla (chip). Contiene en su interior todas las
partes de un ordenador pero de manera reducida. En el caso de Arduino el microcontrolador a utilizar es alguno de
la marca AVR y que en general cuenta con las siguientes prestaciones: un CPU corriendo a 16Mhz, memoria RAM de
33
328 bytes, una memoria de programa ROM (es decir, que no se borra al desconectarlo de la corriente) de 32KB, una
memoria eprom (que se puede autograbar y se mantiene luego del corte de energía) y diversos dispositivos como
una interfaz de comunicación serial, generador de señales con modulación del ancho de pulso (PWM) y lo más impor-
tante, puertos de entrada y salida digitales y analógicos.
Estos puertos nos permiten conectar Arduino con casi cualquier otro dispositivo electrónico (generalmente por me-
dio de algún otro circuito de interfaz) de manera de poder controlarlo remotamente tanto por medio de un software
precargado o utilizando el propio Arduino como interfaz entre un ordenador y el aparato que se requiera controlar.
Los puertos son además bidireccionales de modo que podremos usarlos tanto como salida (si es que queremos con-
trolar algo externo) o como entradas en el caso de que necesitemos utilizar sensores. Arduino (la comunidad y el pro-
yecto en sí) prevé este tipo de usos por lo cual la extensa documentación en su web hace en gran parte su existencia.
34
2. Imagen y sonido en tiempo real
Objetivos
• Conocer y dominar las herramientas de software y hardware más usadas.
• Vincular diversas aplicaciones de imagen y sonido entre sí conociendo los protocolos más utilizados.
• Construir un sistema de generación de imagen a partir de sonido.
2.1. Introducción
En esta unidad presentaremos los bloques fundamentales con los cuales producir una pequeña aplicación interactiva
que pueda generar imagen a partir del sonido. El hecho de poder vincular de manera creativa la imagen y el sonido
para producir un acontecimiento audiovisual interactivo original es central para nuestra disciplina. Consideramos, por
ese motivo, que intentar construir una aplicación de este estilo es un buen punto de partida para comenzar a descu-
brir los detalles de las tecnologías involucradas en nuestro curso de manera progresiva.
Utilizaremos los programas Pure Data (PD) para la captura y análisis de la señal sonora y Processing (P5) para crear un
motor visual que genere imagen a partir de ciertos parámetros. En la siguiente unidad explicaremos los pormenores
acerca de cómo vincular las dos aplicaciones para que funcionen de manera unitaria utilizando el protocolo OSC o MIDI.
Un ejemplo muy claro en este sentido es la obra Messa Di Vocce del colectivo artístico Flong
Fuente: <http://www.youtube.com/watch?v=STRMcmj-gHc>
35
En esos momentos, el tiempo de respuesta de las compu-
tadoras era sustancialmente más rápido que el de la men- El diccionario IBM de la computa-
te humana. La computadora podía resolver problemas ción define mainframe como “una
específicos de manera precisa y en tiempos mucho más gran computadora, en particular
cortos que los que tomaría a un calculador humano. De una a la cual otras computadoras
cualquier forma, es interesante tener en cuenta que cada pueden conectarse para poder
calculación susceptible de ser llevada a cabo en la compu- compartir las funcionalidades que
tadora podía tomar decenas de horas en resolverse. ese mainframe provee (por ejem-
plo, una computadora de la línea
A partir de la entrada de las computadoras en las casas System/370 a la cual computa-
de los amateurs y aficionados a la computación es que doras personales son conectadas
también se dispara el desarrollo y el entusiasmo por para poder subir o bajar progra-
conseguir aplicaciones que resolvieran los problemas mas o datos). El término, general-
e inquietudes de los nuevos propietarios de hardware: mente, se refiere solo al hardware,
el usuario general. es decir, al almacenamiento prin-
cipal, los circuitos de ejecución y
Esos softwares, producidos ya fuera de las universidades, las unidades periféricas”. Fuente:
apuntaban a resolver tareas simples como llevar adelan- <http://www-03.ibm.com/ibm/
te la contabilidad hogareña, calcular fácilmente presu- history/exhibits/mainframe/main-
puestos para pequeños negocios, manipular texto, emu- frame_intro.html>
lar juegos de ingenio y organizar agendas de contactos.
Muy rápidamente las aguas se dividieron en dos grandes grupos: por un lado estaban quienes se juntaban a compar-
tir sus programas de manera amistosa en clubes informáticos y casas de computación. Eran comunes las reuniones de
fin de semana donde se intercambiaban de manera impresa el código escrito para tal o cual máquina y que cumplía
de forma más o menos certera con alguna función particular.
Por otro lado, también estaban quienes sostenían que el trabajo efectuado en resolver e implementar tal o cual algo-
ritmo debía ser tratado como un bien material sujeto a las leyes del mercado, es decir, susceptible de ser “comerciali-
zado”. Nace así el software privativo y la idea de software licenciado.
36
Documento fundacional de la free software fundation:
<http://www.gnu.org/gnu/initial-announcement.html>
Es nuestra intención promover el uso de software libre en todo lo que sea posible puesto que nos parece relevante en
términos educativos poder contar con la posibilidad de explorar cómo se programaron ciertas herramientas que uti-
lizaremos a lo largo de este material. También nos parece importante contar con la posibilidad de exponer y distribuir
nuestro trabajo de manera libre y cooperativa; sin mayores restricciones.
Es en ese sentido que nuestro recorrido estará centrado en dos aplicaciones, en un amplio sentido, fundacionales de
nuestra disciplina. Nos referimos a los softwares PD (Puredata) y Processing.
37
2.3. Herramientas de software
<www.puredata.info>
2.3.1. Pure data (PD)
Fuente: www.puredata.info
Si bien PD permite generar, controlar y producir imagen en movimiento, será nuestra herramienta preferida para todo
lo que tenga que ver con control y procesamiento de sonido ya que es allí donde encontramos sus mayores virtudes.
38
PD es un lenguaje de programación que utiliza una metáfora gráfica
para construir los bloques funcionales. A este tipo de paradigma se lo
conoce como “programación visual” o “programación por flujo de da-
tos”. Al respecto puede consultarse <http://en.wikipedia.org/wiki/Data-
flow_programming> (en Inglés)
PD se encuentra disponible en varios “sabores”. En particular, vamos a instalar la última versión que aparece dispo-
nible en la web de Pure Data. La versión que utilizaremos es la que aparece definida como “pd-extended”. Incluye el
núcleo de la aplicación creado originalmente por Miller Pucket más una serie de adicionales que nos permiten una
mayor cantidad de utilidades.
Fuente: www.puredata.info
Una vez instalado, al iniciar, el programa PD nos ofrece la ventana de terminal. Esta ventana es nuestra puerta de co-
municación con el programa. Todos los lenguajes de programación ofrecen algún medio de entrada y salida de datos.
En este caso, la consola de PD nos muestra información relacionada con el estado actual de la aplicación y de nuestro
programa (de ahora en adelante patch).
39
Imágen 2.3. En la consola de PD podemos ver cómo está funcionando la aplicación
y recibir información sobre la ejecución de nuestro patch.
2.3.2. Processing
Processing es a la vez un lenguaje de programación (un API), un entorno y una comunidad de desarrollo de software.
El proyecto fue originado por dos estudiantes del MIT como proyecto de tesis. La propuesta era construir un sistema
informático para que los alumnos o estudiantes de artes visuales, pudiesen, a la vez que aprenden a programar, cons-
truir imágenes de manera programática.
La fascinación de Casey Reas, uno de los mentores del proyecto, tenía que ver con la posibilidad de aplicar procesos
algorítmicos para la generación de dibujos. Su intención era la de generar dibujos automáticamente.
Si bien esta idea está presente desde hace muchísimo tiempo, no fue hasta el lanzamiento de Processing que el recur-
so informático se empezó a usar de manera casi ubicua en el arte visual digital.
En gran parte, el éxito de Processing se debe al trabajo de Ben Fry (el otro integrante de la dupla) de perfil más técni-
co. Fue Fry quien logró desarrollar un sistema basado en java que permitiese escribir código y ejecutarlo de manera
sencilla y limpia en cualquier computadora y por cualquier usuario sin conocimientos previos de programación.
40
Rápidamente, el crecimiento de la comunidad de usuarios de Processing provocó un efecto de avalancha: estudiantes
ayudando a otros estudiantes a resolver o crear nuevas experiencias visuales utilizando software. El núcleo de este
desarrollo es el foro de consultas en el sitio de Processing.
Actividad 1
A esta altura suponemos que el lector ya ha tenido alguna experiencia previa con
Processing. De verificarse lo contrario les proponemos la siguiente actividad que
consiste en instalar y ejecutar un primer programa a modo de “hola mundo” que
nos permite determinar si el programa ha sido correctamente instalado y si estamos
en condiciones de poder comenzar a trabajar. El programa “hola mundo” es un pro-
grama simple cuya única función es imprimir en la consola o terminal la frase “hola
mundo”. Referirse a esta versión en español del tutorial oficial de Processing:
Tutorial en español: <http://www.arduteka.com/2012/12/introduccion-a-processing/>
Versión original (en Inglés): <http://processing.org/tutorials/gettingstarted/>
De aquí en más centralizaremos todo nuestro trabajo en estas dos aplicaciones fundamentalmente porque se trata
de aplicaciones libres y han demostrado ser fiables para este tipo de actividades, tanto por el tiempo de maduración
y desarrollo con el que cuentan ambas como además por la inmensa comunidad de artistas y amateurs que producen
continuamente aplicaciones específicas en estos lenguajes. Como anticipamos, utilizaremos PD para todo lo relacio-
nado con control de interfaces y sonido y Processing para el desarrollo de imagen interactiva.
Imagen 2.4.
Y allí, desde el menú desplegable input device seleccionaremos la entrada correspondiente (en este caso el micrófono
incorporado a una computadora portátil).
41
Imagen 2.5.
Una vez completado, aceptamos los cambios haciendo clic en apply y luego save all settings para guardar la configuración.
El estado de funcionamiento del micrófono se puede verificar usando un patch de testeo incluido en el programa.
Este “probador” se encuentra ubicado en el menú Media/test audio and midi.
Imagen 2.6.
El indicador audio input debería acusar la entrada de audio en el puerto especificado. Tal vez la característica más po-
tente que tiene PD (como otros lenguajes de programación visuales) es que la documentación suele ser un ejemplo
funcional de lo que intenta documentar. Puede accederse por medio del menú Help/Browser. Esto nos muestra un
navegador con todos los ejemplos disponibles para nuestra versión ordenados por temáticas.
Imagen 2.7.
42
Explicaremos ahora cómo analizar de forma sencilla la señal proveniente de nuestro micrófono para utilizarla luego
como controlador de nuestra aplicación visual.
Si recordamos los conceptos fundamentales que hacen al sonido es fácil darse cuenta que los parámetros más inme-
diatos que se pueden extraer de una señal sonora, para controlar cualquier cosa, son la amplitud o “intensidad” y la
frecuencia o “tono”.
La amplitud representa la fuerza del sonido o sea la sutileza o intensidad con que el sonido llega al micrófono. Como
parámetro de control la amplitud podría emplearse de manera análoga a la saturación de la tinta en una imagen. Por
otra parte la frecuencia podría utilizarse para controlar el tono (tinte).
La primera raíz fundacional que determina cómo fue que surgieron los primeros gráficos computarizados tiene que
ver con el uso de gráficos vectoriales. La idea de los gráficos vectoriales se remonta a las primeras computadoras con
interfaz visual. Ya en el año 1961 se comenzó a producir imagen con esta técnica que consiste en definir una lista de
coordenadas para centros, inicios, y fines de arcos y líneas.
43
Las computadoras de la década de 1960 usaban monitores de tipo vectoriales donde un haz de electrones se mueve
dibujando un punto. Ese haz de electrones puede ser direccionado en dos sentidos, vertical y horizontal. El veloz
movimiento de ese punto da lugar a la formación de líneas continuas que aparecen sobre la superficie de la pantalla
comúnmente recubierta de material fosforescente. Este sistema de generación gráfica es bastante eficiente y econó-
mico (en términos de hardware) ya que la imagen se genera de manera instantánea (no se necesita una memoria para
alojar el resultado de la imagen).
En nuestros días la idea sigue vigente. Programas como Adobe Illustrator y Corel Draw utilizan esta técnica para ge-
nerar imágenes y dibujos. Si bien la manera en que Processing termina generando una imagen es por mapa de bits,
el lenguaje nos permite varias formas de utilización de gráficos vectoriales.
Una de ellas tiene que ver con el uso del método beginShape(), endShape().
Vertex (Vértice):
Un vértice es una posición definida por una coordenada (x,y). Una línea tiene dos
vértices, un triángulo tiene tres, un cuadrilátero tiene cuatro, y así sucesivamente.
Formas orgánicas: tales como manchas o el contorno de una hoja se construyen
mediante la colocación de muchos vértices en los patrones espaciales.
Estas formas son simples en comparación con las posibilidades. En los videojuegos
contemporáneos, por ejemplo, personajes de gran realismo y elementos ambien-
tales pueden estar formados por más de 15.000 vértices. Estos representan usos
más avanzados de esta técnica, pero son creados usando principios similares.
Para crear una forma de puntos de vértice, utilice primero la función beginSha-
pe(), a continuación, especifique una serie de puntos con la función vertex() y para
completar la forma use endShape(). beginShape() y endShape() siempre deben ser
utilizados por parejas. La función vertex() tiene dos parámetros que definen la co-
ordenada ‘x’ y la coordenada ‘y’: vertex(x , y);
Por defecto, todas las formas dibujadas con la función vertex() se llenan de blanco
y tienen un contorno negro que conecta todos los puntos, excepto el primero y el
último. fill(), stroke(), noFill(), noStroke(), y strokeWeight() son funciones de control
de los atributos de las formas dibujadas con la función vertex(). Para cerrar la forma,
utilice la constante CLOSE como parámetro para endShape(). (Reas & Fry, 2007: 67
[traducción de los autores])
Processing también dispone de una clase exclusiva para este tipo de tratamiento de gráficos. La estructura que nos
permite generar formas y conservarlas para uso posterior es la clase PShape. Para eso veremos el ejemplo PolygonP-
Shape incluído en Processing v 2.0.1:
44
Código 2.1.
/**
* PrimitivePShape.
*
* Using a PShape to display a custom polygon.
*/
void setup() {
size(640, 360, P2D);
smooth();
// Primero creamos nuestro objeto PShape
// en este caso no usamos el constructor con “new” puesto que es processing
// el que se encarga de crear ese objeto por nosotros.
estrella = createShape();
estrella.beginShape();
// configuramos el color de relleno y la linea.
estrella.fill(102);
estrella.stroke(255);
estrella.strokeWeight(2);
// Escribimos “a mano” algunos puntos...
estrella.vertex(0, -50);
estrella.vertex(14, -20);
estrella.vertex(47, -15);
estrella.vertex(23, 7);
estrella.vertex(29, 40);
estrella.vertex(0, 25);
estrella.vertex(-29, 40);
estrella.vertex(-23, 7);
estrella.vertex(-47, -15);
estrella.vertex(-14, -20);
// CLOSE le dice a processing que la forma que creamos queremos que sea “cerrada”, esto es:
// el primer punto y el ultimo se conectan
estrella.endShape(CLOSE);
}
void draw() {
background(51);
// podemos mover la estrella como querramos usando translate()
translate(mouseX, mouseY);
// dibujamos la estrella
// esta forma de hacerlo es similar a cuando queremos dibujar una imagen.
// la forma correcta de hacerlo es image(mi_imagen, x, y);
// el metodo shape(PShape p); es análogo a image(PImage, int x, int y);
shape(estrella);
}
Otra forma de utilizar gráficos vectoriales es por medio del formato gráfico svg: es un estándar abierto de gráficos
vectoriales, está desarrollado y mantenido por el World Wide Web Consortium (W3C).
Formato SVG:
<http://es.wikipedia.org/wiki/Scalable_Vector_Graphics>
<http://es.wikipedia.org/wiki/World_Wide_Web_Consortium>
45
Processing nos permite importar nuestros diseños en formato svg y visualizarlos en pantalla además de manipularlos.
Para ello también utilizaremos la clase PShape.
En este caso Processing se encarga de abrir el archivo “archivo_de_forma.svg” que se encuentra en la carpeta /data de
nuestro sketch y que tiene que haber sido compuesto utilizando Inkscape o Adobe Illustrator puesto que no es una
implementación completa del formato svg.
Como contrapartida a los monitores vectoriales, aparecieron los monitores TRC con barrido horizontal. Si bien el prin-
cipio de funcionamiento es similar al de los monitores vectoriales donde también se usa un haz de electrones sobre
una superficie fosforescente, la generación de imagen tiene otro fundamento: el haz de electrones barre la pantalla
de arriba hacia abajo dibujando líneas horizontales.
La modulación de la intensidad del haz es lo que produce la variación en el brillo resultante. Debido a estas caracte-
rísticas de funcionamiento aparece un nuevo concepto: la resolución vertical de la imagen (en términos de líneas).
La utilización de esta clase de monitores dio lugar, hacia finales de la década de 1960, al concepto de raster gráfico.
El raster gráfico es una grilla de puntos donde el encendido o apagado de cada uno de estos puntos construye la
imagen deseada. Surge entonces la idea del píxel como unidad mínima de imagen digital. Es evidente que con solo
determinar dos estados para cada uno de los píxeles en el raster no sería suficiente para producir, por ejemplo, imá-
genes a color.
Las computadoras digitales permiten procesar y almacenar gran cantidad de información lo que ha ido determinan-
do tanto la resolución en términos espaciales como la profundidad de color para las imágenes que un cierto hardware
puede producir. En la actualidad, es prácticamente ubicua la idea del raster gráfico de 32 bits donde cada componen-
te de color (rojo, verde y azul) está determinada por un valor entre 0 y 255 (8 bits) y los restantes 8 bits se utilizan para
guardar información de transparencia (canal alfa).
Processing funciona utilizando este formato. Aplicando los métodos set() y get() podemos modificar y obtener respec-
tivamente el color de cualquier píxel en nuestro sketch.
46
Código 2.2.
/*
Este ejemplo muestra el uso del metodo set(int x, int y, color col); para colocar un color en un
pixel determinado de nuestra ventana de dibujo.
- click con el mouse para activar
*/
Pintor pintores[]; // array de pintores (ie: muchos objetos de tipo Pintor en una lista ordenada)
int cantPintores = 1000;
boolean bandera = false;
void setup(){
frameRate(30);
size(1200, 400);
pintores = new Pintor[cantPintores];
reset();
background(255);
}
void draw(){
void largar(){
bandera = true;
}
void mousePressed(){
largar();
}
void keyPressed(){
if(key == ‘ ‘)
{
bandera = false;
reset();
}
}
47
// definicion de la clase Pintor.
class Pintor {
int posx = 0;
Pintor(){
println(“hola ! soy un autito nuevo 0KM”);
}
Pintor(color colorIncial){
col = colorIncial;
}
Pintor(color _col, int _posx, int _posy, int _velocidad){
col = _col;
posx = _posx;
posy = _posy;
velocidad = _velocidad;
}
void avanzar(){
posx += velocidad;
}
void pintar(){
set(posx, posy, col);
}
El protocolo de comunicación OSC está específicamente diseñado para compartir información entre todo tipo de herra-
mientas vinculadas a la actividad audiovisual. En cierto sentido es el protocolo superador del ya clásico e inmortal MIDI.
OSC permite conectar controladores, instrumentos musicales y softwares de forma prácticamente irrestricta e ilimi-
tada ya que por un lado la conexión física entre los dispositivos está basada en Ethernet y UDP y la parte “lógica” del
protocolo es abierta permitiéndonos generar nuestros propios lenguajes de comunicación para conectar nuestros
propios softwares o hardwares.
El protocolo OSC ya se encuentra implementado en nuestras aplicaciones (PD y Processing). Ambos lenguajes traen
incorporados objetos para enviar y recibir mensajes OSC desde y hasta una conexión de red que puede ser otro puer-
to dentro de la misma máquina, una dirección en la red local o incluso una computadora conectada a Internet.
48
En nuestro ejemplo, tanto el patch programado en PD como el sketch de Processing estarán corriendo en la misma
máquina. El patch de PD será el encargado de emitir mensajes en función de las variaciones de amplitud y tono de
audio que recibe del micrófono. Para ello ampliaremos con el objeto “sendOSC” el ejemplo que vimos anteriormente.
Explicaremos ahora, paso a paso, cómo funciona este patch en apariencia complejo. Para poder estudiarlo es impor-
tante prestar atención a los grupos de objetos que aparecen como “islas” sobre esta especie de mapa visual o plano
de conexiones.
49
Una de las características más importantes (y útiles) de los lenguajes de programación que utilizan este tipo de me-
táforas visuales tiene que ver con que el patch se autoexplica por el solo hecho de la apariencia gráfica que estas
determinan. Es fácil seguir las conexiones en el sentido de lectura convencional: de arriba hacia abajo y de izquierda
a derecha.
Esta lectura es análoga al flujo de los datos en el programa. Es evidente que en la parte superior izquierda comienza
todo: el audio a analizar ingresa al patch por medio del objeto “adc~”.
Este objeto, adc~, nos permite vincular una entrada de audio física (configurada anteriormente de manera global para
todos los patchs de PD) desde el menú de configuración.
Es importante ubicar un multiplicador para poder operar sobre el nivel de entrada. Este parámetro es clave para poder
“calibrar” nuestro programa en la situación de instalación real.
Mucho del trabajo con interfaces que permiten el sensado de alguna de las variables del mundo exterior está centra-
do en la calibración que se haga en la lectura de esos datos en la situación particular: no será igual el funcionamiento
de nuestra instalación que captura audio de un micrófono en una situación de ambiente de galería de arte –donde
el ruido y el flujo de gente es limitado– a la de un espacio más amplio como una discoteca o espacio de alto flujo de
público como una feria o un concierto.
El uso del mensaje de control “pd dsp 1” nos permite activar la funcionalidad de procesamiento de audio. Esta función tam-
bién puede establecerse desde el menú media/audio/on o por medio del comando de teclado command+/ (en mac os x).
50
Imagen 2.10. Encendido y apagado de la entrada y procesamiento de audio
El siguiente paso es procesar ese flujo de datos de audio para obtener el tono y la amplitud instantánea de la señal.
Por suerte contamos con el objeto fiddle~.
El funcionamiento de fiddle~ estará dado por los pará- Para un mayor entendimiento de
metros con los que lo inicializamos. Recordemos que los las posibilidades de este y cual-
parámetros son los datos que se agregan al nombre del quier otro objeto del lenguaje pd~
objeto básico. Por ejemplo, los parámetros “1024 1 20” le referirse al tutorial anteriormente
indican a fiddle~ que debe procesar paquetes de 1024 mencionado y a la documenta-
muestras, cuántas salidas de pitch diferentes tendrá y ción incluida con el programa.
cuántos picos buscar.
En lo que sigue puede verse que tanto la salida de valor de tono como la de valor de amplitud están tratadas de la
misma manera: primero convertimos el valor con decimal en punto flotante a un valor entero. Ese valor es interpolado
temporalmente para evitar las fluctuaciones violentas por medio del objeto “line”. El resultado de esa interpolación
(suavizado del valor en el tiempo) es enviado a la sección del patch que se encarga de producir y enviar el mensaje
osc. Los sliders nos permiten visualizar la diferencia:
51
Con la información obtenida vamos a crear un mensaje osc utilizando nuestras propias etiquetas, en este caso /pitch
para el tono y /amp para la intensidad. El uso de los objetos trigger (“t”) nos asegura que se enviará un mensaje siem-
pre que se modifique el valor de tono o intensidad de manera indistinta y que el orden en que se arme el mensaje ten-
drá lugar siempre con el envío de ‘]’ al final. El objeto sendOSC necesita ese dato como disparador para efectivamente
enviar el mensaje a la red. El caracter ‘]’ viene a cerrar el mensaje dándole la forma adecuada que el objeto sendOSC (y
el protocolo osc) necesita.
Por último, el objeto sendOSC necesita ser configurado. Esa configuración incluye la dirección de red a la que enviar
nuestros mensajes y el puerto de destino. Esto se hace de manera automática al abrir el patch ya que los mensajes
correspondientes son disparados por medio de loadbang al iniciar.
52
Texto Aparte
Generación de sonido
Cuando nuestro proyecto requiera integrar sonido, las opciones con las que con-
tamos son variadas y dependen fundamentalmente del tipo de sonorización que
queremos lograr. En principio, los caminos posibles para lograr que nuestra ins-
talación “suene” son tres: producir sonidos por medio de síntesis digital, disparar
sonidos previamente producidos (playback) y la combinación de ambos.
La consideración de base es que los sonidos que aparecen guardan algún tipo de
relación con la resultante visual, esto es: sonidos que de alguna manera se sincroni-
zan o responden a algún parámetro de la imagen. En ese sentido, el uso de síntesis
nos permitirá explorar variaciones más ligadas a lo generativo, es decir, sonido que
se produce a partir de parámetros que controlan tanto la resultante visual como la
sonora.
53
A partir de la versión 2.1 de Processing podemos instalar librerías por medio del menu Tools /add Library.
Para mayor referencia ver el documento How To Install External Libraries de la wiki de Processing (en in-
glés): <http://wiki.processing.org/w/How_to_Install_a_Contributed_Library>
Código 2.3.
/**
* Ejemplo sobre como recibir mensajes osc en processing.
* ejemplo original tomado de la libreria oscP5:
* http://www.sojamo.de/oscP5
*/
import oscP5.*;
import ndtP5.*;
OscP5 oscP5;
void setup() {
size(400,400);
frameRate(25);
/* inicializamos oscP5, escuchando mensajes en el puerto 12000
*/
oscP5 = new OscP5(this,12000);
}
void draw() {
background(0);
}
Nuestro ejemplo será solamente ilustrativo, por eso la lógica será de lo más sencilla: dibujar un cuadrado que cambie
de color según la frecuencia presente y que modifique su tamaño en función de la intensidad. En ese sentido, resulta-
rá muy útil cambiar el modo de color por defecto de Processing para que funcione con parámetros de tipo HSB (hue,
saturation, brightness o tono, saturación, brillo).
54
De esta forma, todos los parámetros y definiciones de color se harán con base en ese sistema que resulta más natural
a nuestros propósitos ya que el hue puede asociarse directamente al tono y con el valor de intensidad manipularemos
el tamaño del objeto. Cabe recordar que el rango de estos valores está dado en 8 bits por lo cual puede tomar valores
entre 0 y 255. Es evidente que los valores producidos por PD no serán útiles tal cual los recibimos: debemos ajustarlos
a este rango para obtener una variación significativa.
En nuestro caso particular, los valores de pitch están oscilando entre 80 y 92 aproximadamente para la voz humana
cantando y entre 20 y 60 para la amplitud.
Para acondicionar esos valores utilizaremos el método map (int in, int minin, int maxin, int minout, int maxout).
Definiremos dos variables:
int tono = 0;
int intensidad = 0;
Esas variables serán actualizadas en el método oscEvent(...) y utilizadas en draw() para dibujar:
Código 2.4.
/**
* Ejemplo sobre como recibir mensajes osc en processing.
* ejemplo original tomado de la libreria oscP5:
* http://www.sojamo.de/oscP5
*/
import oscP5.*;
import netP5.*;
OscP5 oscP5;
void setup() {
size(400, 400);
frameRate(25);
/* inicializamos oscP5, escuchando mensajes en el puerto 12000 */
oscP5 = new OscP5(this, 12000);
colorMode(HSB);
rectMode(CENTER);
}
int tono = 0;
int intensidad = 0;
void draw() {
background(0);
// ajustamos el tono
fill(tono, 255,127);
// movemos al centro de la pantalla
translate(width/2, height/2);
// aplicamos una rotación
rotate(frameCount * 0.01);
// dibujamos un cuadrado con los parametros recibidos
rect(0,0, intensidad, intensidad);
}
55
/* los mensajes recibidos son redireccionados internamente por la clase OscP5 al método
oscEvent(OscMessage msg) */
void oscEvent(OscMessage theOscMessage) {
/* imprimimos la etiqueta con la que fue enviado el mensaje y el tipo de dato que contiene */
print(“### mensaje recibido.”);
print(“ etiqueta: “+theOscMessage.addrPattern());
println(“ tipo de dato: “+theOscMessage.typetag());
if (theOscMessage.addrPattern().equals(“/pitch”))
{
/*
theOscMessage trae la información. para recuperarla utilizamos el metodo
OscMessage.get(int id) donde “id” es la posición del valor que queremos recuperar.
En nuestro caso es “0” ya que es el único parametro presente en nuestro mensaje
de pitch.
Además debemos asegurarnos de que nuestro mensaje es del tipo que esperamos:
*/
if (theOscMessage.typetag().equals(“f”))
{
/*
Una vez que estamos seguros de que el mensaje es del tipo que esperamos
utilizamos el método get(int id) que nos devuelve un objeto de tipo OscArgument
OscArgument nos permite efectivamente recuperar el valor con el tipo
en el que fue enviado. En este caso usaremos OscArgument.floatValue();
*/
float valorDePitch = theOscMessage.get(0).floatValue();
/*
y por último ajustamos el valor de pitch a un numero entre 0 y 255 y
lo convertimos a entero usando int();
*/
tono = int(map(valorDePitch, 80, 92, 0, 255));
}
}
// y el mismo tratamiento para intensidad:
if (theOscMessage.addrPattern().equals(“/pitch”))
{
if (theOscMessage.typetag().equals(“f”))
{
float valorDeAmplitud = theOscMessage.get(0).floatValue();
56
El resultado es el siguiente:
Imagen 2.15.
Hemos visto entonces el proceso completo para generar la estructura mínima de una aplicación interactiva que con-
juga imagen y sonido en un todo. Esos conocimientos permiten adentrarse ahora en cuestiones más complejas: bus-
car otras formas de producir imagen a través del código y vincular otros parámetros del sonido, por ejemplo detec-
tando rangos de frecuencias para controlar diferentes elementos visuales.
Con las herramientas aprendidas es nuestra sugerencia intentar realizar configuraciones más sofisticadas utilizando
otras funciones de dibujo o algoritmos avanzados para generar composiciones más atractivas y de mayor dinamismo.
57
3. Visión por computadora
Objetivo
• Comprender los principios de los métodos de captura de mo-vimiento mediante el uso de sistemas ópticos.
3.1. Introducción
En esta unidad explicaremos los principios de los métodos de captura de movimiento mediante el uso de sistemas
ópticos, es decir, los que usan cámaras de video para tal fin. Estos métodos permiten captar el movimiento de perso-
nas, en un escenario o el espacio de una instalación artística, y controlar la imagen y el sonido en función de los pará-
metros obtenidos. También, se introducirá el método de captura por substracción de video, a la vez que se explicará
su implementación en el lenguaje de programación Processing.
58
tonos diferentes al negro y de ahí que los movimientos que se realicen frente a la
cámara queden distinguidos de un fondo negro.
Las dos fuentes más comunes de errores en este tipo de captura son los cambios
de iluminación y los movimientos de cámara. Por ejemplo, si se utiliza una técnica
de este tipo, en un espacio donde ingresa luz natural, es probable que los cambios
de iluminación de las diferentes horas del día generen problemas. Otro problema
posible es cuando la cámara sufre algún tipo de movimiento, por ejemplo, por un
leve golpe o alguna vibración del piso. Cualquiera de estas dos cosas podrían ha-
cer, en algunos casos, que la técnica deje de ser efectiva y capte movimiento donde
no lo hay.
Otro de los problemas que existen es que este sistema requiere un buen contraste
en la imagen que obtiene la cámara, dado que es necesario aplicar un filtro para
distinguir entre “cambios de imagen” y “ruido de la cámara o la iluminación”. Por
esto, es un sistema que requiere un buen nivel de luz para funcionar correctamen-
te. Esto, a veces, complica la situación a la hora de querer trabajar con captura de
movimiento en lugares donde se realizan proyecciones de video y que requieren
penumbra u oscuridad. Sin embargo, se puede solucionar con la utilización de cá-
maras y luces infrarrojas. (Causa, 2007)
Mostraremos el funcionamiento de este tipo de captura mediante un ejemplo en Processing. Utilizaremos ese ejem-
plo para hacer un proceso interactivo sobre la misma plataforma. El ejemplo permite captar la presencia de una perso-
na y proyecta su silueta en la pantalla, llenándola con figuras prediseñadas. Este ejemplo capta la presencia en dos re-
giones específicas de la escena, en la región superior izquierda y la superior derecha. Si capta presencia o movimiento
en la región superior izquierda, rellena la silueta con cruces verdes, en cambio, si la captación es en la región superior
derecha, las figuras con que se rellena la silueta son círculos, como puede verse en las imágenes siguientes (3.1. y 3.2.).
59
Imagen 3.1. Imagen generada al capturar movimiento en la zona superior izquierda
60
3.3. Captura de video en Processing
Para abordar la primera parte –instrucciones asociadas a la captación de video– vamos a hacer un extracto del có-
digo del ejemplo que corresponde al proceso de captación de video de la cámara. A continuación se pueden ver las
instrucciones del ejemplo que sirven para trabajar con la cámara. El extracto en cuestión podría funcionar como un
ejecutable en sí:
Código 3.1.
void setup() {
// tamaño de la pantalla
size( 800, 600 );
void draw() {
La captura de video se realiza mediante el objeto de tipo capture llamado “cámara”. Dicho objeto es inicializado en
el void setup() y durante el void draw() se revisa si tiene fotograma disponible para cada ciclo del draw, mediante la
ejecución del método .available() en un condicional (estructura if). Si la consulta devuelve un valor positivo (un valor
de verdad true), entonces se usa el método .read() para cargar el nuevo fotograma. Al final, con la función image(), se
muestra la imagen del nuevo fotograma en pantalla.
61
3.4. La substracción de video
Pasamos ahora a describir la segunda parte (instrucciones asociadas a la captura de movimiento). Para comprender el
funcionamiento del ejemplo siguiente, debemos avanzar en la explicación del algoritmo de captura por substracción
de video. Esta captura, tal como fue explicado al inicio, consiste en comparar dos imágenes mediante una operación
de substracción en la que se resta, píxel por píxel, el valor numérico correspondiente al color.
Dicha substracción devuelve valores cercanos o iguales a cero, cuando los colores son iguales o cercanos, y valores
de mayor diferencia cuando los colores no coinciden. En la imagen (3.3.) se pueden ver, a su vez, dos imágenes arriba,
a la derecha la imagen de referencia, que es la escena sin ningún “sujeto de movimiento” y a la izquierda es la misma
escena con una niña. Inmediatamente debajo aparece la imagen que se construye con la substracción (cabe aclarar
que a la misma se le aplica una función de valor absoluto, que hace que todos los valores sean positivos).
Puesto que los valores de los píxeles casi nunca son exactamente iguales, sino que en el mejor de los
casos son muy cercanos, debido al ruido de captación de la imagen y el de la iluminación, es necesario
distinguir entre las pequeñas diferencias (que corresponden al ruido) y las grandes diferencias, que per-
tenecen al sujeto buscado
Con este procedimiento se genera una nueva imagen bitonal, que está en la parte inferior de la imagen, que indica en
blanco el movimiento y en negro el resto de la escena.
Luego de estas operaciones el proceso concluye con el análisis de los píxeles blancos para determinar el tamaño y
posición del sujeto en la escena.
El proceso de captura de movimiento por substracción de video puede realizarse de dos formas en fun-
ción de cuál es la imagen de referencia con la que se compara la imagen actual. Al primer método lo
llamaremos “captura de movimiento por substracción de video con imagen de referencia”. Debajo puede
verse un diagrama en el que se representa el flujo de la información. La “imagen fija”, que figura en el
mismo, corresponde a una imagen de la escena sin ningún sujeto.
62
Imagen 3.3.
Imagen 3.4. Captura de movimiento por substracción de video con imagen de referencia (fondo) prefijada
63
El otro método es el que llamaremos “captura de movimiento por substracción de video con imagen
retardada” (también podemos llamarla “con delay”). La diferencia con el método anterior consiste en que
la imagen que se utiliza para comparar con la imagen actual proviene del mismo flujo de imágenes de la
cámara pero con un retardo en el tiempo. De esta manera el fotograma que está actualmente ingresando
de la cámara es comparado con uno anterior y deja una cantidad fija de fotogramas de retraso en medio.
Imagen 3.5. Captura de movimiento por substracción de video con imagen retardada
En la siguiente tabla se pueden observar algunas de las diferencias entre los dos métodos.
Captura de movimiento por substracción de video Captura de movimiento por substracción de video
con imagen de referencia (fondo) prefijada. con imagen retardada.
Es muy sensible a las condiciones de la luz y Si bien en principio responde igual que el otro
alteraciones de la imagen en general método (a las variaciones), rápidamente se
(como los movimientos de cámara). estabiliza. Lo que lo hace un método más seguro.
Sirve para captar la silueta del sujeto de movimiento. No sirve para captar la silueta, sino la diferencia
de posición de un fotograma a otro.
Si hay algún cambio de iluminación o movimiento Si el sujeto queda quieto, el método es incapaz de
de cámara, deja de funcionar. captarlo.
Sirve para captar presencia. Sirve para captar movimiento.
64
Código 3.2.
void setup() {
// tamaño de la pantalla
size( 800, 600 );
void draw() {
65
// carga la cantidad de movimiento en la región superior derecha
float movimientoALaDerecha = capturaDeMovimiento.movEnArea(
int( 0.70 * ancho ),
int( 0.05 * alto ),
int( 0.25 * ancho ),
int( 0.45 * alto ),
imagenDelMovimiento );
void keyPressed() {
// si se presiona la tecla espacio
if ( key == ‘ ‘ ) {
// invierte el estado de activación del monitoreo
monitoreo = !monitoreo;
}
}
void mousePressed() {
// si se presionó el botón principal del mouse
if ( mouseButton == LEFT ) {
//LINEA G: si se preciona el botón del mouse se recaptura el fondo
capturaDeMovimiento.recapturarFondo();
}
else {// si se presionó el botón alternativo
//si esta en monitoreo
if ( monitoreo ) {
// si el mouse fue presionado en la región de monitoreo del umbral
if ( mouseX > ancho*2 && mouseX < ancho*2 + 150 &&
mouseY >= 0 && mouseY <= 255 ) {
// calculo un nuevo valor
float nuevoValor = map( mouseY, 0, 255, 255, 0 );
//LINEA H: asigna el nuevo valor de umbral
capturaDeMovimiento.setUmbral( nuevoValor );
}
}
}
}
66
3.5.1. Los métodos principales
La clase PMoCap posee un conjunto de métodos que son usados en el ejemplo del código anterior (código del ejem-
plo 3.2). Allí se han agregado comentarios, alguno de los cuales empiezan con la palabra “LINEA” en mayúsculas,
ordenadas desde A hasta H. Estos comentarios se encuentran por encima de líneas que invocan métodos del objeto
“capturaDeMovimiento” del tipo PmoCap.
Empezaremos por citar los dos principales, el constructor de la clase (en la línea B) y el método .capturar() (en la línea
C). El constructor requiere que se le pasen los siguientes parámetros:
Código 3.3.
El segundo método que nombramos fue .capturar() que se encuentra en la línea C. Este método es el más importante
ya que es el que pone la captura en funcionamiento. Recibe un objeto de tipo PImage (una imagen de mapa de bits)
o un objeto de tipo Capture (es decir, uno para acceder al contenido de la cámara de video). Por eso, se lo ejecuta
inmediatamente después de leer el fotograma de la cámara:
Código 3.4.
La idea es que el objeto “capturaDeMovimiento” reciba uno a uno los fotogramas de la cámara y los procese aplicando
el algoritmo de substracción de video. Cuando funciona con el método de imagen de referencia prefijada, toma como
imagen el primer fotograma que le llega.
Como, generalmente, estos primeros fotogramas no son buenos (a veces vienen en negro hasta que la cámara se
acomoda), es necesario volver a tomar un nuevo fotograma como imagen de referencia. Esto se hace con el método
.recapturarFondo() que se encuentra en el método mousePressed() para poder obtener una nueva imagen al pre-
sionar al botón del mouse. Cuando el objeto está funcionando con el otro método, esto es innecesario y aplicar este
comportamiento no tiene mayores consecuencias.
67
3.5.2. Calibrar el umbral
Durante el funcionamiento del algoritmo a veces es necesario calibrar el valor del umbral, ya que cada situación trae
aparejado su propio nivel de ruido y dicha calibración es un paso inevitable del montaje y puesta en funcionamiento.
Por ejemplo, en la figura siguiente se pueden apreciar tres niveles diferentes de umbral para la escena de la niña que
se ha venido mostrando. El cuadro del centro tiene un nivel de umbral adecuado. El de la izquierda tiene un umbral
muy alto y por ende, parte de la señal queda afuera al ser confundida con ruido. El de la derecha muestra un nivel de
umbral muy bajo, que deja pasar demasiado ruido.
Existen dos métodos que tienen por objeto calibrar el valor del umbral: .getUmbral() y .setUmbral(), sirven para leer y
escribir (respectivamente) el valor de umbral del método.
El método .getUmbral() es usado en el ejemplo de código 3.2 (línea F) para leer el nivel del umbral y mostrarlo en la
pantalla cuando el monitoreo esté activado. El método .setUmbral() es usado (línea H) para asignar un nuevo valor,
cuando el usuario hace clic con el botón alternativo del mouse sobre el monitoreo del umbral.
El método .getImagenAnalisis() se encarga de devolver un objeto PImage que muestra el movimiento capturado, en
blanco sobre negro (en una imagen bitonal). También se indica la región mínima que contiene dicha área (en color
verde), lo que en inglés se conoce como bounding box, así como el “centro de gravedad” de dicha región, que es la
posición promedio de todos los píxeles del área.
En el ejemplo, el método se usa en la línea D y es con el fin de obtener dicha imagen y mostrarla posteriormente en
un monitoreo del proceso.
68
3.5.4. A la búsqueda del movimiento
Todo el proceso que hemos visto hasta acá tiene como objetivo capturar el movimiento de un sujeto y utilizar los
parámetros del mismo para controlar algunas representaciones, imágenes y/o sonidos. La clase PMoCap posee un
conjunto de métodos que apuntan a poder acceder a la medición del movimiento o presencia detectados:
A estos métodos se suman dos que sirven para buscar movimiento en regiones específicas del cuadro de la escena, el
primero es .movEnArea(). Este método se encarga de calcular el área de movimiento en una región específica.
En el ejemplo, en la línea E, aparece un llamado a este método y se le pasa como parámetros la posición en X e Y y el
ancho y alto de la región, seguidos de un PImage que servirá para devolver un monitoreo de la región. En este caso,
los parámetros se calculan en forma relativa a las dimensiones del cuadro de la cámara, por ejemplo: 0,05*ancho. La
función int() es necesaria debido a que los datos son enteros y especifican píxeles.
Código 3.5.
Las dos líneas en las que se invoca al método definen las regiones de captura que se busca analizar y que se muestran
en el gráfico siguiente.
69
El método .movEnArea() devuelve el proporcional de la región que está involucrado en el movimiento, por ejemplo,
si la mitad de la región especificada posee píxeles blancos (píxeles pertenecientes al sujeto de movimiento) entonces
el método devuelve el valor 0,5 , es decir el 50/100. Obviamente, los valores posibles se encuentran en el rango entre
0 y 1 inclusive.
El objeto de imagen (Pimage llamado imagenDelMovimiento) que se pasa como último parámetro sirve como mo-
nitoreo del resultado. En la imagen 3.8. pueden observarse tres figuras (o cuadros), sobre un fondo, de izquierda a
derecha: la imagen original de la cámara, la del análisis del movimiento y el nivel de umbral (representado con un
rectángulo lleno parcialmente). En la segunda, del análisis del movimiento, se muestran: el área del movimiento en
color blanco, el rectángulo contenedor (bounding box) en verde, y otros dos rectángulos que representan las dos
regiones analizadas con las llamadas a .movEnArea(). La de la izquierda se encuentra de color verde (indicando que
contiene movimiento) con la sección de movimiento también resaltada en verde, la de la derecha se encuentra en
rojo, indicando que no hay movimiento.
El segundo método que contiene PMoCap para ese tipo de análisis se llama .movEnPixel() y recibe como parámetros
la posición X e Y de un píxel en la pantalla y una imagen donde se mostrará el monitoreo del resultado. Este método
devuelve un valor booleano, verdadero o falso, ya que solo está viendo un píxel.
70
3.6. Revisión de la clase PMoCap
Revisaremos ahora cómo está implementada la clase PMoCap, lo haremos en forma parcial ya que no tiene sentido
revisar todo su código en este espacio debido a que la mayor parte responde a las problemáticas comunes a la pro-
gramación orientada a objetos en Processing.
Revisaremos, sí, la sección que consiste en el eje del algoritmo de captura por substracción de video. Para esto vere-
mos el método .capturar() que transcribimos abajo con una serie de comentarios, algunos con la etiqueta SECCIóN
ordenadas desde la A hasta la F.
Código 3.6.
//SECCIóN A
if ( !comparaConFondo ) {
// siendo el método “comparar con un fotograma anterior”
// carga la nueva imagen en el buffer
retardo.cargar( entrada );
// toma una imagen del buffer como fondo actual para comparar
imagenAcomparar = retardo.punteroPrimero();
}
else {
//cuando el método es “comparar con fondo”
if ( !fondoTomado ) {
//si aun no se a tomado un fondo, se toma la imagen actual como nuevo fondo fijo
fondoFijo.copy( entrada, 0, 0, ancho, alto, 0, 0, ancho, alto);
//se indica que el fondo fijo ha sido tomado
fondoTomado = true;
}
//tomar la imagen del fondo fijo como imagen para comparar
imagenAcomparar = fondoFijo;
}
//SECCIóN B
//propara las dos imagenes para leer sus pixeles
entrada.loadPixels();
imagenAcomparar.loadPixels();
71
//obtiene la posicion en X e Y en función del orden del pixel
posx = i % ancho;
posy = i / ancho;
//SECCIóN C
//toma el color del pixel i-ésimo de cada una de las imágenes
color actual = entrada.pixels[i];
color delFondo = imagenAcomparar.pixels[i];
//SECCIóN D
// obtiene la diferencia (absoluta, es decir en valor positivo) de cada uno de los componentes
// color: rojo, verde y azul
float difRed = abs( red(actual) - red(delFondo) );
float difGreen = abs( green(actual) - green(delFondo) );
float difBlue = abs( blue(actual) - blue(delFondo) );
// obtiene la diferencia promedio
float gris = ( difRed + difGreen + difBlue ) / 3.0;
//SECCIóN E
//si la diferencia supera el valor de umbral, se lo considera movimiento
boolean conMovimiento = gris>umbral;
72
// si no hubo movimiento el pixel de la imagen bitonal será negro
bitono = color(0, 0, 0);
}
//pinta el pixel de la imagen bitonal
bitonal.pixels[i] = bitono;
}
//SECCIóN F
if ( area>0 ) {
x = totx / area;
y = toty / area;
}
Iniciaremos con la que está etiquetada como SECCIóN A. Que inicia con un condicional (ciclo if) que según el valor de
una variable boolena llamada “comparaConFondo” bifurca el flujo en dos direcciones. Cada uno de estos caminos se
corresponde con los dos métodos de captura por substracción: 1) el que compara con una imagen de referencia pre-
fijada (compara con fondo) y 2) el que compara contra la misma imagen retardada. Cuando la variable comparaCon-
Fondo tiene valor verdadero (true) implementa el primer método (compara con fondo) y con valor falso (false), el otro.
En el código citado, dado que la pregunta está negada, la primera parte del if se dedica al método que comparará
contra una imagen retardada:
Código 3.7.
if ( !comparaConFondo ) {
// siendo el método “comparar con un fotograma anterior”
// carga la nueva imagen en el buffer
retardo.cargar( entrada );
// toma una imagen del buffer como fondo actual para comparar
imagenAcomparar = retardo.punteroPrimero();
Utiliza un objeto llamado “retardo” que pertenece a la clase Buffer-Video y fue implementada para esta aplicación.
La clase BufferVideo funciona como una cola (pool) que almacena objetos de Pimage. Se ingresan imá-
genes por un extremo y se las extrae por el otro, en función de la longitud de dicha cola es el retardo
(contados en fotogramas) que sufre el flujo de video que ingresa de la cámara.
El método .cargar() es el encargado de ingresar una nueva imagen a la cola, y el método .punteroPrimero() retorna
la primera disponible en la salida. Como se ve en el extracto de código anterior (3.7.), la imagen actual de la cámara
ingresa a la cola y de la cola se extrae la imagenAcomparar, que es la que se comparará con la imagen actual.
73
Por el otro lado del if, se verifica si ya existe una imagen de referencia mediante la variable boolena fondoTomado que
funciona como bandera (flag) para este fin. Si aún no existe una imagen de referencia, esta es tomada de la imagen
actual, que está almacenada en el objeto entrada. Luego designa esta imagen de referencia (fondoFijo) como la ima-
genAcomparar.
Código 3.8.
}
else {
//cuando el método es “comparar con fondo”
if ( !fondoTomado ) {
//si aun no se a tomado un fondo, se toma la imagen actual como nuevo fondo fijo
fondoFijo.copy( entrada, 0, 0, ancho, alto, 0, 0, ancho, alto);
//se indica que el fondo fijo ha sido tomado
fondoTomado = true;
}
//tomar la imagen del fondo fijo como imagen para comparar
imagenAcomparar = fondoFijo;
}
La SECCIóN B se encarga de inicializar los arreglos de píxeles de los objetos entrada e imagenAcomparar, mediante el
método .loadPixels(). Con el fin de cargar los píxeles de la imagen en un arreglo unidimensional siguiendo el orden de
la lectura (occidental), de izquierda a derecha y de arriba hacia abajo, esto agiliza la lectura de los valores de los píxeles.
Sin embargo, requiere de un pequeño ajuste para transformar las coordenadas bidimensionales en una
posición unidimensional: si tomamos la variable i del ciclo for, que determina la posición unidimensional,
entonces las operaciones para obtener la posición X e Y de dicha posición son:
Código 3.9
Para obtener X se usa la operación módulo (%) que calcula el resto de la división entera. Para calcular Y
se usa la división entera.
En la SECCIóN C se obtiene el color de las respectivas imágenes, la actual y la que se usará para comparar. Luego en
la SECCIÓN D se separan los componentes color de estos, es decir, los valores de rojo, verde y azul y se los opera rea-
lizando una substracción con valor absoluto:
Código 3.10.
//SECCIóN D
// obtiene la diferencia (absoluta, es decir en valor positivo) de cada uno de los componentes
// color: rojo, verde y azul
float difRed = abs( red(actual) - red(delFondo) );
float difGreen = abs( green(actual) - green(delFondo) );
float difBlue = abs( blue(actual) - blue(delFondo) );
74
Esta operación es la que le da nombre al método de captura. El valor absoluto ( abs() ) vuelve el resultado de la subs-
tracción positivo, sin importar cuál de los valores operados sea mayor.
Posteriormente, se obtiene el promedio de estas substracciones y se toma como valor de gris para crear la imagen
resultante. A continuación, en la SECCIóN E se usa dicho valor de gris para ser comparado con el valor de umbral y
decidir si este corresponde a un píxel que debe ser tomado como movimiento:
Código 3.11.
//SECCIóN E
//si la diferencia supera el valor de umbral, se lo considera movimiento
boolean conMovimiento = gris>umbral;
En la misma SECCIóN E se consulta con un condicional (if ) el valor de la variable booleana conMovimiento (recién car-
gada) para realizar los cálculos pertinentes (esto es actualizar los valores del área, la posición del centro de gravedad y
los límites del bounding box), en caso de ser movimiento, y pintar los píxeles de blanco o negro (según corresponda)
en la imagen bitonal.
Los cálculos del centro de gravedad se completan en la SECCIÓN F, obteniendo el valor promedio de las posiciones de
todos los píxeles blancos (de movimiento).
Código 3.12.
75
// variable que definen el ancho y alto de la imagen de la cámara
int ancho = 320;
int alto = 240;
//diferencia mínima entre los lados izquierdo y derecho para dibujar figuras
float diferenciaMinima = 0.4;
void setup() {
// tamaño de la pantalla
size( 800, 600 );
// suaviza las curvas
smooth();
// tasa de animación
frameRate( 30 );
void draw() {
// pinta todo el fondo con el color gris con mucha transparencia para difuminar las figuras
pasadas
limpiarFondoConEsfumado( color( grisFondo, grisFondo, grisFondo, 10 ) );
76
// leer un nuevo fotograma
camara.read();
// analiza el movimiento en función del nuevo fotograma
capturaDeMovimiento.capturar( camara );
77
// revisa si hay movimiento en ese pixel
if ( capturaDeMovimiento.movEnPixel( xEnCamara, yEnCamara ) ) {
void keyPressed() {
// si se presiona la tecla espacio
if ( key == ‘ ‘ ) {
// invierte el estado de activación del monitoreo
monitoreo = !monitoreo;
// si se quita el monitoreo se vuelve a limpiar el fondo
if ( !monitoreo ) {
background( grisFondo );
}
}
}
78
void mousePressed() {
// si se presionó el botón principal del mouse
if ( mouseButton == LEFT ) {
// si se preciona el botón del mouse se recaptura el fondo
capturaDeMovimiento.recapturarFondo();
}
else {// si se presionó el botón alternativo
//si esta en monitoreo
if ( monitoreo ) {
// si el mouse fue presionado en la región de monitoreo del umbral
if ( mouseX > ancho*2 && mouseX < ancho*2 + 150 &&
mouseY >= 0 && mouseY <= 255 ) {
// calculo un nuevo valor
float nuevoValor = map( mouseY, 0, 255, 255, 0 );
// asigna el nuevo valor de umbral
capturaDeMovimiento.setUmbral( nuevoValor );
}
}
}
}
Este ejemplo, que contiene al anterior, calcula la cantidad de movimiento en las regiones definidas por las llamadas
.movEnArea(), luego, calcula la diferencia entre las dos regiones para determinar la cantidad de figuras que serán
mostradas en pantallas:
Código 3.13.
La idea consiste en que, cuanto mayor diferencia, mayor cantidad de figuras. De esta manera, la cantidad se incre-
menta solo cuando una de las regiones se activa por vez, si ambas se activan se cancelan mutuamente y la cantidad
baja. Por ejemplo: si una persona se encuentra en el centro de la escena y mueve uno de sus brazos (pongamos, por
ejemplo, el izquierdo) entonces su silueta se dibuja con el tipo de figura que le corresponde (círculos o cruces), pero
si extiende ambos brazos, entonces la figura deja de dibujarse.
Una vez que la cantidad es determinada, un ciclo for es ejecutado con dicha cantidad, haciendo que en cada ciclo elija
una posición al azar, en forma normalizada (es decir tomando valores entre 0 y 1), y utilice esta para dibujar una figura
(eligiendo la adecuada según la región activada).
79
Código 3.14.
Como se ve en el extracto anterior, se obtiene una variable X e Y al azar, en forma normalizada y luego se traducen a
posiciones en el cuadro de la cámara.
Por ejemplo, si X tuviera un valor de 0,1, entonces la variable xEnCamara = int( x * ancho) adquiriría
int(0,1*320) = 32.
A continuación se invoca al método .movEnPixel() del objeto capturaDeMovimiento para verificar la existencia de
movimiento en dicho píxel. Si hay movimiento, entonces calcula la posición equivalente en pantalla, elige la figura
correspondiente y la dibuja.
Código 3.15.
80
4. Detección de contenido e interfaces tangibles
Objetivos
• Conocer e implementar algunos de los sistemas y algoritmos existentes para detectar contenido en una imagen.
• Incorporar bibliotecas de código externo en nuestros proyectos.
• Conocer e implementar los sistemas de detección de contenido más populares.
El problema surge si hay dos sujetos de movimiento, ya que este algoritmo no es capaz de distinguir entre uno del
otro. Todo píxel blanco es considerado movimiento, omitiendo el sujeto que los genera. Básicamente, el problema
se reduce a lograr reconocer las manchas inconexas, es decir: ver cuáles píxeles dependen de cuál mancha y no
confundir píxeles de diferentes manchas (diferentes sujetos).
En general, de un blob se puede obtener el área que ocupa (medida en cantidad de píxeles), el ancho y alto de la caja
que lo contiene (bounding box) y el centro de gravedad de la misma.
También se puede examinar la forma del contorno de la mancha, entendiendo a dicho contorno como una secuencia
de segmentos. Otro dato que se puede analizar es su identidad, como un número entero que lo identifica y que no
debería cambiar de un fotograma a otro, producto de los sucesivos análisis.
81
En las figuras siguientes puede observarse, primero, un par de
sujetos captados como una unidad, luego, en la siguiente, los
mismos sujetos captados por separado. En la primera de las dos
figuras se observa cómo todos los píxeles, sin importar la depen-
dencia (a cuál silueta o forma pertenecen), entran en el cálculo
de un único sujeto de movimiento.
82
4.1.1. La librería BlobDetection
Processing posee una librería (conjunto de objetos y funciones preprogramadas) de detección de blobs llamada Blob-
Detection. Dicha librería parte de un sistema de captura por umbral de brillo (o luminosidad).
La captura por umbral funciona sin una imagen de referencia, simplemente se toma la imagen actual y se discrimi-
na, en función de un valor de gris, aquellos valores que son más claros de los más oscuros. En rigor, se compara el
nivel de brillo de cada píxel con el valor de umbral y se transforma a los que están por encima en blanco y a los que
están por debajo en negro.
En este caso, el filtro que discrimina forma de fondo se aplica directamente a la imagen de la cámara en vez de al resul-
tado de una substracción. La librería BlobDetection permite aplicar dicho filtro y hacer un análisis de los blobs resul-
tantes, ya sea buscando las formas claras u oscuras, es decir decidiendo si la relación figura/fondo se aplica tomando
lo claro (o lo oscuro) como figura. La librería se organiza mediante un conjunto de clases principales:
1) La clase BlobDetection que se encarga de hacer el análisis de la imagen y separar los blobs.
2) La clase Blob que se utiliza para ver los atributos de cada uno de los blobs separados.
3) La clase EdgeVertex que sirve para ver los atributos de cada uno de los segmentos del contorno de un blob.
Cada una de esas clases sigue una jerarquía que respeta el orden en que fueron presentadas, es decir, un objeto del
tipo BlobDetection devuelve objetos del tipo Blob y estos, a su vez, devuelven objetos EdgeVertex, siguiendo la lógica
en la cual el BlobDetection es el analizador que devuelve los blobs encontrados y que estos, por su parte, devuelven
la forma de su contorno.
Esta estructura de árbol, que tiene al BlobDetection como raíz, a los Blob como ramas y a los EdgeVertex como hojas,
requiere de un ciclo for para recorrer a los segundos y otro ciclo anidado para recorrer cada una de las hojas.
83
4.1.2. Un ejemplo con detección de blobs
A continuación, veremos un ejemplo que utiliza detección de blobs para captar los contornos de la silueta de dos
personas y sobredibujar el contorno de las mismas con un motivo, en este caso, bucles.
Imagen 4.3. Ejemplo con detección de Blobs que utiliza la información del contorno
El ejemplo tiene un modo de monitoreo que se puede activar o desactivar utilizando la barra espaciadora. Cuando la
barra se encuentra con el monitoreo activado permite ver la imagen captada con los atributos de cada uno de sus blobs.
84
El código principal del ejemplo se encuentra dividido en secciones. La porción expuesta a continuación utiliza un
conjunto de funciones que serán citadas más adelante.
Código 4.1.
//SECCIóN A
//importa las librerías necesarias
import blobDetection.*;
import processing.video.*;
//SECCIóN B
//objeto para capturar la imagen de la cámara
Capture camara;
//objeto que busca los Blobs en la imagen
BlobDetection manchas; //declaracion
//variable que define el nivel del umbral que discrimina entre claro y oscuro
float umbralBlobDetection;
void setup() {
//define el ancho y alto de la pantalla
size( 800, 600 );
//define la cantidad de cuadros por segundo
frameRate( 30 );
noFill();
smooth();
//SECCIóN C
//imprime los dispositivos de video disponibles en el sistema
println( Capture.list() );
//inicializa la cámara
camara = new Capture( this, anchoCamara, altoCamara );
//camara.start(); esto es necesario en Processing 2.0
//SECCIóN D
//inicializa el objeto BlobDetection
manchas = new BlobDetection( anchoCamara, altoCamara );
//define si se busca claro sobre oscuro a al revés
manchas.setPosDiscrimination( buscaClaro);
//define el umbral en 0.4 (40% del rango)
umbralBlobDetection = 0.4;
//establece dicho valor como umbral en el objeto
manchas.setThreshold( umbralBlobDetection );
}
85
void draw() {
//SECCIóN E
//si la cámara tiene un nuevo fotograma disponible
if ( camara.available() ) {
//SECCIóN F
//si el desefonque está activado, entonces lo aplica a la imagen
if (conDesenfoque) fastblur( (PImage) camara, 2 );
//SECCIóN G
//muestra la imagen de la cámara a pantalla completa
image( camara, 0, 0, width, height );
//pone el color de dibujo en verde
stroke( 0, 255, 0);
//ejecuta la función que dibuja los bordes con un motivo de bucles
//en pantalla completa y para los blobs que miden más de un 10% del ancho y
//alto de la imagen
dibujarTodosLosBlobsConMotivo( manchas, 0, 0, width, height,
width*0.1, height*0.1, 10);
//SECCIóN H
//si el monitoreo está activado
if ( monitor ) {
//muestra la imagen de la cámara en el tamaño original
image( camara, 0, 0 );
//dibuja los blobs, sus cajas (boundingbox)
dibujarTodosLosBlobsConAtributos( manchas, 0, 0, anchoCamara, altoCamara,
anchoCamara*0.1, altoCamara*0.1);
}
}
}
void keyPressed() {
//SECCIóN I
//si se preciona la tecla espaciadora se activa/desactiva el monitoreo
if ( key == ‘ ‘ ) {
monitor = !monitor;
}
//SECCIóN J
//si se presiona la tecla ARRIBA se sube el umbral
if ( keyCode == UP ) {
umbralBlobDetection += 0.025;
umbralBlobDetection = constrain( umbralBlobDetection, 0, 1 );
println( “umbralBlobDetection = “+umbralBlobDetection );
manchas.setThreshold( umbralBlobDetection );
86
//si se presiona la tecla ABAJO se baja el umbral
}
else if ( keyCode == DOWN ) {
umbralBlobDetection -= 0.025;
umbralBlobDetection = constrain( umbralBlobDetection, 0, 1 );
println( “umbralBlobDetection = “+umbralBlobDetection );
manchas.setThreshold( umbralBlobDetection );
}
}
}
En la SECCIóN A del ejemplo se importan las dos librerías necesarias: processing.video y blobDetection. Luego se de-
claran tres variables booleanas que sirven para activar y desactivar el monitoreo, para activar el filtro de desenfoque
(blur) y la búsqueda de claro sobre oscuro en la detección de blobs.
La SECCIóN B declara el objeto “cámara” de tipo Capture, y el objeto “manchas” del tipo BlobDetection. Luego declara
tres variables, encargadas de definir el nivel de umbral (de la captura) y las dimensiones de la imagen de la cámara. A
continuación, en el void se-tup(), en la SECCIÓN C, se inicializa el objeto cámara.
En la SECCIÓN D se inicializa el objeto “manchas” del tipo BlobDetection. Este paso requiere de tres métodos. El
primero, obviamente, el constructor de la clase, al que se le pasa las dimensiones de la imagen. El segundo método,
.setPosDiscrimination(), define si se buscan las regiones claras u oscuras. Si se pasa un parámetro con valor true,
entonces las regiones claras son tomadas como figura y las oscuras como fondo. Con el valor false se invierte esta
relación. El tercer método, .setThreshold(), configura el umbral, recibiendo un valor entre 0 y 1. Cuando el valor es
0, el umbral es negro, y cuando el valor es 1, entonces es blanco. Los valores intermedios son valores de gris, por
ejemplo, el 0,8 es un gris claro, y si se usa como umbral, solo grises más altos pasarán por blanco.
Ya en el método void draw(), en la SECCIóN E, se consulta si la cámara tiene un fotograma disponible (método availa-
ble() del objeto Capture) y si la condición se cumple, se usa el método read() para leer dicho fotograma.
La clave del algoritmo se encuentra en la SECCIóN F, en donde se usa el método computeBlobs() del objeto manchas
(del tipo BlobDetection) para que el objeto analice la imagen, ejecute la captura por umbral y detecte los blobs. Este
objeto ejecuta el análisis y construye una lista de blobs, disponible para ser consultadas. Esta consulta se ejecuta en el
método dibujarTodosLosBlobsConMotivo(), dentro de la SECCIóN G, que se encarga de poner la imagen capturada y
el análisis del BlobDetection en la pantalla.
A continuación, mostraremos el código que posee la mayoría de las funciones invocadas en el código principal del ejemplo.
Código 4.2.
//variable que se usa como constante para definir la tolerancia para corroborar
//continuidad entre segmentos en la función
float TOLERANCIA = 10;
//------------------------------------------------
//función que dibuja los Blobs con sus atributos
//recibe como parámetro un objeto BlobDetection, la posición y tamaño para mostrar
//los blobs, y los tamaños mínimos para considerar los blobs (por debajo
//de esas dimensiones desestima el blob)
void dibujarTodosLosBlobsConAtributos( BlobDetection manchas, float x, float y,
float ancho, float alto, float anchoMinimo, float altoMinimo ) {
87
//consulta la cantidad de blobs del objeto BlobDetection
int cantidadBlobs = manchas.getBlobNb();
//declara un objeto Blob para recibir los distintos blobs
Blob unaMancha;
88
//dibuja la caja (bounding box)
rect( x + laForma.xMin*ancho, y + laForma.yMin*alto,
laForma.w*ancho, laForma.h*alto );
//dibuja el centro
ellipse( x + laForma.x * ancho, y + laForma.y * alto, 10, 10 );
}
//------------------------------------------------
//función que recibe un blob y dibuja el contorno
void dibujarBlobContorno( Blob laForma, float x, float y,
float ancho, float alto ) {
//dibuja el segmento
line( x+desde.x*ancho, y+desde.y*alto,
x+hasta.x*ancho, y+hasta.y*alto
);
}
}
}
//------------------------------------------------
//dibuja el contorno del blob adornado con un bucle
//recibe como parámetros un blob, el largo de los motivos (cantidad de segmentos
//que se agrupan para simplificar la forma), la posición y dimensión
void dibujarBordeDelBlobConMotivo( Blob laForma, int largo, float x, float y,
float ancho, float alto ) {
//esta variable se usa para contar la cantidad de segmentos que se toman para
//simplificar la forma
int contador = 0;
89
//variable que guarda el estado actual
String estado = “buscaInicio”;
90
//toma el segmento anterior como final
//deja de contar segmentos, lo cierra aunque no haya alcanzado la longitud
//por que no hay continuidad
fin = anteriorHasta;
//dibuja el segmento recien cerrado
dibujarUnSegmento( x+inicio.x * ancho, y+inicio.y*alto, x+fin.x * ancho, y+fin.y *alto );
inicio = desde;
}
}
//cargar el valor del punto final, para tener de referencia en el siguiente ciclo
anteriorHasta = hasta;
}
}
//a partir del punto anterios obtien otros dos sobre una paralela a la recta
//definida por los dos puntos originales
float x4 = xDistanciaAngulo( x3, angulo+PI, distancia*0.5 );
float y4 = yDistanciaAngulo( y3, angulo+PI, distancia*0.5 );
91
float x5 = xDistanciaAngulo( x3, angulo, distancia*0.5 );
float y5 = yDistanciaAngulo( y3, angulo, distancia*0.5 );
beginShape();
vertex(x2, y2);
bezierVertex(x1, y1, x5, y5, x3, y3);
endShape();
}
//------------------------------------------------
//esta función hace lo mismo que la anterior pero varia la ubicación de los puntos
//y su uso en la curva
void dibujarUnSegmentoOtroBucle( float x1, float y1, float x2, float y2 ) {
beginShape();
vertex(x1, y1);
bezierVertex(x2, y2, x5, y5, x3, y3);
endShape();
beginShape();
vertex(x2, y2);
bezierVertex(x1, y1, x4, y4, x3, y3);
endShape();
}
//------------------------------------------------
//estas dos funciones encuentran un punto a partir de un punto de origen
//un ángulo y una distancia
float yDistanciaAngulo( float y, float angulo, float distancia ) {
return distancia * sin( angulo ) + y;
}
//------------------------------------------------
92
De las funciones mostradas anteriormente seleccionaremos .dibujarTodosLosBlobsConAtributos(), que se encarga
de recorrer los blobs hallados y mostrarlos en pantalla. Para eso, se consulta la cantidad de blobs encontrados en la
última ejecución del método .computeBlobs(), usando el método .getBlobNb(). Luego, se utiliza un ciclo for para reco-
rrer cada uno de los blobs, empleando el contador del ciclo (la variable i) para pedir por orden cada blob, invocando
al método .getBlob( i ). Con esto se carga en el objeto unaMancha (del tipo Blob) el i-ésimo blob. Una vez cargado el
objeto, se puede acceder a los diferentes atributos del blob.
Un dato importante es que estos valores se devuelven en forma normalizada, lo que significa que son valo-res entre
0 y 1. Por ejemplo, si unaMancha.x vale 0,5, entonces se encuentra en la mitad del cuadro, pero para traducir eso a
una posición en la pantalla, hay que multiplicarlo por el ancho de la misma: unaMan-cha.x * width.
Dentro del ciclo for, hay un condicional (if ) que revisa que el blob tenga un tamaño mínimo para ser tenido en
cuenta. Para facilitar los cálculos, se multiplican el ancho y alto del blob (que están normalizados) por el ancho y
alto recibido como parámetro, para hacer el cálculo en píxeles: if ((unaMancha.w*ancho)>anchoMinimo &&
(unaMancha.h*alto)>altoMinimo).
Código 4.3.
93
En el código recién citado se invoca la función .dibujarBlobCajaYCentro() que mostraremos a continuación. Esta reci-
be un blob como parámetro y utiliza sus atributos para imprimir el bounding box y el centro del mismo:
Código 4.4.
//dibuja el centro
ellipse( x + laForma.x * ancho, y + laForma.y * alto, 10, 10 );
}
Otra función que queremos destacar es .dibujarBlobContorno() que recibe un blob como parámetro y usa un ciclo for
para recorrer los segmentos del contorno. Cada segmento se representa con dos puntos, del tipo EdgeVertex, que son
los límites de cada segmento: el punto inicial y el punto final:
Código 4.5.
//dibuja el segmento
line( x+desde.x*ancho, y+desde.y*alto,
x+hasta.x*ancho, y+hasta.y*alto
);
}
}
}
94
4.2. Sistema de detección de patrones para la implementación
de una mesa reactiva
En este apartado veremos un sistema de captación de patrones que permite controlar distintos medios en función
de la ubicación de unas piezas acrílicas. La idea consiste en etiquetar piezas acrílicas con patrones bitonales de forma
que el sistema pueda verlas. Estos patrones tienen motivos en blanco y negro que están preparados para ser captados
por un sistema de visión artificial.
Dentro de este universo, veremos el software ReacTiVision desarrollado por Martin Kaltenbrunner y Ross Bencina en
el Music Technology Group en la Universitat Pompeu Fabra en Barcelona. Este sistema trabaja con un conjunto de
patrones llamados fiduciales. En la imagen siguiente podemos ver un fiducial:
En el siguiente video podemos ver una mesa interactiva desarrollada con ReacTiVision. Esta mesa se llama ReacTable.
Las piezas de acrílico que usa el público poseen fiduciales impresos en la parte de abajo, que permiten que el sistema
reconozca las piezas y controle el sonido en tiempo real.
95
Imagen 4.6. Sitio de ReacTiVision
96
Imagen 4.7. Esquema ReacTable
Tal como explica la cita anterior, la pantalla sensible al tacto se logra mediante un sistema de visión artificial que ve
las posiciones de los patrones y los dedos (sobre la pantalla), mientras un proyector emite una retroproyección de la
imagen para cerrar la ilusión de pantalla que responde al tacto.
Se necesita que la cámara sea infrarroja para que no vea las proyecciones sobre la pantalla; si esto sucediera, dicha
imagen retroalimentaría el sistema de captación, lo que sería perjudicial. Por este motivo, la cámara solo debe ver
el espectro infrarrojo de la luz y no el visible. El proyector de video emite solo luz visible y por ende no sería per-
cibido por el sistema de captación, pero dicho sistema requiere una iluminación que a su vez no interfiera con la
proyección de la imagen, por esto se usa también una iluminación infrarroja que solo es percibida por la cámara y
no por el ojo humano.
Evidentemente, el eje de la problemática gira en torno de la cámara y las condiciones de luz, y aunque parezca un
problema menor (que no lo es), el material del que se construye la pantalla es tan decisivo como la elección de la
cámara y la iluminación:
97
Uno de los mayores focos de problema está en el material que se elige para usar
de superficie de retroproyección (este tema es ampliamente tratado en el texto de
Diego Pimentel). Esta superficie debe cumplir con una serie de requisitos comple-
jos de lograr:
2. Opacidad: debe ser lo suficientemente opaca para permitir ver nítidamente (des-
de abajo) sólo los objetos apoyados, pero no aquellos por encima. Por ejemplo,
deben poder verse los dedos apoyados, pero no la mano por encima de ellos, ni las
caras de las personas.
[...] La iluminación que este sistema utiliza es infrarroja. Nosotros optamos por utili-
zar lámparas incandescentes filtradas con acetatos de color rojo y azul, que permi-
ten generar luz infrarroja con focos comunes. Este tipo de iluminación tiene como
ventaja su bajo costo y la facilidad de encontrar esos elementos en el mercado de
nuestro país. Una de las más importantes desventajas es el calor que genera, lo que
obliga a tomar medidas de ventilación. El sistema alternativo que más se utiliza es
el de reflectores de leds, que no generan calor y tienen una mayor durabilidad.
El mayor problema encontrado con la iluminación fue con la superficie de la pan-
talla, que al no ser antirreflejo funcionaba como un espejo más. Este pseudoespejo
termina haciendo que la cámara vea las lámparas, lo que produce ruido en la cap-
tación de patrones. Debido a esto, nos vimos obligados a poner las lámparas lo
más apartadas hacia los lados posible, para que no entre en el área del reflejo, pero
esta solución genera un tipo de iluminación desigual que complica nuevamente la
captura. (Causa, 2011: 9 y 10)
98
[[…] TUIO es un marco abierto (open framework) que define un protocolo y un API
(interfaz de programación de aplicaciones) común para pantallas multitáctiles tan-
gibles. El protocolo TUIO permite la transmisión de una descripción abstracta de las
pantallas interactivas, incluyendo eventos de toque y estados de objetos tangibles.
Este protocolo codifica datos de control desde una aplicación de seguimiento (por
ejemplo, basado en la visión por ordenador) y la envía a cualquier aplicación de clien-
te que es capaz de decodificar el protocolo […] (Kaltenbrunner, s/f)
Es decir que TUIO es un protocolo con el que una aplicación, que mediante visión artificial interpreta la acción de un
usuario en una pantalla (convirtiéndola en sensible al tacto), transmite a otras aplicaciones dichas acciones para que
estas actúen en consecuencia. La idea es que la aplicación de captura ya esté resuelta y entonces solo quede imple-
mentar la aplicación que utiliza dicha captura.
Este protocolo está pensado para captar patrones bitonales impresos en piezas tangibles (no virtuales) y dedos sobre
una pantalla. A los patrones les llama “fiduciales” u “objetos”, mientras que a los dedos los llama “cursores”. De cada
uno de estos tipos de elementos, el protocolo reconoce tres eventos: 1) el ingreso en escena, 2) la modificación de la
posición o rotación en escena, 3) el egreso de la escena.
[…] TUIO distingue dos tipos de elementos: objetos y cursores. Los objetos son los
patrones bitonales, mientras que los cursores son los dedos que se apoyan sobre
la pantalla (dado que el sistema también es capaz de reconocer el tacto). Cada una
de estas funciones informan un evento de un patrón o de tacto:
Processing posee una librería que permite conectarse a este protocolo y así recibir información de los eventos recién
explicados. Seguidamente puede observarse un ejemplo mínimo de aplicación en Processing que recibe mensajes
mediante el protocolo TUIO:
99
Código 4.6.
import TUIO.*;
TuioProcessing tuioClient;
void setup()
{
tuioClient = new TuioProcessing(this);
}
void draw()
{
}
void addTuioObject(TuioObject tobj) {
}
void removeTuioObject(TuioObject tobj) {
}
void updateTuioObject (TuioObject tobj) {
}
void addTuioCursor(TuioCursor tcur) {
}
void updateTuioCursor (TuioCursor tcur) {
}
void removeTuioCursor(TuioCursor tcur) {
}
void refresh(TuioTime bundleTime) {
redraw();
}
En el ejemplo, puede verse la importación de la librería y la declaración de un objeto de tipo TuioProcessing en las prime-
ras líneas. Luego, en el void setup() se inicializa el objeto. No aparecen líneas en el void draw() ya que no son obligatorias,
pero más adelante veremos que existen formas de acceder a los mensajes de TUIO desde esta estructura.
Debajo del void draw() aparece una serie de siete funciones, de las cuales las seis primeras se corresponden con los
seis tipos de eventos (o mensajes) que el protocolo transmite: addTuioObject, removeTuioObject, updateTuioObject,
addTuioCursor, removeTuioCursor y updateTuioCursor. La última, refresh() la utiliza TUIO para redibujar la escena.
Una cuestión importante de comprender es que Processing, con la librería de TUIO, puede acceder a los eventos y
estados de los objetos y cursores en forma sincrónica y asincrónica. A los eventos accede en forma sincrónica me-
diante las funciones de los eventos que fueron listados en el párrafo anterior. A los estados, de forma asincrónica
consultando durante el void draw(). Los eventos son fenómenos que no tienen duración y que dividen el tiempo.
Mientras que los estados permanecen, tienen duración. En general, un estado está limitado por eventos y/o los
eventos son cambios de estado.
La imagen siguiente (4.8.) muestra un esquema con un ejemplo de cómo se accede en forma sincrónica y asincrónica
a los eventos y estados. La secuencia de eventos que muestra es:
100
Para cada uno de estos eventos hay un recuadro rojo o azul que lo representa y a su izquierda el fiducial que lo genera.
A la derecha se encuentra la secuencia de funciones que se ejecutan en Processing, en amarillo los void draw() y en
verde las que son disparadas por los eventos de TUIO.
Por ejemplo, se puede ver que la primera ejecución del void draw() no accede a ningún objeto. Luego ingresa el fidu-
cial 1 que dispara la función void addTuioObjetct() y pasará como parámetro los datos de dicho evento: posición del
objeto, rotación, identidad, etcétera.
En la siguiente ejecución del void draw(), este accede al estado del fiducial 1, es decir que sabe que está en escena
y dónde se encuentra (así como la rotación, velocidad de movimiento, etc.). Cuando el fiducial se mueve, dispara el
evento void updateTuioObject(), luego el fiducial aparece en escena y dispara su correspondiente evento void add-
TuioObjetct().
Obviamente, la ejecución de las funciones de los eventos solo puede acceder a la información del objeto o cursor que los
generó, mientras que el void draw() puede acceder al estado de todos los objetos o cursores existentes en ese momento,
tal como se ve en el gráfico, en la tercera y cuarta ejecución de esta estructura, que acceden a ambos fiduciales.
Esto hace que uno pueda elegir dos posibles estrategias para acceder a la información: la primera, la que programa en
respuesta de los eventos que se van generando, mientras que la segunda se reduciría a consultar por el estado de los
elementos en las sucesivas ejecuciones del void draw(). Por supuesto que se pueden implementar ambas estrategias
para aprovechar las virtudes de los dos fenómenos: los eventos y los estados.
101
4.2.2. El funcionamiento del ejemplo
Para exponer el funcionamiento del ejemplo, primero explicaremos alguna de las clases en la que se basa. Particular-
mente, nos referiremos al funcionamiento de la clase Agente sin entrar en detalle respecto de su implementación ya
que las problemáticas que toca dicha explicación exceden los objetivos de esta unidad.
Para ahondar en nuestro ejemplo, diremos que existe un arreglo de agentes que se encarga de mover y dibujar en
cada ciclo del void draw(). Los agentes tienen dos estados, activos o inactivos. Cuando se los activa, vagan azarosa-
mente siguiendo una dirección que van cambiando lentamente, a partir del punto donde fueron creados, y luego de
un tiempo (que es distinto para cada agente) se desactivan.
Así, el ciclo de vida del agente tiene este recorrido: se lo crea en una posición de pantalla (nace activo) y vaga por la
pantalla hasta que cumple su tiempo y se desactiva, luego permanece en ese estado a la espera de volver a ser creado
(sobreescrito). Un agente posee un conjunto de métodos de los que nombraremos los más importantes:
• .dibujar(): se encarga de dibujar el agente en cuestión en pantalla. Este método solo se ejecuta si el agente está activo.
• .mover(): se encarga de hacer avanzar al agente un paso en su movimiento por la pantalla. Solo se ejecuta si el agen-
te está activo. También revisa el tiempo que el agente lleva activo para desactivarlo cuando haya pasado el valor
prefijado para este (el cual se fija al momento de crear el agente).
• .desactivar(): desactiva al agente.
• .encontrar(): este método se ejecuta con relación a otro agente (que se le pasa como parámetro) y evalúa si ambos
agentes están activos y si están colisionando en la pantalla, en cuyo caso dibuja un cuadrado con un color que es
mezcla de los de los agentes. Esto sucede si las identidades de los agentes son diferentes entre sí. La identidad se
asigna a los agentes según cuál objeto los haya creado. Es decir, que dos agentes que “nacen” del mismo objeto
(fiducial) no producen un encuentro, aunque estén colisionando.
La idea de la aplicación es que cuando entran fiduciales en escena, cada uno de ellos genera agentes, estos salen a
vagar por la pantalla y cuando agentes (provenientes de diferentes fiduciales) colisionan, entonces se dibujan cuadra-
dos en la posición de la colisión. Por otro lado, cuando ingresa un cursor, es decir que alguien arrastra un dedo en la
pantalla, entonces los agentes se ven influenciados por la dirección del movimiento del cursor.
Por ejemplo, en la imagen siguiente (4.9.) vemos una captura de pantalla en la que se encuentra la aplicación del
ejemplo y a la derecha el simulador de ReacTiVision. Allí, pueden verse los nodos generadores de agentes que produ-
ce cada uno de los fiduciales en escena, como algunos cuadrados generados a partir de las colisiones de los agentes
provenientes de diferentes fiduciales:
102
Imagen 4.9.
La siguiente imagen (4.10.) es otra captura de pantalla en la que se muestra, en el simulador (a la derecha) una línea
curva que representa el movimiento de un cursor y como puede verse a la izquierda, todos los agentes siguen el
movimiento del cursor.
Imagen 4.10.
103
A continuación, podemos observar el código principal de la aplicación: acá no se encuentran ni la transcripción de la
clase Agente ni la implementación de un par de funciones (crearNuevoAgente() y dibujarCuadradoRotado()).
Código 4.7.
//SECCIóN A
//declara un objeto TuioProcessing para acceder al protocolo TUIO
TuioProcessing clienteTUIO;
//es un arreglo que define un conjunto de “tintas” (hue) para los colores de los agentes
float paletaTintas[];
//la cantidad de paletas (tintas) en el arreglo
int cantidadPaleta = 20;
void setup() {
//tamaño de la pantalla
size( 800, 600 );
//cantidad de fotogramas por segundo
frameRate( 30 );
//paleta de color en HSB (tinte saturación y brillo)
colorMode( HSB );
//SECCIóN B
//inicializa el cliente para que reciba el protocolo TUIO
clienteTUIO = new TuioProcessing(this);
//SECCIóN C
//inicializa y da dimensión el arreglo de Agentes
agentes = new Agente[ cantidad ]; //dimension
//recorre con un ciclo for para inicializar uno a uno los objetos del arreglo
for ( int i=0 ; i<cantidad ; i++ ) {
agentes[i] = new Agente( 0, 0, color(0), -1 );
//los desactiva para que no esten en pantalla hasta ser llamados
agentes[i].desactivar();
}
//SECCIóN D
//inicializa y dimensiona el arreglo de paletas
paletaTintas = new float[ cantidadPaleta ];
//recorre con un ciclo for para dar valor (al azar) a cada una
for ( int i=0 ; i<cantidadPaleta ; i++ ) {
paletaTintas[i] = random( 255 );
}
104
//pinta el fondo de blanco
background( 255 );
//suaviza las formas
smooth();
//quita el dibujo del contorno
noStroke();
}
//---------------------------------------------------------
void draw() {
//pinta un rectángulo del tamaño de la pantalla blanco y muy transparente
//para ir cubriendo
fill( 255, 10 );
rect( 0, 0, width, height);
//SECCIóN E
//carga (en un vector) la lista de objetos presentes en TUIO
Vector listaDeObjetosTUIO = clienteTUIO.getTuioObjects();
//consulta la cantidad cargada en el vector
int cantidadDeObjetos = listaDeObjetosTUIO.size();
//SECCIóN F
//con estos dos ciclos for anidados compara todos los agentes contra todos los otros
//sin repetir la comparación, es decir: si compara A->B, entonces no hace B->A
//es decir, considera que la relación es simétrica y por ende igual desde ambos lados
//por ende injustificado hacer la comparación recíproca de cada una
for ( int i=0 ; i<cantidad-1 ; i++ ) {
for ( int j=i+1 ; j<cantidad ; j++ ) {
agentes[i].encontrar( agentes[j] );
}
}
//SECCIóN G
//recorre todos los agentes, los mueve y los dibuja en escena
for ( int i=0 ; i<cantidad ; i++ ) {
agentes[i].mover( seguirAngulo, angulo );
agentes[i].dibujar();
}
//esta función tiene que estar por el uso de TUIO, aunque en este caso no se usen los datos que trae
void addTuioObject(TuioObject tobj) {
//se puede descomentar la linea de abajo para ver los valores que ingresa
//println(“add object “+tobj.getSymbolID()+” (“+tobj.getSessionID()+”) “+tobj.getX()+”
“+tobj.getY()+” “+tobj.getAngle());
}
105
//esta función tiene que estar por el uso de TUIO, aunque en este caso no se usen los datos que trae
void removeTuioObject(TuioObject tobj) {
//se puede descomentar la linea de abajo para ver los valores que ingresa
//println(“remove object “+tobj.getSymbolID()+” (“+tobj.getSessionID()+”)”);
}
//esta función tiene que estar por el uso de TUIO, aunque en este caso no se usen los datos que trae
void updateTuioObject (TuioObject tobj) {
//se puede descomentar la linea de abajo para ver los valores que ingresa
//println(“update object “+tobj.getSymbolID()+” (“+tobj.getSessionID()+”) “+tobj.getX()+”
“+tobj.getY()+” “+tobj.getAngle()
// +” “+tobj.getMotionSpeed()+” “+tobj.getRotationSpeed()+” “+tobj.getMotionAc-
cel()+” “+tobj.getRotationAccel());
}
//esta función tiene que estar por el uso de TUIO, aunque en este caso no se usen los datos que trae
void addTuioCursor(TuioCursor tcur) {
//se puede descomentar la linea de abajo para ver los valores que ingresa
//println(“add cursor “+tcur.getCursorID()+” (“+tcur.getSessionID()+ “) “ +tcur.getX()+”
“+tcur.getY());
}
//SECCIóN H
//esta función tiene que estar por el uso de TUIO
//en este ejemplo se usa para extraer el angulo del último cursor en movimiento
void updateTuioCursor (TuioCursor esteCursor) {
//carga en un vector la lista del puntos del camino recorrido por el cursor
Vector listaDePuntos = esteCursor.getPath();
//obtiene la cantidad de datos de la lista
int cantidad = listaDePuntos.size();
//esta función tiene que estar por el uso de TUIO, aunque en este caso no se usen los datos que trae
void removeTuioCursor(TuioCursor tcur) {
//se puede descomentar la linea de abajo para ver los valores que ingresa
//println(“remove cursor “+tcur.getCursorID()+” (“+tcur.getSessionID()+”)”);
}
106
La estructura central de la aplicación es la misma que la citada en el ejemplo mínimo de ReacTiVision, es decir, el void
setup(), el void draw() y la secuencia de siete funciones que TUIO exige para administrar sus eventos. El código tiene
unas etiquetas “SECCIóN” para destacar algunas porciones del mismo.
Empecemos con la SECCIóN A al inicio de la aplicación, en dicha sección se declara el objeto clienteTUIO que perte-
nece a la clase TuioProcessing. Este objeto es encargado de recibir los mensajes del protocolo TUIO proveniente de
ReacTiVision o del TuioSimulator. Debajo, en la misma sección, se declara el arreglo de agentes[ ] perteneciente a la
clase Agente, que se encarga de poner la colección de agentes en funcionamiento y unas líneas más abajo aparece
el arreglo paletaTintas[ ] del tipo flotante, que se encarga de asignar diferentes colores a los agentes en función del
fiducial del que provienen.
En la SECCIóN B, dentro del void setup() se inicializa el objeto clienteTUIO usando el constructor de la clase y pasando
como parámetro el puntero this que apunta a la aplicación, esto se hace para que la clase TuioProcessing tenga acceso
a la aplicación para enviar los eventos en forma asincrónica (independiente del ciclo) del void draw().
La SECCIóN C se encarga, primero, de inicializar el arreglo agentes[ ] usando la variable cantidad para otorgarle dimen-
sión, luego con un ciclo for recorre una a una las celdas (elementos) del arreglo, inicializando cada uno como objeto
del tipo Agente e inmediatamente a continuación, lo desactiva para que no entre en funcionamiento.
Esto puede parecer arbitrario, pero se debe a que es necesario iniciar los objetos del arreglo para que futuros ciclos
for no se encuentren con un error por puntero nulo (null pointer exception). La idea consiste en que todos los objetos
del arreglo estén inicializados, ya sea activos o inactivos; y cuando el objeto termina su ciclo de vida pasa a desacti-
varse. Inicialmente, todos los objetos Agente empiezan desactivados para que no haya nada en pantalla hasta que un
fiducial los ponga en escena.
Una de las partes más complejas y centrales del algoritmo se encuentra en la SECCIóN E, ya que trata del acceso
asincrónico a los datos de los fiduciales puestos en escena. Para esto se usa un objeto del tipo Vector, llamado lista-
DeObjetosTUIO, que es una suerte de arreglo capaz de almacenar varios elementos. En este caso se usará para que
el objeto clienteTUIO devuelva una lista de los fiduciales que están actualmente en escena, para esto se invoca al
método .getTiuoObjects().
Una vez que el vector fue cargado con los objetos, se usa el método .size() para cargar la cantidad de elementos en la
variable cantidadDeObjetos. Para acceder a cada uno de los elementos del vector es necesario usar un ciclo for que
recorra el vector usando el método .elementAt() que devuelve el i-ésimo elemento del vector:
Código 4.8.
Se usa un objeto de tipo TuioObject llamado esteObjeto para cargar el fiducial de turno. Una vez hecho esto es posible
acceder a los datos del fiducial, como su identidad, posición y rotación. Lo que en este caso es usado para crear un
nuevo agente, en dicha posición (usando .getScreenX() y .getScreenY()), asignándole un color en función de la iden-
tidad del fiducial (usando .getSymbolID() y su rotación (usando .getAngle()):
107
Código 4.9.
float x = esteObjeto.getScreenX(width);
float y = esteObjeto.getScreenY(height);
//ejecuta esta función que crea un nuevo agente en el arreglo (sobreescribiendo uno existente)
crearNuevoAgente( x, y, esteObjeto.getSymbolID(), esteObjeto.getAngle() );
En la SECCIóN G se usa un ciclo for para recorrer el arreglo de agentes y ejecutar dos de sus métodos: .mover() y .di-
bujar(). El método .mover() recibe como parámetros una variable llamada seguirAngulo que es booleana e indica si se
debe o no seguir una nueva dirección.
El segundo parámetro es el ángulo que usará como dirección en caso de ser positiva la variable booleana. Si la va-
riable seguirAngulo tiene valor false entonces el agente en cuestión sigue su dirección preasignada; en cambio, si el
valor es true toma dicho ángulo como nueva dirección. Esto sirve para que todos los agentes sigan la dirección de un
cursor (un dedo sobre la pantalla) cuando este entre en escena.
Al final de la SECCIóN G se encuentra la variable booleana seguirAngulo a la que se le asigna el valor false. La idea de
esto es que la variable siempre tenga ese valor y que solo cambie cuando un evento (que se ejecuta antes del void
draw()) cambie dicho valor. El cambio duraría un fotograma, ya que una vez que vuelva a pasar por el void draw()
recuperaría el valor false, a menos que el evento persistiera y volviera a poner la variable en true antes de cada ciclo.
En la SECCIóN F se usan dos ciclos for para vincular a cada uno de los agentes con todo el resto de los agentes.
El primer ciclo recorre hasta el penúltimo elemento del arreglo y el segundo recorre desde el siguiente agente hasta
el último, de esa manera se hacen todas las combinaciones sin repeticiones: es decir, que si se vincula el agente 4
con el 20, luego ya no se hace el 20 con el 4. Esta vinculación se hace mediante el método .encontrar() en el que al
iésimo agente se le pasa como parámetro el jésimo. Este modo de vincular los elementos se usa cuando la relación
es simétrica, es decir que la relación de A hacia B, es igual que de B hacia A. Por esto los índices de los ciclos for se
organizan de forma tal que no se repitan los pares.
Cada agente que ejecuta este método con otro revisa si ambos están activos, si provienen de identidades diferentes
(es decir, de distintos fiduciales) y si están colisionando; si todas estas condiciones se cumplen, entonces imprime un
cuadrado (con una rotación al azar) en el lugar de la colisión.
Por último, en la SECCIóN H se usa uno de los eventos ejecutados por la clase: void updateTuioCursor(). Que como
hemos dicho anteriormente se ejecuta cuando un cursor que ya está en escena (un dedo sobre la pantalla) cambia
su posición. En este caso, se accede a la lista de puntos que conforman el recorrido del cursor. Gracias a las siguientes
instrucciones se descarga la lista de puntos y se consulta la cantidad de puntos en la lista:
Código 4.10.
//carga en un vector la lista del puntos del camino recorrido por el cursor
Vector listaDePuntos = esteCursor.getPath();
//obtiene la cantidad de datos de la lista
int cantidad = listaDePuntos.size();
108
Luego, con un condicional (if ), si la cantidad de puntos es mayor a UNO (1), entonces se obtienen los dos últimos pun-
tos, llamados “desde” y “hasta”, y con la función atan2() se obtiene el ángulo determinado por dicho par de puntos, el
resultado es cargado en la variable ángulo, que justamente determinará la dirección de movimiento que los agentes
deben seguir.
Para que el seguimiento se ponga en marcha, se activa (pone en valor true) la variable seguirAngulo. Esta última sec-
ción muestra cómo aprovechar un evento para extraer información sobre la interacción del usuario.
Por último veremos cómo trabaja la función crearNuevoAgente( ), que, como su nombre lo indica, genera nuevos
agentes en los lugares en dónde existen fiduciales. Veamos a continuación el código (código 4.11.) de la misma:
Código 4.11.
Tal como vimos, esta función es invocada en la Sección E al momento de ir recorriendo los fiduciales que están pre-
sentes. Al momento de ser invocada recibe como parámetros los del fiducial de turno, es decir: la posición x e y, la
identidad del fiducial y el ángulo de rotación del mismo. Veamos entonces qué uso hace esta función de cada uno
de estos parámetros. Iniciemos por el ID (la identidad), la cual sirve para determinar la paleta de colores con la que se
creará el agente.
En la Sección D del programa principal, se crea una arreglo paletaTintas[ ] usando la variable cantidadPaleta para
definir la cantidad (en este caso tiene asignado un valor de 20). Este arreglo, tiene en cada celda definido un valor
numérico que será el valor de tinte (en un sistema de color HSB) de los colores de los agentes.
109
La relación que se establece para decidir la paleta en función de la identidad es con la siguiente operación:
Código 4.12.
La operación módulo (%) retorna el módulo (resto) de una división entera, por ejemplo: 8 % 3 sería 2, es decir, el resto
de dividir 8 por 3 es 2. Por definición el módulo de N siempre está entre 0 y N-1. Por ende, con esta operación nos
aseguramos que sin importar el número de ID, el valor de cualPaleta está dentro del rango entre 0 y cantidadPaleta-1.
La siguiente cuestión es el uso del ángulo de rotación del fiducial, el cual se utiliza para definir, en forma conjunta, los
valores de la saturación y brillo del color. La forma en que esto se hace es mediante una serie de operaciones (código C):
Código 4.13.
En la primera línea se declara la variable medioangulo, a la que se le asigna el valor de angulo, más 90 grados de giro, lue-
go transformada a grados (para trabajar más cómodo) y pasado a valor absoluto, es decir, siempre con valores positivos.
La suma de los 90 grados responde a un giro que se le agrega por comodidad, en función del tipo de giro que se busca.
La segunda línea se asegura que la variable haya quedado en el rango entre 0 y 360, usando las propiedades de la
operación módulo ya citada. Por último, con el if se transforman los valores entre 180 y 360 al rango 180 a 0. Con esto,
a un ángulo que va desde 0 a 360 en forma sólo creciente, se lo transforma en un rango que va de 0 a 180 y luego
nuevamente a 0, es decir que crece y luego decrece, para ser cíclico (como se ve en la Imagen 4.11.).
110
Las siguientes líneas (código D) traducen el valor de la variable medioAngulo, en los de saturación y brillo. A su vez, se
utiliza el valor de la paleta elegida, más un desfazaje al azar, para establecer el tinte del color. Por último, se establece
la variable colorDeEsteAgente usando los valores de tinte, saturación y brillo, así como el valor de transparencia.
Código 4.14.
Por último, como puede verse en las siguientes líneas (código 4.15.), se utiliza la variable cual para recorrer el arreglo
de objetos agentes[ ] e ir iniciando los agentes usando el color establecido por la rotación y la identidad, así como la
posición del fiducial. En la última línea se puede ver cómo se incrementa la variable cual y se le aplica el módulo para
mantener el valor dentro del rango entre 0 y cantidad-1. Dicho de otra forma, la variable cual va avanzando cíclica-
mente dentro del rango para ir turnando el agente que se reiniciará, para mantener el caudal de agentes en constante
generación.
Código 4.15.
El uso de estas variables ( agentes[ ] y cual ) desde la función es posible debido a que estas están declaradas como
variables globales.
111
Causa, E. (2011), “Desarrollo de un sistema óptico para interfa-
ces tangibles (mesa con pantalla reactiva)”, en: RIM 3: Revista de
Investigación Multimedia, Area Transdepartamental del I.U.N.A,
Buenos Aires.
Causa, E. Tutorial de Arte y Vida Artificial, [en línea]. www.biopus.com.ar. 2007. Dis-
ponible en: <http://www.biopus.com.ar/emiliano/tutorial_vida_artificial/index.
html> [Consulta: 18 de febrero de 2014].
112
Referencias bibliográficas
Unidades 1 y 2
Processing: A Programming Handbook for Visual Designers and Artists Reas, Casey, Fry, Ben Published by The MIT
Press (2007-08-17) ISBN 10: 0262182629 / ISBN 13: 9780262182621
Learning Processing: A Beginners Guide to Programming Images, Animation, and Interaction (Morgan Kaufmann Series
in Computer Graphics) Shiffman, Daniel. Published by Morgan Kaufmann (2008-09-02) ISBN 10: 0123736021 / ISBN
13: 9780123736024
The Nature of Code: Simulating Natural Systems with Processing. Shiffman, Daniel. Published by CREATESPACE
01/02/2014, 2014 ISBN 10: 0985930802 / ISBN 13: 9780985930806
Raymond, E. (1999), The Cathedral & the Baazar, Beijing, Cambridge, Mass, England.
Unidad 3
Causa, E. Algoritmos de captura óptica de movimiento por substracción de video, [en línea]. www.biopus.com.ar, 2007.
Disponible en: < <http://www.biopus.com.ar/html/lista-textos.html> [Consulta: 13 de febrero de 2014].
Noble, J. (2012), Programming Interactivity, 2nd Edition A Designer’s Guide to Processing, Arduino, and openFrameworks,
Editorial O’Reilly Media.
Shiffman, D. (2008), Learning Processing A Beginner’s Guide to Programming Images, Animation and Interaction, Edito-
rial Morgan Kaufmann.
Unidad 4
Causa, E. (2011), “Desarrollo de un sistema óptico para interfaces tangibles (mesa con pantalla reactiva)”, en: RIM 3:
Revista de Investigación Multimedia, Area Transdepartamental del I.U.N.A, Buenos Aires.
Causa, E. (2011), “Desarrollo de una aplicación con interfaces tangibles (mesa con pantalla reactiva)”, en: Red Merco-
sur de Facultades de Diseño y Arte Multimedial. 3ra Jornadas Interuniversitarias, I.U.N.A, Buenos Aires.
Pimentel, D. y Otros (2011), Mundo Circular. Ejemplo low-tech de realidad aumentada e interfaces tangibles, Congre-
so SIGRADI 2011, Santa Fe, Argentina. [Proceedings of the 15th Iberoamerican Congress of Digital Graphics].
Causa, E. (2011), “Diseño de interface para el desarrollo de una pantalla sensible al tacto con aplicación musical”, en:
RIM 3: Revista de Investigación Multimedia, Area Transdepartamental del I.U.N.A, Buenos Aires.
113
Causa, E., Tutorial de Arte y Vida Artificial, [en línea]. www.biopus.com.ar. 2007. Disponible en: <http://www.biopus.
com.ar/emiliano/tutorial_vida_artificial/index.html> [Consulta: 18 de febrero de 2014].
Causa, E., Algoritmos para el análisis de formas y reconocimiento de patrones bitonales, [en línea]. www.biopus.
com.ar. 2007. Disponible en: <http://www.biopus.com.ar/emiliano/tutorial_vida_artificial/index.html> [Consulta: 18
de febrero de 2014].
Kaltenbrunner, M., TUIO protocol, [en línea]. <tuio.org>. Disponible en: <http://tuio.org/>, [Consulta: 18 de febrero
de 2014].
Shiffman, D. (s/d), “Autonomolus Agents”, The Nature of Code. Disponible en: <http://natureofcode.com>.
114