Autores:
- Antonio José Navarro Céspedes
- Miguel Ángel Valenzuela Hidalgo
Descripción del problema que se aborda
El problema abordado en esta práctica es el de reconocer una determinada
posición del usuario así como un gesto. Para ello se ha hecho uso de un
dispositivo Kinect ONE (2.0) que nos aporta la detección del cuerpo humano así
como su profundidad.
En concreto, el problema ha constado de 3 fases:
- Colocar al usuario en la ubicación adecuada para la detección de su cuerpo completo.
- Detectar que el usuario ha realizado la postura que se le indica.
- Comprobar y contabilizar el número de veces que el usuario realiza el gesto indicado.
La postura que se le indicará al usuario será con la mano izquierda a la
misma profundidad que su cuerpo, estando situada a la izquierda del tronco; y
la mano derecha estará situada por delante del cuerpo de forma erguida, a la
misma altura que la cabeza del usuario.
En cuanto al gesto, se hará con la mano derecha a partir de su posición
indicada por la postura, desplazándose hacia la izquierda simulando un gesto de
pasar página.
Descripción de la solución
Vamos
a explicar cómo hemos desarrollado el proyecto de forma general. Para
saber cómo se ha programado todo, lo tenemos perfectamente explicado en
el código de GitHub donde se ven mejor los pasos seguidos. Hemos cogido el código del proyecto de ejemplo Body-Basics-WPF en C# debido a su facilidad de uso.
Reconocimiento de la posición adecuada
Primero, declaramos la ubicación exacta donde queremos que se sitúe el
usuario, declarando las siguientes variables globales:
private const double FloorCenterX =
0.0;
private const double FloorCenterY = -1.0;
private const double FloorCenterZ
= 2.5;
Como vemos, siempre se mostrarán por pantalla indicaciones con texto para
que el usuario pueda seguirlas, así como imágenes para saber qué etapas del
programa ha pasado con éxito. En cuanto al vídeo, se irán mostrando de la misma
forma los puntos a los que tiene que acceder el usuario en las distintas fases
del programa.
Para dibujar en el vídeo esa elipse a partir del punto declarado
anteriormente, hemos creado la siguiente función:
private
double DrawFloor(Joint foot_left, Joint foot_right, Joint head, DrawingContext drawingContext)
Con esta función realizamos varias tareas. Primero, dibujamos la elipse de
la ubicación donde queremos situar al usuario. Además usa la posición de los
pies y la cabeza para comprobar que el usuario aún no está bien situado, con lo
que muestra por pantalla las cadenas de texto que hemos visto en las imágenes
anteriores. Queda comentar que la función devuelve la altura de la cabeza, para
luego adaptar la postura en función de la altura del usuario.
Entendemos que el usuario está bien situado cuando se encuentra en un área
cercana en 30 cm como máximo al área indicada, zona que queda reflejada con el
área de la elipse. De esa forma, cuando el usuario está finalmente situado
correctamente, le indicamos que no se mueva de dicha posición y comenzamos la
etapa de reconocimiento de la postura.
Reconocimiento de Postura
Lo primero es indicarle al usuario que ya se ha posicionado correctamente,
cambiando la imagen de la X de la posición a un tic de color verde. Esto lo
hacemos de la siguiente forma:
System.String
imgPath = Path.GetFullPath(@"..\..\..\Images\good.png");
position.Source
= new BitmapImage(new Uri(imgPath));
La imagen del tic verde es “good.png” , la cual
cargamos cuando nos posicionamos donde se nos indica. De la misma forma, en MainWindows.xaml
cargamos las imágenes que se muestran inicialmente.
Vemos cómo aparecen los
puntos objetivos que el usuario tiene que alcanzar con las manos para reconocer
la postura exitosamente. Cuando coloque una mano en el lugar adecuado, se
mostrará por pantalla cómo el punto objetivo cambia del color rojo al color
verde:
Para que la profundidad
de los objetivos con respecto a las manos quede reflejada, al dibujarse los
círculos se dibujarán más pequeños cuanto más alejados estén de la cámara. Así,
el usuario puede comparar el tamaño de los círculos de su mano con el de los
objetivos, sabiendo si tiene que acercar o alejar la mano. Teniendo esto en
cuenta, tenemos una función para dibujar los dos objetivos:
private bool DrawGoal(Joint goal, Joint hand,
DrawingContext drawingContext)
Con esta función se dibuja un objetivo para una
mano, habiéndose declarado las coordenadas del objetivo con anterioridad.
Además, devuelve un valor booleano indicando si esa mano está situada donde el
objetivo (momento en el que se dibuja de color verde).
Cuando ambos objetivos estén alcanzados, se pasa a la
última etapa del programa: la detección del gesto.
Detección del Gesto
Lo primero tras detectar que se ha realizado correctamente la postura, es
indicárselo al usuario cambiando la imagen correspondiente:
Ya vemos cómo aparece un objetivo de otro color, indicando que es ahí donde debemos colocar la mano para comenzar a realizar el gesto. Al colocarse, se indicará con una línea y otro objetivo el movimiento que hay que realizar:
Vemos cómo el programa va
indicando al usuario hacia dónde debe mover la mano. Todo esto lo hacemos
primero dibujando los objetivos de una forma muy similar a los anteriores, con
la siguiente función:
private bool DrawGesturePoint(Joint goal,
Joint hand, DrawingContext drawingContext)
Con esta función además de dibujar los objetivos
comprobamos que el usuario no se sale de un rango para realizar el gesto (el
rango es 5 cm en todas las dimensiones de los objetivos, con lo que si por
ejemplo levanta demasiado la mano durante el gesto deberá comenzarlo de nuevo).
Esta función no dibuja la línea que simboliza el movimiento, sólo los círculos
objetivos. Para dibujar la línea, utilizamos la función DrawLine entre el punto
de la mano derecha y el objetivo final.
Podemos comprobar cómo al
realizar un gesto, se contabiliza en el texto de la izquierda y volvemos a
situar un objetivo en el inicio del gesto, para que el usuario pueda repetir el
gesto de nuevo.
Problemas encontrados y soluciones empleadas
Debido a que el ejemplo a partir del cual hemos realizado esta práctica
(Body-Basics-WPF) muestra todos los cuerpos detectados por pantalla, nos hemos
encontrado con el problema de que al aparecer varios cuerpos en la pantalla (en
caso de haberlos detectado) se superponían los objetivos de todos los cuerpos
detectados ya que su altura se calcula a partir de la altura del primer cuerpo
detectado. Como queríamos que nuestra aplicación fuera de un solo usuario,
hemos introducido una variable booleana. Nuestro programa trabaja con un vector
de cuerpos de tamaño 6, y si detecta menos de 6 cuerpos el resto de posiciones
son valores nulos. Con nuestra variable booleana controlamos que el bucle “for”
que recorre este vector aplicando nuestro código a cada cuerpo detectado sólo
lo aplica al primer cuerpo detectado, de forma que las siguientes iteraciones
del bucle no realizan nada. Así, nuestro código se ejecutará para el primer
cuerpo detectado, y si aparece algún otro cuerpo durante la ejecución no se
tendrá en cuenta (a no ser que el primero desaparezca).
No hay comentarios:
Publicar un comentario