Skip to content

javascript

dart-neuquant

Hoy he liberado los fuentes de dart-neuquant, un port a Dart del clásico algoritmo Neuquant de Anthony Dekker (1994), que utiliza una red neuronal para convertir una imagen RGBA de 32 bits a otra equivalente de 8 bits. Es decir, que analiza la paleta con los distintos colores presentes en una imagen, que en el caso de 32 bits pueden ser millones, y genera una nueva paleta equivalente a la original, pero de tan sólo 256 colores.

Aunque a priori pueda parecer que el resultado no debería ser bueno, por la diferencia de colores y tonalidades que se pierden, en realidad es todo lo contrario. Por ejemplo, la imagen de la izquierda tiene 68.289 colores distintos, mientras que la de derecha sólo 256, y sin embargo ambas lucen muy similares.

dart-neuquant

Naturalmente hay que tener en cuenta que he reunido las dos imágenes en un única en formato JPG, lo que desvirtúa un tanto el resultado real. Aún así, se puede observar como algunas regiones de la imagen de la derecha presentan ciertos problemas de banding, es decir, que hay un salto brusco de colores en vez de un degradado suave. Esto se observa más en las regiones curvadas, como en algunas partes del caballo por ejemplo. La forma habitual de arreglar esto es aplicando dithering, que consiste en juntar pixeles de distintos colores para que parezca otro color, aunque el autor del algoritmo comenta que esto hace que la medida del error entre la imagen original y la generada crezca.

El algoritmo da más importancia a la componente verde (la G de RGB), ya que el ojo humano es capaz de detectar un rango más amplio de este tipo de tonalidades que del resto. Lo que no es de extrañar ya que es el color que más abunda en la naturaleza. Por lo que el resultado resulta más “agradable” en este sentido.

El proyecto ha ido bastante rápido, ya que el código C original a portar no era muy extenso. La principal dificultad ha estado en que utiliza aritmética entera, con rotaciones en vez multiplicaciones por ejemplo, por lo que he tenido que estar muy atento para que el resultado en cada paso fuera el mismo tanto en Dart como en JavaScript. En la actualidad, casi una década después de cuando se implementó el código original, creo que sería más práctico utilizar aritmética decimal, e incluso puede que más eficiente.

Lo que resulta curioso es que no tenía en mente hacer este proyecto, ha sido un poco de rebote, empecé resolviendo un error en una librería JavaScript que ni siquiera conocía, y he acabado implementando una versión en Dart.

zx-dart

He desarrollado un emulador de ZX Spectrum en Dart. ZX Spectrum fue uno de los ordenadores más populares durante la década de los ochenta, y este año se cumplieron treinta desde su creación. Dart es un nuevo lenguaje de programación que está desarrollando Google. He llamado zx-dart al proyecto y lo he liberado como código abierto.

zx-dart

La versión de código nativo en Dart se puede ejecutar en Dartium, que es una versión de Chrome que incluye la máquina virtual de Dart. Aunque el rendimiento es pésimo, no alcanza ni un frame por segundo. Hay que tener en cuenta que Dart todavía está en desarrollo, las mejoras tienen que ir llegando poco a poco, además de que la especificación oficial del lenguaje cambia cada pocas semanas, por lo que el código escrito hoy puede que no funcione mañana.

Dart viene acompañado de una herramienta llamada (actualmente) dart2js que convierte el código Dart a JavaScript, de forma que se pueda ejecutar directamente en Chrome sin necesidad de la máquina virtual de Dart. Afortunadamente esta versión alcanza los 20 FPS en mi máquina y el emulador se deja probar, aunque para una simulación realista de un Spectrum debería alcanzar al menos 50 FPS.

Demo online: http://www.inmensia.com/files/zxdart/index.html

El emulador que he implementado está basado en JSpeccy. Realiza una emulación básica de un Spectrum 48K, con carga de ROM y ejecución de BASIC. Le faltan muchas características básicas, como la generación de sonido o la posibilidad de cargar snapshots por ejemplo. Pero hoy en día hay muchos emuladores bastante completos para casi cualquier plataforma, por lo que no merece la pena complicar el desarrollo para reinventar la rueda. Quizás un port a Dart de algún emulador ya existente escrito en JavaScript podría ser una mejor opción.

Ha sido emocionante el primer arranque, después de múltiples fallos, y ver el famoso mensaje de copyright.

js-handtracking

He liberado los fuentes de js-handtracking, una librería escrita en JavaScript que captura la imagen de una webcam y la procesa con el objetivo de detectar la presencia de piel humana, preferentemente una mano, y extraer sus características estructurales más importantes.

La librería realiza las siguientes operaciones:

– Detección de las áreas de la imagen que contienen piel
– Extracción de los contornos de las áreas
– Determinación del contorno de mayor área
– Optimización del contorno
– Cálculo de la envolvente convexa del contorno
– Cálculo de los defectos de convexidad

Demo online: http://www.inmensia.com/files/handtracking/demo/index.html

Para probar la demo es necesario utilizar un navegador moderno con soporte para WebRTC, que es la tecnología que permite a JavaScript acceder a la webcam directamente desde el navegador. Por ejemplo, Chrome 18 o superior, con el flag --enable-media-stream.

Para la detección de piel al final he optado por convertir los colores desde RGB hasta HSV y comprobar los valores de los canales H y V para determinar si están dentro del rango de la piel humana:

Las condiciones de iluminación son muy importantes y hacen que los colores sean detectados de forma muy distinta por la webcam. He tenido bastantes problemas ajustando los parámetros. Al final eliminando el equilibrado automático de blancos he conseguido una detección bastante estable.

Lo realmente importante es darse cuenta que hace sólo unos pocos meses era impensable pensar en realizar este tipo de aplicaciones sencillas de visión artificial en JavaScript.

Hand Tracking en JavaScript

Estoy trabajando en una nueva librería escrita en JavaScript. Esta vez estoy tratando de implementar un sistema de tracking, en especial de manos. Mi objetivo es ser capaz de detectar una mano que se encuentre dentro de una imagen y destacar sobre ella sus características principales. Ya tengo un prototipo bastante avanzado, como puede verse en la siguiente imagen:

Hand Tracking

La librería segmenta una imagen dada dividiéndola en regiones que contengan colores dentro del rango que caracteriza la piel humana (blanco). Asumiendo que la región más grande encontrada corresponde a una mano, ya que ese es el objetivo de uso de la librería. A continuación encuentra el contorno de la región (rojo), y sobre ese contorno calcula la envolvente convexa (azul) y sus defectos de convexidad (verde).

Los defectos de convexidad suelen ser de bastante utilidad, ya que se pueden usar para contar el número de dedos que tiene levantados la mano por ejemplo.

Viendo la imagen está claro que la librería ya cumple su objetivo, pero aún me quedan varias pruebas que quiero realizar antes de liberar los fuentes. La parte más complicada del proceso está siendo la primera, la detección de la piel humana. Ya había trabajado antes en el tema, pero me estoy encontrando con problemas debido a que mi webcam es MUY mala y no hay forma de que me proporcione fotogramas con los colores correctos. De momento me estoy limitando a utilizar imágenes estáticas, aunque tengo una demo que acepta como entrada vídeo directamente.

En el apartado de la documentación de referencia, para la detección de piel he probado tres sistemas distintos. Al final el que se encuentra ahora mismo implementado es el que se describe en esta página (en chino):

http://blog.csdn.net/scyscyao/article/details/5468577

De hecho, la imagen de la mano que he puesto es de esa página. La he estado utilizando para comparar resultados. Espero que no haya problemas de copyright.

Antes de liberar la librería me gustaría revisar la implementación que tiene OpenCV de una técnica llamada “adaptive skin detection”, que ajusta los parámetros de detección de piel humana en función de como varía la imagen fotograma a fotograma. Pero no estoy muy convencido, ya he hecho una primera prueba en JavaScript de la parte básica sobre una imagen estática y detecta menos piel que el método que tengo ahora mismo funcionando.