自動セグメント

Last Change:20-Apr-2016.
Author:qh73xe
Reference:https://github.com/julius-speech/segmentation-kit

音声認識機として julius を使う場合、 一般には、”何を言っているのか” を単語で認識したい、 或いは、”ある単語を言っている時間” を取得したい場合の二つに分かれるかと思います。

一方、あまり使用されることはないかもしれないですが、 私個人的にはよく使う目的として、 “既に収録が済んだ音声データがあるとして、これが何を言っているのかも分かっている場合に具体的に開始何秒までが何の音であるのかを音素レベルで欲しい” という場合もあります。

  • こういう作業をコーパス屋は音素アライメントとか言ったりします。

Julius では上記のような目的用に 調整された環境を用意しているので、とても助かります。

このページでは上記の目的を達成する方法について説明します。

Speech Segmentation Toolkit using Julius

上記目的を達成するためのセットとして “Speech Segmentation Toolkit using Julius” が存在します。 幸いなことにこれも github 管理しているので、 まずはこれを clone します。

$ git clone https://github.com/julius-speech/segmentation-kit.git

このセットの中には segment_julius.pl というファイルがあると思います。 これがアライメントを実行するためのスクリプトになります。

スクリプトの実行

スクリプトは perl(!) で記述されていますので以下のように実行します。

$ perl segment_julius.pl

win 環境では上手く実行されるかと思います(未確認) 一方 Linux 環境ではパス周りのエラーが起きるはずです. これは上記スクリプトに Linux 用の設定が記述されていないことが原因です。 必要に応じて以下のように書き換えます。

segment_julius.pl
if ($^O =~ /MSWin/){
    $juliusbin=".\\bin\\julius-4.3.1.exe";
} else {
    # $juliusbin="./bin/julius";
    $juliusbin="path/to/julius";
}

或いは、./bin/ に ビルドした Julius 本体を置いても構いません。

さて、segment_julius.pl が何をやるのかを説明します。 このスクリプトは ./wav 以下にある wavファイル 及び テキストファイル を読み込んで アライメントを実行します。 wav ファイルは当然解析の対象になる音声ファイルですし、 テキストファイルは、その音声が何をいっているのか(発話内容)の書き起しファイルです。 両ファイルは拡張子以外同名である必要があります。

元々のレポジトリには、 sample.wav と sample.txt が入っています。 そして、解析をした結果は sample.lab という名前で保存されます。

解析結果は実体としては単なるテキストファイルです。 このファイルの記法は wavesurfer というアプリケーションのものに準拠しているので、 必要に応じて適当にテキスト変換を行う必要があります。

解析準備

台本に関して

ここで自身のファイルを記述することを考えた場合、 取り敢えず覚えておく必要があるのは txt ファイルの記述方法です。 詳しくは公式ページを確認するのがよいと思いますが基本的に発話された内容を全て平仮名で記述すればよいです。

数点注意点を記述します。 まず、ここで記述する平仮名は音に対する表記であるべきです。 sample.txt を例とするなら、”今日はいい天気です” であるなら “きょうわいいてんきです” と書く必要があります。 “は” ではないので注意してください。

またこのツールキットでは無音の自動挿入機能を有していません。 例えば、”今日は、いい天気です” のように “今日は” と “いい天気です” の間に 無音の部分がある場合、解析精度が落る(これは正しくない表現です。正確には仮定の方が間違えているのですから)可能性があります。

無音を明示的に記述するには ” sp ” のように記述します. “今日は...” の例でしたら “きょーわ sp いいてんきです” と記述します。

なお、このテキストファイルは “UTF-8” で記述されていることを前提にしているので注意してください。

音声ファイルに関して

音声ファイルに関しても幾つか注意点が存在します。 まず、 WAV形式 である必要があります。 拡張子は大文字でも小文字でもよいです。

また、16kHz, 16bit, PCM(無圧縮)形式 である必要があります。 必要に応じてダウンサンプリングするなり、なんなりを行ってください。

  • 個人的にはよく忘れる嵌り所なので
  • 私の場合、よく sox コマンドを使用します

出力を TextGrid に変換する

完全に個人的趣味の問題ですが、 私の場合、上記アライメントを praat というツールで修正することが多いです。

このツールでは TextGrid という拡張子のファイルで 書き起し結果を管理します。

そのため、 julius の出力をこの形式に変換するスクリプトを用意しています。 かなり、適当なものですが、一応公開しておきます。

lab2textgrid.praat
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
w_list = Create Strings as file list: "wavList", "./wav/*.wav"
l_list = Create Strings as file list: "labList", "./wav/*.lab"

number_files = Get number of strings
for i from 1 to number_files
    selectObject (w_list)
    current_token$ = Get string... 'i'
    wav = Read from file: "./wav/" + current_token$
    tg = To TextGrid: "SEG", ""
    selectObject (l_list)
    current_token$ = Get string... 'i'
    lab = Read Table from whitespace-separated file: "./wav/" + current_token$
    selectObject (lab)
    lab_num = Get number of rows
    for ii from 1 to lab_num
        selectObject (lab)
        strTime = Get value: ii, "strTime"
        endTime = Get value: ii, "endTime"
        unit$ = Get value: ii, "unit"
        selectObject (tg)
        Set interval text: 1, ii, unit$
        Insert boundary: 1, endTime
    endfor
    Save as text file: "./wav/" + current_token$ + ".TextGrid"
    selectObject (lab, wav)
    Remove
endfor
selectObject (w_list, l_list)
Remove

このスクリプトは、 .wav ディレクトリに含まれる全ての wav ファイルと lab ファイルを参照し、 TextGrid ファイルに変更します。

警告

上記スクリプトの実行方法に関して

このスクリプトは praat script (praat 独自の言語)で記述されています。

ここではとりあえずの実行方法を書いてきます。

$ praat --run lab2textgrid.praat

スクリプトに関する詳細は公式ドキュメントを参照してください。

上記スクリプトは、julius のヘッダが以下のようになっていることを前提にしています。

*.lab
1
2
3
strTime endTime unit
0.0000000 0.8925000 silB
...

これは segment_julius.pl のデフォルトの出力ではないため 少しファイルを書き換える必要があります。 具体的には ‘parse log and output result’ というコメント付近にある ‘open(Log, “$f.log”)...’ という記述の下に 以下の記述を行います。

segment_julius.pl
1
2
3
4
5
6
7
 #### parse log and output result
 open(RESULT, "> $f.lab") || die "Error: cannot open result file \"$f.lab\"\n";
 open(LOG, "$f.log") || die "Error: cannot open $f.log file\n";
 printf(RESULT "%s %s %s\n", 'strTime', 'endTime', 'unit');
 $sw = 0;
 while(<LOG>) {
 ...