Está en la página 1de 114

Sistemas

interactivos en el arte
Emiliano Causa, Diego Alberti

Índice Introducción Íconos

Unidad 1 Unidad 2 Unidad 3 Unidad 4


Causa, Emiliano
Sistemas interactivos en el arte / Emiliano Causa y Diego Javier Alberti. - 1a ed. -
Bernal: Universidad Virtual de Quilmes, 2014.
E-Book.

ISBN 978-987-3706-08-0

1. Arte. I. Alberti, Diego Javier II. Título


CDD 708

Diseño instruccional y procesamiento didáctico: Bruno De Angelis

Diseño, diagramación y desarrollo web: Alejandro Jobad

Programación: Marisol Martín

Primera edición: mayo de 2014

ISBN: 978-987-3706-08-0

© Universidad Virtual de Quilmes, 2014


Roque Sáenz Peña 352, (B1876BXD) Bernal, Buenos Aires
Teléfono: (5411) 4365 7100 http://www.virtual.unq.edu.ar

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.

Queda hecho el depósito que establece la ley 11723

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.

Cita Lectura obligatoria


Se diferencia de la palabra del autor de la Textos completos, capítulos de libros, artí-
Carpeta a través de la inserción de comi- culos y papers que se encuentran digitali-
llas, para indicar claramente que se trata de zados en el aula virtual.
otra voz que ingresa al texto.

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.

Objetivos del curso


• Conocer las principales herramientas disponibles para producir obras interactivas.
• Reconocer los tipos y variantes de herramientas de manera de poder elegir con libertad cuál o cuáles son las que se
adaptan de modo más preciso a los conceptos que pretenda trabajar en su obra.
• Conocer los principios fundamentales sobre los que funcionan dichas herramientas para poder entonces explotar
sus capacidades al máximo como así también modificarlas según su necesidad.
• Crear sus propias herramientas a partir de otras existentes o desde cero.
• Conocer los algoritmos y técnicas mayormente empleadas en este tipo de dispositivos y utilizarlas, extenderlas o
mejorarlas según su conveniencia.
• Realizar una instalación audiovisual interactiva completa, susceptible de ser expuesta en un ámbito profesional.

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.

Cuando hablamos de sistemas interactivos aplicados al


arte, nos referimos a experiencias en las que el públi- En el siguiente enlace encontra-
co se enfrenta con un objeto, instalación, aplicación o mos información sobre Norbert
performance, con la que puede interactuar generando, Wiener: <http://www.uwiener.edu.
influenciando o controlando el desarrollo de estos. pe/nosotros-norbertwiener.aspx>

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.

1.2. Arquitectura de un sistema interactivo


Jim Campbell, en su Formula for computer art, plantea
un esquema acabado que representa casi cualquier Jim Campbell, artista estadouni-
configuración de una obra interactiva mediada por un dense, pionero en el uso artístico
dispositivo informático. Lo que plantea es un esquema de la tecnología lumínica de los
claramente dividido en tres secciones: input (entrada), LED y considerado entre los más
program/memory (un programa y la memoria en el dis- vanguardistas del Siglo XXI. Li-
positivo), output (salida). En términos prácticos se refie- cenciado en ingeniería eléctrica y
re a las posibilidades intrínsecas de estos dispositivos matemáticas en el Massachusetts
(las computadoras) y las posibilidades que existen de Institute of Technology (MIT) en
que puedan interactuar con el mundo real. 1978, Campbell buscó en el arte
un equilibrio entre su labor en
Según Campbell, cualquier obra mediada con compu- el reconocido centro de inves-
tadoras presenta un dispositivo de entrada que es el tigación tecnológica de EE.UU.
encargado de convertir por medio de diversos sensores y su vida diaria. “Primero fueron
y conversores, estímulos físicos o perceptibles en térmi- películas. A los 10 años de estar
nos naturales o culturales en datos digitales, los cuales trabajando en el MIT empecé a
pueden ser manipulados por sistemas informáticos. integrar mi lado artístico con la
ingeniería. Mi interés siempre
En la entrada del esquema siguiente (Imagen 1.1.)se está en la imagen y en cómo utili-
encuentran como ejemplos de variables del entorno zar la tecnología para presentarla
el ruido, palabras habladas, viento, temperatura, terre- de manera que se vea distinta”.
motos, respiración, color, intensidad lumínica, etcétera. Fuente: Muestra Jim Campbell en
Todos estos estímulos pueden ser “percibidos” por la el Espacio Fundación Telefónica:
computadora a través de conversores y sensores que 20 años de arte electrónico. Dispo-
son específicos para cada magnitud. nible en: <http://planetatelefoni-
ca.com.ar/fundacion/2011/07/21/
jim-campbell-en-el-eft20-anos-
de-arte-electronico/>

10
Imagen 1.1. Formula for computer art

Fuente: imagen obtenida de <http://www.jimcampbell.tv/portfolio/miscellaneous_references/>

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.

Formula for compurter art, en:


<http://jimcampbell.tv/portfolio/miscellaneous_references/>
Se puede ver el mismo esquema en Campbell, (1996), Ilusiones del
diálogo: control y elección en el arte interactivo.

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.

1.2.1. Instalaciones interactivas


Un ejemplo completo de aplicación donde se verifica esta hipótesis es el caso de la obra Residua de Fabián Nonino y
Diego Alberti con coreografía de Maria Zegna, Valeria Cuesta y Rhea Volij.

Imagen 1.2. Residua, instalación interactiva. Centro Cultural Recoleta.

Fuente: cortesía del artista.

En el siguiente enlace se puede acceder a la obra Residua


<http://olaconmuchospeces.com.ar/archives/100>

Texto Aparte

Residua es una instalación interactiva performática para personas con teléfonos


celulares, cantante lírica y software. Fue estrenada en el Centro Cultural Recoleta
en noviembre de 2008, en el marco del Festival Tecnoescena. La instalación prevé
dos estados: un modo de instalación autónoma y otro de performance en vivo.
Antes de ingresar a la sala, los espectadores son informados del funcionamiento:
ingresan a un espacio construido con hojas de papel blanco que, en un principio,
se conserva prístino. El público puede transitar por este espacio.

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.

Comienza así el proceso de polución, degradación y contaminación del espacio. Un


espacio que se escribe virtualmente ya que esos textos desaparecerán al terminar
la función. El objetivo es que el ingreso de palabras en el sistema provoque un
exceso de información tanto visual como sonoro creando así un entorno espectral,
sucio pero a la vez poético y metafórico. El espacio se contamina por el progresivo
aumento de información visual y auditiva, en una serie de acciones y respuestas
encadenadas que inciden en el movimiento y desarrollo de la obra. Un software es-
pecialmente diseñado recompone esos textos y los conecta visualmente unos con
otros a partir de algoritmos de reconocimiento de contexto y sistemas lingüísticos
adaptados para tal propósito. La colección de oraciones evidencia juegos polisémi-
cos. Las conexiones entre los textos suponen un nuevo uso original del lenguaje.

Las oraciones encontradas visualizadas en este espacio representan el uso banal de


las palabras, que por sí solas, son carentes de emociones. Al escuchar una palabra,
esta se carga de sentido, en función de la interpretación que ese otro hace de ella.
El hecho de mostrar y develar todas las oraciones o pedazos de textos en que se en-
cuentra esa palabra enviada pone en evidencia ante el espectador mismo muchas
de las dimensiones posibles en que puede ser comprendido ese vocablo.

Imagen 1.3. Esquema de sistema para tres pantallas

Fuente: elaboración propia

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.

Para ejemplificar casos dentro de estos territorios, la


generación de imágenes en tiempo real es un proble- DAC: acrónimo de Digital Analog
ma del orden virtual, ya que su problemática es casi es- Converter o Conversor digital ana-
trictamente algorítmica. Realizar una detección de mo- lógico. Una explicación exhaus-
vimiento por medios ópticos se encuentra relacionado tiva de los diversos tipos de con-
con ambos aspectos ya que, por un lado se debe obte- vertidores puede encontrarse en
ner información de un dispositivo de entrada (una cá- este documento: <http://www.
mara de video), teniendo en cuenta el ruido propio del asifunciona.com/electronica/af_
dispositivo y la iluminación, pero también es necesario conv_ad/conv_ad_5.htm>
realizar un arduo procesamiento de esa información.

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/>

1.2.3. Los campos de acción: sensado, reproducción, generación y control


En función de lo anteriormente mencionado podemos intentar una separación conceptual de las partes que conforman
el diseño de una instalación multimedial. Decimos “conceptual” puesto que es ingenuo pensar que cada una de estas
partes puede implementarse por separado del resto. Eso, rara vez es posible ya que la instalación o dispositivo multime-
dial que estemos diseñando es en principio un sistema de partes aunadas que funcionan como un todo orgánico. Esta
división tiene como fin poder realizar un estudio esquemático de los problemas asociados a este tipo de desarrollos.

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).

Arduino se convirtió muy rápidamente en la plataforma preferida por


artistas y hobbistas para desarrollar interfaces de sensado y también
como controlador para todo tipo de dispositivo electrónico desde pa-
neles de led hasta robots. <www.arduino.cc>

Los caminos posibles para producir entornos virtuales son


dos: reproducción de material pregrabado y generación En este sentido es que aparecen
en tiempo real por diversos medios. En el primer caso, el disciplinas como la del VJ, que
material audiovisual (y en todo caso sinestésico) está pre- análogamente a la tradición del
producido. Sonidos previamente editados y organizados DJ, ejecuta su arte generalmente
pueden ser disparados en secuencias lógicas relacionales reproduciendo en sincronía con
o en el devenir de los eventos de entrada desde el mundo la música ediciones de material
exterior a modo de triggers (disparadores). videográfico. Audiovisualizer.com
es uno de los sitios más antiguos
Cuando hablamos de “generación en tiempo real” nos que recopila y brinda información
referimos a que el material virtual sea en principio una sobre esta disciplina. Allí pueden
manifestación absolutamente original. En este caso, encontrarse versiones demo de
el software está diseñado para producir en tiempo la mayoría de los softwares utili-
real esas imágenes sinestésicas a partir de pautas de zados para generar visuales en
inteligencia artificial, sistemas de simulación física, o tiempo real. <http://www.audio-
algoritmos de composición musical a partir de teorías visualizers.com/>
matemáticas; situaciones muy frecuentes en la música
electrónica contemporánea.

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>

Un controlador muy popular y original utilizado por muchos artistas


es el Monome: información disponible en <http://monome.org/>

1.2.4. Lenguajes de programación y herramientas de control


La actividad artística mediada por dispositivos electrónicos, y en particular por ordenadores programables, trae apa-
rejadas dos grandes actividades propias de este medio.

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

Todos los softwares de hoy en día vinculados al multimedia y al arte y diseño en


general traen incluidos algún sistema de scripting (pequeñas estructuras de código
que se ejecutan en tiempo real, sin compilación) que permite introducir secuencias
de código que son ejecutadas por el mismo programa o por alguna máquina vir-
tualizada dentro del mismo software (es el caso por ejemplo de Blender, el software
de 3D que permite reprogramarlo completamente utilizando el lenguaje Python).
Es por eso que no podemos afirmar que un lenguaje sea mejor que otro ya que
esto depende de las necesidades específicas de lo que se quiera realizar o de lo que
ya nos es dado por los sistemas existentes.

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.

Imagen 1.4. Pantalla principal de Arkaos

Fuente: imagen extraída de <http://www.arkaos.net/>

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

Pasamos entonces a una breve descripción de cada una de estas herramientas:

Arkaos
<http://www.arkaos.net>
Versión Actual: Grand VJ.

Arkaos es un producto desarrollado en Bélgica, que permite manipulación de vi-


deo en tiempo real, es decir que puede tomar diversos archivos de video (o ma-
terial animado incluyendo composiciones de Quartz Composer –del que habla-
remos en el siguiente apartado–, imágenes fijas y máscaras. Puede trabajar con
hasta ocho capas de video en simultáneo. Posee posibilidad de comunicación MIDI
(protocolo de comunicación que será descripto en los siguientes apartados) en dos
direcciones.

Resolume
<http://www.resolume.com/avenue/>
Versión Actual: 3.0

Resolume, desarrollado en Holanda, está íntegramente escrito en C++ utilizando


librerías OpenGL, esto quiere decir que posee un motor de imagen en tiempo real
de una excelente performance. Puede manejar capas de video en HD y además
tiene integrado un completo reproductor de audio. Reproduce varios formatos,
cuenta con un Analizador de Audio integrado, está pensado para poder realizar
Video Mapping fácilmente. Soporta múltiples pantallas y además posee la posibi-
lidad de conectividad. En la nueva versión 3, el código de Resolume fue reescrito
desde cero (from scratch). Sus desarrolladores aseguran que esta no es solo una
versión mejorada de la original sino que a su vez está pensada desde un nuevo
paradigma tecnológico que tiene relación con la posibilidad de que el software se
siga expandiendo en posibilidades conforme se vaya desarrollando y mejorando
las capacidades de los hardwares gráficos.

VDMX
<http://vidvox.net/>
Versión actual: 5

Las prestaciones son aproximadamente similares a los dos softwares anteriores.


Una de las ventajas que tiene este, por sobre otros, es que permite la ejecución
de varias acciones en paralelo en forma independiente en procesadores de varios
núcleos mediante una estrategia llamada “multihilo” (multi thread), con lo cual el
rendimiento en tiempo real se ve extremadamente mejorado. Además, VDMX se
publicita como la opción más “amiga” de Quartz Composer.

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

Esta herramienta no pertenece en sí al mundo del VJ sino que es su equivalente


musical. Live es en realidad una suite de programas, esto es un compilado de pro-
gramas todos funcionando entre sí para convertir la computadora en una estación
de trabajo musical y performático completa.
La principal virtud de Live es que puede, a la vez que reproduce secuencias de au-
dio pregrabadas, ejecutar secuencias MIDI que controlan sintetizadores y samplers
virtuales que corren en el mismo sistema por medio del estándar VSTi.
Otra de las grandes virtudes que hicieron a Live tan popular es que cuenta con al-
goritmos de Time Stretching sofisticados muy valorados por los artistas provenien-
tes del DJ’ing o la música electrónica. Live puede controlar y a la vez ser controlado
tanto por otros softwares como mediante el uso de controladores discretos que se
conectan al ordenador por USB.

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

Fuente: imagen extraída de <http://cycling74.com/>

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

Pasamos entonces a una breve descripción de cada una de estas herramientas:

Max/MSP y Pure Data


<http://cycling74.com/whatismax/spanish/>
Versión Actual: Max 6

Desarrollado en el IRCAM (Institut de Recherche et Coordination Acoustique/Mu-


sique) por Miller Puckete este software para control, en principio, y generación de
audio digital, después, es central en las artes multimediales ya que fue lo que po-
sibilitó que muchos artistas pudiesen explorar tempranamente la creación de en-
tornos interactivos. Luego de que Cycling 74 comprara el proyecto max/MSP y lo
desarrollara como producto comercial, Puckete lanzó a la comunidad de software
libre la versión libre del Pure Data. Max/MSP o Pure Data con entornos gráficos en
los cuales uno puede desarrollar sintetizadores o estructuras de control complejas
para controlar aparatos MIDI de manera visual conectando objetos unos con otros.

Existen también aplicaciones visuales que permiten programar comportamientos


de manera gráfica. De hecho, Max/MSP tiene su módulo particular destinado al
manejo de imagen llamado Jitter (cuyo análogo en el mundo Pure Data es GEM).
Este módulo permite desarrollar todo tipo de proyectos donde lo central es la ima-
gen en movimiento y el análisis posterior de dicha imagen. Jitter puede ser utiliza-
do tanto para resolver cuestiones de visión por computadora como para producir
de manera algorítmica diferentes tipos de contenidos visuales, ya sea procesando
imagen prediseñada o produciéndola de manera generativa.

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

VVVV es un sintetizador de video en tiempo real e interacción con interfaces físicas.


Está solo disponible para Windows ya que su motor gráfico está construido sobre
la librería DirectX propia de ese sistema operativo. Es 100% tiempo real (no tiene
modos build/run) y además es software libre y recientemente Open Source.

Quartz Composer
<http://en.wikipedia.org/wiki/Quartz_Composer>
Versión Actual: 4 (para Mac OS 10.6 Snow Leopard)

Quartz Composer es un lenguaje de programación visual basado en nodos provis-


to como parte del kit de desarrollo para el sistema operativo OSX. Es solo para Mac,
basado 100% en OpenGL y permite programar filtros de imagen. Además, tiene
un objeto que permite introducir código en lenguaje Javascript (de uso común en
sitios de Internet) y otro que permite modelar Shaders (lenguaje de programación
para el procesador gráfico) utilizando el lenguaje GLSL.

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.

Imagen 1.6. Sitio web de Processing

Fuente: imagen extraída de <http://processing.org/>

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.

Un resumen de toda la funcionalidad de P5, explicada de forma sintéti-


ca y clara por sus creadores puede encontrarse en
<http://processing.org/learning/overview/>

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

Varios proyectos tomaron la intención original de Processing y dieron forma a nue-


vas maneras de acceder a la informática de modo más o menos sencillo. Arduino
es una plataforma de hardware libre por la cual se puede programar un microcon-
trolador de manera sencilla. Un microcontrolador es un dispositivo electrónico (un
chip) que tiene incluidas todas las partes de una computadora completa, es decir
un CPU, una memoria RAM para ejecutar los programas, una memoria ROM para al-
macenar el programa, una memoria Eprom que permite mantener los datos guar-
dados luego de la interrupción de la energía de alimentación y entradas y salidas
digitales y analógicas. <www.arduino.cc>

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.

Una vez superada esta primera instancia de puesta a


punto del entorno de trabajo, las aplicaciones generadas Las visuales de la película Tron
suelen correr más rápido que en Processing por ser C++ Legacy (2010, EE.UU.) dirigida por
un lenguaje no interpretado. Esto significa que el códi- Joseph Kosinski fueron realizadas
go de máquina generado es particular para el sistema utilizando openFrameworks. Se
operativo en el cual se trabaja mientras que el código de pueden ver en el siguiente enla-
máquina generado por Processing es interpretado por ce: <http://jtnimoy.net/?q=178>
una “máquina virtual” que es la que en definitiva interac-
túa con el sistema operativo. Es por eso que el código de
Processing es multiplataforma ya que ese mismo código
es susceptible de ser ejecutado en cualquier ordenador
que tenga instalada una Java Virtual Machine.

Imagen 1.7. Visual de la película Tron Legacy

Fuente: imagen extraída de http://jtnimoy.net/?q=178

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/>

1.2.6. Sistemas operativos


Si bien no es la intención de los autores de este material postular la idea de que un sistema operativo es mejor que
otro, es válido mencionar que históricamente el sistema operativo Mac fue y es el que va a la vanguardia en cuanto a
multimedia se refiere. Es por eso que muchas de las aplicaciones más sofisticadas, versátiles y estables en términos de
performance y actualizaciones están solo disponibles para ese sistema operativo

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.

La versión mas conocida de GNU/Linux es Ubuntu:


<http://www.ubuntu.com/>

1.2.7. Sistemas ópticos de sensado de posición y silueta


Estos sistemas son técnicas, algoritmos y herramientas que utilizan videocámaras para observar una escena y extraer
información a partir del análisis de la imagen captada por este dispositivo. Generalmente, estos algoritmos implican
el análisis de la imagen píxel por píxel para extraer información que de cuenta de aspectos de la escena tales como el
movimiento de una persona, la presencia de ciertos objetos u otras variables.

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:

TSPS permite capturar la entrada de personas en la escena:


<http://opentsps.com/>

reactiVision es utilizado para resolver pantallas táctiles. Constituye una


parte importante del proyecto reacTable:
<http://reactivision.sourceforge.net/>

CCV está pensado para resolver sistemas de pantallas multitouch a gran


escala: <http://ccv.nuigroup.com/>

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.

Ejemplo de proceso de captura de movimiento:

Imagen 1.8. Captura por sustracción de fondo

Fuente: elaboración propia

26
Ejemplo de proceso de captura de movimiento:

Imagen 1.9. Resultado de la captura

Fuente: elaboración propia

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.

Imagen 1.10. Imagen del efecto de la Reflexión total interna frustrada

Fuente: imagen obtenida de <http://cs.nyu.edu/~jhan/ftirsense/>

27
Imagen 1.11. Imagen de las pantallas sensibles al tacto multitacto hecha por Jefferson Han

Fuente: imagen obtenida de: <http://cs.nyu.edu/~jhan/ftirsense/>

El siguiente video muestra las imágenes de las pantallas sensibles al tacto multitacto hechas por Jefferson Han.

Fuente: obtenido de: <http://cs.nyu.edu/~jhan/ftirsense/>

Pantallas sensibles al tacto, multitouch en tela de lycra retropro-


yectada: estas pantallas permiten trabajar con mayores tamaños.
En este caso, se utiliza retroproyección para generar la imagen y
el sistema óptico capta la sombra que generan los dedos al hun-
dir la tela.

28
Imagen 1.12. Imagen de la obra Sensible

Fuente: gentileza del colectivo Proyecto Biopus <www.biopus.com.ar>

Imagen 1.13. Imagen del sistema de la obra Sensible

Fuente: gentileza del colectivo Proyecto Biopus <www.biopus.com.ar>

29
Imagen 1.14. Análisis de imagen realizado por el sistema de pantalla sensible al tacto con tela de Lycra

Fuente: gentileza del colectivo Proyecto Biopus <www.biopus.com.ar>

Fuente: Sensible (2007), Instalación interactiva del Proyecto Biopus <www.biopus.com.ar>

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.

Estas técnicas pertenecen al campo de la realidad aumentada


que implica la vinculación con entidades virtuales mediante la
manipulación de objetos físicos, y por ende, su forma de puesta
en escena busca la integración perceptiva de las entidades vir-
tuales con el entorno físico.

Imagen 1.15. Imagen de un sistema de Realidad aumentada

Fuente: imagen obtenida de


<http://www.interaction-design.de/tag/hci/>

1.2.8. Protocolos de comunicación


Dada la gran complejidad de las tareas que un sistema interactivo debe contemplar para poder generar una expe-
riencia inmersiva, dinámica, en tiempo real de calidad, surge la necesidad de hacer que muchos softwares a la vez
interactúen tanto localmente (en un mismo ordenador) como de forma remota (vía red).

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

Fuente: imagen extraída de <http://www.sequencer.de/syns/moog/Modular.html>

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.

En Windows se puede utilizar el programa midiYoke para virtualizar


puertos midi:
<http://www.midiox.com/index.htm?http://www.midiox.com/myoke.htm>

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.

Sobre OSC se desarrollaron protocolos específicos de la actividad au-


diovisual y de las interacciones en tiempo real entre dispositivos. Tal es
el caso del protocolo TUIO utilizado para implementar de manera están-
dar aplicaciones de interacción multitouch. <http://www.tuio.org/>

1.2.9. Hardware de los sistemas interactivos: sistemas de control y sensado físico


Así como el proyecto Processing encarna cierta revolución respecto del uso de computadoras por parte de artistas
para poder generar imágenes utilizando código, el proyecto Arduino intenta acercar al mismo público el uso de mi-
crocontroladores de manera ágil y sencilla sin mayores complicaciones.

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>

2.2. La computadora personal como herramienta


Con la aparición de la computadora personal (en el año 1976 aproximadamente) comenzó también el desarrollo de
software para esos dispositivos. Hasta ese momento, todo el software era escrito en las universidades con propósitos
fundamentalmente científicos. Grandes mainframes llenaban pisos enteros donde cada científico interesado en acce-
der al recurso tenía que compartir el tiempo de procesamiento con el resto de sus colegas.

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.

Por supuesto, ambas vertientes tienen sus particularidades.

Recién en el año 1983, Richard Stallman acuña el término “software libre


(free software)” al determinarse a generar un sistema operativo de ca-
rácter libre (el sistema GNU). En palabras de Stallman Free software no
significa “software gratuito” sino “software libre”.

“Libre”, en el sentido de ser considerado un bien común para la huma-


nidad y por tanto de ser tratado como cualquier otro bien cultural que
la humanidad haya conseguido a lo largo de su evolución. (Stallman,
1983: <http://www.gnu.org/philosophy/free-sw.html>)

Una analogía pertinente podría ser la siguiente: alguien, en algún


momento, entendió cómo fabricar una pared. Con paredes se pueden
construir casas. Ahora bien, el constructor de casas no paga regalías al
inventor de la pared puesto que ese conocimiento forma parte del acer-
vo cultural de la humanidad. Con el software libre ocurriría más o menos
lo mismo. La diferencia radica en que el software aparece ya en un mun-
do que se rige por las leyes del mercado y la industria.

36
Documento fundacional de la free software fundation:
<http://www.gnu.org/gnu/initial-announcement.html>

Cuando hablamos de software libre nos referimos a


cualquier aplicación sobre la cual uno puede tener El código fuente (en inglés sour-
acceso al código fuente. En ese sentido existen una ce code) es un conjunto de líneas
diversidad de licencias de uso que determinan qué que conforman un bloque de tex-
puedo y qué no puedo hacer con ese software. Funda- to, escrito según las reglas sintác-
mentalmente la cuestión se encuentra dividida en tres ticas de algún lenguaje de pro-
grandes problemas: si puedo o no distribuir mi aplica- gramación destinado a ser legible
ción y/o cómo puedo hacerlo, si puedo o no modificar por humanos. Es un programa en
el código mejorándolo o adaptándolo a mis propias su forma original, tal y como fue
necesidades y/o cómo puedo hacerlo, y si distribuyo escrito por el programador, no es
comercialmente copias de ese software o modifico el ejecutable directamente por el
código original, cuáles son las condiciones que se apli- computador, debe convertirse en
can sobre mi acción. lenguaje de máquina mediante
programas compiladores, ensam-
bladores o intérpretes. Poco tiem-
po atrás, la empresa Apple donó
el código del sistema operativo
que venía incluido en su modelo
Es sumamente importante en este sentido Apple II al museo de historia de
el texto La catedral y el bazar de Eric. S. Ra- la informática: <http://www.com-
ymond (1999), hacker conocido y respeta- puterhistory.org/atchm/apple-ii-
do por la comunidad informática mundial. dos-source-code/>

Sitio oficial de Eric Raymond.


<http://www.catb.org/esr/>

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)

Pure Data (aka Pd, luego devenido max/MSP) es un lenguaje de programación


visual de fuente abierta. PD permite a los músicos, artistas visuales, bailarines y
coreógrafos, investigadores y desarrolladores de software crear gráficamente, sin
necesidad de escribir líneas de código. PD se utiliza para procesar y generar soni-
do, video, gráficos 2D/3D, sensores de interfaz, dispositivos de entrada y MIDI. PD
es adecuado para el aprendizaje de los procesamientos multimedia básicos y mé-
todos de programación visual, así también como herramienta de prototipado de
sistemas más complejos para proyectos de gran escala. (www.puredata.info)

Imágen 2.1. Captura de la web de puredata

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.

Enlace para la descarga del programa:


<http://puredata.info/downloads/pd-extended>

Imágen 2.2. Captura de la página de descarga

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.

Fuente: elaboración propia.

Tutorial/Manual sobre PD, completo y en castellano. Constituye una de


las mejores referencias en español disponibles en la web:
<http://www.lucarda.com.ar/pd-tutorial/>

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.

Processing no es el primer ejemplo de lenguaje para producir imáge-


nes. DBN (Design by Numbers) es una aplicación/lenguaje creado por
un docente del MIT, John Maedea, quien fue profesor de Reas y Fry. En
cierto sentido Processing puede ser considerado como la evolución de
DBN, tal cual quedó registrado en la última actualización del sitio en
2003. <http://dbn.media.mit.edu/>

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.

Sitio del foro de Processing:


<http://forum.processing.org/two/>

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.

2.4. Ejemplo de aplicación


Para nuestro ejemplo utilizaremos la señal sonora producida por un micrófono conectado a nuestra placa de sonido.
Es importante tener en cuenta cómo PD recibe esa señal sonora. Para ello debemos primero asegurarnos de que
nuestro micrófono funciona y que PD está recibiendo esa señal. PD tiene su propia configuración de sonido que de-
pende fundamentalmente del sistema operativo en el cual se encuentre corriendo. En este ejemplo, utilizamos Mac
OS X versión 10.6.8.

Desde el menú de preferencias vamos al setting /audio settings:

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).

El objeto “Fiddle~” nos permite extraer información de amplitud y


frecuencia predominante en una señal de audio. Es la pieza funda-
mental de un análisis de audio sencillo y rápido. Puede consultarse
un ejemplo de cómo usar este objeto en el siguiente recurso:
<http://www.lucarda.com.ar/pd-tutorial/ch03s08.html#id432668>

La tecnología actual permite extraer en forma precisa y en tiempo real


mucha más información de una señal que solo su intensidad o frecuen-
cia. Incluso es posible obtener información semántica de una señal de
audio, como la alocución de mensajes de control para que un teléfono
celular pueda realizar diversas tareas. Si bien estas tecnologías existen
y podrían ser implementadas no sin dificultad en nuestros proyectos,
excede el ámbito de este texto abarcarlas en detalle. Podemos de cual-
quier forma mencionar que todo ese tipo de análisis está basado en el
uso de la llamada transformada rápida de Fourier.

Veremos ahora la cuestión gráfica. Es interesante po-


der pensar la imagen digital no como copia o repre- <http://es.wikipedia.org/wiki/
sentación sino más bien como imagen original, la cual Transformada_r%C3%A1pida_
presenta sus propias problemáticas así como diversas de_Fourier>
materialidades intrínsecas a su propio fundamento tec-
nológico. Es evidente, en este sentido, la idea de “pan-
talla” y de cómo pueden generarse imágenes electróni-
El concepto de gráfico vectorial
cas sobre tal sustrato.
proviene del siglo XVII. Se utili-
zó por primera vez para diseñar
Es importante el hecho de que existan diversas mane-
una tipografía, la Romain Du Roi:
ras de generar imágenes en una pantalla de computa-
<http://en.wikipedia.org/wiki/
dora, y en los dispositivos móviles o proyecciones de
Romain_du_Roi>
todos los tamaños que se puedan imaginar.

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.
*/

// El objeto PShape sobre el que operaremos


PShape estrella;

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.

Documentación de la clase PShape:


<http://processing.org/reference/javadoc/core/>

Si bien la documentación disponible incluida con el programa editor Processing es lo suficientemente


completa como para empezar a trabajar con el lenguaje es conveniente referirse a la documentación en
formato javadoc, ya que es allí donde se intenta brindar la mayor, más completa y actualizada informa-
ción respecto de lo que se intenta utilizar. Son, tanto Processing como PD, softwares que evolucionan a
través del tiempo de manera muy rápida y radical, por tanto, la información contenida en este material
didáctico puede ya no ser válida al momento de su lectura.

En este caso la ejecución es muy sencilla:

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(){

for(int i=0; i < cantPintores; i++)


{
if(bandera == true) {
pintores[i].avanzar();
pintores[i].pintar();
}
}
}

void largar(){
bandera = true;
}

void mousePressed(){
largar();
}

void keyPressed(){
if(key == ‘ ‘)
{
bandera = false;
reset();
}
}

// en el metodo reset() uso un “for” para crear los pintores


void reset()
{

for(int i = 0; i < cantPintores; i++)


{
pintores[i] = new Pintor(
color(random(255), random(255), random(255)), // el color incial al azar
0, // el x inicial
i * height/cantPintores, // el y inicial
1); // la velocidad de avanzce
}
}

47
// definicion de la clase Pintor.
class Pintor {

int posx = 0;

int posy = 100;


color col = #26EACE; // AARRGGBB
int alto = 30;
int ancho = 100;
int velocidad = 1;

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 modelo de capas OSI es el que determina el funcionamiento de In-


ternet. Es interesante como está desarrollado ya que sobre esas ideas
pueden implementarse muchos otros protocolos de comunicación.
Modelo de capas OSI: <http://es.wikipedia.org/wiki/Modelo_OSI>

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.

Tutorial de pitch detection + osc, por Matías Romero Costa, en el cual


está basado nuestro patch.
<http://cargocollective.com/max-pd-tutorial/pitch-detection>

Imagen 2.8. Patch completo de PD

Fuente: elaboración propia

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.

Imagen 2.9. Entrada de audio y su control de volumen

Fuente: elaboración propia

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

Fuente: elaboración propia

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.

Imagen 2.11. El objeto fiddle~ y sus parámetros

Fuente: elaboración propia

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:

Imagen 2.12. Interpolación temporal de los valores amplitud y frecuencia

Fuente: elaboración propia

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.

Imagen 2.13. Compilación del mensaje completo.

Fuente: elaboración propia

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.

Imagen 2.14. Incialización del objeto OSC

Fuente: elaboración propia

La palabra clave localhost refiere siempre a la dirección actual de nues-


tra computadora. localhost es un alias de la dirección ip “127.0.0.1”. Esta
dirección está siempre reservada para referir a la misma máquina.
<http://es.wikipedia.org/wiki/Localhost>

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.

Por el contrario, el uso de samples (muestras) de sonido pregrabado, nos permite


conectar fácilmente un resultado sonoro a un comportamiento visual, por ejem-
plo, una acción de un personaje en la pantalla. Si bien Processing cuenta con una
librería de sonido completa con la cual resolver muchas de estas cuestiones, a los
fines didácticos de este tema utilizaremos PD por encontrarlo más intuitivo y “có-
modo” a la hora de trabajar con sonidos.

En la unidad siguiente explicaremos cómo conectar ambos programas para poder


intercambiar eventos entre uno y otro y lograr así la sincronía antes mencionada.

Cuando hablamos de síntesis sonora nos referimos al fenómeno de pro-


ducir sonidos por medios no acústicos, esto es, utilizando osciladores
electrónicos o medios digitales. En nuestro caso solo nos referiremos
a los últimos aunque los conceptos de generación sonora electrónica
por medios analógicos se encuentran presentes pero de manera emu-
lada. Es decir, osciladores y módulos digitales que emulan las funcio-
nes de los mismos dispositivos electrónicos analógicos. Síntesis sonora:
<http://es.wikipedia.org/wiki/S%C3%ADntesis_(sonido)>

En el siguiente enlace puede verse un tutorial completo sobre cómo


emular un sintetizador Moog Minimoog en PD.
<http://en.flossmanuals.net/pure-data/audio-tutorials/simple-synth/>

2.5. Generación de imagen a partir de Processing y datos por osc


Comenzaremos viendo cómo es que Processing recibe
la información generada por el patch de PD. Para ello, La librería se encuentra disponi-
nuestro sketch utiliza la librería oscP5. Esta librería no ble en: <http://www.sojamo.de/
forma parte del núcleo de base de Processing por lo libraries/oscP5/>
tanto debe ser instalada individualmente.

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.*;

// el objeto oscP5 nos permite recibir mensajes osc a través de


la
// red.

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);
}

/* 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());
}

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).

Para ello utilizaremos la instrucción:

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.*;

// el objeto oscP5 nos permite recibir mensajes osc a través de la


// red.

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();

intensidad = int(map(valorDeAmplitud, 30, 80, 20, 150));


}
}
}

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.

Pueden encontrarse muchas ideas y ejemplos de forma visual, con


su código disponible para ser usado en Processing a través del si-
tio openprocessing que ofrece una galería de sketches subidos por
usuarios de Processing de todo el mundo. Allí también pueden en-
contrarse tutoriales y recursos de todo tipo.
<http://www.openprocessing.org/>

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.

3.2. Captura óptica de movimiento


Los métodos que veremos (en esta unidad y la siguiente) son los de captura por substracción de video, detección de
Blobs y captura de patrones bitonales. A su vez, cada uno de estos métodos será abordado desde una estrategia de
programación diferente, ya que en el primero el método será totalmente programado en el mismo código del ejem-
plo; en el segundo caso se usará una librería que ya está preprogramada y en el tercer caso nos conectaremos con un
software que se encarga de la captura y transmite los datos mediante un protocolo de comunicación.

Para ver otras técnicas de captura de movimiento se recomienda esta


página: <http://sabia.tic.udc.es/gc/Contenidos%20adicionales/trabajos/
Peliculas/Mocap/tecnol.htm>

3.2.1. Captura por substracción de movimiento


En este apartado veremos un tipo de captura óptica de movimiento que utiliza la substracción como método para
captar cambios en la imagen de la cámara y por ende movimiento en la escena captada.

En el campo de la captura de movimiento (Motion Capture) las técnicas de subs-


tracción de video, son una de las más sencillas. Esta técnica consiste en tomar una
imagen de la escena sin movimiento, a la que llamaremos “fondo”, y a partir de
ahí, restársela a cada nueva imagen capturada en la escena. Se realiza, en el flujo
de video que proviene de la cámara, una substracción entre cada fotograma y el
fotograma de referencia, el fondo. Esta resta se realiza operando píxel por píxel,
de una de estas imágenes, con sus respectivos píxeles de la otra imagen. Cuando
los píxeles de una y otra imagen coinciden, la substracción entre valores idénticos
resulta cero, lo que traducido a color es el negro (cero luz, o falta total de luz); y en
el caso contrario aparecen otros tonos. De esta forma, es fácil descubrir los píxeles
que cambiaron entre los dos fotogramas (el actual y el fondo), dado que adoptan

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.

Este tipo de captura es muy sencilla,


Al igual que en el texto citado usa-
dado que no requiere que el sujeto
remos “sujeto” o “sujeto de movi-
del movimiento esté intervenido de
miento” para referirnos a aquellas
ninguna forma; es decir, por ejemplo,
personas o elementos de los que
si se quiere capturar el movimiento
se desea captar el movimiento y
de una persona, no requiere que se
en oposición al resto de la escena,
le coloque nada a la persona, ni tra-
es una oposición análoga a la de
jes especiales, ni sensores. La captura
figura/fondo.
se realiza por simple comparación de
imágenes.
Esto, que en principio es una ventaja, tiene como desventaja hacer a esta técnica
muy sensible a los cambios de iluminación. Dado que el criterio es considerar equi-
valentes el “cambio de imagen” a la “existencia de movimiento”, cualquier cambio
en la imagen es automáticamente tomado por un movimiento.

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

Imagen 3.2. Imagen generada al capturar movimiento en la zona superior derecha

Para explicar el ejemplo, podemos dividirlo en tres partes:

a. Las instrucciones asociadas a la captación de video.


b. Las instrucciones asociadas a la captura de movimiento.
c. Las instrucciones asociadas a la generación de la imagen.

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.

// se importa la librería de video de Processing


import processing.video.*;

// objeto para ver la cámara


Capture camara;

// variable que definen el ancho y alto de la imagen de la cámara


int ancho = 320;
int alto = 240;

void setup() {
// tamaño de la pantalla
size( 800, 600 );

// imprime las cámaras disponibles en el sistema operativo


println( Capture.list() );
// inicializa el objeto de la cámara
camara = new Capture( this, ancho, alto );
//con Processing 2.0
//camara.start();
}

void draw() {

// si la cámara tiene un fotograma nuevo disponible


if ( camara.available() ) {

// leer un nuevo fotograma


camara.read();

// muestra la imagen de la cámara


image( camara, 0, 0 );
}
}

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

En la imagen 3.3. se aprecian niveles de gris bajo para el fondo,


donde no hubo cambios, y niveles de grises claros (altos) en don-
de aparece el sujeto. Para realizar dicha discriminación se usa una
función de umbral que a partir de un valor discrimina lo que se
encuentra por arriba o abajo del mismo. Lo que supere el valor de
umbral es señal (movimiento), lo que esté por debajo será ruido.

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.

Fuente: elaboración propia.

Imagen 3.4. Captura de movimiento por substracción de video con imagen de referencia (fondo) prefijada

Fuente: elaboración propia.

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

Fuente: elaboración propia.

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.

3.5. Cómo se programa con la captura de movimiento


Avanzaremos ahora con el uso de una “clase” programada por nosotros para implementar la captura de movimiento.
Extenderemos, entonces, el código citado anteriormente, agregando las partes necesarias para ejecutar la captura de
movimiento. Aclaramos que el código que aparece a continuación no es ejecutable en forma independiente, ya que
requiere de la definición de un objeto de tipo PMoCap que será explicado más adelante. En principio, nos vamos a
limitar a explicar cómo se lo invoca y utiliza para realizar la captura. Luego,detallaremos en detalle cómo se encuentra
implementado.

64
Código 3.2.

// se importa la libreria de video de Processing


import processing.video.*;

// objeto para ver la cámara


Capture camara;
//LINEA A: objeto para la captura de movimiento
PMoCap capturaDeMovimiento;

// variable que definen el ancho y alto de la imagen de la cámara


int ancho = 320;
int alto = 240;

// variable que indica si se monitorea la captura


boolean monitoreo = true;

void setup() {
// tamaño de la pantalla
size( 800, 600 );

// imprime las cámaras disponibles en el sistema operativo


println( Capture.list() );
// inicializa el objeto de la cámara
camara = new Capture( this, ancho, alto );
//con Processing 2.0
//camara.start();

// valor de umbral para la captura


float umbral = 20;
// cuadros de retardo entre el fotograma actual y el de comparación
int cuadrosDelay = 5;
// define el tipo de captura con “fondo prefijado”
boolean conFondo = true;
//LINEA B: inicializa el objeto de captura
capturaDeMovimiento = new PMoCap( ancho, alto, umbral, cuadrosDelay, conFondo );
}

void draw() {

// si la cámara tiene un fotograma nuevo disponible


if ( camara.available() ) {

// leer un nuevo fotograma


camara.read();
//LINEA C: analiza el movimiento en función del nuevo fotograma
capturaDeMovimiento.capturar( camara );

//LINEA D: carga en un objeto PImage el resultado del análisis del movimiento


PImage imagenDelMovimiento = capturaDeMovimiento.getImagenAnalisis();

//LINEA E: carga la cantidad de movimiento en la región superior izquierda


float movimientoALaIzquierda = capturaDeMovimiento.movEnArea(
int( 0.05 * ancho ),
int( 0.05 * alto ),
int( 0.25 * ancho ),
int( 0.45 * alto ),
imagenDelMovimiento );

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 );

//si el monitoreo está activado


if ( monitoreo ) {
// muestra la imagen de la cámara
image( camara, 0, 0 );
// muestra el resultado del análisis del movimiento
image( imagenDelMovimiento, ancho, 0 );
// muestra el nivel del umbral
fill(0);
stroke( 0, 255, 0 );
rect( ancho*2, 0, 150, 255 );
//LINEA F:
float umbral = capturaDeMovimiento.getUmbral();
fill( 0, 255, 0, 100 );
rect( ancho*2, 255-umbral, 150, umbral );
}
}
}

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:

1) El ancho de la imagen a procesar (de tipo entero int).


2) El alto de la imagen a procesar (de tipo entero int).
3) El valor de umbral que se aplicará para distinguir entre señal y ruido (de tipo flotante –flota).
4) La cantidad de fotogramas de retardo entre la imagen actual y la demorada para comparar (de tipo entero –int).
5) Una variable booleana que indica cuál método de captura se usa, si el valor es true se usa la captura con imagen de
referencia prefijada (con fondo), si es falsa se usa el método de imagen retardada (con delay).

Así, la línea queda:

Código 3.3.

capturaDeMovimiento = new PMoCap( ancho, alto, umbral, cuadrosDelay, conFondo );

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.

// leer un nuevo fotograma


camara.read();
//LINEA C: analiza el movimiento en función del nuevo fotograma
capturaDeMovimiento.capturar( camara );

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.

Imagen 3.6. Captura de movimiento con diferentes niveles de umbral de ruido

Fuente: elaboración propia.

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.

3.5.3. Monitoreo del resultado


El ejemplo de código anterior incluye una serie de procesos de monitoreo que pueden activarse o desactivarse. Esto
se realiza con una variable booleana llamada “monitoreo” y que mediante la presión de la barra espaciadora permite
cambiar su valor y, por ende, activar o desactivar. La variable funciona como llave interruptor mediante un par de
condicionales (if) que contienen las instrucciones para poner en escena el monitoreo de la captura.

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:

- .getArea() devuelve el área (superficie) del movimiento/presencia captado.


- .getX() y .getY() devuelven la posición del centro de gravedad del área.
- .getArriba() .getAbajo() .getIzquierda() .getDerecha() devuelven los límites del rectángulo que contiene el área
(bounding box).

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.

//LINEA E: carga la cantidad de movimiento en la región superior izquierda


float movimientoALaIzquierda = capturaDeMovimiento.movEnArea(
int( 0.05 * ancho ),
int( 0.05 * alto ),
int( 0.25 * ancho ),
int( 0.45 * alto ),
imagenDelMovimiento );

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.

Imagen 3.7. Regiones de análisis de la captura de movimiento

Fuente: elaboración propia.

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.

Imagen 3.8. Monitoreo de los diferentes parámetros de la captura de movimiento

Fuente: elaboración propia.

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.

void capturar( PImage entrada ) {

// se decide en función del método de captura “compara con fondo”


// o “compara con fotograma anterior”

//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();

//inicializa las variables


hayMovimiento = false;
area = 0;
x = 0;
y = 0;

int posx, posy;


long totx = 0;
long toty = 0;

//usa el ciclo for para recorrer los pixeles de las imágenes


for ( int i=0 ; i<largo ; i++ ) {

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;

//carga el resultado de la substracción en la imagen para tal fin


substraccion.pixels[i] = color( gris, gris, gris );

//SECCIóN E
//si la diferencia supera el valor de umbral, se lo considera movimiento
boolean conMovimiento = gris>umbral;

//variable de color para la image bitonal


color bitono;

//si hay movimiento en este pixel


if ( conMovimiento ) {

//el pixel de la image bitonal sera blanco


bitono = color(255, 255, 255);

//si no había movimiento en los pixeles anteriores


if ( !hayMovimiento ) {
//marca la existencia de movimiento
hayMovimiento = true;
//inicia los valores de los bordes del área de movimiento
abajo = arriba = posy;
derecha = izquierda = posx;
}
//actualiza los valores de los bordes del área de movimiento si
//la posición es de un borde
abajo = max( abajo, posy );
arriba = min( arriba, posy );
derecha = max( derecha, posx );
izquierda = min( izquierda, posx );
//suma la posición para obtener la posición promedio
totx += posx;
toty += posy;
//contabiliza el nuevo pixel en el área de movimiento
area ++;
}
else {

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;
}

//actualiza los pixeles de las imagenes resultantes de la substracción y el filtro bitonal


substraccion.updatePixels();
bitonal.updatePixels();
}

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

for ( int i=0 ; i<largo ; i++ ) {

//obtiene la posicion en X e Y en función del orden del pixel


posx = i % ancho;
posy = i / ancho;

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.

// obtiene la diferencia promedio


float gris = ( difRed + difGreen + difBlue ) / 3.0;

//carga el resultado de la substracción en la imagen para tal fin


substraccion.pixels[i] = color( gris, gris, gris );

//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).

Para ver otras técnicas sobre implementaciones de la captura de movi-


miento se recomienda esta página de Daniel Shiffman:
<http://www.learningprocessing.com/examples/chapter-16/exam-
ple-16-13/>

3.7. Aplicación de la captura de movimiento


Pasaremos ahora a mostrar la versión completa del ejemplo. A continuación, vemos el código principal de la aplica-
ción, sin el código de la clase PMoCap. En esta versión completa hay un comentario, dentro del void draw(), que dice
“SECCIóN encargada de la representación”: es donde se encuentran los agregados más significativos.

Código 3.12.

// se importa la libreria de video de Processing


import processing.video.*;

// objeto para ver la cámara


Capture camara;
// objeto para la captura de movimiento
PMoCap capturaDeMovimiento;

75
// variable que definen el ancho y alto de la imagen de la cámara
int ancho = 320;
int alto = 240;

// cantidad mínima y máxima de figuras para dibujar por fotograma


int cantidadMinima = 50;
int cantidadMaxima = 500;

//diferencia mínima entre los lados izquierdo y derecho para dibujar figuras
float diferenciaMinima = 0.4;

//tamaño de la figuras en la pantalla, escrito así para no utilizar ‘ñ’


float tamanio = 20;

// variable que indica si se monitorea la captura


boolean monitoreo = true;

// valor de gris del fondo


float grisFondo = 50;

void setup() {
// tamaño de la pantalla
size( 800, 600 );
// suaviza las curvas
smooth();
// tasa de animación
frameRate( 30 );

// imprime las cámaras disponibles en el sistema operativo


println( Capture.list() );
// inicializa el objeto de la cámara
camara = new Capture( this, ancho, alto );
//con Processing 2.0
//camara.start();

// valor de umbral para la captura


float umbral = 20;
// cuadros de retardo entre el fotograma actual y el de comparación
int cuadrosDelay = 5;
// define el tipo de captura con “fondo prefijado”
boolean conFondo = true;
// inicializa el objeto de captura
capturaDeMovimiento = new PMoCap( ancho, alto, umbral, cuadrosDelay, conFondo );

//pinta el fondo del color gris del fondo


background( grisFondo );
}

void draw() {

// si la cámara tiene un fotograma nuevo disponible


if ( camara.available() ) {

// 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 );

//carga en un objeto PImage el resultado del análisis del movimiento


PImage imagenDelMovimiento = capturaDeMovimiento.getImagenAnalisis();

// carga la cantidad de movimiento en la región superior izquierda


float movimientoALaIzquierda = capturaDeMovimiento.movEnArea(
int( 0.05 * ancho ),
int( 0.05 * alto ),
int( 0.25 * ancho ),
int( 0.45 * alto ),
imagenDelMovimiento );

// 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 );

//SECCION encargada de la representación


// calcula la diferencia entre el movimiento captado en cada extremo
float diferencia = abs( movimientoALaDerecha - movimientoALaIzquierda );
// esta variable define la cantidad de figuras a ser dibujadas
int cantidad = 0;

// si la diferencia de movimiento es mayor a la diferencia mínima


if ( diferencia>diferenciaMinima ) {
// determina la cantidad en función de la diferencia
cantidad = int( map( diferencia, diferenciaMinima, 1,
cantidadMinima, cantidadMaxima ) );
}

// guarda la configuración de los atributos de dibujo


pushStyle();
// cambia la paleta de color a HSB
colorMode( HSB );
// configura para dibujar sin relleno
noFill();

// se usa el ciclo for para dibujar la cantidad decidida de figuras


for ( int i=0 ; i<cantidad ; i++ ) {

//decide una posición al azar en forma normalizada (entre 0 y 1)


float x = random( 1 );
float y = random( 1 );

// dimensiona las posición al tamaño de la cámara


int xEnCamara = int( x * ancho );
int yEnCamara = int( y * alto );

77
// revisa si hay movimiento en ese pixel
if ( capturaDeMovimiento.movEnPixel( xEnCamara, yEnCamara ) ) {

// calcula la posicion equivalente en la pantalla


float xEnPantalla = x * width;
float yEnPantalla = y * height;

// en función de cuál de las dos regiones tiene más movimiento


// decide cuál figura dibuja
if ( movimientoALaDerecha > movimientoALaIzquierda ) {
// elige un color entre el rojo y el violeta al azar
stroke( random( 200, 255 ), 200, 250 );
// dibuja un círculo en dicha posición
ellipse( xEnPantalla, yEnPantalla, tamanio, tamanio );
}
else {
// elige un color entre cercano al verde al azar
stroke( random( 40, 110 ), 200, 250 );
// dibuja una cruz en dicha posición
cruz( xEnPantalla, yEnPantalla, tamanio );
}
}
}
//retorna a la configuración de atributos de dibujo original
popStyle();

//si el monitoreo está activado


if ( monitoreo ) {
// muestra la imagen de la cámara
image( camara, 0, 0 );
// muestra el resultado del análisis del movimiento
image( imagenDelMovimiento, ancho, 0 );
// muestra el nivel del umbral
fill(0);
stroke( 0, 255, 0 );
rect( ancho*2, 0, 150, 255 );
float umbral = capturaDeMovimiento.getUmbral();
fill( 0, 255, 0, 100 );
rect( ancho*2, 255-umbral, 150, umbral );
}
}
}

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.

// calcula la diferencia entre el movimiento captado en cada extremo


float diferencia = abs( movimientoALaDerecha - movimientoALaIzquierda );
// esta variable define la cantidad de figuras a ser dibujadas
int cantidad = 0;

// si la diferencia de movimiento es mayor a la diferencia mínima


if ( diferencia>diferenciaMinima ) {
// determina la cantidad en función de la diferencia
cantidad = int( map( diferencia, diferenciaMinima, 1,
cantidadMinima, cantidadMaxima ) );
}

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.

// se usa el ciclo for para dibujar la cantidad decidida de figuras


for ( int i=0 ; i<cantidad ; i++ ) {

//decide una posición al azar en forma normalizada (entre 0 y 1)


float x = random( 1 );
float y = random( 1 );

// dimensiona las posición al tamaño de la cámara


int xEnCamara = int( x * ancho );
int yEnCamara = int( y * alto );

// revisa si hay movimiento en ese pixel


if ( capturaDeMovimiento.movEnPixel( xEnCamara, yEnCamara ) ) {

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.

// calcula la posicion equivalente en la pantalla


float xEnPantalla = x * width;
float yEnPantalla = y * height;

// en función de cuál de las dos regiones tiene más movimiento


// decide cuál figura dibuja
if ( movimientoALaDerecha > movimientoALaIzquierda ) {
// elige un color entre el rojo y el violeta al azar
stroke( random( 200, 255 ), 200, 250 );
// dibuja un círculo en dicha posición
ellipse( xEnPantalla, yEnPantalla, tamanio, tamanio );
}
else {
// elige un color entre cercano al verde al azar
stroke( random( 40, 110 ), 200, 250 );
// dibuja una cruz en dicha posición
cruz( xEnPantalla, yEnPantalla, tamanio );
}

Causa, E. Algoritmos de captura óptica de movimiento por substrac-


ción de video, [en línea]. www.biopus.com.ar, 2007. Disponible
en: < <http://www.biopus.com.ar/html/lista-textos.html> [Con-
sulta: 13 de febrero de 2014].

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.

4.1. Detección de blobs Blob es un término que significa


Binary Large Object (objeto bina-
En el ejemplo de detección que desarrollamos y ana- rio grande) y que en esta discipli-
lizamos en la unidad 3 siempre se da por supuesto un na se asocia con un conjunto de
único sujeto presente en la captura de movimiento. píxeles que conforman una man-
Como consecuencia, cuando queremos obtener un cha o figura amorfa. Esta figura
contorno, un centro de gravedad y un área, lo hacemos puede ser una persona o cual-
siempre sobre la integridad de la imagen capturada. No quier otra cosa que surja como
podemos diferenciar distintos objetos independientes producto de un proceso de cap-
uno de otro. Las diferencias son tratadas en conjunto tura de movimiento.
como si fueran una unidad.

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.

Imagen 4.1. Captura sin atender a las dependencias

Fuente: elaboración propia

Imagen 4.2. Captura que atiende la independencia


entre los dos sujetos

Fuente: elaboración propia

La segunda figura está resuelta mediante una detección de blobs.

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).

Enlace de descarga para la librería BlogDetection


<www.v3ga.net/processing/BlobDetection/>

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

Fuente: elaboración propia

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.

Imagen 4.4. Ejemplo ejecutando el modo de “monitoreo”

Fuente: elaboración propia

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.*;

//variable que permite activar y desactivar el monitoreo


boolean monitor = true;
//variable que permite activar y desactivar el desenfoque para suavizar la imagen
boolean conDesenfoque = false;
//variable que permite definir si el sujeto el claro sobre fondo oscuro o al revés
boolean buscaClaro = true;

//esta variable define cual es el tipo de bucle que se usa


int CUAL_BUCLE = 1;

//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;

//ancho y alto de la cámara


int anchoCamara = 320;
int altoCamara = 240;

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() ) {

//lee el nuevo fotograma de la cámara


camara.read();

//SECCIóN F
//si el desefonque está activado, entonces lo aplica a la imagen
if (conDesenfoque) fastblur( (PImage) camara, 2 );

//ordena al objeto BlobDetection analizar los blobs en la imagen


manchas.computeBlobs( camara.pixels );

//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;
}

//si el monitoreo está activo


if ( 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;

//recorre los blobs


for ( int i=0 ; i<cantidadBlobs ; i++ ) {

//toma el i-ésimo blob


unaMancha = manchas.getBlob( i );

//revisa que el blob de turno supere el tamaño mínimo definido


if ( (unaMancha.w*ancho)>anchoMinimo && (unaMancha.h*alto)>altoMinimo ) {

//pone contorno de color rojo


stroke( 255, 0, 0);
//dibuja la caja (bounding box) y el centro del blob
dibujarBlobCajaYCentro( unaMancha, x, y, ancho, alto );
//pone contorno de color amarillo
stroke( 255, 255, 0 );
//dibuja el contorno del blob
dibujarBlobContorno( unaMancha, x, y, ancho, alto );
}
}
}
//------------------------------------------------
//función que dibuja los contornos de los Blobs
//adornados con bucles
//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 dibujarTodosLosBlobsConMotivo( BlobDetection manchas, float x, float y,


float ancho, float alto, float anchoMinimo, float altoMinimo, int largo) {

//consulta la cantidad de blobs del objeto BlobDetection


int cantidadBlobs = manchas.getBlobNb();
//declara un objeto Blob para recibir los distintos blobs
Blob unaMancha;

//recorre los blobs


for ( int i=0 ; i<cantidadBlobs ; i++ ) {

//toma el i-ésimo blob


unaMancha = manchas.getBlob( i );

//revisa que el blob de turno supere el tamaño mínimo definido


if ( (unaMancha.w*ancho)>anchoMinimo && (unaMancha.h*alto)>altoMinimo ) {

//dibuja el contorno del blob de turno adornado con bucles


dibujarBordeDelBlobConMotivo( unaMancha, largo, x, y, ancho, alto );
}
}
}
//------------------------------------------------
//función que recibe un blob y dibuja su caja y centro
void dibujarBlobCajaYCentro( Blob laForma, float x, float y,
float ancho, float alto ) {

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 ) {

//consulta la cantidad de segmentos


int cantidadSegmentos = laForma.getEdgeNb();

//crea dos objetos para tomar los puntos de cada segmento


EdgeVertex desde, hasta;

//recorre los segmentos


for ( int j=0 ; j<cantidadSegmentos ; j++ ) {

//consigue el punto inicial del i-ésimo segmento


desde = laForma.getEdgeVertexA(j);
//consigue el punto final del i-ésimo segmento
hasta = laForma.getEdgeVertexB(j);

//si ambos puntos existen


if (desde !=null && hasta !=null) {

//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 ) {

//consulta la cantidad de segmentos


int cantidadSegmentos = laForma.getEdgeNb();

//crea dos objetos para tomar los puntos de cada segmento


EdgeVertex desde, hasta;

//esta variable se usa para contar la cantidad de segmentos que se toman para
//simplificar la forma
int contador = 0;

//objetos para simplificar la forma


EdgeVertex anteriorHasta = null;
EdgeVertex inicio = null;
EdgeVertex fin = null;

89
//variable que guarda el estado actual
String estado = “buscaInicio”;

//recorre los segmentos


for ( int j=0 ; j<cantidadSegmentos ; j++ ) {

//consigue el punto inicial del i-ésimo segmento


desde = laForma.getEdgeVertexA(j);
//consigue el punto final del i-ésimo segmento
hasta = laForma.getEdgeVertexB(j);

//si ambos puntos existen


if (desde !=null && hasta !=null) {

//si esta buscando el inicio


if ( estado.equals( “buscaInicio” ) ) {

//carga el punto inicial


inicio = desde;
//pasa al nuevo estado: buscar el punto final
estado = “buscaFin”;
//cuenta el primer segmento del motivo (adorno)
contador = 1;
//pone fin en null
fin = null;

//si el estado es buscar el punto segmento


}
else if ( estado.equals( “buscaFin” ) ) {

//calcula la distancia con respecto al segmento anterior


//para ver que haya continuidad
float distancia = dist( desde.x*ancho, desde.y*alto,
anteriorHasta.x*ancho, anteriorHasta.y*alto );

//si la distancia es menor al valor de tolerancia


//es que hay continuidad
if ( distancia < TOLERANCIA ) {

//cuenta el segmento actual


contador ++;

//si se alcanzó el largo


if ( contador>=largo ) {
//carga el segmento final
fin = hasta;

//dibuja el motivo recien terminado


dibujarUnSegmento( x+inicio.x * ancho, y+inicio.y*alto, x+fin.x * ancho, y+fin.y *alto );

//busca un nuevo inicio


estado = “buscaInicio”;
}
}
else {

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;
}
}

//si cerro el ciclo for y quedó un motivo abierto, lo cierra


if ( estado.equals( “buscaFin” ) ) {
fin = anteriorHasta;
dibujarUnSegmento( x+inicio.x * ancho, y+inicio.y*alto, x+fin.x * ancho, y+fin.y *alto );
}
}
//------------------------------------------------
//dibuja el motivo (adorno)
void dibujarUnSegmento( float x1, float y1, float x2, float y2 ) {

//decide cual motivo se dibuja


if ( CUAL_BUCLE == 1 ) {
dibujarUnSegmentoBucle( x1, y1, x2, y2 );
}
else {
dibujarUnSegmentoOtroBucle( x1, y1, x2, y2 );
}
}
//------------------------------------------------
//usa curvas de bezir para dibujar el bucle
void dibujarUnSegmentoBucle( float x1, float y1, float x2, float y2 ) {

//obtiene el angulo entre los dos puntos


float angulo = atan2( y2-y1, x2-x1 );
//obtiene la distancia entre los dos puntos
float distancia = dist( x1, y1, x2, y2 );

//obtiene el punto medio partiende el primer punto y recorriendo


//medio camino en la misma dirección
float xMedio = xDistanciaAngulo( x1, angulo, distancia*0.5 );
float yMedio = yDistanciaAngulo( y1, angulo, distancia*0.5 );

//parte del punto medio y consigue un nuevo punto girando -90º y


//recorriendo la misma distancia
float x3 = xDistanciaAngulo( xMedio, angulo-HALF_PI, distancia*2 );
float y3 = yDistanciaAngulo( yMedio, angulo-HALF_PI, distancia*2 );

//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 );

//dibuja la curva de bezier usando dichos puntos como parámetros


beginShape();
vertex(x1, y1);
bezierVertex(x2, y2, x4, y4, x3, y3);
endShape();

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 ) {

float angulo = atan2( y2-y1, x2-x1 );


float distancia = dist( x1, y1, x2, y2 );

float xMedio = xDistanciaAngulo( x1, angulo, distancia*0.5 );


float yMedio = yDistanciaAngulo( y1, angulo, distancia*0.5 );

float x3 = xDistanciaAngulo( xMedio, angulo-HALF_PI, distancia );


float y3 = yDistanciaAngulo( yMedio, angulo-HALF_PI, distancia );

float x4 = xDistanciaAngulo( x3, angulo+PI, distancia*0.5 );


float y4 = yDistanciaAngulo( y3, angulo+PI, distancia*0.5 );

float x5 = xDistanciaAngulo( x3, angulo, distancia*0.5 );


float y5 = yDistanciaAngulo( y3, angulo, distancia*0.5 );

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;
}
//------------------------------------------------

float xDistanciaAngulo( float x, float angulo, float distancia ) {


return distancia * cos( angulo ) + x;
}

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.

Con unaMancha.w y unaMancha.h se accede al ancho y alto de la


caja del blob, mientras que con unaMan-cha.x y unaMancha.y se
accede a la posición 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.

//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 ) {

//consulta la cantidad de blobs del objeto BlobDetection


int cantidadBlobs = manchas.getBlobNb();
//declara un objeto Blob para recibir los distintos blobs
Blob unaMancha;

//recorre los blobs


for ( int i=0 ; i<cantidadBlobs ; i++ ) {

//toma el i-ésimo blob


unaMancha = manchas.getBlob( i );

//revisa que el blob de turno supere el tamaño mínimo definido


if ( (unaMancha.w*ancho)>anchoMinimo && (unaMancha.h*alto)>altoMinimo ) {

//pone contorno de color rojo


stroke( 255, 0, 0);
//dibuja la caja (bounding box) y el centro del blob
dibujarBlobCajaYCentro( unaMancha, x, y, ancho, alto );
//pone contorno de color amarillo
stroke( 255, 255, 0 );
//dibuja el contorno del blob
dibujarBlobContorno( unaMancha, x, y, ancho, alto );
}
}
}

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.

//función que recibe un blob y dibuja su caja y centro


void dibujarBlobCajaYCentro( Blob laForma, float x, float y,
float ancho, float alto ) {

//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 );
}

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.

//función que recibe un blob y dibuja el contorno


void dibujarBlobContorno( Blob laForma, float x, float y,
float ancho, float alto ) {

//consulta la cantidad de segmentos


int cantidadSegmentos = laForma.getEdgeNb();

//crea dos objetos para tomar los puntos de cada segmento


EdgeVertex desde, hasta;

//recorre los segmentos


for ( int j=0 ; j<cantidadSegmentos ; j++ ) {

//consigue el punto inicial del i-ésimo segmento


desde = laForma.getEdgeVertexA(j);
//consigue el punto final del i-ésimo segmento
hasta = laForma.getEdgeVertexB(j);

//si ambos puntos existen


if (desde !=null && hasta !=null) {

//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:

Imagen 4.5. Patrón fiducidal

Fuente: imagen disponible en: <http://reactivision.sourceforge.net/>

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.

Fuente: disponible en: <https://www.youtube.com/watch?v=Mgy1S8qymx0>

95
Imagen 4.6. Sitio de ReacTiVision

Fuente: imagen disponible en <http://reactivision.sourceforge.net/>

Esta tecnología permite reconocer patrones bitonales (llamados “fiducials”) impre-


sos a piezas de interfaces tangibles que funcionan sobre una pantalla sensible al
tacto. Esta interface tangible consiste en piezas de acrílico que se apoyan sobre una
pantalla sensible, esta es capaz de reconocer las piezas, gracias a los patrones bi-
tonales y generar respuestas audiovisuales en consecuencia. Los creadores, cons-
truyeron este software para desarrollar una pantalla sensible para interpretación
musical en tiempo real (un instrumento de improvisación de música electrónica)
llamada ReacTable. (Fuente: ReacTiVision <http://reactivision.sourceforge.net/>)

96
Imagen 4.7. Esquema ReacTable

Fuente: elaboración propia.

Como muestra la figura […], ReacTiVision permite hacer el reconocimiento de pa-


trones bitonales, a través de un sistema óptico, que en el caso de la ReacTable se
implementa con luces y cámara infrarrojas. La pantalla es un acrílico con superficie
esmerilada, las imágenes se retro proyectan desde abajo usando un cañón de vi-
deo, a su vez una luz infrarroja permite iluminar los patrones que serán captados
por una cámara, también infrarroja. Dicha luz y la cámara son infrarrojas para no
interferir la luz del proyector de video (que pertenece al rango visible de la luz) y
para que la cámara no vea a su vez las proyecciones. (CAUSA, 2011: 2)

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:

1. Resistencia: debe ser lo suficientemente resistente para permitir apoyar objetos


y posar los dedos para interactuar (a esto se suma el hecho de que la altura de la
mesa invita al público a apoyarse).

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.

3. Transparencia y nitidez: también debe poder ser lo suficientemente transparen-


te para dejar ver la imagen proyectada por detrás, pero lo suficientemente opaca
como para atrapar la luz y no dejarla pasar. Por ejemplo: si es muy transparente, la
proyección seguirá de largo (proyectándose en el techo, en vez de la pantalla), si es
muy opaca no podrá verse bien la imagen (hasta puede perder nitidez).

4. Antirreflejo: la superficie debajo de la pantalla debe poseer un tratamiento anti-


rreflejo ya que el sistema de captura es muy sensible a los altos contrastes, princi-
palmente si los producen los reflejos en esta superficie. Cuando la superficie no es
antirreflejos, funciona como un espejo que refleja el sistema de iluminación.

En nuestra implementación no pudimos encontrar una superficie que cumpla con


todos estos requisitos. El material que mejor cumplía el segundo y tercer requisito
es el papel vegetal (de tipo calco), pero claramente no cumplía el primer requisito,
lo que obligó a apoyarlo sobre un acrílico que no cumplía con el cuarto requisito. [...]

[...] 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)

4.2.1. El protocolo TUIO


ReacTiVision se realizó con el protocolo TUIO (A Protocol for TableTop Tangible User Interfaces–Protocolo para interfa-
ces tangibles de usuario). Processing posee una librería preparada para recibir el protocolo TUIO. Según los creadores
de este protocolo:

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:

addTuioObject: informa la aparición de un nuevo patrón sobre la pantalla.

removeTuioObject: informa que un patrón salió de la pantalla.

updateTuioObject: informa los cambio que sufre un patrón, ya sea de posición


como de rotación.

addTuioCursor: informa la aparición de un dedo sobre la pantalla.

removeTuioCursor: informa que un dedo salió de la pantalla.

updateTuioCursor: informa los cambios que sufre un dedo, un cambio de posición


[…] (Causa, 2011)

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:

a) Ingresa el fiducial (objeto) 1,


b) el mismo fiducial se mueve,
c) ingresa el fiducial 2,
d) se quita el fiducial 1,
e) se quita el fiducial 2.

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.

Imagen 4.8. Ejemplo de cómo se accede a los eventos y estados

Fuente: elaboración propia

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.

Causa, E. (2011), Tutorial de arte y vida En la sección de Simulación de


artificial, disponible en Ecosistema/Movimiento, del Tu-
<http://www.biopus.com.ar/emilia- torial de Arte y Vida Artificial, se
no/tutorial_vida_artificial/> encuentra una detallada explica-
ción de cómo desarrollar agentes
autónomos que se mueven en un
espacio bidimensional usando sis-
temas de coordenadas polares.

Shiffman, D. (s/d), “Autonomolus The Nature of Code posee una sec-


Agents”, The Nature of Code. Disponi- ción llamada Autonomous Agents
ble en: <http://natureofcode.com>. donde se puede ver una versión
más compleja del mismo tema.

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.

Fuente: elaboración propia completar

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.

Fuente: elaboración propia

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.

//importa la libreria de TUIO


import TUIO.*;

//SECCIóN A
//declara un objeto TuioProcessing para acceder al protocolo TUIO
TuioProcessing clienteTUIO;

//declara un arreglo de objetos Agente


Agente agentes[];

//se usa para definir la cantidad de agentes


int cantidad = 300;
//apunta a la posición del arreglo donde se generará un nuevo Agente
int cual = 0;

//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;

//el angulo del último cursor ingresado


float angulo = 0;
//variable que funciona como flag (bandera) para activar el seguimiento de un cursor
boolean seguirAngulo = false;
//---------------------------------------------------------

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();

//recorre con un ciclo for la lista de objetos


for ( int i=0 ; i<cantidadDeObjetos ; i++ ) {
//extrae un objeto de la lista
TuioObject esteObjeto = (TuioObject) listaDeObjetosTUIO.elementAt(i);
//obtiene la posición en pantalla del objeto
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() );
}

//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();
}

//desactiva el seguimiento del cursor


seguirAngulo = false;
}
//---------------------------------------------------------

//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();

//si hay al menos dos puntos en el camino


if ( cantidad>1 ) {
//obtiene el anteúltimo punto
TuioPoint desde = (TuioPoint) listaDePuntos.elementAt( cantidad-2 );
//obtiene el último punto
TuioPoint hasta= (TuioPoint) listaDePuntos.elementAt( cantidad-1 );

//obtiene el ángulo de dirección entre los dos puntos


angulo = atan2( hasta.getScreenY(height) - desde.getScreenY(height),
hasta.getScreenX(width) - desde.getScreenX(width) );
//activa el seguimiento del cursor
seguirAngulo = true;
}
//se puede descomentar la linea de abajo para ver los valores que ingresa
//println(“update cursor “+tcur.getCursorID()+” (“+tcur.getSessionID()+ “) “ +tcur.getX()+”
“+tcur.getY()
// +” “+tcur.getMotionSpeed()+” “+tcur.getMotionAccel());
}

//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()+”)”);
}

//esta función tiene que estar por el uso de TUIO


void refresh(TuioTime bundleTime) {
redraw();
}

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.

//recorre con un ciclo for la lista de objetos


for ( int i=0 ; i<cantidadDeObjetos ; i++ ) {
//extrae un objeto de la lista
TuioObject esteObjeto = (TuioObject) listaDeObjetosTUIO.elementAt(i);

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() );

Más adelante, veremos cómo es la función crearNuevoAgente().

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.

void crearNuevoAgente( float x, float y , int id , float angulo ) {

int cualPaleta = id % cantidadPaleta;

float medioAngulo = abs( degrees( angulo + HALF_PI ) );


medioAngulo = medioAngulo % 360;
if( medioAngulo>180 ){
medioAngulo = map( medioAngulo , 180 , 360 , 180 , 0 );
}
float brillo = map( medioAngulo , 0 , 180 , 255 , 0 );
float saturacion = map( medioAngulo , 0 , 90 , 0 , 255 );
saturacion = constrain( saturacion , 0 , 255 );
float tinta = paletaTintas[ cualPaleta ] + random(-VARIACION_COLOR,VARIACION_COLOR);
tinta = constrain( tinta , 0 , 255 );

color colorDeEsteAgente = color( tinta , saturacion , brillo , TRANSPARENCIA_AGENTES );

agentes[cual] = new Agente( x , y , colorDeEsteAgente , id );


cual = (cual+1) % cantidad;
}

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.

Para más información del sistema HSB, ver:


<http://es.wikipedia.org/wiki/Modelo_de_color_HSV>

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.

int cualPaleta = id % cantidadPaleta;

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.

float medioAngulo = abs( degrees( angulo + HALF_PI ) );


medioAngulo = medioAngulo % 360;
if( medioAngulo>180 ){
medioAngulo = map( medioAngulo , 180 , 360 , 180 , 0 );
}

Estas operaciones se encargan de tomar el valor de la <http://es.wikipedia.org/wiki/


variable angulo están definidos en radianes. Radi%C3%A1n>

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.).

imagen 4.11. Evolución de la variable medioAngulo en función del giro

Fuente: elaboración propia

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.

float brillo = map( medioAngulo , 0 , 180 , 255 , 0 );


float saturacion = map( medioAngulo , 0 , 90 , 0 , 255 );
saturacion = constrain( saturacion , 0 , 255 );
float tinta = paletaTintas[ cualPaleta ] + random(-VARIACION_COLOR,VARIACION_COLOR);
tinta = constrain( tinta , 0 , 255 );

color colorDeEsteAgente = color( tinta , saturacion , brillo , TRANSPARENCIA_AGENTES );

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.

agentes[cual] = new Agente( x , y , colorDeEsteAgente , id );


cual = (cual+1) % cantidad;

El uso de estas variables ( agentes[ ] y cual ) desde la función es posible debido a que estas están declaradas como
variables globales.

4.3. Breve comentario final


En esta unidad hemos explorado algunos métodos de captura de movimiento, utilizándolos para controlar y producir
imágenes, en el primer caso vinculada a los sujetos captados y en el segundo relacionadas con sistemas generativos.
Como puede verse de estos dos ejemplos, los sistemas de captación de movimiento son potentes herramientas para
crear aplicaciones interactivas con fines artísticos. Por supuesto que dichas herramientas tienen sus límites y aún es-
tamos en un estadio primario en el desarrollo de estas tecnologías, seguramente los siguientes años veremos grandes
avances en el campo, sin embargo (a pesar de estas limitaciones) la captación de movimiento permite explorar el
cuerpo del público en la experiencia artística y como tal nos abre un terreno fértil para la expresión artística.

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. (2011), “Desarrollo de una aplicación con interfaces tan-


gibles (mesa con pantalla reactiva)”, en: Red Mercosur de Faculta-
des 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 aumen-


tada e interfaces tangibles, Congreso SIGRADI 2011, Santa Fe, Argentina. [Procee-
dings 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.

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].

Causa, E. Algoritmos para el análisis de formas y reconocimiento de patrones bitona-


les, [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].

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

Bonsiepe, G. (1999), Del objeto a la interfase, Ediciones infinito, Buenos Aires.

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.

Szeliski, R. (2011), Computer Vision, Algorithms and Applications, Editorial Springer.

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

También podría gustarte