Kinect SDK 1.0 – 1 – Introduction à l’API

 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

 

Cela fait maintenant plus de deux mois que le SDK 1.0 de Kinect for Windows a été lâché dans la nature! En attendant la sortie de la version 1.5 (annoncée pour fin mai), je vous propose de faire un petit tour de la version actuelle, histoire de prendre en main la Kinect !

Pour commencer, assurez-vous de remplir les conditions suivantes:

  • Avoir quelques connaissances en C# (ou VB, ça marche aussi, mais les exemples de code que je donnerai seront en C# )
  • Avoir une installation de Visual Studio 2010 (si vous ne l’avez pas, la version Visual Studio 2010 Express gratuite est suffisante)
  • et enfin, the last but not least: avoir une Kinect!

Rapide tour du matériel

Sans trop rentrer dans les détails, la Kinect est pourvue d’une caméra, et d’un couple émetteur/récepteur d’infrarouges qui vous permettra d’obtenir une vision en profondeur de l’environnement.

La Kinect est également équipée de 4 micros, et est posée sur un socle motorisé. Il est possible d’ajuster sa position en la faisant bouger de haut en bas.

Télécharger et installer le SDK

Pour aller plus loin, j’vous invite à cliquer sur l’image suivante pour télécharger la dernière version du SDK:

Télécharger le Kinect SDK 1.0

Si tout se déroule bien, vous devriez trouver les programmes suivants dans Panneaux de configuration >> Programmes et fonctionnalités:

Installation completed

Notez que la reconnaissance vocale n’est disponible qu’en anglais dans le SDK 1.0, mais le français devrait arriver fin mai! 😉

Microsoft a eu la très bonne idée de proposer un ensemble de samples lors de l’installation. Ils vont nous être utiles pour la suite! Si vous les avez manqués, vous pouvez les retrouver dans le menu démarrer:

Une fois lancé, vous pouvez déjà installer la solution KinectExplorer, qui vous donne accès à des contrôles réutilisables pour rapidement prendre en main votre Kinect, et aussi pourquoi pas le jeu ShapeGame, histoire de vous détendre!

Un peu de théorie

L’API du Kinect SDK 1.0 ne contient pas énormément de classes et est plutôt intuitive. Vous trouverez de la doc dans le dossier d’installation: ~ProgramsMicrosoft SDKsKinectv1.0DocumentationKinectSDK.chm ou sur MSDN : Kinect for Windows SDK et Microsoft.Kinect namespace.

La classe centrale d’un projet utilisant la Kinect, est évidemment la classe KinectSensor. Une instance de KinectSensor représente une Kinect (connectée ou non) à votre PC. Cette classe va donc simplement vous permettre de contrôler votre Kinect et de récupérer les informations que vous lui demanderez.

Il y a ensuite principalement 3 classes, héritant toutes de ImageFrame: ColorImageFrame, DepthImageFrame et SkeletonFrame. Chacun de ces objets représentent ce qu’a vu la Kinect à un temps T,  sous différents aspects (et les noms des classes vous laissent supposer lesquels).

Voyons d’abord comment détecter si une Kinect est branchée à votre PC (depuis le SDK 1.0, il est possible de gérer jusqu’à 4 Kinect simultanément) !

Créez un nouveau projet WPF, ajoutez-y une référence à la librairie et éditez la classe MainWindow:

MainWindow.xaml.cs
        void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            // Walk through KinectSensors to find the first one with a Connected status
            var firstKinect = (from k in KinectSensor.KinectSensors
                               where k.Status == KinectStatus.Connected
                               select k).FirstOrDefault();

            if (firstKinect != null)
                SetNewKinect(firstKinect);

            KinectSensor.KinectSensors.StatusChanged += KinectSensors_StatusChanged;
        }

Avec ce code on va chercher une Kinect dès que la page sera chargée. Pour ça, on utilise la propriété statique KinectSensors de la classe KinectSensor. Cette propriété est de type KinectSensorCollection (qui hérite en fait de ReadOnlyCollection<KinectSensor>), et permet de parcourir la liste des capteurs disponibles. On cherche ici un KinectSensor avec le Status KinectStatus.Connected, qui indique que la Kinect est branchée et prête à l’emploi!

Si on en trouve une, on va appeler la méthode SetNewKinect, et dans tous les cas on va s’abonner à l’event StatusChanged de la collection de Kinect. Ca nous permettra par la suite de savoir qu’une nouvelle Kinect vient d’être branchée, ou débranchée !

        private void SetNewKinect(KinectSensor newKinect)
        {
            if (kinect != newKinect)
            {
                if (kinect != null)
                    StopKinect(Kinect);

                if (newKinect != null)
                    OpenKinect(newKinect);
            }

            Kinect = newKinect;
        }

Dans la méthode SetNewKinect, on se charge d’activer et de stopper les KinectSensor et d’attribuer le capteur courant à la propriété Kinect. Pour arrêter la Kinect, la méthode Stop est suffisante pour libérer toutes les ressources. Si vous appelez la méthode Dispose, il faudra débrancher et rebrancher la Kinect pour pouvoir à nouveau l’utiliser!

        private void OpenKinect(KinectSensor newKinect)
        {
            // TODO: Enable needed streams with desired formats and parameters
            newKinect.Start();
        }

        private void StopKinect(KinectSensor oldKinect)
        {
            oldKinect.Stop();
        }

Et pour finir l’event handler qui va comparer la Kinect qui a changé de statut avec celle actuellement utilisée. Si la nouvelle Kinect a le statut « Connected« , on remplacera l’existante. Et dans le cas où c’est la Kinect existant qui a changé de statut et qui n’est donc plus connectée, on appellera la méthode SetNewKinect avec la valeur null. De cette manière, on s’assure que la propriété Kinect contient à tout moment soit une Kinect connectée, soit null.

        void KinectSensors_StatusChanged(object sender, StatusChangedEventArgs e)
        {
            if (e.Sensor != null && e.Status == KinectStatus.Connected)
            {
                SetNewKinect(e.Sensor);
            }
            else if (e.Sensor == Kinect)
            {
                // The current Kinect isn't connected anymore
                SetNewKinect(null);
            }
        }

C’est tout ce qu’il vous faut pour commencer à utiliser votre Kinect!

Comprendre le KinectStatus

Le tableau suivant reprend l’ensemble des statuts que peuvent avoir un objet de type KinectSensor, et la signification de chacun. Dans une application Kinect, il est important de donner un feedback à l’utilisateur concernant le statut de sa Kinect, ou bien même pour indiquer que votre application en requiert une!

Statuts possibles pour la Kinect

Maintenant que vous avez compris comment ça fonctionne, n’hésitez pas à utiliser le contrôle KinectSensorChooser du projet WpfViewers disponible dans le SDK 1.0. A la fin de cet article, vous verrez comment vous en servir!

Si vous êtes un développeur et que vous avez installé le Kinect SDK, vous pouvez utiliser une Kinect pour Xbox plutôt qu’une Kinect pour PC, mais il est recommandé d’utiliser une Kinect PC pour profiter pleinement du SDK. On reparlera par la suite des différences entre les deux capteurs!

Deux approches: event-driven vs. polling

Maintenant qu’on a récupéré notre Kinect, on aimerait pouvoir en faire quelque chose. Pour ça il faut utiliser les différents flux que le capteur met à notre disposition: couleurs, profondeurs, skeletons. L’API propose deux manières de récupérer ces flux, et vous choisrez l’une ou l’autre en fonction du type d’application que vous faites.

Si vous développez une application classique, par exemple en WPF ou Winforms, dite « event-driven« , vous laisserez la Kinect vous avertir à chaque fois que des nouvelles données seront disponibles. Si par contre vous être dans un jeu, qui tourne déjà dans une boucle, vous pourrez vous même réclamer des informations quand vous en avez besoin.

Dans les exemples qui suivront, je vais principalement utiliser la première approche.

Premier projet: contrôler le Tilt Motor

Pour suivre plus facilement, vous pouvez télécharger les sources: WpfKinect1

Pour être sûr que la Kinect répond correctement, on va tout de suite créer un premier projet. Le but ici est d’afficher le stream de la caméra vidéo, et les propriétés du moteur d’inclinaison qu’on va pouvoir modifier dynamiquement.

Créons un nouveau projet WPF, que l’on appellera WpfKinect, avec toujours une référence à Microsoft.Kinect.dll. Cette fois-ci, on va également inclure dans la solution le projet WpfViewers que vous avez dû installer en même temps que le SDK !

Il y a donc maintenant deux projets dans la solution. Dans le projet WpfKinect, ajoutez une référence au projet WpfViewers. Si vous ouvrez l’éditeur XAML, vous constaterez que la Toolbox contient des contrôles en plus. Ce sont ceux définis dans le projet WpfViewers et que vous pouvez réutiliser comme bon vous semble!

Microsoft.Samples.Kinect.WpfViewers

J’ai légèrement modifié le code XAML de la page MainWindow pour afficher 3 contrôles:

  • Un KinectColorViewer qui va afficher l’image de la caméra de la Kinect
  • Un TiltMotorController qu’on va créer juste après pour changer l’angle d’inclinaison de la Kinect.
  • Un KinectSensorChooser qui va grandement nous faciliter les choses pour trouver une Kinect connectée!
    <Grid>
        <Grid.ColumnDefinitions >
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>
        <viewers:KinectColorViewer Grid.Column="0" Width="640" Height="480"
                                   Kinect="{Binding ElementName=SensorChooser, Path=Kinect}"/>
        <local:TiltMotorController Grid.Column="1"
                                   HorizontalAlignment="Center" VerticalAlignment="Center"
                                   Kinect="{Binding ElementName=SensorChooser, Path=Kinect}"/>
        <viewers:KinectSensorChooser Grid.Column="1" x:Name="SensorChooser"
                                     KinectSensorChanged="SensorChooser_KinectSensorChanged"
                                     VerticalAlignment="Stretch" Background="White" Margin="10" />
    </Grid>

Au niveau de l’utilisation des contrôles du projet WpfViewers, c’est assez simple. Le KinectSensorChooser possède une propriété Kinect de type KinectSensor. Le KinectColorViewer a besoin pour fonctionner de recevoir également une Kinect. Donc ce qu’on va simplement faire, c’est utiliser le binding pour faire en sorte que le color viewer utilise la propriété Kinect du KinectSensorChooser.

Dans le code-behind on va juste implémenter l’event handler SensorChooser_KinectSensorChanged, pour démarrer le capteur et activer les flux dont on a besoin, c’est-à-dire dans ce cas-ci le ColorStream.

        private void SensorChooser_KinectSensorChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if (e.OldValue != null)
            {
                ((KinectSensor)e.OldValue).Stop();
            }

            if (e.NewValue != null)
            {
                var newSensor = (KinectSensor)e.NewValue;
                newSensor.ColorStream.Enable();
                newSensor.Start();
            }
        }

Le contrôle TiltMotorController va être construit de la même façon, et vous allez donc voir au passage comment créer une propriété utilisable directement dans le XAML. Ce n’est pas une simple propriété mais une DependencyProperty.

Pour enregistrer une nouvelle dependency property, on donne les 4 paramètres suivants:

  • Le nom de la propriété (qui sera utilisable ensuite dans le XAML)
  • Le type de la propriété
  • Le type parent de la propriété, donc celui auquel on veut ajouter cette propriété
  • Et enfin un objet UIPropertyMetadata qui prend comme paramètre la valeur par défaut de la propriété, et un callback(static) appelé lorsque la valeur de la propriété est modifiée.
public static readonly DependencyProperty KinectProperty =
            DependencyProperty.Register("Kinect",
            typeof(KinectSensor),
            typeof(TiltMotorController),
            new UIPropertyMetadata(null, new PropertyChangedCallback(KinectChanged)));

Une propriété va nous permettre d’accéder/modifier la valeur de la dependency property.

        public KinectSensor Kinect
        {
            get { return (KinectSensor)GetValue(KinectProperty); }
            set { SetValue(KinectProperty, value); }
        }
Et finalement, le callback, qui doit être une méthode statique, et une méthode qui va ajouter/retirer les event handlers sur les capteurs.
        private static void KinectChanged(DependencyObject d, DependencyPropertyChangedEventArgs args)
        {
            TiltMotorController tiltMotor = (TiltMotorController)d;
            tiltMotor.OnKinectChanged((KinectSensor)args.OldValue, (KinectSensor)args.NewValue);
        }

        private void OnKinectChanged(KinectSensor oldKinectSensor, KinectSensor newKinectSensor)
        {
            if (oldKinectSensor != null)
            {
                oldKinectSensor.AllFramesReady -= newKinectSensor_AllFramesReady;
            }

            if (newKinectSensor != null && newKinectSensor.Status == KinectStatus.Connected)
            {
                newKinectSensor.AllFramesReady += newKinectSensor_AllFramesReady;
            }
        }

Pour voir le XAML du contrôle TiltMotorController, j’vous invite à télécharger la solution. Mais globalement ce n’est rien de très compliqué: j’affiche un slider, dont la valeur reflète la propriété ElevationAngle (degrés) de la Kinect. Deux boutons, Up et Down, vont respectivement incrémenter et décrémenter la valeur de l’angle de 5°. Si une exception est lancée lorsqu’on tente de modifier la valeur de l’angle, elle est affichée dans un TextBlock.

                if (Kinect != null)
                {
                    try
                    {
                        Kinect.ElevationAngle = value;
                    }
                    catch (Exception e)
                    {
                        AngleException = e.Message;
                        OnNotifyProperty("AngleException");
                    }
                }

Pourquoi catcher une éventuelle exception? En fait, il n’est pas conseillé d’utiliser le moteur de la Kinect, tout simplement parce qu’il n’est pas conçu pour effectuer des mouvements longs ou répétés. Pour limiter le risque de mauvaise utilisation, l’API impose quelques limitations:

  • L’angle est modifiable tout au plus une fois par seconde
  • Après 15 appels consécutifs, un temps d’attente de 20 secondes est obligatoire
Dans le cas du non-respect de l’une de ces conditions, une exception sera lancée.

Si vous exécutez le projet, et que vous prenez votre Kinect en main, vous constaterez que la valeur de l’angle varie! C’est parce que pour la Kinect, l’angle de 0 degré correspond au plan horizontal, et pas à une position fixe dans son référentiel à elle.

Fin de cette première partie!

Nous ne sommes pas encore allés très loin, mais vous savez maintenant que votre Kinect est bien fonctionnelle, ou bien ça vous a laissé le temps de vous en procurer une! Dans les prochains articles, on verra comment utiliser les différents formats du ColorStream, tracker le squelette et la position des utilisateurs, utiliser le capteur de profondeur et finalement comment ajouter de la reconnaissance vocale dans votre application! 😉

Article original: Kinect SDK 1.0 – 1 – Introduction à l’API
Version anglaise: http://www.renauddumont.be/en/2012/kinect-sdk-1-0-1-introduction-a-lapi

One comment

/

[…] […]

Comments are closed.