FR - Prenez le contrôle avec Kinect pour Windows SDK - Eternal Coding - HTML5 / Windows / Kinect / 3D development - Site Home - MSDN Blogs

FR - Prenez le contrôle avec Kinect pour Windows SDK


 

FR - Prenez le contrôle avec Kinect pour Windows SDK

  • Comments 2

20110616 - Microsoft - Kinect-windows-sdk-300x250

Introductionimage

Microsoft Research vient de publier la première béta de la version gratuite de son kit de développement pour Kinect sur Windows. Vous pourrez d’ores et déjà trouver toutes les ressources sur le sujet (et télécharger le SDK) à cette adresse :

http://msdn.microsoft.com/fr-fr/windows/sdk-kinect

Ce SDK se charge également de vous installer les drivers pour que Windows reconnaisse votre Kinect. Attention toutefois, le SDK ne s’installe que sur Windows 7 (x86 et x64).

Nous allons donc nous attacher lors de cet article à découvrir ce que nous propose ce SDK.

Le premier point qu’il est important de noter est que ce dernier est en fait disponible en deux versions : un pour les développeurs C++ et une pour les développeurs managed.

Il n’y a donc pas de jaloux. Pour ma part, mes exemples seront présentés en C# (question de gout uniquement).

En ce qui concerne le mode de licence, cette version réalisée par Microsoft Research, est libre d’utilisation dans le cadre privé (en gros, tout ce qui n’est pas une utilisation commerciale).

Une autre version verra le jour assez rapidement et proposera un mode de licence permettant de vendre des solutions à base de Kinect.

Architecture

Le capteur Kinect envoie vers le système un ensemble de trois flux:

image

Le flux image peut être affiché comme pour n’importe quelle caméra (pour par exemple faire de la réalité augmentée). Le capteur Kinect peut retourner le flux en 640x480 (à 30 images par seconde) et en 1280x1024 (mais à 15 images par seconde).

Le flux de profondeur est le facteur déterminant dans notre affaire. Il va en effet donner à chaque pixel une profondeur depuis le capteur. Ainsi en plus de la position en 2D de chaque pixel (et de leur couleur) nous disposons désormais de leur profondeur. Cela va énormément faciliter les recherches de formes.

Un troisième flux est également envoyé depuis le capteur : il s’agit du flux audio en provenance des quatre microphones du Kinect (nous reviendrons sur ce sujet à la fin de l’article).

Le point clef ici concerne donc la capacité du Kinect à nous donner des informations tri-dimensionnelles. En utilisant ces dernières la librairie NUI (qui est fournie avec le SDK et qui est l’acronyme de Natural User Interfaces) est capable de détecter la présence d’humains en face du capteur. Elle peut ainsi “voir” jusqu’à 4 personnes et en suivre précisément deux.

Quand Kinect suit précisément une personne, elle peut fournir au développeur un squelette formé de points clefs détectés sur la personne en question :

image

Comme on peut le voir sur ce schéma de l’homme de Vitruve, il y a 20 point clefs (que l’on appellera joint) qui sont détectés et suivis par la librairie NUI.

Pour obtenir les meilleurs résultats, il faut que l’on se tienne à un distance comprise entre 1,2 et 3,5 mètres. Au delà de ces limites, la précision du capteur et de la librairie NIU décroit rapidement. Il n’est donc pas possible (pour le moment) de suivre un utilisateur assis face à son ordinateur.

Mise en œuvre

Pour utiliser Kinect pour Windows SDK dans votre application .Net, il suffit de référencer l’assembly Microsoft.Research.Kinect.dll.

image

Vous disposez alors de deux nouveaux espaces de noms: un pour l’accès aux flux vidéos et aux squelettes et un pour l’audio.

  1. using Microsoft.Research.Kinect.Nui;
  2. using Microsoft.Research.Kinect.Audio

Pour initialiser la librairie NUI, il faut instancier un objet de classe Runtime et définir ce que l’on veut recevoir des flux:

  1. kinectRuntime = new Runtime();
  2. kinectRuntime.Initialize(RuntimeOptions.UseDepthAndPlayerIndex | RuntimeOptions.UseSkeletalTracking | RuntimeOptions.UseColor);

Dans notre exemple, nous demandons d’initialiser la libraire avec le support du flux de profondeur, du flux vidéo et du suivi des squelettes.

Le buffer vidéo

Pour se servir du flux vidéo, il faut au préalable définir le format que nous attendons, Pour cela, nous allons demander à la librairie NUI de récupérer les données vidéos dans une certaine résolution avec un certain format de pixel :

  1. kinectRuntime.VideoStream.Open(ImageStreamType.Video, 2, ImageResolution.Resolution640x480, ImageType.Color);

Ici, nous demandons une résolution de 640x480 avec un format de pixel en RGB. Il est également possible de demander une résolution de 1280x1024 (avec une baisse des performances) et un format de pixel en YUV. La résolution comme évoqué plus tôt à un impact sur les performances. Ce n’est toutefois pas le cas du format de pixel qui peut donc être choisi selon ce qui arrange l’application.

Par la suite, pour être informé de la disponibilité de chaque image en provenance du flux vidéo, il faut s’abonner à un évènement de la librairie :

  1. kinectRuntime.VideoFrameReady += kinectRuntime_VideoFrameReady;

Dans le gestionnaire de cet évènement, nous pouvons simplement produire un BitmapSource pour l’afficher dans une application WPF:

  1. public class ColorStreamManager
  2. {
  3.     public BitmapSource ColorBitmap { get; private set; }
  4.  
  5.     public void Update(ImageFrameReadyEventArgs e)
  6.     {
  7.         PlanarImage Image = e.ImageFrame.Image;
  8.  
  9.         ColorBitmap = BitmapSource.Create(Image.Width, Image.Height, 96, 96, PixelFormats.Bgr32, null, Image.Bits, Image.Width * Image.BytesPerPixel);
  10.     }
  11. }


Le buffer de profondeur

A coté du flux vidéo, le Kinect peut nous envoyer un flux issu du capteur infra-rouge qui donne les informations de profondeurs.

L’initialisation est comparable à celle du flux vidéo:

  1. kinectRuntime.DepthStream.Open(ImageStreamType.Depth, 2, ImageResolution.Resolution320x240, ImageType.DepthAndPlayerIndex);
  2. kinectRuntime.DepthFrameReady += kinectRuntime_DepthFrameReady;

Les données de profondeurs sont stockées sous la forme de tableaux d’entiers sur 16 bits. Le flux de profondeur peut être récupéré en 320x240 ou en 80x60.

Sur les 16 bits de chaque pixel, les 13 bits de poids forts donnent la distance en millimètres de chaque pixel. Les 3 bits de poids faible donnent l’index de la personne présente (et cet index vaut 0 si le Kinect n’a détecté personne à cet endroit).

Ainsi, si l’on veut afficher ce flux de profondeur tout en coloriant les zones occupées par des humains, il est possible d’utiliser ce code :

  1. void ConvertDepthFrame(ImageFrameReadyEventArgs e)
  2. {
  3.     depthFrame32 = new byte[e.ImageFrame.Image.Width * e.ImageFrame.Image.Height * 4];
  4.  
  5.     byte[] depthFrame16 = e.ImageFrame.Image.Bits;
  6.  
  7.     for (int i16 = 0, i32 = 0; i16 < depthFrame16.Length && i32 < depthFrame32.Length; i16 += 2, i32 += 4)
  8.     {
  9.         // R?cup?ration de l'utilisateur courant
  10.         int user = depthFrame16[i16] & 0x07;
  11.  
  12.         // Profondeur (en mm)
  13.         int realDepth = (depthFrame16[i16 + 1] << 5) | (depthFrame16[i16] >> 3);
  14.  
  15.         // Profondeur->Intensit?
  16.         byte intensity = (byte)(255 - (255 * realDepth / 0x0fff));
  17.  
  18.         depthFrame32[i32] = 0;
  19.         depthFrame32[i32 + 1] = 0;
  20.         depthFrame32[i32 + 2] = 0;
  21.         depthFrame32[i32 + 3] = 255;
  22.  
  23.         switch (user)
  24.         {
  25.             case 0: // no one
  26.                 depthFrame32[i32] = (byte)(intensity / 2);
  27.                 depthFrame32[i32 + 1] = (byte)(intensity / 2);
  28.                 depthFrame32[i32 + 2] = (byte)(intensity / 2);
  29.                 break;
  30.             case 1:
  31.                 depthFrame32[i32] = intensity;
  32.                 break;
  33.             case 2:
  34.                 depthFrame32[i32 + 1] = intensity;
  35.                 break;
  36.             case 3:
  37.                 depthFrame32[i32 + 2] = intensity;
  38.                 break;
  39.             case 4:
  40.                 depthFrame32[i32] = intensity;
  41.                 depthFrame32[i32 + 1] = intensity;
  42.                 break;
  43.             case 5:
  44.                 depthFrame32[i32] = intensity;
  45.                 depthFrame32[i32 + 2] = intensity;
  46.                 break;
  47.             case 6:
  48.                 depthFrame32[i32 + 1] = intensity;
  49.                 depthFrame32[i32 + 2] = intensity;
  50.                 break;
  51.             case 7:
  52.                 depthFrame32[i32] = intensity;
  53.                 depthFrame32[i32 + 1] = intensity;
  54.                 depthFrame32[i32 + 2] = intensity;
  55.                 break;
  56.         }
  57.     }
  58. }

Ce flux peut s’avérer extrêmement intéressant pour détecter des formes sur le flux vidéo du Kinect. Il est possible ainsi de suivre une main et de détecter les mouvements de doigts pour produire de nouveaux moyens d’interagir avec le PC.

Le suivi de squelettes

La grosse force du Kinect pour Windows SDK est sa capacité à retrouver le squelette de joints des humains présents devant le capteur. Et contrairement aux hacks qui ont fleuri sur le net (comme OpenNI), le Kinect pour Windows SDK intègre un système de reconnaissance extrêmement rapide et ne nécessitant aucun apprentissage à l’utilisation. C’est le résultat d’une longue formation de la “learning machine” présente dans le SDK. Microsoft Research a donné un nombre énorme de cas d’exemples au système de reconnaissance pour qu’il fasse son apprentissage en amont.

Ainsi, dès que vous passez devant le capteur (à la bonne distance bien sur), la librairie NUI va rechercher votre squelette et dès qu’elle l’aura trouver elle lèvera un évènement pour que le développeur le récupère.

Pour activer le support du suivi des squelettes, il faut activer le flux de profondeur et se brancher sur l’évènement adéquat :

  1. kinectRuntime.DepthFrameReady += kinectRuntime_DepthFrameReady;

Dans le gestionnaire de cet évènement, nous allons pouvoir parcourir l’ensemble des squelettes détectés par le système :

  1. void kinectRuntime_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
  2. {
  3.     SkeletonFrame skeletonFrame = e.SkeletonFrame;
  4.  
  5.     foreach (SkeletonData data in skeletonFrame.Skeletons)
  6.     {
  7.         if (data.TrackingState == SkeletonTrackingState.Tracked)
  8.         {
  9.  
  10.             foreach (Joint joint in data.Joints)
  11.             {
  12.                 switch (joint.ID)
  13.                 {
  14.                     case JointID.HandLeft:
  15.                         if (joint.Position.W > 0.6f) // Quality check
  16.                             leftHandGestureRecognizer.Add(joint.Position.ToVector3());
  17.                         break;
  18.                     case JointID.HandRight:
  19.                         if (joint.Position.W > 0.6f) // Quality check
  20.                             rightHandGestureRecognizer.Add(joint.Position.ToVector3());
  21.                         break;
  22.                 }
  23.             }
  24.             return;
  25.         }
  26.     }
  27. }

Plusieurs points sont à relever ici :

  • La librairie NUI ne peut suivre (c’est à dire donner les informations de joints) que 2 squelettes au maximum. C’est la propriété TrackingState == SkeletntrackingState.Tracked qui définit si un squelette est ‘tracké’ ou non. Les squelettes qui ne sont pas suivis ne donne que leur position.
  • Chaque joint possède une propriété Position qui est défini par un vecteur4 : (x, y, z, w). Les trois premiers attributs définissent la position dans l’espace du joint en rapport à la caméra de Kinect (l’espace du squelette). Le dernier attribut (w) donne le niveau de qualité (entre 0 et 1) de l’information. Cela permet de filtrer et de ne prendre que les données dont la librairie est quasi-sure.

image

  • Chaque squelette porte une propriété TrackingID qui reste la même sur chaque frame. Cela permet de repérer de manière unique les squelettes à chaque appel.
  • Chaque joint est identifié par un enum pour définir sa position de référence (Main, Tête, etc.)

Il est également possible de faire du pooling sur la récupération des squelettes via la méthode SkeletonEngine.GetNextFrame().

Finalement, la librairie NUI propose un algorithme de filtrage des données qui arrivent depuis le capteur. En effet, par défaut, les données du squelettes sont remontées sans lissage ni filtrage. Or, le capteur de profondeur du Kinect n’a pas une résolution suffisante pour garantir une précision constante sur la durée. De ce fait, les données arrivent et donnent l’impression de vibrer autour de leur position. Pour corriger ce problème, il est possible d’activer le système de lissage du squelette :

  1. kinectRuntime.SkeletonEngine.TransformSmooth = true;
  2. var parameters = new TransformSmoothParameters
  3. {
  4.     Smoothing = 1.0f,
  5.     Correction = 0.1f,
  6.     Prediction = 0.1f,
  7.     JitterRadius = 0.05f,
  8.     MaxDeviationRadius = 0.05f
  9. };
  10. kinectRuntime.SkeletonEngine.SmoothParameters = parameters;

Comme nous pouvons le voir, il est possible de lisser les données mais également de les corriger. En fonction des usages, il faudra manipuler ces valeurs pour proposer la meilleure expérience possible.

Une fois que le code de récupération du squelette est en place, c’est à votre imagination de jouer pour proposer les applications du futur. Il est ainsi possible de chercher des gestures pour piloter les applications (le célèbre contrôle Jedi du Powerpoint) ou bien encore s’amuser à faire de la réalité augmentée pour rajouter des informations sur la vidéo issue de Kinect…

Kinect c’est aussi du son

Kinect est livré avec un groupe de quatre microphones (Microphone array) qui permettent de capturer du son avec une très bonne qualité. en effet, directement sur le capteur, un processeur de traitement du signal (DSP) permet de supprimer le bruit de fond et d’annuler les effets d’écho.

De plus, toujours grâce à son groupe de microphones, Kinect peut fournir la direction de la source sonore qu’il enregistre. On appelle cela du beamforming. Il devient alors par exemple possible de savoir qui parle dans une réunion (ou au moins de quelle direction vient la voix).

Le Kinect pour Windows SDK sait également servir de source aux API Microsoft.Speech et ainsi il devient possible de faire de la reconnaissance vocale de ce que capture le SDK.

Pour tout cela, il suffit d’instancier un objet de classe KinectAudioSource :

  1. var source = new KinectAudioSource {SystemMode = SystemMode.OptibeamArrayOnly};

Cette instanciation peut se faire en mode groupe de microphone ou microphone unique avec ou sans annulation de l’écho (Audio Echo Cancellation : AEC).

Pour faire une capture, nous allons juste demander à notre source de nous fournir le flux audio :

  1. byte[] buffer = new byte[16000];
  2. var audioStream = source.Start();
  3. audioStream.Read(buffer, 0, buffer.Length);

Le flux audio arrive en 16khz et en mono 16bits.

Pour utiliser les services de Beamforming, il faut mettre les microphones en mode détection des faisceaux sonores (beams) :

  1. source.MicArrayMode = MicArrayMode.MicArrayAdaptiveBeam;

Il existe plusieurs modes pour détecter le faisceau sonore. Ici, nous demandons que le système sélectionne tout seul le faisceau sonore adéquat. Il serait aussi possible de faire la sélection manuellement, ou de ne prendre que le faisceau central ou encore de faire la moyenne de tous les faisceaux.

A partir du moment ou un mécanisme de sélection du faisceau sonore est en place, le système peut lever un évènement lorsque ce dernier se déplace :

  1. source.BeamChanged += source_BeamChanged;
  1. static void source_BeamChanged(object sender, BeamChangedEventArgs e)
  2. {
  3.     Console.WriteLine("Angle : {0} radians", e.Angle);
  4. }

L’angle ici retourné est exprimé en radians et est relatif au centre de votre Kinect. Si vous êtes en face de votre Kinect, un angle de 0 indique que le son vient d’en face du capteur, un angle < 0 indique que le son vient de gauche et un angle > 0 indique que le son vient de droite.

Il est également possible à tout moment d’interroger source.SoundSourcePosition pour obtenir l’information d’angle :

  1. if (source.SoundSourcePositionConfidence > 0.9)
  2.     Console.Write("Position (radians): {0}", source.SoundSourcePosition);


Comme pour les positions des joints des squelettes, nous pouvons obtenir un niveau de qualité de l’information pour décider si nous souhaitons utiliser la donnée.

En ce qui concerne les filtres (anti-écho et réduction du bruit) la classe KinectAudioSource permet aussi de contrôler les algorithmes pour obtenir les résultats attendus.

Conclusion

Nous avons donc pu voir que le Kinect pour Windows SDK fournit de très nombreux outils pour nous amuser avec de nouvelles formes d’interaction homme/machine. Il manque bien sur des outils de plus haut niveau (comme une librairie de gestures) mais il y a fort à parier que les prochaines semaines verront apparaitre de nombreux projets en provenance de la communauté et de Microsoft (voir d’ores et déjà le Coding4Fun Kinect Toolkit sur Codeplex).

Pour aller plus loin

http://channel9.msdn.com/coding4fun/kinect/Of-course-our-first-Kinect-for-Windows-SDK-Project-has-to-involve-a-Light-Saber

Kinect%20Sabre%20Screenshot_thumb[1]

Leave a Comment
  • Please add 2 and 4 and type the answer here:
  • Post
  • Un article très intéressant pour se lancer dans le NUI avec Kinect : merci David ;-)

  • Il ya til des possibilités e communiqué en osc par la suite.

    (afin de beneficier de la calibration rapide et de pouvoir recuperer les informations du skeleton pour les interpretés via d'autres logiciels (pure data, etc...)

Page 1 of 1 (2 items)