viernes, 30 de octubre de 2015

Desarrollo del reto 3

Añadiendo otras funcionalidades


Una vez hecho el reconocimiento de dar la patada identificado con las dos posturas, hemos decidido añadir otras funcionalidades para mejorar la experiencia del usuario, nuestra primera medida será añadir un botón a cierta distancia encima del usuario, que al pulsarlo se inicie la aplicación y a su vez, empiece a sonar una canción. Para crear dicho botón, añadimos la textura del Ying-Yang, como mencionamos anteriormente:

ImageBrush yinyangBrush = new ImageBrush(new BitmapImage(new Uri(@"PATH IMAGEN")));

Así, creamos un pincel (Objeto Brush) con la imagen del Ying-Yang, ahora tenemos que dibujarla, para ello previamente creamos una variable Tipo Point en la que vamos a definir la ubicación del objeto, para ello utilizaremos de referencia la posición de la cabeza del body, y la distanciamos 35 unidades en el eje Y para que se pose sobre el usuario:

Point posEsferaMusica = new Point(jointPoints[JointType.Head].X, (jointPoints[JointType.Head].Y) - 35.0);

Y la dibujamos en esta posición, encima de la cabeza del body a 35 unidades de distancia:

dc.DrawEllipse(yinyangBrush, null, posEsferaMusica, 20, 20);

Una vez creada la esfera, toca darle funcionalidad. En este caso le vamos a otorgar que sea el botón de Start de la aplicación y que al pulsarlo se lance la música y comiencen las siguientes fases, pero para ello necesitamos cargar las siguientes librerías y variables:

using System.Media; //Librería Música

private SoundPlayer kungfu = new SoundPlayer(@"PATH MUSICA.wav");

Una vez añadido esto, solo nos queda implementar dicha funcionalidad, para ello hay que crear un método que nos detecte cuando estamos tocando la esfera implementada anteriormente:

private bool PosturaStart(){}

Esta función devuelve true cuando el Joint de la mano izquierda o derecha se encuentra exactamente a la misma altura que la esfera, es decir, la toca.

De este modo queda definida la funcionalidad de la esfera. Ahora solo nos queda la interacción con el usuario y ordenar secuencialmente las acciones. Queda de esta manera:

Figura 3.

Interacción con el usuario:

Necesitamos guiar al usuario para conseguir que represente las posturas,  decidimos interactuar con el usuario por medio de imágenes y algo de texto, para hacerlo más intuitivo y fácil. En primer lugar pensamos en utilizar XAML añadiendo imágenes y habilitando y deshabilitando la visibilidad de estas para cambiarlas cuando sea necesario para las diferentes fases. Como se puede ver en la Figura 4, tenemos una serie de imágenes preparadas con figuras y textos que ofrecen unas instrucciones que permiten al usuario pasar por las diferentes fases.
dogo2.jpg
Figura 4.
El método que estábamos utilizando con las imágenes XAML no nos pareció el mejor y una vez que aprendimos a crear Brushes con texturas, decidimos utilizar varios Rectangles con las imágenes de las fases como texturas.
Fases de la aplicación:
La aplicación se compone de cuatro fases:
  • Fase 1: Se muestra la imagen inicial en la que se pide al usuario que toque el Yin Yang para comenzar. Al tocar el YinYang empieza a sonar la música y comenzará la fase 2.
  • Fase 2: Se pide al usuario que imite la postura de Karate Kid como hace la figura de ejemplo. Aquí entra en acción la función de la postura mirando que la rodilla esté a la altura de la cadera. Al hacer la primera postura con la pierna izquierda comienza la fase 3.
  • Fase 3: Se pide al usuario hacer la segunda postura, en este caso con la pierna derecha. Hacer esta postura dará comienzo a la fase 4.
  • Fase 4: Fase final de la aplicación en la que se invita al usuario a hacer un saludo como indica la figura de referencia. Este saludo parará la música y finalizará la aplicación.
Para el pase de fases hemos contabilizado el tiempo en que se estaba en una postura, esto se hace para que no se acepte la postura al mover partes del cuerpo por error. El tiempo en que se debe estar en una postura viene dado por el número de frames que han pasado desde que se tomó dicha postura, nosotros hemos puesto que las posturas esten entre 30 y 40 frames pues la Kinect 2 recibe 30 fps.
Una vez hecho esto, ya tenemos definida entera nuestra aplicación, el repositorio es: Proyecto Github


jueves, 29 de octubre de 2015

Desarrollo del reto 2

Detección de posturas con ángulos:

Nuestra primera idea para detectar la postura de Karate Kid (mostrada en la Figura 2) fue utilizar álgebra vectorial básica, calculamos los vectores correspondientes a las partes del cuerpo cuyos ángulos queremos comparar, en este caso probamos a generar los vectores del muslo y la pantorrilla. La postura sería correcta cuando el ángulo de nuestra pierna estuviese entre un rango razonable de valores como 70 grados y 100.

Para calcular el vector utilizamos:

V = (Pbx - Pax, Pby - Pax) Siendo Pa y Pb puntos del espacio con sus coordenadas (x,y) correspondientes.

Usando los puntos del tobillo, rodilla y cadera obtenemos los vectores de la pantorrilla y el muslo, necesarios para saber el ángulo de la pierna  y utilizando trigonometría calculamos el ángulo que forma la pierna y después de calcularlo aplicar un margen de error para darle mayor tolerancia. El cálculo del ángulo es el siguiente:

Figura 2.
Una vez tenemos el ángulo lo único que necesitamos hacer es ponerle un umbral para que la postura no fuese exacta, que admitiese cierta tolerancia y para ello pusimos un intervalo razonable de entre [70º-100º]

Tuvimos problemas con la detección de las posturas con este método pues no detectaba los ángulos correctamente, la solución fue darle otro enfoque.
Detección de posturas con alturas de los joints:

La solución que finalmente adoptamos fué  mirar a qué altura se encuentran dos joints en cuestión, en nuestro caso nos fijaremos en el joint central de la cadera (SpineBase) y el joint de la rodilla de la pierna en cuestión.

Para ello creamos una función que detectase que la cadera y la rodilla se encontrasen alineados y devolviese true cuando este hecho se produjera con cierto grado de error introduciendo las variables max y min, de nuevo para ofrecer mayor flexibilidad a la hora de detectar que ambos joints esten a la misma altura.

Esta función recibe dos BodyPartCoords, struct que hemos creado para obtener las coordenadas (x,y) de los joints y utilizarlos más cómodamente.

Esta idea de comprobar la postura utilizando alturas de los joints la hemos sacado de un tutorial de Kinect 1 que nos guió en la implementación.  Tutorial skeletal traking.
Para finalizar este apartado cabe mencionar que la detección de alturas es la herramienta que hemos utilizado para hacer las funciones de las posturas (Pose Karate Kid con pierna izquierda primero y despues pierna derecha).

Desarrollo del Reto 1

BodyBasics-D2D


La primera parte fué comprender el código de BodyBasics, en él realiza un tracking de las personas que se encuentren delante de Kinect y las pinta representando los huesos y articulaciones, de la siguiente forma:

FIgura 1:

BodyBasics2D representa las articulaciones del cuerpo en cuestión como joints, y entre los joints crea líneas representando las distintas partes del cuerpo. Las líneas que se ven en la Figura 1 son bones, creados a partir de parejas de tipos de Joint (JointType).
           // Torso

           this.bones.Add(new Tuple<JointType, JointType>(JointType.Head, JointType.Neck));
           this.bones.Add(new Tuple<JointType, JointType>(JointType.Neck, JointType.SpineShoulder));
           this.bones.Add(new Tuple<JointType, JointType>(JointType.SpineShoulder, JointType.SpineMid));
       this.bones.Add(new Tuple<JointType, JointType>(JointType.SpineMid, JointType.SpineBase));
           this.bones.Add(new Tuple<JointType, JointType>(JointType.SpineShoulder, JointType.ShoulderRight));
           this.bones.Add(new Tuple<JointType, JointType>(JointType.SpineShoulder, JointType.ShoulderLeft));
           this.bones.Add(new Tuple<JointType, JointType>(JointType.SpineBase, JointType.HipRight));
           this.bones.Add(new Tuple<JointType, JointType>(JointType.SpineBase, JointType.HipLeft));

           // Brazo Derecho
           this.bones.Add(new Tuple<JointType, JointType>(JointType.ShoulderRight, JointType.ElbowRight));
           this.bones.Add(new Tuple<JointType, JointType>(JointType.ElbowRight, JointType.WristRight));
           this.bones.Add(new Tuple<JointType, JointType>(JointType.WristRight, JointType.HandRight));
           this.bones.Add(new Tuple<JointType, JointType>(JointType.HandRight, JointType.HandTipRight));
           this.bones.Add(new Tuple<JointType, JointType>(JointType.WristRight, JointType.ThumbRight));

           // Brazo Izquierdo
           this.bones.Add(new Tuple<JointType, JointType>(JointType.ShoulderLeft, JointType.ElbowLeft));
           this.bones.Add(new Tuple<JointType, JointType>(JointType.ElbowLeft, JointType.WristLeft));
           this.bones.Add(new Tuple<JointType, JointType>(JointType.WristLeft, JointType.HandLeft));
           this.bones.Add(new Tuple<JointType, JointType>(JointType.HandLeft, JointType.HandTipLeft));
           this.bones.Add(new Tuple<JointType, JointType>(JointType.WristLeft, JointType.ThumbLeft));

           // Pierna derecha
           this.bones.Add(new Tuple<JointType, JointType>(JointType.HipRight, JointType.KneeRight));
           this.bones.Add(new Tuple<JointType, JointType>(JointType.KneeRight, JointType.AnkleRight));
           this.bones.Add(new Tuple<JointType, JointType>(JointType.AnkleRight, JointType.FootRight));

           // Pierna izquierda
           this.bones.Add(new Tuple<JointType, JointType>(JointType.HipLeft, JointType.KneeLeft));
           this.bones.Add(new Tuple<JointType, JointType>(JointType.KneeLeft, JointType.AnkleLeft));
           this.bones.Add(new Tuple<JointType, JointType>(JointType.AnkleLeft, JointType.FootLeft));


En este código podemos ver como crea bones, una pareja de tipos de Joints, para después pintar el esqueleto en pantalla. El dibujado del cuerpo entero se lleva a cabo en DrawBody. Esto nos ha sido muy útil para poder saber las posición de los Joints a la hora de probar la aplicación. Además del dibujado del cuerpo, viene en BodyBasics también un rectángulo dibujado que encuadran los bodies dibujados, esto se hace con la función DrawRectangle de Drawing Context.

dc.DrawRectangle(Brush.White, null, new Rect(0.0, 0.0, this.displayWidth, this.displayHeight));

Con esa función estamos dibujando un rectángulo de color blanco con un tamaño displayWidth y displayHeight predefinidos anteriormente. Ese cuadrado va a permitir a los bodies dibujados están dentro de un entorno definido y moverse por él. En nuestro caso hemos utilizado este método pero en lugar de utilizar un Brush con un color predefinido hemos creado un color con una imagen como textura. Para poder crear texturas a partir de imágenes podemos utilizar este código.

ImageBrush <nombre> = new ImageBrush(new BitmapImage(new Uri(“PATH IMAGEN”)));


Una vez visto como se dibuja el cuerpo y el entorno en el que se mueve, vamos a explicar la función en la que se ejecuta el código que hemos incluido en este ejemplo.

private void Reader_FrameArrived(object sender, BodyFrameArrivedEventArgs e){}

En esta función es donde definimos la lista de cuerpos que estamos detectando y cada vez que se recibe un frame se ejecuta la segunda parte de la función, donde ejecutaremos los dibujados y controlaremos el flujo de la aplicación (if (dataReceived)).

En primer lugar se crea un drawinContext que vamos a necesitar para todo lo que queramos dibujar. En este link se puede ver todo sobre drawinContext para más información: https://msdn.microsoft.com/en-us/library/system.windows.media.drawingcontext(v=vs.110).aspx

Una vez definido el drawingContext, dependiendo de la fase en la que estamos de la aplicación (fase 1 empezar tocando el yinyang, fase 2 para poner la primera postura, fase 3 para poner la segunda postura y fase 4 para finalizar la aplicación) definiremos un brush basado en texturas de los diferentes fondos que hemos preparado con texto para interaccionar con el usuario y dar las instrucciones de la aplicación):


Después de esta parte que hemos añadido nosotros, se crean para cada cuerpo un diccionario de tipos de Joint y las coordenadas del Joint(x,y) que nos será de mucha utilidad posteriormente. Una vez disponemos del diccionario jointPoints comenzamos a buscar las posturas y otras funciones para la aplicación.





Reto Kinect

¿Reto?


Con motivo de la primera práctica de la asignatura de 4º GI de la UGR, NPI (Nuevos Paradigmas de Interacción), en la primera práctica tenemos la misión de usar un dispositivo de detección de movimientos, a elegir entre LEAP, KINECT Y KINECT V2. Nosotros hemos elegido Kinect 2 (Xbox One), hay que aclarar que es la primera vez que usamos Kinect, asi que es la primera toma de contacto con el dispositivo, aunque cabe destacar que para esta práctica podemos utilizar funcionalidades de la SDK.




Bien, los objetivos del reto consisten en:

- Familiarizarnos con los dispositivos de detección de movimientos.

- Generar módulos de detección que sean reutilizables por los compañeros de la asignatura y por la comunidad en general.


- Presentar ante el grupo los problemas asociados a la detección asignada, la solución implementada y como se utiliza.

Las actividades a realizar consisten en dos partes:


Realizar una aplicación de detección de una postura fija y un gesto, Debe actuar del siguiente modo:

- Mostrar al usuario la posición a adoptar, antes de realizar el gesto, ayudándolo mediante indicaciones.
- Ayudar al usuario por medio de marcas virtuales (primitivas geométricas, texto, imágenes etc)

(Además para dichas acciones debe poder tolerar un margen de error, es decir reconocer acciones parecidas, pues es difícil para el usuario adoptar posiciones exactas sin demasiadas indicaciones)

Realizar un tutorial detallado, explicando que hemos hecho, cómo lo hemos hecho y las piedras que nos hemos encontrado en el camino. Es decir, los módulos que hemos utilizado, que hemos modificado, los parámetros que requieren, referencias y ayudas que hemos empleado, problemas que hemos tenido y como hemos conseguido solucionarlos.

Primeros pasos


Primero debemos instalar el SDK de Kinect que nos proporciona Microsoft  Link SDK Kinect V2, hecho esto, el primer paso es ver que funcionalidades nos ofrece Microsoft para poder emplearlas en nuestra práctica, nosotros hemos decidido hacerla en C# puesto que es un lenguaje que desconocemos y como todo en la vida, cuanto mas sepas, mejor (o al menos eso creemos, somos gente curiosa). Como nos pide realizar un "traking" del esqueleto del usuario, la funcionalidad "BodyBasics2D" nos viene perfecta para dicha función.

La comprensión del código de BodyBasics2D creado por Microsoft será explicada en otra entrada.

¿Qué hacemos?


Probablemente esta pregunta sea difícil, dado que la imaginación es algo que escasea normalmente. A nosotros se nos ocurrió la idea de hacer una especie de aplicación/minijuego/fit que tengas que dar una patada en el aire y cambiar de pierna (estilo kung fu), y como buena patada de kung fu, tiene que tener música de ambiente ("Everybody loves kung fu fighting" parece idónea para dicha tarea). 

(Algo así, pero sin levantar tanto la pierna, para que cualquier usuario pueda hacerlo)


Posteriormente contaremos como hemos ido realizando dichas funcionalidades y la aplicación como tal en GitHub