Kinect SDK 1.0 – 5 – Reconnaissance vocale

 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

 

Voici le dernier article de cette série consacrée au Kinect SDK 1.0. Dans les précédents articles on a vu comment afficher une vidéo avec le ColorImageStream, comment tracker les utilisateurs avec le SkeletonStream, comment faire une vidéo 3D avec le DepthImageStream.

Pour terminer et ajouter un petit plus à vos applications en termes de Natural User Interface, on va voir comment utiliser la reconnaissance vocale!

Pensez expérience utilisateur

Quand vous développez une application utilisant la Kinect, essayez de vous mettre à la place des utilisateurs. Quelle est l’utilisation que vous en faites? Est-ce que les utilisateurs devront prendre des postures spécifiques? Ou bien réaliser un certain mouvement pour déclencher une action? Est-ce que cette tâche sera répétitive?

Rappelez-vous que les gestures (mouvements) sont difficiles à détecter avec certitude dû à leur différentes interprétations par les utilisateurs. Rappelez-vous également que l’éventail de mouvement est limité! Vous pouvez facilement différencier un « salut » de la main droite, d’un de la main gauche. Une main en bas, ou en haut.

Demandez-vous aussi, comment gérer le fait de devoir déclencher plusieurs actions simultanément?

Si vous deviez arriver au point de faire des mouvements trop complexes, ou difficiles à exécuter, les utilisateurs risqueraient vite d’être fatigués, ou lassés de votre application. Et dès lors, ils en auront une mauvaise expérience.

Dans ce contexte, l’utilisation de la reconnaissance vocale semble être une bonne idée!

SDK 1.0 et reconnaissance vocale

En installant le SDK 1.0, vous avez installé par la même occasion le Microsoft Kinect Speech Recognition Language Pack (en-Us). Et en effet pour le moment ce pack n’est disponible qu’en anglais. Toutefois, le français est annoncé dans la release 1.5 au côté d’autres langues telles que l’Italien, le Japonais, et l’Espagnol.

Même si le français n’est pas encore là, rien n’empêche de déjà y jeter un oeil! Ainsi vous serez prêt pour la suite.

De plus il faut savoir que la reconnaissance vocale n’est pas quelque chose de lié à la Kinect. Vous pourriez le faire avec n’importe quel autre micro!

Hands-on

Comme toujours, je vous propose de télécharger les sources directement pour suivre plus facilement:

WpfKinect - 5 - Speech Recognition

Pour cet exemple, on va reprendre un projet dans lequel des sphères étaient dessinées pour représenter les joueurs: un cercle pour chaque main, et un plus grand pour la tête. On n’affiche que les deux joueurs trackés.

A ce programme de base, on va ajouter la possibilité de changer les formes affichées (des cercles ou des carrés), ainsi que la couleur des joueurs indépendamment l’un de l’autre.

Ainsi, le but c’est de pouvoir dire (en anglais bien sûr :) ) « Utiliser des carrés rouges pour le joueur 1 » et de voir l’image se mettre à jour en conséquence.

1/ Initialiser le Recognizer

Pour commencer il va falloir ajouter une référence à la librairie Microsoft.Speech.dll. A ne pas confondre avec System.Speech.dll, qui ne vous donnera pas accès au recognizer de la Kinect. Les deux librairies sont très proches l’une de l’autre et sont destinées à ne faire qu’une prochainement.

La doc du SDK propose une méthode helper pour retrouver le recognizer de la Kinect. On a besoin de cette méthode parce que vous avez déjà d’autres SpeechRecognitionEngine, mais nous on veut celui du SDK Kinect.

Cette méthode, elle fait quoi: elle crée une fonction qui prends en paramètre un RecognizerInfo et qui retourne un booléen. Le RecognizerInfo c’est un objet qui va décrier un outils de reconnaissance vocale installé sur votre machine. Cet objet a notamment un attribut de type Dictionary qui contient tout un tas d’informations décrivant le recognizer comme par exemple les cultures et langues supportées, la version, le nom, ou tout autre propriété spécifique à ce recognizer.

Cette fonction va donc retourner true si la propriété Kinect du dictionnary AdditionalInfo contient true, et si la culture est « en-US » (la seule disponible pour le moment).

Et on va ensuite utiliser une requête Linq dans laquelle on va faire appel à cette fonction. Ainsi, on va récupérer la liste de tous les recognizers installés, et on prendra le premier parmi ceux répondant aux critères de la fonction.

        private static RecognizerInfo GetKinectRecognizer()
        {
            Func<RecognizerInfo, bool> matchingFunc = r =>
            {
                string value;
                r.AdditionalInfo.TryGetValue("Kinect", out value);
                return "True".Equals(value, StringComparison.InvariantCultureIgnoreCase)
                    && "en-US".Equals(r.Culture.Name, StringComparison.InvariantCultureIgnoreCase);
            };
            return SpeechRecognitionEngine.InstalledRecognizers().Where(matchingFunc).FirstOrDefault();
        }

On va ajouter une méthode pour instancier le SpeechRecognizerEngine. Si on ne trouve pas de RecognizerInfo, on affiche un message d’erreur. Pareil si on arrive pas à instancier un SpeechRecognitionEngine à partir du RecognizerInfo récupéré.

        private void InitializeSpeechRecognition()
        {
            RecognizerInfo ri = GetKinectRecognizer();

            if (ri == null)
            {
                MessageBox.Show(
                    @"There was a problem initializing Speech Recognition.
Ensure you have the Microsoft Speech SDK installed.",
                    "Failed to load Speech SDK",
                    MessageBoxButton.OK,
                    MessageBoxImage.Error);
                return;
            }

            try
            {
                speechRecognizer = new SpeechRecognitionEngine(ri.Id);
            }
            catch
            {
                MessageBox.Show(
                    @"There was a problem initializing Speech Recognition.
Ensure you have the Microsoft Speech SDK installed and configured.",
                    "Failed to load Speech SDK",
                    MessageBoxButton.OK,
                    MessageBoxImage.Error);
            }
            if (speechRecognizer == null)
                return;

            // Ajouter la suite ici!
        }
2/ Préparer les mots-clés

Pour nous aider dans la suite, on va associer un ensemble de termes sous forme de chaîne de caractère à des valeurs:

        #region Phrase mapping

        private Dictionary<string, Shape> Shapes = new Dictionary<string, Shape>
        {
            { "Circle", Shape.Circle },
            { "Square", Shape.Square },
        };

        private Dictionary<string, SolidColorBrush> BgrColors = new Dictionary<string, SolidColorBrush>
        {
            { "Yellow", Brushes.Yellow },
            { "Blue", Brushes.Blue },
            { "Red", Brushes.Red },
        };

        private Dictionary<string, int> PlayerIndexes = new Dictionary<string, int>
        {
            { "One", 0 },
            { "Two", 1 },
        };

        #endregion

Notez que l’énumération Shape a été créée pour l’occasion. Ce n’est pas nécessaire, mais ça permet d’y voir plus clair et d’ajouter facilement de nouvelles possibilités.

    public enum Shape
    {
        Circle,
        Square
    }
3/ Créer la sémantique

A la suite du code dans la méthode InitializeSpeechRecognition, on va construire la phrase telle qu’elle devrait être déclarée par l’utilisateur. On va par exemple créer des objets Choices, contenant l’ensemble des valeurs que l’on pourrait s’attendre à recevoir à une position dans la phrase.

A la fin, on construit concrètement la phrase à partir de valeurs fixes, et de choix: ainsi, la phrase commencera toujours par « Use » et pourra ensuite être suivie de n’importe laquelle des couleurs, ensuite de n’importe quelle forme parmi les possibilités données évidemment, etc…

            // Create choices containing values of the lists
            var shapes = new Choices();
            foreach (string value in Shapes.Keys)
                shapes.Add(value);

            var colors = new Choices();
            foreach (string value in BgrColors.Keys)
                colors.Add(value);

            var playerIndexes = new Choices();
            foreach (string value in PlayerIndexes.Keys)
                playerIndexes.Add(value);

            // Describes how the phraze should look like
            var gb = new GrammarBuilder();
            //Specify the culture to match the recognizer in case we are running in a different culture.
            gb.Culture = ri.Culture;
            // It should start with "Use"
            gb.Append("Use");
            // And then we should say any of the colors value
            gb.Append(colors);
            // Then one of the two possible shapes
            gb.Append(shapes);
            // then again the words "for player"
            gb.Append("for player");
            // and finally the player that we want to update
            gb.Append(playerIndexes);

            // Create the actual Grammar instance, and then load it into the speech recognizer.
            var g = new Grammar(gb);

            speechRecognizer.LoadGrammar(g);

Au final, on peut construire des objets très complexes. L’ensemble de l’objet GrammarBuilder aurait pu être ajouté à un nouvelle objet Choices en face d’un second objet GrammarBuilder tout aussi complexe, ou d’un simple autre mot.

4/ Lancer la reconnaissance

Toujours à la suite du code précédent, on va s’abonner aux évènements de notre speech recognizer:

            speechRecognizer.SpeechRecognized += speechRecognizer_SpeechRecognized;
            speechRecognizer.SpeechHypothesized += speechRecognizer_SpeechHypothesized;
            speechRecognizer.SpeechRecognitionRejected += speechRecognizer_SpeechRecognitionRejected;

Et pour finir on va attribuer la source audio (celle de notre Kinect), démarrer cette source, et lancer effectivement la reconnaissance!

            var audioSource = this.Kinect.AudioSource;
            audioSource.BeamAngleMode = BeamAngleMode.Adaptive;
            var kinectStream = audioSource.Start();

            speechRecognizer.SetInputToAudioStream(
                    kinectStream, new SpeechAudioFormatInfo(EncodingFormat.Pcm, 16000, 16, 1, 32000, 2, null));
            speechRecognizer.RecognizeAsync(RecognizeMode.Multiple);

L’énumération RecognizeMode indique que la reconnaissance doit s’arrêter directement après un premier élément reconnu (Single), ou qu’elle continue jusqu’à ce qu’on l’arrête (Multiple).

5/ Traiter les résultats

Finalement, on va décortiquer ce qu’on pense avoir entendu. Ces deux premiers eventhandlers ne nous intéressent pas vraiment. Le premier, rejected, indique simplement qu’une phrase n’a pas été reconnue (dans le sens où elle ne match pas avec une phrase attendue) avec assez d’assurance. Le second, Hypothesized, indique qu’un mot ou groupe de mots a été reconnu et qu’il correspond à plusieurs phrases possibles.

        void speechRecognizer_SpeechRecognitionRejected(object sender, SpeechRecognitionRejectedEventArgs e)
        {
            Console.WriteLine("Rejected: " + e.Result.Text);
        }

        void speechRecognizer_SpeechHypothesized(object sender, SpeechHypothesizedEventArgs e)
        {
            Console.WriteLine("Hypothesized: " + e.Result.Text);
        }

L’event qui va nous intéresser est le SpeechRecognized. Il va nous donner un résultat, caractérisé par une propriété Confidence. Cette propriété indique combien ce résultat semble être la bon résultat comparés à d’autres. Les autres résultats possibles peuvent être consultés via la propriété Alternatives, qui est une collection de RecognizedPhrase.

La phrase reconnue en elle-même est stockée dans la propriété Text sous forme d’une chaîne de caractère ou bien dans la propriété Words, sous la forme d’une collection de RecognizedWordUnit. Chacun de ces mots possède également une propriété Confidence.

Dans l’exemple ci-dessous, on va analyser la phrase et modifier les settings d’un joueur en fonction de son contenu.

        void speechRecognizer_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
        {
            // Confidence indicates the likelihood that a phrase is recognized correctly
            // compared to alternatives. 0.8 confidence doesn't mean 80M chance of being correct.
            if (e.Result.Confidence < 0.50)
            {
                Console.WriteLine("Rejected: " + e.Result.Text + ", " + e.Result.Confidence);
                if (e.Result.Alternates.Count > 0)
                {
                    // Print the alternatives
                    Console.WriteLine("Alternates available: " + e.Result.Alternates.Count);
                    foreach (RecognizedPhrase alternate in e.Result.Alternates)
                    {
                        Console.WriteLine("Alternate: " + alternate.Text + ", " + alternate.Confidence);
                    }
                }
                return;
            }
            Console.WriteLine("Recognized: " + e.Result.Text + ", " + e.Result.Confidence);

            var index = PlayerIndexes[e.Result.Words[5].Text];

            var playerConfig = playerConfigs[index];

            if (playerConfig != null)
            {
                playerConfig.Brush = BgrColors[e.Result.Words[1].Text];

                playerConfig.Shape = Shapes[e.Result.Words[2].Text];
            }
        }

Article original: Kinect SDK 1.0 – 5 – Reconnaissance vocale
Version anglaise: http://www.renauddumont.be/en/2012/kinect-sdk-1-0-5-reconnaissance-vocale

One comment

/

[…] […]

Comments are closed.