J’avais introduit le Kinect Toolbox dans un article précédent : http://blogs.msdn.com/b/eternalcoding/archive/2011/07/04/gestures-amp-kinect.aspx.

image_thumb[2]

Or, il se trouve que la nouvelle version de cet outil (v1.1) est désormais disponible et apporte quelques fonctions sympathiques:

  • Support des postures à base de modèles (templates)
  • Système de commandes vocales
  • Paquet NuGet

Vous pouvez le télécharger ici : http://kinecttoolbox.codeplex.com ou en passant par un paquet NuGet: http://nuget.org/List/Packages/KinectToolbox

Détection de postures à base de modèles

En utilisant le même algorithme que le détecteur de gestures, vous allez dorénavant pouvoir utiliser une learning machine couplée à un système de reconnaissance de formes pour détecter des postures. Dans l’exemple qui vient avec la toolbox, je me suis amusé à détecter le fait que le corps forme la lettre T:

image_thumb[5]

Pour réaliser cela j’ai écris une nouvelle classe : TemplatedPostureDetector. Cette dernière utilise la classe LearningMachine :

public class TemplatedPostureDetector : PostureDetector
{
    const float Epsilon = 0.02f;
    const float MinimalScore = 0.95f;
    const float MinimalSize = 0.1f;
    readonly LearningMachine learningMachine;
    readonly string postureName;

    public LearningMachine LearningMachine
    {
        get { return learningMachine; }
    }

    public TemplatedPostureDetector(string postureName, Stream kbStream) : base(4)
    {
        this.postureName = postureName;
        learningMachine = new LearningMachine(kbStream);
    }

    public override void TrackPostures(ReplaySkeletonData skeleton)
    {
        if (LearningMachine.Match(skeleton.Joints.ToListOfVector2(), Epsilon, MinimalScore, MinimalSize))
            RaisePostureDetected(postureName);
    }

    public void AddTemplate(ReplaySkeletonData skeleton)
    {
        RecordedPath recordedPath = new RecordedPath(skeleton.Joints.Count);

        recordedPath.Points.AddRange(skeleton.Joints.ToListOfVector2());

        LearningMachine.AddPath(recordedPath);
    }

    public void SaveState(Stream kbStream)
    {
        LearningMachine.Persist(kbStream);
    }
}

Pour l’utiliser, vous devez juste l’instancier et lui donner quelques modèles de références (qui peuvent bien sur être chargés depuis un flux). Par la suite, elle se chargera de détecter les références pour chaque squelette qu’elle recevra :

Stream recordStream = File.Open(letterT_KBPath, FileMode.OpenOrCreate);
templatePostureDetector = new TemplatedPostureDetector("T", recordStream);
templatePostureDetector.PostureDetected += templatePostureDetector_PostureDetected;
templatePostureDetector.TrackPostures(skeleton);
void templatePostureDetector_PostureDetected(string posture)
{
    MessageBox.Show("Give me a......." + posture);
}

Voice Commander

Une des choses importantes à noter lorsque l’on développe avec Kinect, c’est le fait que l’on va passer notre temps à se lever et à s’assoir. Dans l’article précédent, j’introduisais le système d’enregistrement qui permettait de rejouer autant que l’on voulait une session Kinect.

Toutefois si l’on est un pauvre développeur esseulé, même le fait d’enregistrer une session est pénible car l’on ne peut être à la fois devant le capteur du Kinect et devant son clavier pour lancer/arrêter l’enregistrement.

C’est pourquoi j’ai rajouté la classe Voice Commander qui comme son nom l’indique permet de donner des ordres vocaux. Ainsi, il suffit de lui donner les ordres “record” et “pause” à reconnaitre et il devient possible d’enregistrer ses propres sessions en autonome (je recommande toutefois l’usage d’un ami/stagiaire/collègue Sourire).

Grâce à Kinect pour Windows SDK et à Microsoft Speech Platform SDK, le code est très simple:

public class VoiceCommander
{
    const string RecognizerId = "SR_MS_en-US_Kinect_10.0";
    Thread workingThread;
    readonly Choices choices;
    bool isRunning;

    public event Action<string> OrderDetected;

    public VoiceCommander(params string[] orders)
    {
        choices = new Choices();
        choices.Add(orders);
    }

    public void Start()
    {
        workingThread = new Thread(Record);
        workingThread.IsBackground = true;
        workingThread.SetApartmentState(ApartmentState.MTA);
        workingThread.Start();  
    }

    void Record()
    {
        using (KinectAudioSource source = new KinectAudioSource
        {
            FeatureMode = true,
            AutomaticGainControl = false,
            SystemMode = SystemMode.OptibeamArrayOnly
        })
        {
            RecognizerInfo recognizerInfo = SpeechRecognitionEngine.InstalledRecognizers().Where(r => r.Id == RecognizerId).FirstOrDefault();

            if (recognizerInfo == null)
                return;

            SpeechRecognitionEngine speechRecognitionEngine = new SpeechRecognitionEngine(recognizerInfo.Id);

            var gb = new GrammarBuilder {Culture = recognizerInfo.Culture};
            gb.Append(choices);

            var grammar = new Grammar(gb);

            speechRecognitionEngine.LoadGrammar(grammar);
            using (Stream sourceStream = source.Start())
            {
                speechRecognitionEngine.SetInputToAudioStream(sourceStream, new SpeechAudioFormatInfo(EncodingFormat.Pcm, 16000, 16, 1, 32000, 2, null));

                isRunning = true;
                while (isRunning)
                {
                    RecognitionResult result = speechRecognitionEngine.Recognize();

                    if (result != null && OrderDetected != null && result.Confidence > 0.7)
                        OrderDetected(result.Text);
                }
            }
        }
    }

    public void Stop()
    {
        isRunning = false;
    }
}

L’utilisation est alors un jeu d’enfant :

voiceCommander = new VoiceCommander("record", "stop");
voiceCommander.OrderDetected += voiceCommander_OrderDetected;

voiceCommander.Start();
void voiceCommander_OrderDetected(string order)
{
    Dispatcher.Invoke(new Action(() =>
    {
        if (audioControl.IsChecked == false)
            return;

        switch (order)
        {
            case "record":
                DirectRecord(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "kinectRecord" + Guid.NewGuid() + ".replay"));
                break;
            case "stop":
                StopRecord();
                break;
        }
    }));
}

Conclusion

Grâce à cette nouvelle version, vous avez une gamme complète d’outils pour vous faciliter les développements autour du Kinect pour Windows SDK.

Si vous avez des idées d’améliorations (et même si je ne promets rien^^), je vous invite à les poster sur l’Issue Tracker du site codeplex : http://kinecttoolbox.codeplex.com