Reconocimiento de posturas con Kinect II
En este segundo artículo de la serie sobre reconocimiento de posturas usando el sensor Kinect de Microsoft voy a mostrar estructuras y clases genéricas usadas para aislar la aplicación de las diferentes versiones de SDK existentes. La versión del SDK que debemos utilizar depende del modelo de sensor que estemos utilizando. En el código de ejemplo yo he utilizado la versión 2.0, para el sensor de la Xbox One.
En este enlace puedes acceder al primer artículo de esta serie. En este otro puedes descargar el código fuente de la solución KinectGestures, escrita en csharp con Visual Studio 2015.
Se trata de una aplicación muy simple en la que solamente tenemos una barra de herramientas con dos botones, uno para poner en marcha el sensor y otro para pararlo. En la esquina superior izquierda se muestra un texto con la posición del cuerpo normalizada y en el resto de la ventana se dibuja el esqueleto completo en su posición actual.
Los bordes de la ventana se marcan en color rojo cuando el sensor detecta que el cuerpo se está saliendo del cuadro por alguna de las cuatro direcciones, izquierda, derecha, arriba o abajo.
El fondo de la ventana cambiará de color cuando pongamos cualquiera de los dos antebrazos en un ángulo cercano a los 90 grados. En azul para el brazo derecho:
En amarillo para el brazo izquierdo:
O en rosa para los dos brazos a la vez:
La librería de clases KinectInterface
El proyecto KinectInterface es una librería de clases genéricas que evita que la aplicación principal dependa de las clases de datos de cada una de las versiones del SDK de Kinect. Además proporciona una estructura genérica de vector en tres dimensiones para facilitar las operaciones de normalización del cuerpo y clases especializadas que realizan dicha normalización.
La estructura Vector3D
Se trata de una estructura especializada en operaciones con vectores de tres dimensiones, principalmente obtener el ángulo que forma un vector con cualquiera de los tres ejes y rotarlo alrededor de cada uno de ellos.
Las propiedades de esta estructura son simplemente las coordenadas X, Y y Z del vector con el tipo de datos float.
Los métodos XYNormalizedPosition, ZYNormalizedPosition y XZNormalizedPosition se utilizan para proyectar el vector en uno de los tres planos de coordenadas y convertir esta proyección en un vector unitario, con lo cual obtenemos el seno y el coseno del ángulo que forma con los dos ejes de coordenadas pertenecientes a dicho plano. Por ejemplo, con XYNormalizedPosition se proyectará el vector sobre el plano que forman los ejes X e Y, y en la coordenada X del vector resultantes tendremos el coseno del ángulo que forma con el eje X y en la coordenada Y el seno de dicho ángulo, o bien en Y tendremos el coseno del ángulo con el eje Y y en X el seno.
Con los métodos InvertXY, InvertZY e InvertXZ se invierte una de las coordenadas del vector para obtener una imagen especular con respecto a uno de los planos. Por ejemplo, con InvertXY invertimos la coordenada Z.
Los métodos RotateXCC, RotateYCC y RotateZCC se utilizan para rotar un determinado ángulo en sentido contrario a las agujas del reloj alrededor de uno de los tres ejes de coordenadas. Los argumentos son el coseno y el seno de dicho ángulo. Por ejemplo, RotateXCC rota el vector con el eje X como eje de giro. Para rotar en sentido de las agujas del reloj existen otros tres métodos, RotateXTC, RotateYTC y RotateZTC.
Por último, el operador - permite obtener la diferencia entre dos vectores.
Enumeraciones y estructuras de datos auxiliares
La enumeración Joint define todas las diferentes articulaciones del cuerpo. HandSt es una enumeración con los diferentes estados en que pueden estar las manos. BodyPart es otra enumeración con diferentes partes compuestas del cuerpo, como el antebrazo, el brazo completo o el brazo completo más la mano. Esta enumeración se utiliza para obtener los puntos de una de estas partes de cara a realizar su normalización.
BodyPoint es una estructura utilizada para contener los datos de una de las articulaciones del cuerpo. La propiedad Accuracy contiene un valor que representa la precisión con la que el sensor ha determinado su posición: -1 si no se ha podido determinar, 0 si la posición es inferida o 1 si ha sido posible determinarla con precisión. También existe la propiedad Vector, de tipo Vector3D, con las coordenadas en tres dimensiones del punto, y Point, de tipo PointF, con las coordenadas proyectadas en dos dimensiones para poder dibujar el punto en pantalla.
Por último, la estructura ClippedEdges se utiliza para indicar si el cuerpo se está saliendo del cuadro del sensor por la izquierda, derecha, arriba o abajo.
La clase BodyVector
La clase BodyVector representa un array de estructuras BodyPoint con todas las articulaciones del cuerpo. Proporciona el estado de las manos con las propiedades LeftHand y RightHand, la situación del cuerpo con respecto a los bordes del cuadro del sensor con la propiedad Clip, el momento en que ha sido tomada la posición del cuerpo en la propiedad Time y el tamaño total del cuadro del sensor en la propiedad Size, para ayudarnos a escalar el dibujo del esqueleto.
Con el método GetBodyPart podemos obtener un array de estructuras BodyPoint con los puntos de una determinada parte del cuerpo.
En el próximo artículo mostraré la encapsulación del acceso al sensor Kinect.