Программа, апорт!

в 7:06, , рубрики: .net, Kinect, Microsoft Kinect, speech recognition, Программирование, метки: , ,

В прошлой статье я затронул тему работы с Microsoft Kinect for Windows и продемонстрировал возможности сенсора на примере игры в кубики. Замечу, что слежение за фигурой (skeleton tracking) не единственная возможность сенсора, и сегодня мне бы хотелось рассказать о распознавании речи.

Для знакомства с Microsoft Speech Platform мы напишем простое приложение, в котором произвольный объект (например, танчик) будет перемещаться по плоскости. Я не стал добавлять голосовые команды в предыдущий пример по двум причинам. Во-первых, хронологически этот пример появился раньше. А, во-вторых, хотелось сконцентрироваться в каждом примере на отдельной функциональности (такой код проще изучать).

Определим, какие пакеты пригодятся нам:

  • Microsoft Speech Platform — SDK. Необходимый пакет. Это платформа и инструментарий для начала работы с распознаванием речи.
  • Microsoft Speech Platform — Runtime Languages. Опциональный пакет. По умолчанию, в SDK входит только библиотека для распознавания английской речи. Вы можете скачать дополнительные библиотеки для работы с другими языками.
  • Microsoft Kinect for Windows — SDK. Опциональный пакет.

Открою страшную тайну, наличие сенсора не является обязательным условием для возможности распознавания речи. Speech Platform развивается независимо от Kinect, но в тоже время в Kinect SDK вы обязательно найдете примеры с реализацией распознавания речи. Наш пример будет работать как с сенсором, так и с обычным микрофоном.

В первую очередь необходимо понимать, что же собственно программировать. Последовательность действий чрезвычайно проста:

  1. выбрать обработчик (engine) распознавания из доступных в системе для требуемого языка;
  2. создать словарь команд и передать его в полученный обработчик;
  3. установить для обработчика источник аудиосигнала. Это могут быть Kinect, микрофон, аудиофайл;
  4. дать команду обработчику для начала распознавания.

Теперь в Visual Studio создадим новый проект WPF Application. Я буду писать на C#.

Начнем с того, что попытаемся найти подключенный сенсор. Класс KinectSensor предоставляет такую возможность с помощью свойства KinectSensors:

KinectSensor kinect = KinectSensor.KinectSensors
    .Where(s => s.Status == KinectStatus.Connected)
    .FirstOrDefault();

Обработчик распознавания речи – класс SpeechRecognitionEngine, его статический метод InstalledRecognizers() помогает получить информацию обо всех установленных в системе обработчиках.

RecognizerInfo info = SpeechRecognitionEngine.InstalledRecognizers()
    .Where(ri => string.Equals(ri.Culture.Name, "en-US", StringComparison.InvariantCultureIgnoreCase))
    .FirstOrDefault();

Нетрудно догадаться, что таким образом мы получаем информацию об обработчике распознавания английской речи (RecognizerInfo), если такой обработчик есть. Метод InstalledRecognizers не возвращает экземпляров обработчиков, а лишь информацию о них. Поэтому следующим шагом будет создание экземпляра обработчика. Просто передаем в конструктор идентификатор обработчика:

var sre = new SpeechRecognitionEngine(info.Id);

Теперь подумаем вот о чем. Нам нужно управлять объектом на плоскости. Какие команды подойду для этого? Думаю, что 4 команд достаточно: UP (вверх), DOWN (вниз), LEFT (влево), RIGHT (вправо). И для разнообразия можно добавить пятую команду EXIT (выход). Замечу, что я писал код для распознавания команд на английском языке, но вы можете выбрать любой другой из 54х доступных. Создаем словарь команд и загружаем его в обработчик распознавания.

var commands = new Choices();
commands.Add("up");
commands.Add("down");
commands.Add("left");
commands.Add("right");
commands.Add("exit");

var gb = new GrammarBuilder(commands) { Culture = info.Culture };
sre.LoadGrammar(new Grammar(gb));

В объекте типа Choices создается список слов (команд) для распознавания. Следующим шагом создается объект грамматики, связанный с культурой команд, и далее грамматика загружается в обработчика распознавания.

Каждое сказанное вами слово, обработчик сравнивает с шаблонами слов в грамматике, чтобы определить, не произнесли ли вы какую-нибудь команду. Но помните, что каждая попытка распознавания сопровождается некоторой вероятностью ошибки, чуть дальше вы увидите это на примере.

Теперь можно определить обработчики для событий распознавания речи. Для нас важно обработать событие SpeechRecognized возникающее, когда обработчик распознавания находит в словаре соответствие произнесенной команде. В объекте SpeechRecognizedEventArgs нам доступно свойство Result в котором можно найти: распознанное слово, величину вероятности того, что слово распознано правильно и многое другое. Два других события SpeechHypothesized и SpeechRecognitionRejected представляют интерес скорее для отладки, нежели для реального использования. Первое событие возникает, когда обработчик распознавания делает предположение распознавания. Второе,- когда обработчик распознавания может определить слово лишь с малой долей вероятности.

private void Sre_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
{
    // если вероятность распознавания больше 70%
    if (e.Result.Confidence >= 0.7) 
    {
        Action handler = null;
        switch (e.Result.Text.ToUpperInvariant())
        {
            case "UP":
            case "DOWN":
            case "LEFT":
            case "RIGHT":
                handler = () => { /* some actions */ };
                break;
            case "EXIT":
                handler = () => { this.Close(); };
                break;
            default:
                break;
        }

        if (handler != null)
        {
            // обработчик вызывается асинхронно, поэтому действия связанные с обновлением UI необходимо делать через диспетчер
            Dispatcher.BeginInvoke(handler, DispatcherPriority.Normal);
        }
    }
}

Нам осталось только установить источник аудиосигнала и начать распознавание. Здесь хочется отметить особенность Kinect. Аудиопоток готов для работы примерно через 4 секунды после инициализации. Это стоит учитывать и, например, создавать таймер, чтобы запускать распознавание с 4-х секундной задержкой.

Помните, я в начале говорил, что наш код будет работать как с Kinect так и с обычным микрофоном? Для того, чтобы это реализовать достаточно правильно установить источник аудиосигнала.

if (kinect != null)
{
    var audioSource = kinect.AudioSource;
    audioSource.BeamAngleMode = BeamAngleMode.Adaptive;
    var kinectStream = audioSource.Start();

    // захват с микрофонов Kinect
    sre.SetInputToAudioStream(kinectStream, new SpeechAudioFormatInfo(EncodingFormat.Pcm, 16000, 16, 1, 32000, 2, null));
}
else
{
    // будем захватывать аудиосигнал с микрофона установленного по умолчанию
    sre.SetInputToDefaultAudioDevice();
}
// начинаем распознавание. Параметр говорит обработчику распознавания не останавливаться после первой распознанной команды.
sre.RecognizeAsync(RecognizeMode.Multiple);

Что касается UI, то здесь все просто. Рисуем объект любой формы (это может быть даже картинка), я нарисовал танк.

tank

И добавляем анимацию для перемещения. Конечно, чтобы не создавать комичных ситуаций, когда танк движется «боком», я добавил еще и анимацию для разворота в нужную сторону. Пример анимации для выполнения команды LEFT (налево):

<Storyboard x:Key="LEFT">
    <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)" Storyboard.TargetName="PART_Tank">
        <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="-90"/>
    </DoubleAnimationUsingKeyFrames>
    <DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" Storyboard.TargetName="PART_Tank" Duration="0:0:1" By="-30"/>
</Storyboard>

Распознавание в действии:

Файлы исходного кода и скомпилированную версию вы найдете в конце статьи.

Подводя итог скажу, что Microsoft Speech Platform действительно большой и интересный продукт, я коснулся лишь его малой части. Интересующимся я бы посоветовал посмотреть примеры работы с этой платформой в Kinect SDK, мне кажется это хорошая отправная точка.

В заключение хочется поблагодарить компанию VIAcode за предоставленный для экспериментов сенсор.

P.S. В следующей статье мы посмотрим сколько у Kinect глаз и ушей, чтобы следить за нами, и как мы можем использовать эти данные через Kinect SDK 1.0.

Сборка примера
Исходный код примера

Автор: Lardite

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js