inmensia |
JavaScript
Juan Mellado, 14 Febrero, 2012 - 13:32
Un vídeo de Hangar en acción. Demo online:
Juan Mellado, 13 Febrero, 2012 - 18:05
Hoy he liberado los fuentes del último proyecto en JavaScript en el que he estado trabajando. Es un visor de modelos 3D almacenados en formato AC3D (.ac) que utiliza WebGL para renderizar. He estado utilizando modelos de aviones de FlightGear para probarlo, así que he decidido llamarlo Hangar. He creado una pequeña página web con cuatro modelos escogidos de prueba. Tiene implementado un controlador a modo de trackball que permite rotar y ampliar los modelos utilizando el botón izquierda y la rueda del ratón. Necesita un navegador moderno como las últimas versiones de Chrome o Firefox para funcionar (Y no, IE 9 no es un navegador moderno) ![]() El formato AC3D lo utilizan algunos programas como FlightGear, un simulador aéreo de código abierto, o Torcs, un simulador de conducción deportiva también de código abierto. Es un formato de texto plano que originalmente creó un programa de modelado 3D llamado, lógicamente, AC3D. Mi idea era hacer algo sencillo, pero al final he acabado incorporando un montón de esas pequeñas características que pasan desapercibidas cuando funcionan bien. AC3D Los objetos están definidos de una manera jerárquica, de forma que un objeto puede tener hijos. Además, cada objeto tiene una matriz de rotación y un vector de rotación que aplica a sí mismo y a todos su hijos, por lo que hay que ir calculando la transformación resultante a medida que se baja por el árbol de descendencias. Teselación Me he encontrado con modelos de todo tipo y definidos de casi cualquier forma. Al final he decidido ignorar los polígonos degenerados. Es decir, polígonos con menos de tres vértices, polígonos con coordenadas colineales, polígonos con vértices no contenidos dentro de un único plano, polígonos con aristas que se cruzan, ... Normales La normal a un vértice concreto se calcula como la suma de todas las normales de todas las caras que comparten dicho vértice. Aunque a este respecto el fichero incorpora un parámetro por objeto llamado "crease". Este parámetro es un ángulo, y sirve para generar lo que habitualmente se conocen como bordes duros (hard edges). Si el ángulo que forman la normal de una cara y su vecina supera dicho parámetro entonces esa normal vecina no se tiene en cuenta para calcular la normal en el vértice compartido. SGI Mi implementación soporta .sgi, .rgba, .rgb, .ra y .bw. Es decir, todas las combinaciones posibles, aunque he ignorado deliberadamente algunas características de la especificación, como la posibilidad de usar paletas de colores o escoger entre 8 ó 16 bits por canal. En la práctica todas las imágenes acaban siendo de cuatro canales y con 8 bits por canal en el clásico formato RGBA. Loader Muy útil. Renderer El formato AC3D permite incluir luces en los modelos, pero las he ignorado a la hora de renderizar, utilizando en cambio una luz fija estática direccional sin factores de atenuación. Mi idea era representar modelos de objetos independientes, no escenas completas, por lo que no he encontrado sentido en incluirlas. Aparte de que acabado no fiándome de los modelos, con materiales un tanto extraños a mi parecer. Para minimizar los cambios de estado de la tarjeta gráfica he agrupado todos los polígonos por programa, material, tipo, y todo lo que se me ha ocurrido. Por ello, a diferencia de otros proyectos anteriores, he decidido utilizar Transparencias Lo que me duele es que se supone, repito "se supone", que los materiales incluyen un atributo para indicar si tienen algún tipo de transparencia. Desgraciadamente esto no ocurre así en la práctica. Al final cuando algún modelo no se visualizaba correctamente, abría el fichero, localizaba el material y lo cambiaba a mano. Esto es algo que en general creo que tendría que revisar con el objetivo de entender la manera en que interpretan los materiales programas como FligthGear o Torcs aprovechándome del hecho de que son de código abierto. Autofit De esto también me gustaría escribir un post individual en el futuro, ya que he encontrado distintas soluciones en Internet y no me he quedado del todo satisfecho con la que yo mismo he implementado. TrackBall Resumiendo Una aproximación más práctica al problema hubiera sido utilizar un motor 3D ya existente, como el popular Three.js, y limitarme a hacer un conversor del formato .ac a algún formato que acepte el motor. Pero claro, ¡me hubiera perdido la parte más divertida!
Juan Mellado, 16 Diciembre, 2011 - 18:10
Buscando información acerca de métodos de tracking (seguimiento) de objetos en tiempo real, me he encontrado que hay toda una teoría desarrollada para la detección de regiones dentro de una imagen que parezcan corresponder a piel humana. Hay muchas técnicas, pero las más sencillas parecen ser curiosamente las que mejor funcionan. Y básicamente consisten en convertir los pixeles originales desde RGB a otro espacio donde sea más fácil estudiarlos. Al final he acabado montado mi pequeño "laboratorio" en JavaScript para comprobar una de estas teorías, concretamente la explicada en este paper que realiza el estudio de los pixels en el espacio de color YCbCr. Para ello he escrito un conversor de RGB a YCbCr utilizando las siguientes fórmulas propuestas en el documento: Y = 16 + 65.481 * R + 128.553 * G + 24.966 * BLas fórmulas admiten un valor RGB dentro del rango [0, 1] y generan su equivalente YCbCr en el rango [0, 255]. La idea clave es que dentro de dicho espacio el color correspondiente a la piel humana se encuentra dentro del rango [80, 120] para la componente Cb y [133, 173] para la componente Cr. La componente Y simplemente es ignorada. ¡Veamos si es cierto! Al seleccionar una imagen del desplegable de abajo se muestra tal cual arriba a la izquierda. En los cuadros inferiores se dibujan los histogramas calculados para las componentes Cb (en azul) y Cr (en rojo). Las zonas destacadas en amarillo son los rangos que se corresponden teóricamente a la piel humana. Y la imagen superior derecha es el resultado de filtrar la imagen original dejando pasar sólo los pixels que se encuentran dentro de los rangos que corresponden a la piel humana. Después de probar con una serie de imágenes he dejado tres fotografías de caras a modo de referencia. La de Lucy Liu corresponde a un caso bastante claro donde está bastante diferenciado del fondo y el algoritmo hace un buen trabajo. La de Nicole Kidman se complica un poco, ya que el pelo tiene una tonalidad tan clara que no se distingue de la piel. Y por último la de Whoopi Goldberg, que se corresponde al caso en que el algoritmo se confunde debido al color que tiene la ropa que lleva puesta. El programa permite cambiar los rangos haciendo click sobre los histogramas para desplazar los valores mínimos y máximos. Por ejemplo, para la foto de Whoopi, si se aumenta un poco el mínimo del histograma de la componente Cb (azul) entonces el algoritmo ya no detecta la ropa como piel. De igual forma, jugando un poco con los rangos es posible eliminar la mayor parte del pelo de la foto de la Kidman. Reconozco que el control con el ratón es un poco complicado, la idea es que se modifica el límite más cercano a donde se hace el click. Otra posibilidad que ofrece el programa es seleccionar una región dentro de la imagen original para ver los histogramas de esa región en concreto. Por defecto está seleccionada la imagen entera, pero haciendo click sobre ella se puede definir una región rectangular. La idea es poder ver como se distribuyen los valores para una región en concreto con más detalle para tener un mejor criterio a la hora de modificar los rangos. Lo ideal es seleccionar una región que incluya sólo piel, con brillos y sombras, y ajustar los mínimos y máximos de los histogramas obtenidos. Pulsando escape se restaura la composición original. Resumiendo un poco, está claro que el criterio es válido en términos generales, pero hay mucha probabilidad de falsos positivos en función de la condiciones del entorno en que se pruebe. Los pixels aislados no representan un problema, ya que normalmente se eliminarán aplicando una operación morfológica del tipo erode. Y de igual forma los huecos se rellenarán con una operación dilate. Para mejorar el proceso de detección en general hay que aplicar más criterios, y así lo hacen generalmente las técnicas propuestas, normalmente el estudio de la imagen en algún espacio de color, como el YCbCr o el HSV por ejemplo, es sólo la primera parte del proceso. Algunas técnicas más elaboradas utilizan una imagen de referencia, a modo de calibración del sistema, con la que se construyen unos umbrales de referencia. Otros sistemas, sobre todo cuando se trata de procesar vídeo en vez de imágenes estáticas, ajustan los umbrales del los histogramas de forma dinámica en función de la variación que se va produciendo en las imágenes que se van sucediendo. En otro orden de cosas, buscando más información por la red, he encontrado fórmulas de conversión de RGB a YCbCr más sencillas que las propuestas en el paper. Por ejemplo las siguientes: Y = 0.299 * R + 0.587 * G + 0.114 * BEstas fórmulas tienen además la ventaja de que los valores de entrada RGB están dentro del rango [0, 255] en vez de [0, 1].
Juan Mellado, 12 Diciembre, 2011 - 16:25
Los defectos de convexidad son importantes en la medida que se corresponden con ciertos puntos que caracterizan un determinado contorno. El ejemplo más clásico es el de una mano. Si se calcula la envolvente convexa que engloba el contorno de una mano y sobre ella se marcan los puntos que se separan más de dicha envolvente, para cada segmento de la misma, estos puntos se corresponderán con el nacimiento de los dedos, es decir, donde dejan la palma. Estos puntos son los defectos de convexidad, y permiten determinar de una forma muy sencilla algunas características concretas, como por ejemplo el número de dedos que tiene levantados la mano. Siguiendo la implementación de la función cvConvexityDefects de OpenCV he creado un pequeño programa en JavaScript que calcula los defectos de convexidad. Para hacerlo un poco más atractivo visualmente le he dado forma de un pequeño generador automático de constelaciones. El programa genera varios puntos al azar asegurándose de que forman un polígono simple, o dicho de otra forma, que no se cruzan sus líneas. A continuación calcula la envolvente convexa. Y por último los defectos de convexidad. La zona azul claro corresponde al área cubierta por la envolvente convexa. Los puntos blancos con doble círculo son los que se encuentran sobre el borde de la envolvente, el resto está dentro de ella. Los puntos amarillos son los defectos de convexidad, es decir, los puntos más alejados para cada segmento de la envolvente.
Juan Mellado, 7 Diciembre, 2011 - 15:42
Un experimento rápido y un poco tonto con js-aruco usando las sombras como marcadores de realidad aumentada. Demo online: |