Menu

A

|

A

Kinect 2 : Détection du squelette (Part 1)

Kinect 2 : Détection du squelette (Part 1)

Dans ce second billet consacré à la détection de squelette avec Kinect 2, nous allons regarder ensemble comment récupérer les informations, ouvrir les flux et reconnaître un squelette qui interagi avec le capteur. Je vous rappelle que le sommaire des articles se trouve par ici.

Si vous n’avez pas installer le SDK c’est le moment de le faire en le téléchargeant à cette adresse, il vous faudra par contre Visual Studio 2013 pour pouvoir l’utiliser.

A savoir pour commencer

Pour ceux qui ont déjà barouder sur le SDK de Kinect 1, il y a une chose qui a vraiment changé. Avant, quand une application demandais de l’information au capteur elle le monopolisait et seul cette application pouvais utiliser Kinect, de plus il fallait bien fermer l’accès au capteur sous peine que la prochaine application peine à ouvrir les flux. Maintenant, pour Kinect 2, la logique est un peu différente car il y a maintenant un service résident qui est le seul à pouvoir récupérer les données du capteur.

En d’autre terme, votre application ne va plus demander l’accès au capteur, mais consommer les flux mis à disposition par le service. Ce principe permet alors d’avoir autant d’application que l’on souhaite qui s’exécute simultanément. Le service a donc pour rôle de capter les données et de les distribuer aux applications qui se sont enregistré à lui. Ce qui a aussi pour effet de ne plus vraiment gérer l’ouverture et la fermeture de Kinect.

Voici maintenant comment accéder à ces flux et en extraire les informations du squelette.

Première étape : Connexion aux flux

Dans un premier temps, il faut que votre application se connecte au service Kinect et préciser les flux que vous voulez suivre (Squelette, vidéo, son, etc.). Pour se faire nous devons récupérer une instance du capteur afin d’interagir avec lui, cela se fait avec le code ci-dessous :

KinectSensor sensor = KinectSensor.GetDefault();

Avec cette ligne de code, on récupère une instance qui va nous permettre d’interagir avec le capteur (en fait avec le service). Il faut savoir que si aucune Kinect n’est connecté au PC, la fonction « GetDefault() » vous renverras toujours une instance, car c’est en fait un pointeur vers le service.

Ensuite, il faut « écouter » les flux que le service peut nous envoyer, il en existe 7, pour récupérer les informations d’un flux il suffit d’ouvrir ce qu’on appelle un « Reader », qui est en fait une instance qui va récupérer les flux dans le service pour vous les transmettre dès qu’ils sont prêts.

// Récupérer le flux de la détection du squelette
BodyFrameReader bodyReader = sensor.BodyFrameSource.OpenReader();
bodyReader.FrameArrived += OnBodyFrameArrived; 

// Récupérer le flux pour la détection des corps (BodyIndex)
BodyIndexFrameReader bodyIndexReader = sensor.BodyIndexFrameSource.OpenReader();
bodyIndexcReader.FrameArrived += OnBodyIndexFrameArrived;

// Récupérer le flux caméra
ColorFrameReader colorReader = sensor.ColorFrameSource.OpenReader();
colorReader.FrameArrived += OnColorFrameArrived;

// Récupérer le flux de profondeur
DepthFrameReader depthReader = sensor.DepthFrameSource.OpenReader();
depthReader.FrameArrived += OnDepthFrameArrived;

// Récupérer le flux infrarouge
InfraredFrameReader infraReader = sensor.InfraredFrameSource.OpenReader();
infraReader.FrameArrived += OnInfraFrameArrived;

// Récupérer le flux infrarouge à longue exposition
LongExposureInfraredFrameReader longInfraReader = sensor.LongExposureInfraredFrameSource.OpenReader();
longInfraReader.FrameArrived += OnLongInfraFrameArrived;

// Récupérer le flux audio
AudioBeamFrameReader audioReader = sensor.AudioSource.OpenReader();
audioReade.FrameArrived += OnAudioFrameArrived;

Ensuite, une fois votre flux déclarer via le « reader », vous pouvez vous abonner à l’événement « FrameArrived » du « Reader » qui se déclenche dès que la frame est prête du côté du service. Il vous faudra alors traiter cette frame pour en extraire les différentes informations. Cependant cette méthode possède un petit problème. En effet, il est parfois nécessaire d’avoir plusieurs flux simultanés pour faire certains traitements, or comme les événements sont asynchrone et arrive dans un ordre qui n’est pas prédéterminé, ça devient l’enfer pour synchroniser tout cela.

Heureusement, il y a une méthode bien plus efficace, on peut ouvrir un « Reader » multi-flux, ce qui est intéressant dans ce reader c’est que le service va attendre pour une frame donnée, que tous les flux déclarer dans ce « Reader » soit traité pour les envoyer en une fois, ce qui a pour avantage de ne traiter qu’une passe pour les flux et cela nous garantit que les flux envoyés sont bien cohérent entre eux. Pour déclarer ce « Reader » multi-flux, voici la méthode (vous noterez que l’événement change de nom mais le principe est le même) :

// Ouverture d'un flux multipe couleur et profondeur
MultiSourceFrameReader multiReader = sensor.OpenMultiSourceFrameReader(FrameSourceTypes.Color | FrameSourceTypes.Depth);

// Pour s'abonner a l'événement de la frame
multiReader.MultiSourceFrameArrived += OnFrameArrived;

C’est super ! On a déclaré nos flux, qu’ils soient indépendant ou ensemble, maintenant il faut dire au service qu’on est prêt à recevoir les informations. Pour cela, il faut invoquer la méthode magique :

sensor.Open();

Une fois fait, vous recevrez les frames quand elles sont prêtes. Il reste juste une petite chose qu’il est utile d’ajouter, mais ça c’est à vous de voir. Je parle du monitoring du statut de la Kinect. En gros, si je débranche le senseur en cours de route, ou s’il plante tout simplement, il y a un événement pour ça :

// Propriété pour savoir si la Kinect est disponible
if (!sensor.IsAvailable)
    throw(new Exception("La Kinect n'est pas présente."));

// Evénement pour connaitre si la Kinect change de statut
sensor.IsAvailableChanged += OnIsAvailableChanged;

Cela permet d’adapter son application pour répondre aux imprévus.

Dernière chose, quand vous quittez votre application il faut bien penser à libérer les ressources, cela passe par un « Dispose() » sur tous vos « Reader » et par un « Close() » sur l’instance du senseur :

// Fermeture du flux
multiReader.Dispose();

// Fermeture de l'accès au service
sensor.Close();

Deuxième étape : Récupération des informations du squelette

Bien nous allons donc ouvrir un flux pour la manipulation du squelette :

KinectSensor sensor = KinectSensor.GetDefault();
MultiFrameReader reader = sensor.OpenMultiSourceFrameReader(FrameSourceTypes.Depth | FrameSourceTypes.Body | FrameSourceTypes.BodyIndex);
reader.MultiSourceFrameArrived += OnFrameArrived;
sensor.Open();

Voilà nous allons recevoir des informations, j’ai volontairement pris un « MultiFrameReader » pour vous montrer comment ça se passe dans ce cas, sachant que dans le cas de la frame unique ça sera la même chose, sauf qu’on traitera qu’un flux.

Maintenant que les données arrivent, voici comment on extrait les informations brutes pour le squelette (a noter qu’ici je ne récupère que le flux « Body ») :

private void OnFrameArrived(object sender, MultiSourceFrameArrivedEventArgs e)
{
  // Récupération de l'ensemble des frames
  MultiSourceFrame multiFrame = e.FrameReference.AcquireFrame();

  // Extraction de la frame Body qui contient les informations du squelette
  using (BodyFrame bodyFrame = multiFrame.BodyFrameReference.AcquireFrame())
  {
    if (bodyFrame != null)
    {
      // On initialise un tableau pour récupérer les informations
      Body[] bodies = new Body[bodyFrame.BodyCount];
      // On demande au reader de copier ces informations dans notre tableau
      bodyFrame.GetAndRefreshBodyData(bodies);
    }
  }
}

Voilà, grâce à ce code nous avons récupérer les informations de tous les squelettes dans un tableau, il n’y a plus qu’à traiter l’information. Il est à noter une chose importante, tant que vous n’invoquer pas la méthode « GetAndRefreshBodyData() », les données ne sont pas copier dans l’espace mémoire de votre application, c’est important car si vous ne traitez pas toutes les frames cela permet de gagner sur l’empreinte mémoire et en temps d’exécution.

Dans notre exemple ci-dessus, on remarque que les informations sont stockées dans une classe « Body », cette classe va contenir toutes les informations que nous avons besoin. Je détaillerais cette classe dans un second billet très bientôt. Si vous avez des questions sur cette première partie, n’hésitez pas m’envoyer un petit message, je serais heureux de vous aider.

Kinect 2 : Détection du squelette (Part 1)

Kinect 2 : Détection du squelette (Part 1)

Dans ce second billet consacré à la détection de squelette avec Kinect 2, nous allons regarder ensemble comment récupérer les informations, ouvrir les flux et reconnaître un squelette qui interagi avec le capteur. Je vous rappelle que le sommaire des articles se trouve par ici.

Si vous n’avez pas installer le SDK c’est le moment de le faire en le téléchargeant à cette adresse, il vous faudra par contre Visual Studio 2013 pour pouvoir l’utiliser.

A savoir pour commencer

Pour ceux qui ont déjà barouder sur le SDK de Kinect 1, il y a une chose qui a vraiment changé. Avant, quand une application demandais de l’information au capteur elle le monopolisait et seul cette application pouvais utiliser Kinect, de plus il fallait bien fermer l’accès au capteur sous peine que la prochaine application peine à ouvrir les flux. Maintenant, pour Kinect 2, la logique est un peu différente car il y a maintenant un service résident qui est le seul à pouvoir récupérer les données du capteur.

En d’autre terme, votre application ne va plus demander l’accès au capteur, mais consommer les flux mis à disposition par le service. Ce principe permet alors d’avoir autant d’application que l’on souhaite qui s’exécute simultanément. Le service a donc pour rôle de capter les données et de les distribuer aux applications qui se sont enregistré à lui. Ce qui a aussi pour effet de ne plus vraiment gérer l’ouverture et la fermeture de Kinect.

Voici maintenant comment accéder à ces flux et en extraire les informations du squelette.

Première étape : Connexion aux flux

Dans un premier temps, il faut que votre application se connecte au service Kinect et préciser les flux que vous voulez suivre (Squelette, vidéo, son, etc.). Pour se faire nous devons récupérer une instance du capteur afin d’interagir avec lui, cela se fait avec le code ci-dessous :

KinectSensor sensor = KinectSensor.GetDefault();

Avec cette ligne de code, on récupère une instance qui va nous permettre d’interagir avec le capteur (en fait avec le service). Il faut savoir que si aucune Kinect n’est connecté au PC, la fonction « GetDefault() » vous renverras toujours une instance, car c’est en fait un pointeur vers le service.

Ensuite, il faut « écouter » les flux que le service peut nous envoyer, il en existe 7, pour récupérer les informations d’un flux il suffit d’ouvrir ce qu’on appelle un « Reader », qui est en fait une instance qui va récupérer les flux dans le service pour vous les transmettre dès qu’ils sont prêts.

// Récupérer le flux de la détection du squelette
BodyFrameReader bodyReader = sensor.BodyFrameSource.OpenReader();
bodyReader.FrameArrived += OnBodyFrameArrived; 

// Récupérer le flux pour la détection des corps (BodyIndex)
BodyIndexFrameReader bodyIndexReader = sensor.BodyIndexFrameSource.OpenReader();
bodyIndexcReader.FrameArrived += OnBodyIndexFrameArrived;

// Récupérer le flux caméra
ColorFrameReader colorReader = sensor.ColorFrameSource.OpenReader();
colorReader.FrameArrived += OnColorFrameArrived;

// Récupérer le flux de profondeur
DepthFrameReader depthReader = sensor.DepthFrameSource.OpenReader();
depthReader.FrameArrived += OnDepthFrameArrived;

// Récupérer le flux infrarouge
InfraredFrameReader infraReader = sensor.InfraredFrameSource.OpenReader();
infraReader.FrameArrived += OnInfraFrameArrived;

// Récupérer le flux infrarouge à longue exposition
LongExposureInfraredFrameReader longInfraReader = sensor.LongExposureInfraredFrameSource.OpenReader();
longInfraReader.FrameArrived += OnLongInfraFrameArrived;

// Récupérer le flux audio
AudioBeamFrameReader audioReader = sensor.AudioSource.OpenReader();
audioReade.FrameArrived += OnAudioFrameArrived;

Ensuite, une fois votre flux déclarer via le « reader », vous pouvez vous abonner à l’événement « FrameArrived » du « Reader » qui se déclenche dès que la frame est prête du côté du service. Il vous faudra alors traiter cette frame pour en extraire les différentes informations. Cependant cette méthode possède un petit problème. En effet, il est parfois nécessaire d’avoir plusieurs flux simultanés pour faire certains traitements, or comme les événements sont asynchrone et arrive dans un ordre qui n’est pas prédéterminé, ça devient l’enfer pour synchroniser tout cela.

Heureusement, il y a une méthode bien plus efficace, on peut ouvrir un « Reader » multi-flux, ce qui est intéressant dans ce reader c’est que le service va attendre pour une frame donnée, que tous les flux déclarer dans ce « Reader » soit traité pour les envoyer en une fois, ce qui a pour avantage de ne traiter qu’une passe pour les flux et cela nous garantit que les flux envoyés sont bien cohérent entre eux. Pour déclarer ce « Reader » multi-flux, voici la méthode (vous noterez que l’événement change de nom mais le principe est le même) :

// Ouverture d'un flux multipe couleur et profondeur
MultiSourceFrameReader multiReader = sensor.OpenMultiSourceFrameReader(FrameSourceTypes.Color | FrameSourceTypes.Depth);

// Pour s'abonner a l'événement de la frame
multiReader.MultiSourceFrameArrived += OnFrameArrived;

C’est super ! On a déclaré nos flux, qu’ils soient indépendant ou ensemble, maintenant il faut dire au service qu’on est prêt à recevoir les informations. Pour cela, il faut invoquer la méthode magique :

sensor.Open();

Une fois fait, vous recevrez les frames quand elles sont prêtes. Il reste juste une petite chose qu’il est utile d’ajouter, mais ça c’est à vous de voir. Je parle du monitoring du statut de la Kinect. En gros, si je débranche le senseur en cours de route, ou s’il plante tout simplement, il y a un événement pour ça :

// Propriété pour savoir si la Kinect est disponible
if (!sensor.IsAvailable)
    throw(new Exception("La Kinect n'est pas présente."));

// Evénement pour connaitre si la Kinect change de statut
sensor.IsAvailableChanged += OnIsAvailableChanged;

Cela permet d’adapter son application pour répondre aux imprévus.

Dernière chose, quand vous quittez votre application il faut bien penser à libérer les ressources, cela passe par un « Dispose() » sur tous vos « Reader » et par un « Close() » sur l’instance du senseur :

// Fermeture du flux
multiReader.Dispose();

// Fermeture de l'accès au service
sensor.Close();

Deuxième étape : Récupération des informations du squelette

Bien nous allons donc ouvrir un flux pour la manipulation du squelette :

KinectSensor sensor = KinectSensor.GetDefault();
MultiFrameReader reader = sensor.OpenMultiSourceFrameReader(FrameSourceTypes.Depth | FrameSourceTypes.Body | FrameSourceTypes.BodyIndex);
reader.MultiSourceFrameArrived += OnFrameArrived;
sensor.Open();

Voilà nous allons recevoir des informations, j’ai volontairement pris un « MultiFrameReader » pour vous montrer comment ça se passe dans ce cas, sachant que dans le cas de la frame unique ça sera la même chose, sauf qu’on traitera qu’un flux.

Maintenant que les données arrivent, voici comment on extrait les informations brutes pour le squelette (a noter qu’ici je ne récupère que le flux « Body ») :

private void OnFrameArrived(object sender, MultiSourceFrameArrivedEventArgs e)
{
  // Récupération de l'ensemble des frames
  MultiSourceFrame multiFrame = e.FrameReference.AcquireFrame();

  // Extraction de la frame Body qui contient les informations du squelette
  using (BodyFrame bodyFrame = multiFrame.BodyFrameReference.AcquireFrame())
  {
    if (bodyFrame != null)
    {
      // On initialise un tableau pour récupérer les informations
      Body[] bodies = new Body[bodyFrame.BodyCount];
      // On demande au reader de copier ces informations dans notre tableau
      bodyFrame.GetAndRefreshBodyData(bodies);
    }
  }
}

Voilà, grâce à ce code nous avons récupérer les informations de tous les squelettes dans un tableau, il n’y a plus qu’à traiter l’information. Il est à noter une chose importante, tant que vous n’invoquer pas la méthode « GetAndRefreshBodyData() », les données ne sont pas copier dans l’espace mémoire de votre application, c’est important car si vous ne traitez pas toutes les frames cela permet de gagner sur l’empreinte mémoire et en temps d’exécution.

Dans notre exemple ci-dessus, on remarque que les informations sont stockées dans une classe « Body », cette classe va contenir toutes les informations que nous avons besoin. Je détaillerais cette classe dans un second billet très bientôt. Si vous avez des questions sur cette première partie, n’hésitez pas m’envoyer un petit message, je serais heureux de vous aider.

2 Comments

Michaël Polla

6 juillet , 2015 at 11:14

Salut Nicolas ! Merci pour cet article, c'est un bon aide-mémoire pour l'utilisation des différents flux.Une question : dans beaucoup d'exemples que j'ai pu voir, même sur MSDN, le multi-flux est utilisé, même si un seul flux nous intéresse au final. Par exemple, si je veux seulement afficher le flux "caméra", est-ce que j'ai meilleur temps de m'abonner au ColorFrameReader (pour des questions de performances, par exemple) ou est-ce que cela ne change rien de s'abonner au MultiFrameReader ? l'avantage étant que si par la suite je veux utiliser un autre flux, je n'ai pas besoin de m'abonner à un autre Reader ?Merci d'avance, et une belle journée :-)

Nicolas Calvi

4 septembre , 2015 at 5:41

Tu as les mêmes temps que tu sois en Mono Flux ou en Multi. Mais comme tu le dis toi même, si un jour tu as besoin d'en ajouter un, au moins c'est déjà fait. Je suis plutôt partisan du Multi plutôt que du mono.

Comments Are Closed