Kinect SDK 1.0 – 3 – Tracker les mouvements avec le SkeletonStream
1. Introduction à l’API |
2. Utilisation du ColorImageStream |
3. Tracker le squelette avec le SkeletonStream |
4. Kinect en profondeur avec le DepthStream |
5. Reconnaissance vocale |
Dans les articles précédents, on a vu comment se connecter à une Kinect, et comment se servir du ColorStream. Globalement, avec ça, on n’a pas vraiment utilisé la Kinect différemment d’une bête webcam…
Mais cette fois-ci on entre dans le vif du sujet! On va parler du SkeletonStream…
Le SkeletonStream, c’est quoi?
C’est un flux de données que renvoie la Kinect et qui vous permet de connaître la position d’un utilisateur face à la Kinect, mais pas seulement: on peut obtenir jusqu’à 20 points (Joint) du corps, positionnés dans un espace à 3 dimensions!
L’image suivante vous montre l’ensemble des 20 points qui peuvent être trackés!
Le squelette d’un utilisateur et l’ensemble de ses points sont représentés par un objet de type Skeleton (MSDN).
Le Skeleton en détails
Le Skeleton ne présente pas beaucoup de propriétés mais elles sont très utiles :
- TrackingState : Le statut vous indique si le Skeleton est tracké (Tracked) ou non (NotTracked), ou si on a que la position globale de l’utilisateur (PositionOnly). La Kinect peut suivre jusqu’à 6 joueurs maximum dont 2 en mode Tracked et 4 en mode PositionOnly. Le mode Tracked permet d’avoir accès au squelette complet (20 Joint).
- Joints : Une collection de Joint représentant les 20 points du corps humain comme montré sur l’image ci-dessus. (Tracked)
- TrackingId : Identifie un Skeleton sur la durée.
- Position : La position centrale d’un squelette (Tracked et PositionOnly).
- ClippedEdges : Indique si des parties du corps de l’utilisateur se trouver en dehors du champs de vision.
Il est important de noter que pour le Skeleton et chacun des Joint, les positions sont représentées par un objet de types SkeletonPosition qui a 3 propriétés X, Y et Z. Les distances sont exprimées en mètres, et le référentiel est comme indiqué sur le schéma ci-dessus: X va de la gauche vers la droite, Y du bas vers le haut et Z représente la profondeur. Concrètement, la Kinect est le point (0,0,0).
Tracker et dessiner le squelette!
Ce qu’on veut désormais, c’est utiliser le SkeletonStream. On va commencer par l’activer, de la même manière qu’on activait le ColorStream dans les articles précédents :
/// <summary> /// Ce tableau va contenir les Skeleton /// reçus avec chaque nouvelle SkeletonFrame /// </summary> private Skeleton[] skeletonData = new Skeleton[6]; /// <summary> /// Démarrer le capteur Kinect. /// </summary> /// <param name="newKinect">Le nouveau capteur.</param> private void OpenKinect(KinectSensor newKinect) { Kinect = newKinect; // Active le SkeletonStream avec les paramètres par défaut. newKinect.SkeletonStream.Enable(); // On s'abonne au SkeletonFrameReady pour savoir quand des données sont disponibles newKinect.SkeletonFrameReady += Kinect_SkeletonFrameReady; // Démarre le capteur newKinect.Start(); }
Ensuite on implémente l’eventhandler qui va bien:
void Kinect_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e) { // On ouvre la SkeletonFrame reçue using (SkeletonFrame skeletonFrame = e.OpenSkeletonFrame()) { // Il se peut que la frame soit null, si on arrive // trop tard ou si la Kinect a été stoppée. if (skeletonFrame == null) return; // On copie les infos dans un tableau de Skeleton (6 éléments) skeletonFrame.CopySkeletonDataTo(skeletonData); // TODO : Faire quelque chose avec les Skeletons } }
Maintenant on peut commencer à faire des traitements en utilisant les infos reçues! On va commencer par dessiner les squelettes des deux joueurs qui seront entièrement trackés (s’il y en a).
Pour faire ça proprement, j’ai créé une classe SkeletonDrawing, qui va me permettre de dessiner un squelette à partir de tous les Joints. Le code source est disponible dans le projet!
Dans le todo de l’eventhandler Kinect_SkeletonFrameReady, on va ajouter quelques lignes de codes pour récupérer les squelettes trackés et mettre à jour les SkeletonDrawing correspondants:
D’abord, on ajoute une variable d’instance pour garder une trace des squelettes dessinées:
// Les squelettes dessinés sur la scène private Dictionary<int, SkeletonDrawing> drawnSkeletons;
Ensuite on peut ajouter la logique pour chercher les squelettes trackés, et les représenter ou les mettre à jour sur l’écran:
// Récupère les Skeleton avec le statut Tracked var trackedSkeletons = skeletonData.Where(s => s.TrackingState == SkeletonTrackingState.Tracked); // Par défaut, on indique que tous les squelettes sont inactifs foreach (SkeletonDrawing skeleton in drawnSkeletons.Values) skeleton.Status = ActivityState.Inactive; // On parcours chaque item de la liste des Skeleton trackés (2 max.) foreach (Skeleton trackedSkeleton in trackedSkeletons) { SkeletonDrawing skeletonDrawing; // Et on regarde s'il est déjà dessiné if (!drawnSkeletons.TryGetValue(trackedSkeleton.TrackingId, out skeletonDrawing)) { // Si pas, on crée un nouveau dessin skeletonDrawing = new SkeletonDrawing(this.SkeletonCanvas); drawnSkeletons.Add(trackedSkeleton.TrackingId, skeletonDrawing); } // et on met à jour le dessin du squelette Update(trackedSkeleton, skeletonDrawing); skeletonDrawing.Status = ActivityState.Active; } foreach (SkeletonDrawing skeleton in drawnSkeletons.Values) { // On peut ensuite effacer tous les dessins de squelettes avec un statut Inactive if (skeleton.Status == ActivityState.Inactive) skeleton.Erase(); }
Et la méthode Update qui va mettre à jour la position de chaque points du squelette dessiné:
private void Update(Skeleton skeleton, SkeletonDrawing drawing) { // On parcourt la liste des Joint du Skeleton foreach (Joint joint in skeleton.Joints) { // On récupère la position correspondante dans un repère où le coin topleft est (0,0) var colorPoint = Kinect.MapSkeletonPointToColor(joint.Position, Kinect.ColorStream.Format); // On met les points à l'échelle compte tenu de la taille de la fenêtre var point = new Point((int)colorPoint.X / 640.0 * this.ActualWidth, (int)colorPoint.Y / 480.0 * this.ActualHeight); // On met à jour le Joint correspondant du dessin drawing.Update(joint.JointType, point); } }
Notez l’utilisation de la méthode MapSkeletonPointToColor! La classe KinectSensor expose quelques méthodes d’aide pour convertir la position d’un point d’une frame vers un point d’une autre frame.
Voici l’ensemble des méthodes disponibles :
Ces méthodes vont vous éviter d’attraper mal à la tête 🙂
Comme vous le savez, le SkeletonStream donne des informations dans un référentiel différent du ColorStream et du DepthStream. Pour les deux derniers, vous aurez des tableaux de pixels, et donc chaque pixel aura une position dans un espace à deux dimensions avec le point top-left ayant les coordonnées (0,0).
Les SkeletonPoints quant à eux sont placés dans un référentiel à 3 axes. Maintenant, si vous voulez replacer le squelette dans le référentiel 2D du ColorStream, vous devrez convertir les positions pour être sûre que les points correspondent. C’est à ça que sert la méthode MapSkeletonPointToColor, qui vous retournera une ColorImagePoint basé sur un SkeletonPoint et le ColorImageFormat cible.
Le cas de la conversion depuis une DepthImageFrame vers une ColorImageFrame est un peu différent. Le capteur de profondeur n’est pas tout à fait à la même place que le capteur de couleur. Il y a donc une petite différence entre les positions (mais on en parlera dans le prochain article ) !
Jusqu’à maintenant on a pas utilisé la valeur de Z, mais si vous téléchargez les sources vous verrez que l’on peut également l’utiliser pour modifier l’échelle des formes dessinées et ainsi donner une impression de perspective !
Postures et Gestures
Les gestures, pour ceux qui ne savent pas, c’est un mouvement identifiable que l’utilisateur fait et qui permet de déclencher une action. Si vous êtes familier avec le développement Windows Phone, vous avez certainement déjà entendu parler du Flick, du Pinch, etc… Ces mouvements sont courants sur des devices prévus pour le touch.
Si vous pensez à la Kinect, vous pensez naturellement à un mouvement de balaiement avec la main, par exemple pour faire défiler une liste horizontale. Ce ce qu’on appelle le Swipe.
J’ai été surpris en découvrant le SDK de voir que rien n’était prévu pour la détection de gestures. Ce n’est pas une tâche facile, parce que contrairement à un mouvement fait avec les doigts sur un écran tactile, il y a plein de façons différentes de faire le même mouvement, et il faut pouvoir différencier un mouvement fait volontairement d’un simple déplacement de l’utilisateur devant la Kinect. Il y a heureusement quelques projets que vous pouvez trouver sur le web et que vous pouvez réutiliser, comme l’excellent Kinect Toolbox.
Article original: Kinect SDK 1.0 – 3 – Tracker les mouvements avec le SkeletonStream
Version anglaise: http://www.renauddumont.be/en/2012/kinect-sdk-1-0-3-tracker-les-mouvements-avec-le-skeletonstream
1 commentaire
Kinect SDK 1.0 – Hands-on! | Renaud Dumont - { .NET blog } · 27/04/2012 à 11:56
[…] […]
Les commentaires sont fermés.