jueves, 19 de abril de 2012

Medidor de confianza

Tal como teníamos implementada la red, ésta devolvía a cada momento el número de neurona con máxima salida; este número era el número de gesto identificado. Esto no tenía en cuenta de ninguna forma si el valor de salida máximo era muy superior al segundo valor más alto, por ejemplo, así como el valor absoluto de este valor máximo.

Pudimos comprobar experimentalmente que el valor medio de las salidas máximas de la red neuronal cuando el sistema acertaba era significativamente mayor que el valor medio de las salidas máximas cuando la hipótesis emitida era incorrecta. Por así decirlo, el valor de la salida máxima indica "cómo de seguro" está la red neuronal de que su hipótesis es correcta. La diferente entre el valor más alto de salida y el segundo valor también resulta un parámetro significativo.

Debido a que es más importante que el sistema acierte al emitir una hipótesis que que emita hipótesis constantemente, la mayoría acertadas y algunas falsas, hemos añadido un medidor de confianza como segunda etapa tras la red neuronal.

Esta etapa evalúa el nivel de "confianza" de la red neuronal según:
                confidence =((max_salidas - siguiente_max)/max_salidas);
Y si este nivel es mayor que 0.7/1, entonces considera la predicción de la red neuronal como válida y se la transmite al usuario. Si es menor de 0.7 consideramos que la red neuronal no está lo suficientemente "segura" de su hipótesis y la descartamos. En este estado de desarrollo, gráficamente el usuario ve una ? como gesto reconocido en la pantalla del ordenador cuando no se ha podido identificar el gesto.

La red neuronal está constantemente sacando hipótesis. Esto quiere decir que, por ejemplo, en la transición de un gesto de la mano a otro buscará en cada instante qué gesto de la base de datos es el más similar; aunque la máxima salida de la red sea baja, reconocerá gestos mientras que el usuario no está efectuando ninguno. El añadir este nivel de comprobación asegurará que estas hipótesis sean bloqueadas en el medidor de confianza, que dará un resultado inferior al umbral de 0.7, de forma que el sistema no reaccione a estos gestos.

La importancia de elegir un buen set de entrenamiento

En la entrada inmediatamente anterior se menciona el proceso de entrenamiento de las redes neuronales, así como el modo en que capturamos imágenes de entrenamiento para nuestra red neuronal.

Pensando en conseguir que el sistema sea capaz de reconocer gestos de diferentes personas, con manos en posiciones levemente distintas e inclinaciones variadas aun conservando el gesto en si, realizamos una serie de capturas para la base de datos en las que movíamos la mano, variando significativamente el ángulo, la posición del brazo, la separación de los dedos... Después de dividir la base de datos en set de entrenamiento y set de test y de entrenar la red, terminamos por encontrarnos con unos resultados algo peores de lo esperado.

No fue inmediato darnos cuenta de que nuestro exceso de celo resultó más perjudicial que otra cosa para nuestro sistema. Algunos de los movimientos capturados, a pesar de conservar características propias de su gesto original, como el número de dedos extendidos, tenían una inclinación tan exagerada que pasaban a tener características de otras imágenes e inducían a error a la red neuronal. De esta forma la red neuronal se fijaba en características como el ángulo del brazo por encima de los dedos extendidos y se equivocaba clasificando las imágenes.

En las siguientes capturas para creación de base de datos limitamos la variación de los gestos.

En este punto del desarrollo nuestra red tenía doce neuronas de salida, una por gesto; de esta forma cuando la entrada era un fondo vacío, sin mano, lo detectaba como una mano de canto (gesto que menos pixels activos tiene). Le hemos añadido una neurona más de salida correspondiente al fondo, para lo cual precisamos también de un set de entrenamiento. Realizamos una función simple de matlab que genera una serie de imágenes de fondos con algunos píxeles de ruido en posición aleatoria (uno, dos o tres por imagen) y la utilizamos para entrenar la red.

miércoles, 18 de abril de 2012

Redes neuronales: recolección de base de datos y entrenamiento

Como ya comentamos anteriormente, entrenar una red neuronal mediante backpropagation consiste en buscar los valores óptimos de en nuestro caso más de 14000 parámetros libres que definen el comportamiento de las neuronas de la red, para que una función asociada, conocida como función de coste, minimice su valor. Estos parámetros libres son constantes una vez fijados, y no son las variables de las funciones matemáticas implicadas. Por tanto estos parámetros habrá que buscarlos con algún criterio determinado y para un subconjunto finito de posibles valores de las variables.

Para poder entrenar la red necesitamos una base de datos de imágenes de gestos similares a las que va a utilizar el sistema final, clasificadas de forma que el algoritmo de entrenamiento conozca a qué gesto corresponde cada imagen. Para entrenar la red se comenzará con los parámetros libres ajustados completamente al azar (y con valores pequeños) y se irán pasando una a una todas las imágenes de la base de datos de entrenamiento, observando cómo clasifica la red cada imagen y si su clasificación es correcta o errónea. La función de coste será menor cuantos más aciertos tenga la red dentro del set de entrenamiento. Una vez calculada la función de coste se aplica el algoritmo de backpropagation que calcula el gradiente de algo que tiene que ver con el error cometido al clasificar las imágenes, y conocido este gradiente calcula un nuevo set de parámetros libres mediante métodos numéricos que traten de minimizar el error cometido y la función de coste. El proceso vuelve a repetirse con los nuevos parámetros y se continúa iterando hasta un número de veces fijado por el usuario. Cada vez el coste se va reduciendo y la red va aprendiendo a clasificar mejor los gestos de su set de entrenamiento. Esto puede dar lugar a un fenómeno conocido como "overfitting" en que la red queda entrenada de tal forma que reconoce a la perfección la mayoría de las imágenes de su base de datos de entrenamiento, pero no funciona bien ante nuevas imágenes que nunca había visto antes. Por ello la función de coste también penaliza en cierta medida que los parámetros tomen valores propensos a provocar overfitting, y se debe valuar el comportamiento de la red con nuevos gestos desconocidos para determinar el número óptimo de ciclos de entrenamiento y otros parámetros hasta que el sistema funcione de forma aceptable, mostrando un buen comportamiento tanto con los gestos de entrenamiento como con nuevos gestos.

Para entrenar nuestra red nos hemos propuesto obtener una base de datos de unas 10000-15000 imágenes de unas 10 manos distintas. Como estas imágenes deben ser idénticas a las que el sistema va a recibir una vez sea autónomo, es el PIC32 el encargado de capturar las imágenes y enviarselas al ordenador donde se almacenarán. Para simplificar la tarea de la captura de imágenes hemos realizado un pequeño sketch en lenguaje Processing (un lenguaje que utiliza java como base y está optimizado para trabajar con imágenes). Este sketch toma los datos de la imágen que el PIC32 envía por puerto serie y los transforma en una imágen png, que guarda en un directorio determinado según el gesto al que corresponda, lo cual indica el usuario mediante un menú de control.

[se añadiran imagenes ilustrativas]

El asistente de cámara elige en el menú el gesto que va a grabar e indica al usuario que haga el gesto. Una vez la mano se vea estable en la pantalla, el asistente de cámara pulsará el botón de grabación, momento en el cual se empiezan a guardar las imágenes enviadas por el PIC. Durante el tiempo que dure la captura, que va a unos 15-20 fotogramas por segundo limitada por la velocidad de acceso al disco a la hora de guardar imágenes, el usuario deberá mover ligeramente la mano sin dejar de hacer el gesto, para obtener muchas imágenes distintas.
En sesiones de unos 5 minutos somos capaces de obtener algo más de 1000 imágenes para la base de datos de entrenamiento. Actualmente disponemos de unas 14000 imágenes, de las cuales solo 6000 son válidas, pues tuvimos que cambiar el alfabeto de gestos y descartar las imágenes capturadas con el alfabeto anterior.

Una vez capturadas las imágenes, un script de Matlab recorre la estructura de directorios y extrae todas las imágenes que encuentra, convirtiéndolas a una gran matriz de 576 columnas (una por pixel) y tantas filas como imágenes haya, y otra matriz de una columna y el mismo número de filas que contiene, para cada imagen, el número de gesto al que corresponde.

Cabe destacar un problema que tuvimos debido a que cuando Matlab pasa una matriz a un vector lo hace columna a columna, es decir, dada una matriz
A=[1 2 3
4 5 6]
El comando A(:) de matlab lo transformaría en [1 4 2 5 3 6]. En el PIC32 las imágenes se capturan fila a fila y se guardan como un array unidimensional fila a fila ([1 2 3 4 5 6]). Por tanto nuestra primera prueba de la red neuronal en el PIC32 resultó un fracaso, ya que al no darnos cuenta del detalle anterior la red neuronal se había entrenado en realidad para reconocer las imágenes que captura el PIC rotadas 90 grados (traspuestas). El problema se arregla simplemente trasponiendo la matriz que representa cada imágen antes de transformarla en un vector en Matlab.

Procesamiento de imagen II: resumen y añadidos

Desde que comenzamos a escribir el blog hemos ido avanzando poco a poco en las tareas de procesamiento de vídeo. Esta entrada sirve de resumen de lo ya visto, pero además añadimos una modificación a nivel del escalado y un par de añadidos posteriores a la etapa de procesamiento.
Este post será actualizado más adelante con imágenes ilustrativas del proceso.

Actualmente, a cada fotograma capturado de 48x48 píxeles se le aplican los siguientes procesos (recordamos que debido a problemas con el tiempo que debía haber entre dos capturas sucesivas, no pudimos hacer la captura a 96x96 como planeábamos inicialmente):

UNO -Filtro de mediana con radio 1 (una explicación más detallada puede consultarse en esta entrada).

DOS -Escalado a la mitad.
Utilizamos un algoritmo equivalente al box filter que ya se explicó, pero esta vez dividiendo por 2 en vez de por 4. La implementación del nuevo escalado está algo menos optimizada; se trata de dos bucles for anidados que recorren la imagen, tomando una columna de cada dos y una fila de cada dos, y para cada pixel considerado llama a una función getUnos, también empleada en el autocalibrado. Dicha función simplemente cuenta el número de pixels a 1 en una imagen, en un parche cuyas coordenadas iniciales y coordenadas finales se pasan por parámetro. Se cuenta por tanto el número de unos en un parche de 2x2 desde el pixel considerado; si hay dos o mas se sustituye el parche por un 1, y si hay menos se sustituye por un 0. La imagen obtenida es por tanto de 24x24.

TRES- Centrado de la imagen en la coordenada vertical.
Para que las imágenes con las que debe trabajar el sistema se parezcan más entre sí y la red tenga más sencillo hacer su trabajo, hemos decidido aplicar un algoritmo de centrado en la coordenada vertical a cada fotograma. El proceso consiste en calcular la coordenada vertical del centro de masas de la imagen, y ver cuánto se desvía del centro real de la imagen. Después se aplica un desplazamiento al fotograma para que ambos centros coincidan. De esta forma nos independizamos de parte de los movimientos del brazo, quedando la imagen siempre centrada verticalmente; pudimos observar que los movimientos verticales, subiendo y bajando la mano, son muy comunes e involuntarios según se van realizando los gestos. Así, mientras la mano quepa verticalmente en el campo de visión de la cámara, la imagen que le llegará a la red neuronal será muy parecida independientemente de la posición vertical de la mano.

CUATRO- Alinear a la izquierda (alignLeft).
Nuestro alfabeto de gestos implica la extensión de uno o más dedos de la mano para la mayoría de los gestos, dando lugar a imágenes "largas" que tienden a ocupar todo el espacio horizontal del campo visual de la cámara. Pero aunque el usuario tiene menos libertad de movimiento en la coordenada horizontal, la variación sí puede llegar a ser significativa, con lo que implementamos un equivalente a un centrado horizontal.
No es posible utilizar el mismo método que para el centrado vertical, basado en el cálculo del centro de masas, dado que no sería efectivo por ocupar el brazo del usuario todo un lado de la pantalla. Hemos recurrido por tanto a otra alternativa:
Dada una imagen, la recorremos desde la izquierda para encontrar la primera columna en la que tengamos imagen de mano (1s). En ese momento, comenzamos a desplazar la imagen columna a columna, de tal forma que la primera columna con 1s quede a la izquierda del todo, la segunda justo al lado, etc. Donde antes estaba la imagen que se ha desplazado, rellenamos con todo 0s.
Obtenemos de esa forma una mano siempre a la izquierda del fotograma, pero con brazo inacabado.
Hacemos notar que el bucle de recorrido de columnas comienza en la columna 1 en lugar de en la cero. Esto es porque, si la mano ocupaba toda la imagen horizontalmente, al buscar el offset a aplicar resultaba ser 0 (en la primera columna ya hay imagen). En este momento copiaba la columna en si misma y después la borraba, con lo que obteníamos imágenes consistentes en todo ceros.
En lugar de hacer uso de estructuras if, simplemente comenzamos a verificar si las columnas contienen imagen a partir de la columna 1. En tal caso podría perderse la primera columna al ocupar una imagen toda la imagen horizontalmente, pero la diferencia no es significativa.

CINCO- Extensión del brazo (armGenerator)
Volvemos a recorrer la imagen, pero ahora desde la derecha en busca de la primera columna que tenga tres unos o más (el grosor del brazo nunca será de menos de 2 pixeles, con lo que si encontramos sólo dos unos o menos en una columna se tratará de ruido). En ese momento la identificamos como brazo, copiamos la columna y realizamos copias de ella en cada columna hacia la derecha hasta llegar al final de la imagen. De esta forma obtenemos un brazo completo que llega hasta el borde del fotograma.
Esto tiene la ventaja adicional de que hasta ahora era necesario arremangarse para utilizar el sistema. Desde este momento, bastará con despejar la muñeca para que el sistema mismo haga la extensión de brazo.

Código de alingLeft y armGenerator.

martes, 3 de abril de 2012

Red neuronal en PIC32

Se ha comentado que parte del interés de esta práctica reside en trabajar con redes neuronales. Una vez determinado el alfabeto de gestos y entrenada la red neuronal en Matlab, procedemos a implementarla en C.

En una red neuronal, la salida de cada neurona es una función de la suma de todas sus entradas, ponderadas por unos parámetros θ (cuyos valores hemos obtenido entrenando la red neuronal en Matlab). Para esta función, llamada función de activación, suele utilizarse una sigmoide; con el fin de implementarla en el PIC32 nos informamos sobre diversas formas de realizar aproximaciones de la función sigmoide, siendo la más sencilla por tramos de rectas. 

Pero a la hora de llevarlo a la práctica determinamos finalmente hacer uso de la función exp de la librería matemática en punto flotante para PIC32 incluida en el compilador de MPLAB. La función exp requiere 133 ciclos, y para calcular la sigmoide se necesita además una división. Dado que nuestra capa oculta es de 25 neuronas y, por tener un set de 12 gestos posibles, la capa de salida consta de 12 neuronas, se realizaran un total de 37 operaciones de sigmoide por imagen. 

Además de esto, para obtener la salida de cada neurona sería necesario multiplicar el valor de cada una de las entradas por un theta y sumarlas, lo cual supone un gran número de operaciones de multiplicación, costosas en tiempo. Pero debido a que trabajamos con sólo dos niveles de color, la imagen de entrada está compuesta por píxeles con valor únicamente 1 ó 0. Esto implica que, para cada neurona de la capa oculta, ponderar cada entrada por los valores theta equivale simplemente a sumar el valor de theta si la entrada vale 1, o no sumarlo si vale 0. En lugar de las 577 (incluyendo neurona de bias) * 25 operaciones de multiplicación tenemos el mismo número de estructuras if y sumas, lo cual es un ahorro muy importante de ciclos de trabajo.

Para obtener la salida de la red neuronal sí será necesario tomar el valor de cada neurona de la capa oculta (mas la de bias), ponderarla por un theta y aplicarle la sigmoide a la suma de todas ellas; pero el número de operaciones de multiplicación en este caso (12(número de neuronas de salida)*26(salidas de la capa oculta)) es mucho menor que las que tendríamos que haber efectuado en la capa anterior de no ser por la enorme simplificación que supone trabajar a dos niveles de color.

Toda la red neuronal puede implementarse de esta forma mediante dos sencillos bucles for. El método en el que está implementada devuelve además el número de neurona cuya salida es máxima, que se corresponde con el número del gesto identificado. De esta forma hemos conseguido trabajar a tiempo real, a pesar de no haber realizado una aproximación menos costosa en tiempo para la sigmoide, identificando el gesto en cada fotograma capturado.

Esta función podría ser modificada en el futuro si queremos tener en cuenta otros datos en la salida de la red neuronal, como la varianza de las salidas, la diferencia en la estimación de las dos salidas más altas, etc.

Métodos de sigmoid y predict (red neuronal).

Implementación de la red neuronal en Matlab

Para el reconocimiento de gestos hemos decidido utilizar un algoritmo basado en red neuronal. Nuestra red neuronal será un clasificador de imágenes con una estructura que intenta simular matemáticamente el comportamiento de un grupo de neuronas a las que entrenaremos para que sean capaces de clasificar las imágenes fijándose en ciertas características determinadas por el entrenamiento.

El entrenamiento de la red neuronal puede hacerse de diversas formas (algoritmos genéticos, backpropagation, manualmente...). Nosotros hemos elegido utilizar la técnica de backpropagation utilizando una gran base de datos de imágenes de gestos clasificados capturados por nosotros mismos y tratando de minimizar una función de coste que depende entre otras cosas del número de aciertos y de fallos que tiene la red al clasificar los gestos de la base de datos de entrenamiento.

La red neuronal depende de un juego de parámetros libres que serán los que ajustará el algoritmo de entrenamiento. En nuestro caso, cada neurona de la capa intermedia u oculta recibe una entrada de cada una de las neuronas de entrada, y asigna un peso distinto entre 0 y 1 a cada una de estas entradas. La suma de todas las entradas ponderadas por su correspondiente peso se pasa como entrada a una función de activación de tipo sigmoide (en nuestro caso con el sesgo siempre a 0), y el valor que tome esta función será la salida de la neurona. El funcionamiento es análogo entre las capa oculta y la capa de salida. Además, a la capa de entrada y a la capa oculta se les añade una neurona extra que no tiene entradas y siempre vale 1 y que se conoce como "bias unit", y que se incluye para evitar algunos problemas en caso de que ninguna de las neuronas de verdad se active.

Por tanto, con 576 píxeles por imagen (24x24) y sumando la bias unit, habrá 577 neuronas en la capa de entrada. Para la capa intermedia hemos fijado un total de 25 neuronas más la bias unit, total 26. Y la capa de salida tendrá tantas neuronas como gestos, en nuestro caso 12. Por tanto los parámetros libres de la red suman un total de 577x25 + 26*12 = 14737 parámetros. Entrenar la red con backpropagation significa esencialmente buscar los 14737 valores que hacen mínima una función de esos 14737 parámetros, lo cual resulta una tarea muy pesada que realizamos con Matlab.

Una vez entrenada la red tendremos un juego de parámetros entrenados, que llamaremos Theta1 (una matriz de 25 x 577) y Theta2 (12x26). La imagen se pasa a la red como un vector de 576 componentes (más la bias unit) correspondiendo cada componente del vector al valor de un pixel de la imagen. Realizando las operaciones que aparecen en la imágen (g(x) es la función sigmoide 1/(1+exp(-x)) ) obtendremos una hipótesis (un valor real entre 0 y 1) para cada neurona de salida. Las neuronas de salida van numeradas del 1 al 12 igual que nuestros gestos, y el número del gesto reconocido se corresponderá con el número de la neurona de salida cuya hipótesis sea máxima.

La implementación del sistema de entrenamiento está basado en un ejercicio propuesto para un curso online impartido por la universidad de Stanford sobre aprendizaje de máquinas. En este ejercicio se proponía implementar de forma más o menos guiada todo el algoritmo de backpropagation, programando entre otras cosas las funciones que determinan el gradiente de la función de coste y la función que clasifica una imagen una vez esté entrenada la red. El propósito de esta red era el reconocimiento de imágenes de números manuscritos.

Las modificaciones realizadas incluyen la eliminación de toda la parte del código del ejercicio que no tenía que ver con la red neuronal y la programación de todo el sistema que procesa y transforma las imágenes capturadas de la base de datos al formato adecuado (esencialmente idéntico al formato en que se capturan las imágenes en el PIC32) para trabajar con ellas.
También hemos creado un sistema de evaluación del comportamiento de la red que muestra el porcentaje de aciertos para cada uno de los gestos, y te permite visualizar cómo se ha reconocido cada gesto, para ver con cuales tiene más dificultades y poder tomar las decisiones adecuadas para mejorar el comportamiento.

Para pasarle los parámetros entrenados al PIC32 utilizamos un fichero de datos que creamos automáticamente con matlab gracias a un script que genera el fichero con el formato adecuado, listo para copiar, pegar y compilar.

Determinando el alfabeto de gestos

Es una parte importante de nuestra práctica el determinar el alfabeto de gestos a los que podrá recurrir el usuario para interactuar con el sistema. Inicialmente el objetivo de nuestra práctica era poder escribir un texto por pantalla a base de hacer gestos con la mano, para lo cual serían necesarios 27 gestos diferentes (uno por cada letra del alfabeto), mas opcionalmente otros 10 para los dígitos del 0 al 9.

Por ser demasiados gestos diferentes, pronto comenzamos a considerar la posibilidad de reducir el alfabeto para hacerlo más abordable para el usuario; una primera opción fue limitarlo a sonidos y no a letras en sí: por ejemplo, que no haya h, hacer equivalentes la qu y la k, etc. Pero más adelante reconsideramos la orientación de nuestra práctica; en lugar de escribir textos mediante gestos, un proyecto complejo y vistoso pero poco útil, decidimos orientarnos a control domótico.

Así, los gestos que sean identificados por el sistema servirán para encender y apagar luces, variar luminosidad, encender una televisión, o similares según el material disponible del laboratorio. Esto requiere un set de gestos mucho más reducido, lo que tiene dos ventajas: es más fácil para el usuario aprenderlos y más sencillo para el sistema diferenciarlos si están bien escogidos.

Debido a la forma en la que capturamos la entrada de vídeo ("Captura de vídeo"), la imagen sufre una compresión horizontal (la imagen tiene una relación de aspecto 4/3 y la capturamos cuadrada), lo que hace que los gestos sean más difíciles de distinguir los unos de los otros si colocamos la mano en vertical frente a la cámara. Teniendo esto en cuenta hemos definido los gestos en horizontal.

Determinamos una serie de gestos básicos simples que pudieran sernos útiles para el control del sistema (números del 1 al 5 con la mano, palma cerrada, signo de OK...), y reunimos a conocidos y familiares para probar con ellos lo intuitivos que les resultaban, así como para obtener una base de datos de gestos de diferentes personas. La dividimos en set de entrenamiento y set de test y la utilizamos para entrenar la red neuronal; comprobamos en MATLAB los porcentajes de acierto en el set de test para cada uno de los gestos después de tan sólo 50 ciclos de entrenamiento.


Ejemplo de porcentajes de acierto en el set de test obtenidos para cada gesto, después de 50 ciclos de entrenamiento.

En la imagen anterior puede observarse cómo algunos gestos son reconocidos prácticamente siempre, mientras que otros tienden a ser confundidos sistemáticamente con otros. En este ejemplo vemos cómo ha confundido un puño cerrado (gesto tipo 1) con un dos (gesto 3). Esto puede deberse también a una base de datos o a un entrenamiento insuficiente.


Estas pruebas nos permitieron modificar o sustituir algunos de los 12 gestos que teníamos pensados inicialmente. Un ejemplo es el del número 3: Inesperadamente encontramos gente incapaz de hacer este gesto correctamente y sin forzar la mano con los dedos índice, corazón y anular. Además, la red neuronal tenía una tendencia elevada a confundirlo con el 2, el 4, u otros gestos. En este caso hemos resuelto el problema definiendo que el gesto para el 3 se haga con lo dedos pulgar, índice y corazón.

Los gestos actualmente definidos se recogen a continuación:


Set actual de gestos del usuario

Autocalibrado

Un comparador interno del PIC32 es el encargado de determinar si un píxel será blanco o negro en la imagen capturada. Para fijar el umbral de dicho comparador, utilizamos un conversor digital/analógico externo de 8 bit controlado por el bus I2C, modelo MCP4706. La elección del DAC vino únicamente determinada por su bajo coste, ya que para la aplicación no necesitamos ninguna característica especial. Para fijar la tensión de referencia positiva del DAC utilizamos un potenciómetro ajustable manualmente. De esta forma, podremos ajustar la tensión de referencia al nivel de tensión máximo de nuestra señal de vídeo, disponiendo de un umbral ajustable con 256 niveles distribuidos uniformemente en casi todo el rango dinámico de la parte visible de la señal de vídeo.

Con la tensión de referencia convenientemente ajustada el PIC podrá variar el umbral de su propio comparador en 256 niveles.

A la hora de capturar vídeo imponemos que haya un contraste entre el color de la piel de la mano y el color del fondo, y que el fondo sea lo más uniforme posible, o que al menos todo el fondo mantenga un nivel aceptable de contraste, sin que haya claros en caso de fondo oscuro o sombras oscuras en caso de fondo claro.

Para capturar correctamente la mano primero hay que calibrar el umbral del comparador, para que solo la mano aparezca como píxeles activos dentro de la imagen y el fondo no sea tomado en consideración. El filtro de mediana relaja la tolerancia del umbral, al eliminar eliminar píxeles sueltos o pequeños grupos de píxeles que puedan tomar un color similar al de la mano de forma aleatoria dentro de la imagen, o a causa de pequeñas variaciones en la iluminación. No obstante sigue siendo crucial elegir adecuadamente el umbral, y para ello hemos creado un sistema de autocalibrado.

Para realizar el autocalibrado, se comienza colocando el umbral en su nivel más bajo, se captura un forograma en el que únicamente aparezca el fondo de la imagen y se cuenta el número de píxeles activos (a 1) que contenga. Si este número es superior a un umbral, que hemos fijado en 25, se incrementa el umbral y se repite la operación hasta que finalmente prácticamente todos los píxeles del fondo se consideren como inactivos, momento en que el umbral se deja fijo. También cabe la posibilidad de incrementar un poco el umbral más allá del valor calculado, para que el sistema sea más robusto frente a las pequeñas variaciones de iluminación. De momento no nos ha hecho falta en las condiciones en que hemos probado el sistema.

La orden de calibrado se manda a través de dos botones incluidos en el hardware. Uno de ellos es para realizar la calibración con fondo oscuro y mano clara, y otro para mano oscura y fondo claro. El sistema de calibrado funciona de forma que en ambos casos las imagenes capturadas son totalmente equivalentes, ya que en caso de mano oscura y fondo claro invertimos la salida del comparador y fijamos el umbral en sentido contrario, comenzando por el nivel de tensión más alto y disminuyendolo hasta que el fondo quede eliminado.