Sensor de profundidad. Adquisición.

Estaba yo pensando… ¿Será un juguete o será realmente útil?

Cuando compré el ASUS Xtion Pro Live en Amazon por 143,71€ ya imaginaba que no sería lo mismo que los sensores de Prometheus, que escaneaban la cueva con una agilidad y precisión propias de una película de ciencia ficción, pero no sabía hasta qué punto el sensor iba a ser realmente útil o sería simplemente un juguete de dudosa utilidad asociado a una consola de videojuegos. La verdad era que los videojuegos con interface “natural” no habían tenido el éxito que se esperaba de ellos.

Éste era el primer paso: averiguar si el sensor es suficientemente estable y tiene suficiente precisión como para que los resultados tengan cierto valor. También me preocupaba si el software asociado al sensor tendría la suficiente calidad como para usar el sensor con tranquilidad y poder construir sobre él alguna aplicación básica que me permitiera hacer lo que estaba buscando.

Bien, pues voy a tratar de obtener la información que genera el sensor de profundidad.

El sensor dispone de una conexión USB para comunicarse con un host. Además, se alimenta por esta conexión USB por lo que no necesita una alimentación separada.

La información del sensor se obtiene a través de la librería OpenNI (OpenNI2 en su versión actual). Esta librería facilita la adquisición de la información que proporciona el sensor a través de la conexión USB. Se distribuye en código fuente bajo licencia Apache Versión 2.0, que confieso no haber leído aunque, de momento, no me preocupa mucho ya que la he usado “just for fun” y no he ganado nada.

Como ya dije en mi post anterior, era mantenida por PrimeSense hasta que fue comprada por Apple. Desde entonces es mantenida por Occipital, que ha generado un repositorio en github.com y ha publicado un sitio (http://structure.io/openni) desde el que se puede obtener información y descargar los fuentes de la última versión. Está disponible para desarrollar en Windows, Linux, OS X y Android.

Voy a hacer la primera prueba. Con el sensor viene un CD etiquetado como “PrimeSense Software Package 20.4.2.20” pero sorprendentemente no se puede leer. ¡Qué desastre! Rebuscando por el sitio de ASUS encuentro una imagen iso identificada como “V1164_1202.iso”, me la bajo, me hago un disco y con este tengo más suerte. Encuentro un ejecutable Windows para instalar un SDK. Lo ejecuto y me instala el driver USB del sensor y la librería OpenNi. Veo que con la librería vienen una serie de aplicaciones ejemplo de cómo usarla. Están compiladas. Enchufo el conector USB del sensor en mi portátil. Veo cómo aparecen dos nuevos dispositivos USB en Windows. Empieza a subirme la adrenalina. Pincho compulsivamente dos veces en el fichero NIViewer.exe y … ¡vaya, parece que funciona!

Estoy viendo la representación de dos streams de video en tiempo real. A la derecha de la pantalla de mi portátil se ve mi habitación, en color, como si se estuviera viendo con una cámara USB. A la izquierda de la pantalla se ve una imagen de video coloreada con distintos tonos de amarillo. Al cabo de un rato mirándola me doy cuenta de que es la misma escena que se ve en color a la derecha pero coloreada con distintos tonos de amarillo en función de la distancia de los objetos de la escena al sensor. Bueeeno, la imagen en amarillo es bastante fea pero es una manera de representar la información de profundidad que adquiere el sensor.

Imagen de profundidadImagen de color

Es un buen comienzo. Merece la pena seguir. Pero no me gusta la imagen en amarillo, quiero ver una nube de puntos.

A partir de aquí voy a trabajar con Linux. Windows me ha servido para ver algo deprisa, es muy cómodo, uno está acostumbrado al entorno y me manejo con soltura con carpetas, ficheros, editores, etc. En Windows instalas el paquete mediante un ejecutable, se instalan los recursos necesarios, el ejemplo viene compilado, se ejecuta y, generalmente, funciona. Pero para ir más lejos me parece mejor usar Linux. Mi intención es generar una aplicación sobre OpenNI que permita manipular las imágenes obtenidas. Si hay que compilar, depurar o bucear por el código fuente, me parece mejor hacerlo en Linux. Además, de momento, voy a desarrollar en mi ordenador portátil pero considero que es un requerimiento clave que este trabajo se pueda trasladar a un equipo embebido.

Uso la última versión LTS de Ubuntu. Me descargo la última versión de OpenNI2 para x86. Es un bz2 que se descomprime en home. Tiene un script de instalación que ejecuto y se instala. También vienen los ejemplos compilados pero antes de probar a ejecutarlos hay que instalar un par de paquetes: freeglut3-dev y libusb-1.0-0-dev. Los instalo, enchufo el sensor, ejecuto NiViewer y ¡funciona!

Mi objetivo es poder compilar y depurar de una manera cómoda. Voy a usar Eclipse, pero la compilación del paquete (librerías y aplicaciones de ejemplo) la hago con make en la línea de comandos. Van saliendo errores: el compilador no encuentra algún .h. Corresponden a paquetes que usa la librería y que tengo que ir instalando para que vaya compilando. He instalado, además de los que ya había instalado antes, doxigen, graphviz, libudev-dev y openjdk-6-jdk. Finalmente compila, pruebo NiViewer y funciona. Voy avanzando.

Ahora voy a compilar con Eclipse el ejemplo NiViewer. Ya se ha compilado con make pero quiero usar Eclipse para compilar en Release y Debug y poder depurar cómodamente. Me genero un Workspace y un proyecto del tipo “Makefile Project with existing code” con objetivo de clean, release y debug. Tengo que compilar el paquete completo en modo debug para que estén disponibles también las librerías en modo debug. Finalmente, tras ajustar la configuración típica de Eclipse de paths y demás, consigo que compile y funcione el ejecutable, pero no puedo depurar: gbd no encuentra las librerías dinámicas. Esto se arregla configurando la variable de entorno LD_LIBRARY_PATH con el valor apropiado.

Bueno, ya estoy en condiciones de generar aplicaciones cómodamente sobre OpenNI. Puedo compilar, linkar y depurar. Voy a rebuscar en qué formato vienen los datos en esos streams de video que he visto con la aplicación de ejemplo NiViewer.

Para familiarizarse con la librería OpenNI uno puede: bajarse del sitio de Occipital y leerse el “OpenNI Programers Guide”, sólo te da una idea del alcance de la librería y qué es lo que se puede hacer con cada clase; comprar y leerse por encima parándose en algún ejemplo el libro “OpenNI Cookbook” de Soroush Falahati; y bucear por el código fuente de la librería y de los ejemplos de aplicaciones que incluye. Sin duda esta última es la más efectiva pero las dos primeras también son muy útiles.

La librería está desarrollada en C++.

El sensor se gestiona mediante una instancia de la clase Device. Las dos imágenes de video que se representan con el ejemplo NiViewer se corresponden con dos streams que genera el sensor. Uno de ellos envía la información de profundidad y el otro la información de color. Estos dos streams se gestionan mediante instancias de la clase VideoStream.

Para ponerlo en marcha, el sensor se “abre” con el método open de la clase Device y se arranca con el método start. Tras ésto, se pueden arrancar los streams. Se generan con el método create. Con este método se identifica el sensor del que procede y su tipo, es decir si se trata del stream de profundidad o del stream de color. Se ponen en marcha con el método start.

Una vez arrancados los streams, si queremos información coherente de profundidad y de color, es necesario sincronizarlos. La sincronización se habilita con el método setDepthColorSyncEnabled de la clase Device y se elige el modo de la sincronización con el método setImageRegistrationMode.

Los streams van llegando frame a frame. Los frames se gestionan con una instancia de la clase VideoFrameRef.

Mediante la función OpenNI::waitForAnyStream() se espera con un timeout a que llegue un nuevo frame de alguno de los dos streams. Como los streams están sincronizados, con esta funcion recibiremos secuencialmente los streams de profundidad y de color que van marcados con el mismo indice. Este índice numera cada frame desde un origen común a los dos streams. Será necesario verificar que el índice coincide en los dos frames, de profundidad y de color, que van a tratarse juntos.

¿En qué formato tenemos la información en los frames?

Un frame de profundidad está compuesto por un mapa de pixels representado por un array de 320×240 estructuras de tipo DepthPixel que no es más que un unsigned int de 16 bits. De esta manera, a cada pixel se le asigna un valor de profundidad que representa la distancia desde un plano que pasa por el objetivo de la cámara hasta el objeto de la escena representado por ese pixel. Las coordenados X e Y son simplemente las posiciones en el mapa de pixels donde el origen es la esquina superior izquierda de la imagen.

Un frame de color está compuesto por un mapa de pixels representado por un array de 320×240 estructuras de tipo RGB888Pixel. Esta estructura tiene tres componentes, cada uno de ellos es un unsigned int de 8 bits y representa cada una de las componentes RGB de color del pixel. Esto es lo que habitualmente se conoce por un pixel RGB de 24 bits.

Las coordenadas X Y del frame de color coinciden con las del frame de profundidad por lo que en el frame de color esta la información del color con el que la camara RGB ve el punto representado en el frame de profundidad por las mismas coordenadas XY.

Ahora se entiende mejor qué es lo que representa el video pintado de amarillo. Son los mismos pixels del frame de video RGB pero pintados en distintos tonos de amarillo en función de su distancia a la cámara. Los pixels más brillantes están más cerca de la cámara y los más oscuros están más lejos.

About Juan Serrano

Juan Serrano es especialista en ingeniería eléctrica y en el diseño y desarrollo de equipos de telecontrol
This entry was posted in Broadcasting, Depth sensors, Sensors and tagged , , , , , , , . Bookmark the permalink.

Leave a Reply