Использование Google Cloud Speech API v2 в Asterisk для распознавания русской речи

Добрый вечер, коллеги. Недавно возникла необходимость добавить систему голосовых заявок в нашу ticket-систему. Но не всегда удобно каждый раз прослушивать голосовой файл, поэтому возникла идея добавить к этому систему автоматического распознавания голоса, к тому же в будущем она бы пригодилась в других проектах. В ходе этой работы были испробованы два варианта API наиболее популярных систем распознавания речи от google и yandex. В конечном итоге выбор пал на первый вариант. К сожалению, не нашел подробной информации об этом в интернете, поэтому решил поделиться полученным опытом. Если интересно, что из этого получилось добро пожаловать под кат.

Выбор API для распознавания речи


Я рассматривал только вариант api, коробочные решения были не нужны, поскольку требовали ресурсы, данные для распознания не критичны для бизнеса, да и использование их существенно сложнее и требует больше человеко-часов.

Первым был Yandex SpeechKit Cloud. Мне он сразу понравился простотой использования:

curl -X POST -H "Content-Type: audio/x-wav" --data-binary "@speech.wav" "https://asr.yandex.net/asr_xml?uuid=<идентификатор пользователя>&key=<API-ключ>&topic=queries"

Ценовая политика 400 рублей за 1000 запросов. Первый месяц бесплатно. Но после этого пошли только разочарования:

— На передачу большого предложения, приходил ответ из 2-3 слов
— Распознавались эти слова в странной последовательности
— Попытки изменения топика положительных результатов не принесли

Возможно это было связано со средним качеством записи, мы все тестировали через голосовые шлюзы и древние телефоны panasonic. Пока планирую его в дальнейшем использовать для построения IVR.

Следующим стал сервис от Google. Интернет пестрит статьями, в которых предлагается использовать API для разработчиков Chromium. Сейчас ключей для этого API уже так просто получить нельзя. Поэтому мы будем использовать коммерческую платформу.

Ценовая политика — 0-60 минут в месяц бесплатно. Далее 0,006 $ за 15 секунд речи. Каждый запрос округляется к цифре кратной 15. Первые два месяца бесплатно, для создания проекта нужна кредитная карта. Варианты использования API в базовой документации разнообразны. Мы будем использовать скрипт на Python:

Скрипт из документации
"""Google Cloud Speech API sample application using the REST API for batch
processing."""

import argparse
import base64
import json

from googleapiclient import discovery
import httplib2
from oauth2client.client import GoogleCredentials


DISCOVERY_URL = ('https://{api}.googleapis.com/$discovery/rest?'
                 'version={apiVersion}')


def get_speech_service():
    credentials = GoogleCredentials.get_application_default().create_scoped(
        ['https://www.googleapis.com/auth/cloud-platform'])
    http = httplib2.Http()
    credentials.authorize(http)

    return discovery.build(
        'speech', 'v1beta1', http=http, discoveryServiceUrl=DISCOVERY_URL)


def main(speech_file):
    """Transcribe the given audio file.

    Args:
        speech_file: the name of the audio file.
    """
    with open(speech_file, 'rb') as speech:
        speech_content = base64.b64encode(speech.read())

    service = get_speech_service()
    service_request = service.speech().syncrecognize(
        body={
            'config': {
                'encoding': 'LINEAR16',  # raw 16-bit signed LE samples
                'sampleRate': 16000,  # 16 khz
                'languageCode': 'en-US',  # a BCP-47 language tag
            },
            'audio': {
                'content': speech_content.decode('UTF-8')
                }
            })
    response = service_request.execute()
    print(json.dumps(response))

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument(
        'speech_file', help='Full path of audio file to be recognized')
    args = parser.parse_args()
    main(args.speech_file)

Подготовка к использованию Google Cloud Speech API


Нам необходимо будет зарегистрировать проект и создать ключ сервисного аккаунта для авторизации. Вот ссылка для получения триала, необходим гугл-аккаунт. После регистрации необходимо активировать API и создать ключ для авторизации. После необходимо скопировать ключ на сервер.

Перейдем к настройке самого сервера, нам необходимы будут:

— python
— python-pip
— python google api client

sudo apt-get install -y python python-pip
pip install  --upgrade google-api-python-client

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

export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service_account_file.json
export GCLOUD_PROJECT=your-project-id

Скачаем тестовый аудио файл и попытаемся запустить скрипт:

wget https://cloud.google.com/speech/docs/samples/audio.raw
 python voice.py audio.raw
{"results": [{"alternatives": [{"confidence": 0.98267895, "transcript": "how old is the Brooklyn Bridge"}]}]}

Отлично! Первый тест успешен. Теперь изменим в скрипте язык распознавания текста и попробуем распознать его:

nano voice.py
   service_request = service.speech().syncrecognize(
        body={
            'config': {
                'encoding': 'LINEAR16',  # raw 16-bit signed LE samples
                'sampleRate': 16000,  # 16 khz
                'languageCode': 'ru-RU',  # a BCP-47 language tag

Нам необходим .raw аудио файл. Используем для этого sox

apt-get install -y sox
sox test.wav -r 16000 -b 16 -c 1 test.raw
python voice.py test.raw
{"results": [{"alternatives": [{"confidence": 0.96161985, "transcript": "\u0417\u0434\u0440\u0430\u0432\u0441\u0442\u0432\u0443\u0439\u0442\u0435 \u0412\u0430\u0441 \u043f\u0440\u0438\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u044f"}]}]}

Гугл возвращает нам ответ в юникоде. Но мы хотим видеть нормальные буквы. Поменяем немного наш voice.py:

Вместо

print(json.dumps(response))

Мы будем использовать

s = simplejson.dumps({'var': response}, ensure_ascii=False)
    print s

Добавим import simplejson. Итоговый скрипт под катом:

Voice.py
"""Google Cloud Speech API sample application using the REST API for batch
processing."""

import argparse
import base64
import json
import simplejson

from googleapiclient import discovery
import httplib2
from oauth2client.client import GoogleCredentials


DISCOVERY_URL = ('https://{api}.googleapis.com/$discovery/rest?'
                 'version={apiVersion}')


def get_speech_service():
    credentials = GoogleCredentials.get_application_default().create_scoped(
        ['https://www.googleapis.com/auth/cloud-platform'])
    http = httplib2.Http()
    credentials.authorize(http)

    return discovery.build(
        'speech', 'v1beta1', http=http, discoveryServiceUrl=DISCOVERY_URL)


def main(speech_file):
    """Transcribe the given audio file.

    Args:
        speech_file: the name of the audio file.
    """
    with open(speech_file, 'rb') as speech:
        speech_content = base64.b64encode(speech.read())

    service = get_speech_service()
    service_request = service.speech().syncrecognize(
        body={
            'config': {
                'encoding': 'LINEAR16',  # raw 16-bit signed LE samples
                'sampleRate': 16000,  # 16 khz
                'languageCode': 'en-US',  # a BCP-47 language tag
            },
            'audio': {
                'content': speech_content.decode('UTF-8')
                }
            })
    response = service_request.execute()
     s = simplejson.dumps({'var': response}, ensure_ascii=False)
    print s

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument(
        'speech_file', help='Full path of audio file to be recognized')
    args = parser.parse_args()
    main(args.speech_file)

Но перед его запуском нужно будет экспортировать ещё одну переменную окружения export PYTHONIOENCODING=UTF-8. Без неё у меня возникли проблемы с stdout при вызове в скриптах.

export PYTHONIOENCODING=UTF-8
python voice.py test.raw
{"var": {"results": [{"alternatives": [{"confidence": 0.96161985, "transcript": "Здравствуйте Вас приветствует компания"}]}]}}

Отлично. Теперь мы можем вызывать этот скрипт в диалплане.

Пример Asterisk dialplan


Для вызова скрипта я буду использовать простенький диалплан:

exten => 1234,1,Answer
exten => 1234,n,wait(1)
exten => 1234,n,Playback(howtomaketicket)
exten => 1234,n,Playback(beep)
exten => 1234,n,Set(FILE=${CALLERID(num)}--${EXTEN}--${STRFTIME(${EPOCH},,%d-%m-%Y--%H-%M-%S)}.wav)
exten => 1234,n,MixMonitor(${FILE},,/opt/test/send.sh support@test.net "${CDR(src)}" "${CALLERID(name)}" "${FILE}")
exten => 1234,n,wait(28)
exten => 1234,n,Playback(beep)
exten => 1234,n,Playback(Thankyou!)
exten => 1234,n,Hangup()

Я использую для записи mixmonitor и после окончания запускаю скрипт. Можно использовать record и это, пожалуй, будет лучше. Пример send.sh для отправки — он предполагает, что у вас уже настроен mutt:

#!/bin/bash
#скрипт для отправки уведомлений

# экспортируем необходимые переменные окружения
# файл лицензии гугла
export GOOGLE_APPLICATION_CREDENTIALS=/opt/test/project.json
# название проекта
export GCLOUD_PROJECT=project-id
# кодировка для питона
export PYTHONIOENCODING=UTF-8
#список переменных на входе
EMAIL=$1
CALLERIDNUM=$2
CALLERIDNAME=$3
FILE=$4

# перекодируем звуковой файл в raw для того, чтобы отдать его гугл апи

sox /var/spool/asterisk/monitor/$FILE -r 16000 -b 16 -c 1 /var/spool/asterisk/monitor/$FILE.raw

# присваиваем переменной значение выполненного скрипта по конвертации звука в текст и обрезаем не нужное

TEXT=`python /opt/test/voice.py /var/spool/asterisk/monitor/$FILE.raw  | sed -e 's/.*transcript"://' -e 's/}]}]}}//'`

# отправляем письмо, включаем в письмо распознанный текст

echo "новое уведомление от номера: $CALLERIDNUM  $CALLERIDNAME
 $TEXT " | mutt -s "Это заголовок письма" -e 'set from=test@test.net realname="я присылаю оповещения"' -a "/var/spool/asterisk/monitor/$FILE" -- $EMAIL

Заключение


Таким образом мы решили поставленную задачу. Надеюсь кому-то пригодится мой опыт. Буду рад комментариям (пожалуй только ради этого и стоит читать Хабр!). В будущем планирую реализовать на основе этого IVR с элементами голосового управления.
Share post

Similar posts

Comments 10

    0
    Каков процент правильно распознанных фраз в реальных условиях?
      0
      К концу следующей недели смогу сказать, когда будет больше данных. Предварительно 80-90%.
        0
        У яндекса для длинных аудиофайлов надо нормально разбивать на чанки во время отправки, либо воспользоваться их утилитой на питоне, и всё замечательно распознается
        0
        Использую его для распознования фамилий, очень плохо распознает. Самый большой минус нельзя указать предметную область.
        +1
        А попробуйте вот это
        sox test.wav -r 16000 -b 16 -c 1 test.raw

        вместе со Speechkit
        curl -X POST -H «Content-Type: audio/x-pcm;bit=16;rate=16000» --data-binary "@test.raw" «https://asr.yandex.net/asr_xml?uuid=<идентификатор пользователя>&key=<API-ключ>&topic=queries»

        есть ли разница с первоначальным?

        еще есть вариант через потоковое апи/клиент, это более правильный способ для длинных фраз https://github.com/yandex/speechkitcloud/tree/master/python
          0
          А попробуйте вот это


          Безрезультатно.

          еще есть вариант через потоковое апи/клиент, это более правильный способ для длинных фраз


          Спасибо за ссылку, проглядел ее в документации! Обязательно попробую, когда будет время. Есть пара идей, как это можно использовать.
          0
          Очень интересно, продолжайте писать на эту тему!
            0
            Есть проект https://github.com/zaf/asterisk-speech-recog
            Вот только не знаю работает ли он с новым Google Cloud Speech API, со старым v2 (https://www.google.com/speech-api/v2/recognize) он прекрасно работает, но все упирается в ключ от старого API, их уже не выдают, благо я в свое время отхватил безлимитный и надеюсь пожизненный, так что пока работаю с ним.
              +1
              Распознавание сильно зависит от качества потока. Мы, для исправления ситуации, меняли кодек на шлюзах с 729 на 711 — разница в распознавании значительна.
                0
                Как заставить вашу систему распознавать сигнал, записанный с частотой дискретизации 48 кГц?

                Only users with full accounts can post comments. Log in, please.