Middleware :
No proximity bugs anymore.
The skeleton disappear if a tracked person is too close or not tracked anymore.
Processing :
There are no laggs anymore when an user stay too long moving his hands and drawing
tons of ellipses. (TUIO Cursors are not taken by their vectors, only the last position
of the cursors are caught to be drawn).
/*
* Projet : TraKERS
* Module : MIDDLEWARE
* Sous-Module : Tracking/Gestures
* Classe : PushDetector
*
* Auteur : alexandre.bastien@iri.centrepompidou.fr
*
* Fonctionnalités : Permet de détecter si l'utilisateur a effectué un Push, en se basant sur
* des règles appliquées à la positions des noeuds dans le temps.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Kinect;
namespace Trakers.Tracking.Gestures
{
public class PushDetector : GestureDetector
{
public enum Direction { PUSH, PULL };
public enum Hand { LEFT, RIGHT, BOTH, NONE };
Debug.DebugWindow debug;
public PushDetector(Debug.DebugWindow _d) : base()
{
debug = _d;
gesturePeriod = (float)0.3;
indexesPerSecond = 30;
indexesToCheck = (int)(gesturePeriod * indexesPerSecond);
}
/*
* Lit les noeuds de l'historique du squelette afin de détecter un Push.
* Règles :
* Se fait avec une main (gauche ou droite).
* Chaque nouvelle position de la main doit être plus profonde que la précédente.
* Chaque nouvelle position de la main ne doit pas dévier trop de l'axe perpendiculaire au plan (X, Y).
* Le geste doit mesurer en profondeur une certaine distance.
*/
public Hand CheckForPush()
{
//Crée un historique de squelette local, puisque l'historique est mis à jour toutes les ~1/30 s.
List<List<Joint>> localHistory = new List<List<Joint>>(history);
//Si il n'y a pas assez de positions dans l'historique local pour vérifier le geste.
if (localHistory.Count < indexesToCheck + 1)
return Hand.NONE;
//La distance de référence est ici la distance entre le milieu du dos et le milieu des épaules.
refDistance = Math.Abs(localHistory[0][(int)JointType.Spine].Position.Y - localHistory[0][(int)JointType.ShoulderCenter].Position.Y);
//On commence la position pour les indexesToCheck dernières postures (celle à l'index 0 étant la dernière).
SkeletonPoint startPointLeft = localHistory[localHistory.Count - indexesToCheck][(int)JointType.HandLeft].Position;
SkeletonPoint startPointRight = localHistory[localHistory.Count - indexesToCheck][(int)JointType.HandRight].Position;
//Booléens indiquant si le mouvement serait valide pour la main gauche ou droite.
bool leftHandOK = true, rightHandOK = true;
//De la position p1 à pn, on suit l'algorithme.
for (int i = localHistory.Count - indexesToCheck + 1; i < localHistory.Count; i++)
{
//Si la position Y de la main est plus haute que la tête
//OU si la position Y de la main est plus basse que la hanche
//OU si la nouvelle position Z de la main est moins profonde que la précédente
//OU si la nouvelle position X de la main est plus éloignée de la distance N par rapport à la première position X
//OU si la nouvelle position Y de la main est plus éloignée de la distance N par rapport à la première position Y
//Alors la main en question ne fait pas de push.
if (localHistory[i][(int)JointType.HandLeft].Position.Y < localHistory[i][(int)JointType.Head].Position.Y ||
localHistory[i][(int)JointType.HandLeft].Position.Y > localHistory[i][(int)JointType.HipCenter].Position.Y ||
localHistory[i][(int)JointType.HandLeft].Position.Z > localHistory[i - 1][(int)JointType.HandLeft].Position.Z ||
Math.Abs(localHistory[i][(int)JointType.HandLeft].Position.X - startPointLeft.X) > refDistance / 5 ||
Math.Abs(localHistory[i][(int)JointType.HandLeft].Position.Y - startPointLeft.Y) > refDistance / 5)
leftHandOK = false;
if (localHistory[i][(int)JointType.HandRight].Position.Y < localHistory[i][(int)JointType.Head].Position.Y ||
localHistory[i][(int)JointType.HandRight].Position.Y > localHistory[i][(int)JointType.HipCenter].Position.Y ||
localHistory[i][(int)JointType.HandRight].Position.Z > localHistory[i - 1][(int)JointType.HandRight].Position.Z ||
Math.Abs(localHistory[i][(int)JointType.HandRight].Position.X - startPointRight.X) > refDistance / 5 ||
Math.Abs(localHistory[i][(int)JointType.HandRight].Position.Y - startPointRight.Y) > refDistance / 5)
rightHandOK = false;
if (!leftHandOK && !rightHandOK)
return Hand.NONE;
}
//Si la distance en Z du geste a été plus courte que la distance N
//Alors on retourne faux.
if (Math.Abs(localHistory[localHistory.Count - 1][(int)JointType.HandLeft].Position.Z - localHistory[localHistory.Count - indexesToCheck][(int)JointType.HandLeft].Position.Z) * 100 < 20)
leftHandOK = false;
if (Math.Abs(localHistory[localHistory.Count - 1][(int)JointType.HandRight].Position.Z - localHistory[localHistory.Count - indexesToCheck][(int)JointType.HandRight].Position.Z) * 100 < 20)
rightHandOK = false;
//Si la dernière position de la main droite/gauche est sur le côté gauche/droit du corps
//OU si la première position calculée de la main droite/gauche est sur le côté gauche/droit du corps
//Alors on retourne faux.
if (localHistory[localHistory.Count - 1][(int)JointType.HandLeft].Position.X > localHistory[localHistory.Count - 1][(int)JointType.HipCenter].Position.X ||
localHistory[localHistory.Count - indexesToCheck][(int)JointType.HandLeft].Position.X > localHistory[localHistory.Count - 1][(int)JointType.HipCenter].Position.X)
leftHandOK = false;
if (localHistory[localHistory.Count - 1][(int)JointType.HandRight].Position.X < localHistory[localHistory.Count - 1][(int)JointType.HipCenter].Position.X ||
localHistory[localHistory.Count - indexesToCheck][(int)JointType.HandRight].Position.X < localHistory[localHistory.Count - 1][(int)JointType.HipCenter].Position.X)
rightHandOK = false;
if (!leftHandOK && !rightHandOK)
return Hand.NONE;
//On supprime l'historique local.
localHistory.Clear();
debug.ExceptionLbl.Background = System.Windows.Media.Brushes.White;
//Si on est arrivé jusqu'ici, toutes les conditions pour un push ont été remplies.
if (leftHandOK && rightHandOK)
return Hand.BOTH;
else if (leftHandOK)
return Hand.LEFT;
else if (rightHandOK)
return Hand.RIGHT;
return Hand.NONE;
}
/*
* Lit les noeuds de l'historique du squelette afin de détecter un Pull.
* Règles :
* Se fait avec une main.
* Chaque nouvelle position de la main doit être moins profonde que la précédente.
* Chaque nouvelle position de la main ne doit pas dévier trop de l'axe perpendiculaire au plan (X, Y).
* Le geste doit mesurer en profondeur une certaine distance.
*/
public Hand CheckForPull()
{
//Crée un historique de squelette local, puisque l'historique est mis à jour toutes les ~1/30 s.
List<List<Joint>> localHistory = new List<List<Joint>>(history);
//Si il n'y a pas assez de positions dans l'historique local pour vérifier le geste.
if (localHistory.Count < indexesToCheck + 1)
return Hand.NONE;
//La distance de référence est ici la distance entre le milieu du dos et le milieu des épaules.
refDistance = Math.Abs(localHistory[0][(int)JointType.Spine].Position.Y - localHistory[0][(int)JointType.ShoulderCenter].Position.Y);
//On commence la position pour les indexesToCheck dernières postures (celle à l'index 0 étant la dernière).
SkeletonPoint startPointLeft = localHistory[localHistory.Count - indexesToCheck][(int)JointType.HandLeft].Position;
SkeletonPoint startPointRight = localHistory[localHistory.Count - indexesToCheck][(int)JointType.HandRight].Position;
//Booléens indiquant si le mouvement serait valide pour la main gauche ou droite.
bool leftHandOK = true, rightHandOK = true;
//De la position p1 à pn, on suit l'algorithme.
for (int i = localHistory.Count - indexesToCheck + 1; i < localHistory.Count; i++)
{
//Si la position Y de la main est plus haute que la tête
//OU si la position Y de la main est plus basse que la hanche
//OU si la nouvelle position Z de la main est plus profonde que la précédente
//OU si la nouvelle position X de la main est plus éloignée de la distance N par rapport à la première position X
//OU si la nouvelle position Y de la main est plus éloignée de la distance N par rapport à la première position Y
//Alors la main en question ne fait pas de push.
if (localHistory[i][(int)JointType.HandLeft].Position.Y < localHistory[i][(int)JointType.Head].Position.Y ||
localHistory[i][(int)JointType.HandLeft].Position.Y > localHistory[i][(int)JointType.HipCenter].Position.Y ||
localHistory[i][(int)JointType.HandLeft].Position.Z < localHistory[i - 1][(int)JointType.HandLeft].Position.Z ||
Math.Abs(localHistory[i][(int)JointType.HandLeft].Position.X - startPointLeft.X) > refDistance / 5 ||
Math.Abs(localHistory[i][(int)JointType.HandLeft].Position.Y - startPointLeft.Y) > refDistance / 5)
leftHandOK = false;
if (localHistory[i][(int)JointType.HandRight].Position.Y < localHistory[i][(int)JointType.Head].Position.Y ||
localHistory[i][(int)JointType.HandRight].Position.Y > localHistory[i][(int)JointType.HipCenter].Position.Y ||
localHistory[i][(int)JointType.HandRight].Position.Z < localHistory[i - 1][(int)JointType.HandRight].Position.Z ||
Math.Abs(localHistory[i][(int)JointType.HandRight].Position.X - startPointRight.X) > refDistance / 5 ||
Math.Abs(localHistory[i][(int)JointType.HandRight].Position.Y - startPointRight.Y) > refDistance / 5)
rightHandOK = false;
if (!leftHandOK && !rightHandOK)
return Hand.NONE;
}
//Si la distance en Z du geste a été plus courte que la distance N
//Alors on retourne faux.
if (Math.Abs(localHistory[localHistory.Count - 1][(int)JointType.HandLeft].Position.Z - localHistory[localHistory.Count - indexesToCheck][(int)JointType.HandLeft].Position.Z) * 100 < 20)
leftHandOK = false;
if (Math.Abs(localHistory[localHistory.Count - 1][(int)JointType.HandRight].Position.Z - localHistory[localHistory.Count - indexesToCheck][(int)JointType.HandRight].Position.Z) * 100 < 20)
rightHandOK = false;
//Si la dernière position de la main droite/gauche est sur le côté gauche/droit du corps
//OU si la première position calculée de la main droite/gauche est sur le côté gauche/droit du corps
//Alors on retourne faux.
if (localHistory[localHistory.Count - 1][(int)JointType.HandLeft].Position.X > localHistory[localHistory.Count - 1][(int)JointType.HipCenter].Position.X ||
localHistory[localHistory.Count - indexesToCheck][(int)JointType.HandLeft].Position.X > localHistory[localHistory.Count - 1][(int)JointType.HipCenter].Position.X)
leftHandOK = false;
if (localHistory[localHistory.Count - 1][(int)JointType.HandRight].Position.X < localHistory[localHistory.Count - 1][(int)JointType.HipCenter].Position.X ||
localHistory[localHistory.Count - indexesToCheck][(int)JointType.HandRight].Position.X < localHistory[localHistory.Count - 1][(int)JointType.HipCenter].Position.X)
rightHandOK = false;
if (!leftHandOK && !rightHandOK)
return Hand.NONE;
//On supprime l'historique local.
localHistory.Clear();
debug.ExceptionLbl.Background = System.Windows.Media.Brushes.Black;
//Si on est arrivé jusqu'ici, toutes les conditions pour un push ont été remplies.
if (leftHandOK && rightHandOK)
return Hand.BOTH;
else if (leftHandOK)
return Hand.LEFT;
else if (rightHandOK)
return Hand.RIGHT;
return Hand.NONE;
}
}
}