3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

VOSKとargostranslateとpyttsx3でオフライン通訳させてみました

Posted at

はじめに

  • 最近のAIは学習済みでツールとして使えるようになったものがあります。
    例えば、音声認識ツール:VOSK、翻訳ツール:argostranslate
  • これらと読上げライブラリ:pyttsx3 をくっつけて、音声認識ー翻訳ー読み上げをしてみました。
  • これらはオフラインで動くツールで、その実力を試してみました。

工夫したこと

  1. ローカル化
     ネットワーク環境に縛られないツールを組み合わせました。
  2. 翻訳ループ防止
     そのまま繋げたら、PCが自分で読上げた音声を再認識して、再翻訳して、再読上げして、、、と無限ループしたので、読上げ中は認識を止めるようにしました。

内容

1. PC環境

 実施したPC環境は以下のとおりです。

CPU Celeron N4100
メモリ 8GB LPDDR4

2. 前準備

 Windowsで使えるようにするため、以下のツール / システムをインストールしました。
インストール時に参考にしたサイトも記載します。

  1. python(使用したのはVer.3.7 )
     実行時のベースシステムです。
    (参考)https://qiita.com/ssbb/items/b55ca899e0d5ce6ce963

  2. pip(使用したのはVer.21.2.4 )
     他のツールをダウンロードする際に使うツールです。
    (python3系ではバージョン3.4以降であれば、pythonのインストールと共にpipもインストールされます。)
    (参考)https://gammasoft.jp/python/python-library-install/

  3. VOSK(使用したのはVer.0.3.42 )
     音声認識ツールです。
    (参考)https://alphacephei.com/vosk/

  4. VOSK models(使用したのはvosk-model-small-ja-0.22 )
     日本の音声認識で使用するモデルです。
     展開したファイル群をVOSK_try/models/jaというフォルダに入れました。
    (参考)https://alphacephei.com/vosk/models

  5. sounddevice(使用したのはVer.0.4.4 )
     マイクを使うためのライブラリです。
    (参考)https://www.wizard-notes.com/entry/python/sounddevice

  6. argostranslate(使用したのはVer.1.7.0)
     翻訳ツールです。
    (参考)https://portal.kemu.site/blog/1258346492

  7. pyttsx3(使用したのはVer.2.90 )
     読上げライブラリです。
    (参考)https://pypi.org/project/pyttsx3/

 また、作成にあたり、以下のサイトを参照しました。

  1. VOSK:
    (参照)https://qiita.com/hatt_takumi/items/0aedc8f6768cd8efee5e
    (参照)https://github.com/alphacep/vosk-api/blob/master/python/example/test_simple.py
    (参照)https://github.com/alphacep/vosk-api/blob/master/python/example/test_microphone.py
  2. argostranslate:
    (参照)https://www.argosopentech.com/
  3. pyttsx3
    (参照)https://qiita.com/be_tiger/items/370ce30a81f4b420f0dd

3.pyファイルの作成

組んだコードは次のとおりです。

#--- VOSK 初期化 ---
import argparse
import os
import queue
import sounddevice as sd
import vosk
import sys

#---
#--- argotranslate 初期化 ---
import argostranslate.package, argostranslate.translate

from_code = "ja"
to_code = "en"

# Download and install Argos Translate package
available_packages = argostranslate.package.get_available_packages()
available_package = list(
    filter(
        lambda x: x.from_code == from_code and x.to_code == to_code, available_packages
    )
)[0]
download_path = available_package.download()
argostranslate.package.install_from_path(download_path)

# Translate
installed_languages = argostranslate.translate.get_installed_languages()
from_lang = list(filter(
	lambda x: x.code == from_code,
	installed_languages))[0]
to_lang = list(filter(
	lambda x: x.code == to_code,
	installed_languages))[0]
translation = from_lang.get_translation(to_lang)
translation.translate("")#dummy 'input over flow'を表示させないため

#---
#--- pyttsx3 初期化 ---
import pyttsx3
engine = pyttsx3.init()

#rate
rate = engine.getProperty('rate')   # getting details of current speaking rate
print (rate)                        #printing current voice rate
engine.setProperty('rate', 125)     # setting up new voice rate

#volume
volume = engine.getProperty('volume')   #getting to know current volume level (min=0 and max=1)
print (volume)                          #printing current volume level
engine.setProperty('volume',1.0)    # setting up volume level  between 0 and 1

#voice
voices = engine.getProperty('voices')       #getting details of current voice

#---

# rec : VOSKの音声認識エンジン
# translation : argostranslateの翻訳エンジン
# engine : pyttsx3の読上げエンジン
#--- メインルーチン ---
q = queue.Queue()

rec_flg = True

def int_or_str(text):
    """Helper function for argument parsing."""
    try:
        return int(text)
    except ValueError:
        return text

def callback(indata, frames, time, status):
    """This is called (from a separate thread) for each audio block."""
    if status:
        print(status, file=sys.stderr)
    if rec_flg:
        q.put(bytes(indata))

parser = argparse.ArgumentParser(add_help=False)
parser.add_argument(
    '-l', '--list-devices', action='store_true',
    help='show list of audio devices and exit')
args, remaining = parser.parse_known_args()
if args.list_devices:
    print(sd.query_devices())
    parser.exit(0)
parser = argparse.ArgumentParser(
    description=__doc__,
    formatter_class=argparse.RawDescriptionHelpFormatter,
    parents=[parser])
parser.add_argument(
    '-f', '--filename', type=str, metavar='FILENAME',
    help='audio file to store recording to')
parser.add_argument(
    '-d', '--device', type=int_or_str,
    help='input device (numeric ID or substring)')
parser.add_argument(
    '-r', '--samplerate', type=int, help='sampling rate')
args = parser.parse_args(remaining)

try:
    if args.samplerate is None:
        device_info = sd.query_devices(args.device, 'input')
        # soundfile expects an int, sounddevice provides a float:
        args.samplerate = int(device_info['default_samplerate'])

    model = vosk.Model("../VOSK_try/models/ja")

    if args.filename:
        dump_fn = open(args.filename, "wb")
    else:
        dump_fn = None

    with sd.RawInputStream(samplerate=args.samplerate, blocksize = 8000, device=args.device, dtype='int16',
                            channels=1, callback=callback):
            print('#' * 80)
            print('Press Ctrl+C to stop the recording')
            print('#' * 80)

            rec = vosk.KaldiRecognizer(model, args.samplerate)

# -- メインループ start --
            while True:
                data = q.get()
                if rec.AcceptWaveform(data):

#--- 音声認識の結果から文字だけ抽出
                    txt = rec.Result()
                    txt2 = txt.split("\"")[3]
                    txt2 = txt2.replace(' ','')
#---
                    if len(txt2) > 0:
                        rec_flg = False
#--- 認識結果の表示・読み上げ
                        print(txt2)
                        engine.setProperty('voice', voices[0].id)  #changing index, changes voices. o for japanese female
                        engine.say(txt2)
#---
                        engine.setProperty('voice', voices[1].id)  #changing index, changes voices. o for english female
#--- argostranslateでの翻訳と表示・読み上げ
                        translatedText = translation.translate(txt2)
                        print(translatedText)
                        engine.say(translatedText)
#---
                        engine.runAndWait() #engineが終わるまで待つ
                        engine.stop()
                        rec_flg = True

                if dump_fn is not None:
                    dump_fn.write(data)
# -- メインループ end --

except KeyboardInterrupt:
    print('\nDone')
    parser.exit(0)
except Exception as e:
    parser.exit(type(e).__name__ + ': ' + str(e))

4.説明

1. ローカル化

 3つのツールを組み合わせただけなので、特記なしです。

2. 翻訳ループ防止

rec_flgというBOOL変数を入れ、これがTrueのときだけindataを記録するように変更、

    if rec_flg:
        q.put(bytes(indata))

 読上げ前にrec_flgFalseに、読上げ後Trueに変更するようにしました。

                        rec_flg = False
                        
                        #省略(読上げ処理コード)
                        
                        rec_flg = True

5.結果

  • 同時通訳とはいきませんが、まあまあな速さで処理できました。
  • 音声認識は、はきはき話せば大体正しく伝わりました。数字はあまり得意ではなさそうです。
  • 翻訳は少々意訳されるように感じました。
    例えば「私は今日東京駅に行きます」が「I go to Tokyo Station」になりました。
  • 今回は日本語から英語への変換ですが、様々な言語が用意されてます。

参考

参考コード
https://github.com/alphacep/vosk-api/blob/master/python/example/test_microphone.py

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?