ホール*オブ*アス

断続 断裂

WebSpeechAPI を使って動画からの文字起こし。

 

自作の音声認識文字起こしサービス作るぞー!

最近の音声認識エンジンは凄くて、Googleドキュメントでの(半)自動文字起こしGoogle Cloud Speech APIを使っての音声からの文字起こしなど、ちょちょいとコードを書いたりなんだりすれば文字起こしなんかすぐできちゃうような状況です。

 

ただ今回は、WebSpeechAPIという無料で使えてjavascriptで様々いじくれるAPIを使用して動画からの文字起こしをしようと考えました。理由は、

Googleドキュメントを使用するやり方は、30秒に一回音声認識が自動で止まるので手動で再開させなければならない。

Google Cloud Speech APIのほうは月あたり60分以上使用すると有料になる。

 というデメリットがあったから。加えてリアルタイムに文字起こしした文章を編集していきたいというneedがあり、後々作ることになるプロダクトにとってそのあたりのコントロールを得ておくことが重要だったからです。

 

結論を言うと、「連続的な音声認識は一応できているものの、動画からの文字起こしとして満足に使えるレベルではない」。ただ、マイクからの入力であれば、不自由なく使えはします。

というわけで想定していたものを基準に考えるとイマイチって感じなんですが、できてないとも言えない。もうちょい工夫すれば(っていうかAPI自体を変えるとか)やりたいことはやれるかもっていう感触です。BingとかIBMのWatsonとかも音声認識APIあるらしいけど、普通にGoogleAPIの精度が一番いいとのことなので、ケチらないでGoogle使えばかなりいい線まではいけるはず。

 

ではまず、作ったもののコードとデモを置いておきましょう。

コードの解説は今回はしません。コメント見ればわかる人はわかるし、わからない人はちょっとした説明だけじゃわからないでしょう。(時間がないだけ)

 

サンプルコード

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Web Speech APIのデモ</title>
</head>
<body>
    <textarea id="result_text" cols="100" rows="10"></textarea>
    <br>
    認識経過:<textarea id="recognizing_text" cols="80" rows="2"></textarea>
    <br>
    ステータス:<span id="status">None</span>
    <br>
    <input type="button" id="speech_start" value="音声認識開始">
    <input type="button" id="speech_stop" value="音声認識一時停止">
    <hr>
    <!-- 筋トレ動画のほうが認識率は高い。スピーチ動画の方はぜんっぜん。雑音とか音質の違いか? -->
    <iframe width="560" height="315" src="https://www.youtube.com/embed/SNAVGsFpaJY" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>
    <iframe width="560" height="315" src="https://www.youtube.com/embed/DHtbmpvxJMc" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>
    
    <!-- scriptはbodyの直前で。inputの前にあると -->
    <script>
        var speechIsValid = true; // 認識停止フラグ
    
        function vr_function() {
            if(!speechIsValid) { return; } // 一時停止フラグが立っていたら抜ける
            var recognition = new webkitSpeechRecognition(); // WebSpeechAPIのインスタンス化
            recognition.lang = 'ja'; // 何語で認識するか。この設定でも英語も結構通る
            recognition.continuous = true; // マイク使用許可を一回だけにする
            recognition.interimResults = true; // 中間結果を返す

            // 認識開始
            change_text('status', 'Ready');
            recognition.start();
    
            // 各種イベントに対する処理
            recognition.onsoundstart = function() {
                change_text('status', '認識中');
            };
            recognition.onnomatch = function() {
                change_text('status', 'もう一度試してください');
            };
            recognition.onerror = function(event) {
                change_text('status', 'error:' + event.error);
                console.log('error:' + event.error);
                vr_function(); // abortedエラーで頻繁に止まるので再帰
            };
            recognition.onsoundend = function() { // これいらないと思う
                change_text('status', '停止中');
                vr_function();
            };
    
            // 認識結果の受け取り
            recognition.onresult = function(event) {
                // 再帰後もインスタンスが生きててonresultイベントが発生するので認識一時停止時もすぐ関数を抜ける処理を入れる
                if(!speechIsValid) { return; }
                // 音声認識の結果は(interimResults == true なら)リアルタイムに認識候補結果を返してくる
                var results = event.results;
                for (var i = event.resultIndex; i < results.length; i++) {
                    if (results[i].isFinal) // resultの最後の要素が最終的な認識結果
                    {
                        append_text('result_text', results[i][0].transcript);
                        change_text('recognizing_text', '');
                        vr_function(); // 最終結果が確定したあと再帰。新しいインスタンスでstart()しなければならないため。
                    }
                    else // 認識途中
                    {
                        change_text('recognizing_text', results[i][0].transcript);
                    }
                }
            }
        }

        /**
         * 表示部分
         */
        //ステータス表示を変える
        var change_text = function (field_id, str) {
            document.getElementById(field_id).innerHTML = str;
        }
        // 認識結果テキストのアペンド
        var append_text = function (field_id, str) {
            document.getElementById(field_id).innerHTML += str;
        }

        /**
         * 音声認識開始ボタンのクリックイベント制御
         */
        // 音声認識開始
        document.getElementById('speech_start').onclick = function() {
            speechIsValid = true;
            vr_function('speechstart');
        }
        // 一時停止
        document.getElementById('speech_stop').onclick = function() {
            speechIsValid = false;
            change_text('status', '音声認識停止中');
        }
    </script>
</body>
</html>

デモ

デモはこちら。

音声認識開始」ボタンを押すと音声認識が始まります。マイク入力を許可するか否か訊かれるので、許可してください。マイクで話してる間は基本的に聞き取りが途切れることはありません。精度もまあまあ、Googleドキュメントみたいに30秒で途切れることもないので、筆記の代替としては結構使えるかも。あるいは、ノートパソコンをどっかに置いておいてフガフガ、、みたいなことができたりもするのかな?(普通に音とれ)

 

 

Web Speech API、今の所あんまり使えないという結論

です。マイク入力からの動画音声の認識はかなり精度が悪く、聞き取り自体しないことも多いです。デモでいうと、筋トレのほうの動画は80%以上は認識するのに対し、スピーチの方は5%くらいしか認識しない。

 

でもステレオミキサーを使ってPC内部で音を音声入力に回してやるとスピーチ動画のほうもそこそこ認識してくれます。80%ー95%くらい。ステレオミキサーへの切り替え方は色んなとこに書いてあります。

私のPCにはデフォルトではステレオミキサー入ってなかったので安いサウンドボード?じゃなくてUSBオーディオインターフェースかな?を買って代用してみました。これ。(そういえば動画配信もやろうとか思ってたな。。これは動画配信にも使えるやつです)

B00EOCUNVQ
Sound Blaster X-Fi Go! Pro r2 Creative USBオーディオインターフェース SB-XFI-GPR2

 

というわけで次はGoogle Cloud Speech APIを使ってもっと精度がよくて「使える」文字起こしプログラムを書く予定です。ドットインストールみたいな動画のシークバーとの連動まで考えてるので、次に作るやつはそのあたりも実装していきます。

 

 

デモにherokuを初めて使ってみたけど、、

それと、今回デモ用のデプロイサーバとしてherokuを初めて使ったんですけど、デモ用としては使い勝手はいい、、、のかな?

正直な話、デメリットが多すぎる印象なのでVPSでも借りて普通にその中にデモを置いたほうがいいように思えました。

今回は、私がまだVPS借りれる状況じゃなかったのと、Web Speech APIがローカルでは動作せず、web上でしか動かせないとのことだったのでherokuを試しに使ってみたっていう事情でした。

blog.mah-lab.com

 

 herokuって何?っていう話だったりとか、基本的な使い方だったりとかはドットインストールで無料のチュートリアルが公開されてるので2倍速くらいでサクッと見とくといいかもしれません。

 

 

以上、WebSpeechAPIを使うにあたって参考にさせていただいたサイトです。

jellyware.jp

 

 あとがき

Googleさんは字幕とかもすげえし、車輪の再発明感をひしひしと感じる。

あとコードの解説もろくにしてないのにちょっとブログ書くのに時間かかりすぎてるので速さ上げてく