middleware/Tracking/Gestures/PushDetector.cs
changeset 15 4b78f179e7ce
child 17 fda26bfcabef
equal deleted inserted replaced
14:10d5199d9874 15:4b78f179e7ce
       
     1 /*
       
     2 * This file is part of the TraKERS\Middleware package.
       
     3 *
       
     4 * (c) IRI <http://www.iri.centrepompidou.fr/>
       
     5 *
       
     6 * For the full copyright and license information, please view the LICENSE_MIDDLEWARE
       
     7 * file that was distributed with this source code.
       
     8 */
       
     9 
       
    10 /*
       
    11  * Projet : TraKERS
       
    12  * Module : MIDDLEWARE
       
    13  * Sous-Module : Tracking/Gestures
       
    14  * Classe : PushDetector
       
    15  * 
       
    16  * Auteur : alexandre.bastien@iri.centrepompidou.fr
       
    17  * 
       
    18  * Fonctionnalités : Permet de détecter si l'utilisateur a effectué un Push, en se basant sur
       
    19  * des règles appliquées à la positions des noeuds dans le temps.
       
    20  */
       
    21 
       
    22 using System;
       
    23 using System.Collections.Generic;
       
    24 using System.Linq;
       
    25 using System.Text;
       
    26 using Microsoft.Kinect;
       
    27 
       
    28 namespace Trakers.MainModule.Gestures
       
    29 {
       
    30     public class PushDetector : GestureDetector
       
    31     {
       
    32         public enum Direction { PUSH, PULL };
       
    33         public enum Hand { LEFT, RIGHT, BOTH, NONE };
       
    34 
       
    35         public PushDetector() : base()
       
    36         {
       
    37             gesturePeriod = (float)0.3;
       
    38             indexesPerSecond = 30;
       
    39             indexesToCheck = (int)(gesturePeriod * indexesPerSecond);
       
    40         }
       
    41 
       
    42         /*
       
    43          * Lit les noeuds de l'historique du squelette afin de détecter un Push.
       
    44          * Règles :
       
    45          * Se fait avec une main (gauche ou droite).
       
    46          * Chaque nouvelle position de la main doit être plus profonde que la précédente.
       
    47          * Chaque nouvelle position de la main ne doit pas dévier trop de l'axe perpendiculaire au plan (X, Y).
       
    48          * Le geste doit mesurer en profondeur une certaine distance.
       
    49          */
       
    50         public Hand CheckForPush()
       
    51         {
       
    52             //Crée un historique de squelette local, puisque l'historique est mis à jour toutes les ~1/30 s.
       
    53             List<List<Joint>> localHistory = new List<List<Joint>>(history);
       
    54             
       
    55             //Si il n'y a pas assez de positions dans l'historique local pour vérifier le geste.
       
    56             if (localHistory.Count < indexesToCheck + 1)
       
    57                 return Hand.NONE;
       
    58 
       
    59             //La distance de référence est ici la distance entre le milieu du dos et le milieu des épaules.
       
    60             refDistance = Math.Abs(localHistory[0][(int)JointType.Spine].Position.Y - localHistory[0][(int)JointType.ShoulderCenter].Position.Y);
       
    61             //On commence la position pour les indexesToCheck dernières postures (celle à l'index 0 étant la dernière).
       
    62             SkeletonPoint startPointLeft = localHistory[localHistory.Count - indexesToCheck][(int)JointType.HandLeft].Position;
       
    63             SkeletonPoint startPointRight = localHistory[localHistory.Count - indexesToCheck][(int)JointType.HandRight].Position;
       
    64 
       
    65             //Booléens indiquant si le mouvement serait valide pour la main gauche ou droite.
       
    66             bool leftHandOK = true, rightHandOK = true;
       
    67 
       
    68             //De la position p1 à pn, on suit l'algorithme.
       
    69             for (int i = localHistory.Count - indexesToCheck + 1; i < localHistory.Count; i++)
       
    70             {
       
    71                 //Si la position Y de la main est plus haute que la tête
       
    72                 //OU si la position Y de la main est plus basse que la hanche
       
    73                 //OU si la nouvelle position Z de la main est moins profonde que la précédente
       
    74                 //OU si la nouvelle position X de la main est plus éloignée de la distance N par rapport à la première position X
       
    75                 //OU si la nouvelle position Y de la main est plus éloignée de la distance N par rapport à la première position Y
       
    76                 //Alors la main en question ne fait pas de push.
       
    77                 if (localHistory[i][(int)JointType.HandLeft].Position.Y < localHistory[i][(int)JointType.Head].Position.Y ||
       
    78                 localHistory[i][(int)JointType.HandLeft].Position.Y > localHistory[i][(int)JointType.HipCenter].Position.Y ||
       
    79                 localHistory[i][(int)JointType.HandLeft].Position.Z > localHistory[i - 1][(int)JointType.HandLeft].Position.Z ||
       
    80                 Math.Abs(localHistory[i][(int)JointType.HandLeft].Position.X - startPointLeft.X) > refDistance / 5 ||
       
    81                 Math.Abs(localHistory[i][(int)JointType.HandLeft].Position.Y - startPointLeft.Y) > refDistance / 5)
       
    82                     leftHandOK = false;
       
    83                 if (localHistory[i][(int)JointType.HandRight].Position.Y < localHistory[i][(int)JointType.Head].Position.Y ||
       
    84                 localHistory[i][(int)JointType.HandRight].Position.Y > localHistory[i][(int)JointType.HipCenter].Position.Y ||
       
    85                 localHistory[i][(int)JointType.HandRight].Position.Z > localHistory[i - 1][(int)JointType.HandRight].Position.Z ||
       
    86                 Math.Abs(localHistory[i][(int)JointType.HandRight].Position.X - startPointRight.X) > refDistance / 5 ||
       
    87                 Math.Abs(localHistory[i][(int)JointType.HandRight].Position.Y - startPointRight.Y) > refDistance / 5)
       
    88                     rightHandOK = false;
       
    89 
       
    90                 if (!leftHandOK && !rightHandOK)
       
    91                     return Hand.NONE;
       
    92             }
       
    93 
       
    94             //Si la distance en Z du geste a été plus courte que la distance N
       
    95             //Alors on retourne faux.
       
    96             if (Math.Abs(localHistory[localHistory.Count - 1][(int)JointType.HandLeft].Position.Z - localHistory[localHistory.Count - indexesToCheck][(int)JointType.HandLeft].Position.Z) * 100 < 20)
       
    97                 leftHandOK = false;
       
    98             if (Math.Abs(localHistory[localHistory.Count - 1][(int)JointType.HandRight].Position.Z - localHistory[localHistory.Count - indexesToCheck][(int)JointType.HandRight].Position.Z) * 100 < 20)
       
    99                 rightHandOK = false;
       
   100             
       
   101             //Si la dernière position de la main droite/gauche est sur le côté gauche/droit du corps
       
   102             //OU si la première position calculée de la main droite/gauche est sur le côté gauche/droit du corps
       
   103             //Alors on retourne faux.
       
   104             if (localHistory[localHistory.Count - 1][(int)JointType.HandLeft].Position.X > localHistory[localHistory.Count - 1][(int)JointType.HipCenter].Position.X ||
       
   105                localHistory[localHistory.Count - indexesToCheck][(int)JointType.HandLeft].Position.X > localHistory[localHistory.Count - 1][(int)JointType.HipCenter].Position.X)
       
   106                 leftHandOK = false;
       
   107             if (localHistory[localHistory.Count - 1][(int)JointType.HandRight].Position.X < localHistory[localHistory.Count - 1][(int)JointType.HipCenter].Position.X ||
       
   108                localHistory[localHistory.Count - indexesToCheck][(int)JointType.HandRight].Position.X < localHistory[localHistory.Count - 1][(int)JointType.HipCenter].Position.X)
       
   109                 rightHandOK = false;
       
   110 
       
   111             if (!leftHandOK && !rightHandOK)
       
   112                 return Hand.NONE;
       
   113 
       
   114             //On supprime l'historique local.
       
   115             localHistory.Clear();
       
   116 
       
   117             //Si on est arrivé jusqu'ici, toutes les conditions pour un push ont été remplies.
       
   118             
       
   119             if (leftHandOK && rightHandOK)
       
   120                 return Hand.BOTH;
       
   121             else if (leftHandOK)
       
   122                 return Hand.LEFT;
       
   123             else if (rightHandOK)
       
   124                 return Hand.RIGHT;
       
   125 
       
   126             return Hand.NONE;
       
   127         }
       
   128 
       
   129         /*
       
   130          * Lit les noeuds de l'historique du squelette afin de détecter un Pull.
       
   131          * Règles :
       
   132          * Se fait avec une main.
       
   133          * Chaque nouvelle position de la main doit être moins profonde que la précédente.
       
   134          * Chaque nouvelle position de la main ne doit pas dévier trop de l'axe perpendiculaire au plan (X, Y).
       
   135          * Le geste doit mesurer en profondeur une certaine distance.
       
   136          */
       
   137         public Hand CheckForPull()
       
   138         {
       
   139             //Crée un historique de squelette local, puisque l'historique est mis à jour toutes les ~1/30 s.
       
   140             List<List<Joint>> localHistory = new List<List<Joint>>(history);
       
   141 
       
   142             //Si il n'y a pas assez de positions dans l'historique local pour vérifier le geste.
       
   143             if (localHistory.Count < indexesToCheck + 1)
       
   144                 return Hand.NONE;
       
   145 
       
   146             //La distance de référence est ici la distance entre le milieu du dos et le milieu des épaules.
       
   147             refDistance = Math.Abs(localHistory[0][(int)JointType.Spine].Position.Y - localHistory[0][(int)JointType.ShoulderCenter].Position.Y);
       
   148             //On commence la position pour les indexesToCheck dernières postures (celle à l'index 0 étant la dernière).
       
   149             SkeletonPoint startPointLeft = localHistory[localHistory.Count - indexesToCheck][(int)JointType.HandLeft].Position;
       
   150             SkeletonPoint startPointRight = localHistory[localHistory.Count - indexesToCheck][(int)JointType.HandRight].Position;
       
   151 
       
   152             //Booléens indiquant si le mouvement serait valide pour la main gauche ou droite.
       
   153             bool leftHandOK = true, rightHandOK = true;
       
   154 
       
   155             //De la position p1 à pn, on suit l'algorithme.
       
   156             for (int i = localHistory.Count - indexesToCheck + 1; i < localHistory.Count; i++)
       
   157             {
       
   158                 //Si la position Y de la main est plus haute que la tête
       
   159                 //OU si la position Y de la main est plus basse que la hanche
       
   160                 //OU si la nouvelle position Z de la main est plus profonde que la précédente
       
   161                 //OU si la nouvelle position X de la main est plus éloignée de la distance N par rapport à la première position X
       
   162                 //OU si la nouvelle position Y de la main est plus éloignée de la distance N par rapport à la première position Y
       
   163                 //Alors la main en question ne fait pas de push.
       
   164                 if (localHistory[i][(int)JointType.HandLeft].Position.Y < localHistory[i][(int)JointType.Head].Position.Y ||
       
   165                 localHistory[i][(int)JointType.HandLeft].Position.Y > localHistory[i][(int)JointType.HipCenter].Position.Y ||
       
   166                 localHistory[i][(int)JointType.HandLeft].Position.Z < localHistory[i - 1][(int)JointType.HandLeft].Position.Z ||
       
   167                 Math.Abs(localHistory[i][(int)JointType.HandLeft].Position.X - startPointLeft.X) > refDistance / 5 ||
       
   168                 Math.Abs(localHistory[i][(int)JointType.HandLeft].Position.Y - startPointLeft.Y) > refDistance / 5)
       
   169                     leftHandOK = false;
       
   170                 if (localHistory[i][(int)JointType.HandRight].Position.Y < localHistory[i][(int)JointType.Head].Position.Y ||
       
   171                 localHistory[i][(int)JointType.HandRight].Position.Y > localHistory[i][(int)JointType.HipCenter].Position.Y ||
       
   172                 localHistory[i][(int)JointType.HandRight].Position.Z < localHistory[i - 1][(int)JointType.HandRight].Position.Z ||
       
   173                 Math.Abs(localHistory[i][(int)JointType.HandRight].Position.X - startPointRight.X) > refDistance / 5 ||
       
   174                 Math.Abs(localHistory[i][(int)JointType.HandRight].Position.Y - startPointRight.Y) > refDistance / 5)
       
   175                     rightHandOK = false;
       
   176 
       
   177                 if (!leftHandOK && !rightHandOK)
       
   178                     return Hand.NONE;
       
   179             }
       
   180 
       
   181             //Si la distance en Z du geste a été plus courte que la distance N
       
   182             //Alors on retourne faux.
       
   183             if (Math.Abs(localHistory[localHistory.Count - 1][(int)JointType.HandLeft].Position.Z - localHistory[localHistory.Count - indexesToCheck][(int)JointType.HandLeft].Position.Z) * 100 < 20)
       
   184                 leftHandOK = false;
       
   185             if (Math.Abs(localHistory[localHistory.Count - 1][(int)JointType.HandRight].Position.Z - localHistory[localHistory.Count - indexesToCheck][(int)JointType.HandRight].Position.Z) * 100 < 20)
       
   186                 rightHandOK = false;
       
   187 
       
   188             //Si la dernière position de la main droite/gauche est sur le côté gauche/droit du corps
       
   189             //OU si la première position calculée de la main droite/gauche est sur le côté gauche/droit du corps
       
   190             //Alors on retourne faux.
       
   191             if (localHistory[localHistory.Count - 1][(int)JointType.HandLeft].Position.X > localHistory[localHistory.Count - 1][(int)JointType.HipCenter].Position.X ||
       
   192                localHistory[localHistory.Count - indexesToCheck][(int)JointType.HandLeft].Position.X > localHistory[localHistory.Count - 1][(int)JointType.HipCenter].Position.X)
       
   193                 leftHandOK = false;
       
   194             if (localHistory[localHistory.Count - 1][(int)JointType.HandRight].Position.X < localHistory[localHistory.Count - 1][(int)JointType.HipCenter].Position.X ||
       
   195                localHistory[localHistory.Count - indexesToCheck][(int)JointType.HandRight].Position.X < localHistory[localHistory.Count - 1][(int)JointType.HipCenter].Position.X)
       
   196                 rightHandOK = false;
       
   197 
       
   198             if (!leftHandOK && !rightHandOK)
       
   199                 return Hand.NONE;
       
   200 
       
   201             //On supprime l'historique local.
       
   202             localHistory.Clear();
       
   203 
       
   204             //Si on est arrivé jusqu'ici, toutes les conditions pour un push ont été remplies.
       
   205             if (leftHandOK && rightHandOK)
       
   206                 return Hand.BOTH;
       
   207             else if (leftHandOK)
       
   208                 return Hand.LEFT;
       
   209             else if (rightHandOK)
       
   210                 return Hand.RIGHT;
       
   211 
       
   212             return Hand.NONE;
       
   213         }
       
   214     }
       
   215 }