Solución Reto SDK de Kinect: Reconocer gestos con Skeletal tracking

Después de ver cómo podíamos detectar posturas con el SDK de Kinect en el anterior reto ahora toca utilizar un mecanismo similar para reconocer gestos. En la explicación del reto pudimos ver cómo se detectaba el gesto de “deslizar” o swipe de izquieda a derecha. Ahora lo completaremos implementando el código para detectar el swipe de derecha a izquierda y también el vertical.

Para solucionar este reto necesitamos:

 
Visual Studio. Si ya tienes VS instalado perfecto, pero por si acaso, aquí tienes el enlace de descarga de la versión gratuita express o de la Ultimate Trial.
 

 

SDK de Kinect para Windows. SDK necesario para utilizar las capacidades del sensor Kinect en nuestras aplicaciones. Descárgalo aquí.

Como siempre partiremos de la estructura vista en la explicación al reto donde inicializamos en el método Loaded la clase Runtime, activando el Skeletal tracking. Tendremos que crear el evento para capturar los datos del esqueleto cuando estén listos. Para acabar tenemos el evento Closed donde finalizamos el uso del sensor.

 

Runtime kinect;

public MainWindow()
{
    InitializeComponent();
    Loaded += new RoutedEventHandler(MainWindow_Loaded);
    Closed += new EventHandler(MainWindow_Closed);
}

void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
    kinect = new Runtime();
    kinect.Initialize(RuntimeOptions.UseSkeletalTracking);  
                kinect.SkeletonFrameReady += new EventHandler<SkeletonFrameReadyEventArgs>(kinect_SkeletonFrameReady);
}

void MainWindow_Closed(object sender, EventArgs e)
{
    kinect.Uninitialize();
}

 

Obtener posición de la mano

El deslizamiento lo haremos con la mano derecha. Para ello dentro del método kinect_SkeletonFrameReady cogeremos la posición de la mano como hemos visto en otros artículos.

 

void kinect_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
{
    foreach (SkeletonData skeleton in e.SkeletonFrame.Skeletons)
        if (skeleton.TrackingState == SkeletonTrackingState.Tracked)
        {
            Vector3 handRight = new Vector3();
            handRight.X = skeleton.Joints[JointID.HandRight].Position.X;
            handRight.Y = skeleton.Joints[JointID.HandRight].Position.Y;
            handRight.Z = skeleton.Joints[JointID.HandRight].Position.Z;
            
            //Código del reconocimiento de gestos

 

Utilizaremos un struct para poder manejar mejor los datos de las posiciones.

 

[Serializable]
public struct Vector3
{
    public float X;
    public float Y;
    public float Z;
    public DateTime date;
}

 

Prepararnos para reconocer el gesto

Antes de empezar con el método que tendrá el código para reconocer el gesto necesitamos una serie de propiedades. Estas propiedades las utilizaremos para definir algunos parámetros del gesto como la longitud mínima, la duración, etc. También tenemos un par de listas con datos de las posiciones anteriores y de los gestos reconocidos.

 

List<Vector3> positionList = new List<Vector3>();
List<Gesture> gestureAcceptedList = new List<Gesture>();

const float SwipeMinimalLength = 0.4f;
const float SwipeMaximalHeight = 0.2f;
const int SwipeMininalDuration = 250;
const int SwipeMaximalDuration = 1500;
DateTime lastGestureDate = DateTime.Now;
int MinimalPeriodBetweenGestures = 0;

 

También usaremos un enumerado para los gestos.

 

public enum Gesture
{
    None,
    SwipeLeftToRight,
    SwipeRightToLeft,
    SwipeVertical
}

 

Reconocer deslizamiento de derecha a izquierda

Ahora veamos como implementamos la función que va a reconocer el gesto. Como vimos en la explicación del reto tenemos que recorrer la lista de posiciones que tenemos para ir comparando con la anterior, trataremos el gesto como una sucesión de posturas.

Vamos a explicar qué partes hay que cambiar para hacer el Swipe de derecha a izquierda. Copiaremos el siguiente método cambiándolo de nombre por SwipeRightToLeft y después lo iremos modificando para lograr reconocer el gesto bien. Si queréis ver más en detalle el funcionamiento del método podéis verlo en la explicación del reto anterior.

 

void SwipeLeftToRight()
{
    int start = 0;
    for (int index = 0; index < positionList.Count - 1; index++)
    {
        if ((Math.Abs(positionList[0].Y - positionList[index].Y) > SwipeMaximalHeight) || (positionList[index].X - positionList[index + 1].X > -0.01f))
        {
            start = index;
        }

        if ((Math.Abs(positionList[index].X - positionList[start].X) > SwipeMinimalLength))
        {
            double totalMilliseconds = (positionList[index].date - positionList[start].date).TotalMilliseconds;
            if (totalMilliseconds >= SwipeMininalDuration && totalMilliseconds <= SwipeMaximalDuration)
            {
                if (DateTime.Now.Subtract(lastGestureDate).TotalMilliseconds > MinimalPeriodBetweenGestures)
                {
                label1.Content = " SwipeLeftToRight " + gestureAcceptedList.Count;
                gestureAcceptedList.Add(Gesture.Swipe);
                lastGestureDate = DateTime.Now;
                positionList.Clear();
                }
            }
        }

    }
}

 

La primera comprobación que se realiza es para ver si el movimiento de la mano sigue siempre la misma altura y en la dirección correcta. Si queremos cambiar el sentido para que identifique de derecha a izquierda debemos cambiar la segunda condición de “mayor que” a “menor que” quedando de la siguiente forma:

 

if ((Math.Abs(positionList[0].Y - positionList[index].Y) > SwipeMaximalHeight) || (positionList[index].X - positionList[index + 1].X < -0.01f))

 

Después cambiamos el código para que muestre por pantalla que hemos realizado el movimiento de derecha a izquierda y añadimos a la lista de gestos aceptados el nuevo gesto.

 

label1.Content = "SwipeRightToLeft " + gestureAcceptedList.Count;
gestureAcceptedList.Add(Gesture.SwipeRightToLeft);

 

El resto del método lo dejamos igual.

Reconocer deslizamiento vertical

Para reconocer el Swipe vertical habrá que hacer más cambios. Empezaremos al igual que antes copiando el método SwipeLeftToRight para ir modificándolo. Una vez copiado le cambiamos el nombre por SwipeVertical y empezamos a realizar los demás cambios.

Esta vez el SwipeMaximalHeight, que es la anchura del gesto, no hay que compararla con la altura ya que la altura corresponde con la dirección del gesto (de arriba abajo). Así que permutamos las condiciones cambiando las Xs por las Ys. Ahora la dirección la comprobamos en el eje Y y el ancho del gesto en el eje X.

 

if ((Math.Abs(positionList[0].X - positionList[index].X) > SwipeMaximalHeight) || (positionList[index].Y - positionList[index + 1].Y > -0.01f))

 

El siguiente paso es cambiar la comprobación de la longitud del gesto. En los 2 casos anteriores la longitud se media en el eje X, ya que el gesto era horizontal. Ahora hay que cambiarlo por el eje Y porque el movimiento es vertical.

 

if ((Math.Abs(positionList[index].Y - positionList[start].Y) > SwipeMinimalLength))

 

Por último cambiamos el texto que se muestra por pantalla por el correcto y el gesto que se inserta en la lista de gestos aceptados.

 

label1.Content = "SwipeVertical " + gestureAcceptedList.Count;
gestureAcceptedList.Add(Gesture.SwipeVertical);

 

El último paso

Para acabar debemos hacer las llamadas correspondientes a los métodos de reconocimiento de gestos en el método kinect_SkeletonFrameReady y añadir la posición de la mano a la lista de posiciones.

 

//Código del reconocimiento de gestos
positionList.Add(new Vector3() 
{ 
    X = handRight.X, 
    Y = handRight.Y, 
    Z = handRight.Z, 
    date = DateTime.Now 
}

SwipeLeftToRight();
SwipeRightToLeft();
SwipeVertical();

if (positionList.Count() > 20)
{
    positionList.RemoveAt(0);
}

 

Con esto ya tenemos solucionado el cuarto reto de Kinect: Reconocer gestos con Skeletal tracking.