文字起こし形式
Potato が読める文字起こし・字幕形式の一覧。Whisper、WhisperX、Deepgram、AssemblyAI、AWS Transcribe、SRT、WebVTT、TTML、YouTube 字幕、CTM、Praat TextGrid、ELAN EAF に加え、サイドカーファイルと正規化されたターンモデルについて。
Potato は 21 種類の文字起こし・字幕入力を読み、表示する前にすべてを同じターンモデルへ変換します。音声認識パイプラインや字幕ツールが何を出力していても、変換スクリプトを書かずにそのまま Potato を向けられます。 このページは、何が使えるか、各形式がどう判定されるか、それぞれが何をもたらすかの参照資料です。手順を追った説明はWhisper の文字起こしをアノテーションする方法とYouTube の字幕をアノテーションする方法にあります。
サイドカーの SRT ファイルが話者バブルとして表示され、各ターンに質問が付いている
Potato は文字起こしをしない
音声認識と話者分離は、Potato が何かを見る前に行われます。まず Whisper、WhisperX、pyannote、あるいはクラウド API を実行してください。Potato はその結果を取り込みます。このページに書かれていることでモデルが動くことはありません。
Note: Think-Aloud モードはたしかにローカルの Whisper モデルを動かしますが、目的が違います。作業中に声に出して考えるアノテーターを録音する機能です。これは収録の機能であって、文字起こしの取り込みではありません。
判定は拡張子ではなく中身で行う
Potato はファイル名を信用せず、中身を調べます。captions.txt という名前の WebVTT ファイルも WebVTT として解析されます。実務上の意味は、同じ文字起こしがデータファイルに埋め込まれていても、ディスク上のサイドカーファイルから読まれても、設定を変えずに機能するということです。
対応形式
音声認識の出力
| 形式 | 判定材料 | 話者 | 単語単位の時間 |
|---|---|---|---|
| Whisper JSON | segments 配列 | なし | あり(--word_timestamps 使用時) |
| WhisperX / 話者分離済み JSON | speaker を含む segments | あり | あり |
| whisper.cpp JSON | transcription 配列 | なし | なし |
| Whisper TSV | start/end/text ヘッダー | なし | なし |
| AWS Transcribe | results.items または results.audio_segments | あり | あり |
| Deepgram | results.channels または results.utterances | あり(diarize=true 時) | あり |
| AssemblyAI | text と words/utterances | あり(speaker_labels 時) | あり |
| Rev.ai | monologues 配列 | あり | あり |
| SPoRC | turn_text/turnText の行 | あり(推定) | なし |
字幕・キャプション
| 形式 | 判定材料 | 話者 |
|---|---|---|
SubRip(.srt) | キューの矢印、,mmm 区切り | Name: 接頭辞から |
WebVTT(.vtt) | WEBVTT ヘッダー | <v Name> タグまたは Name: 接頭辞から |
SubStation Alpha(.ass、.ssa) | [Script Info] / Dialogue: | Name フィールドから |
| TTML / DFXP | <tt> ルート要素 | speaker/agent 属性から |
| YouTube srv1/srv2/srv3 | <transcript><text> XML | Name: 接頭辞から |
| YouTube json3 | events 配列 | なし。自動生成字幕には話者情報がない |
SubRip と WebVTT で、実際に出会うものの大半はカバーできます。TTML は放送アーカイブや、業務用字幕ツールからの書き出しで見かけます。
アライメントと言語学的アノテーション
| 形式 | 判定材料 | 話者 |
|---|---|---|
| NIST CTM | 空白区切りの列、数値の開始時刻と長さ | チャンネルフィールドから |
| Praat TextGrid | File type = "ooTextFile" | 話者ごとに 1 ティア |
| ELAN EAF | ANNOTATION_DOCUMENT ルート | PARTICIPANT から、なければティア ID |
TextGrid は長形式・短形式のどちらも解析されます。ELAN ファイルでは、Potato が ALIGNABLE_ANNOTATION と REF_ANNOTATION を TIME_ORDER テーブルに対して解決し、ヘッダーからメディア参照を読み取ります。
TextGrid の各ティアが話者になり、その間の空区間は飛ばされる
その他
三つの汎用的な形が 21 の残りを埋めます。
{speaker, start, end, text}辞書の単純なリスト。{"audio": ..., "turns": [...]}オブジェクト。- 時間情報のまったくない平文の段落。一つのバブルとして表示されます。
非対応
これらのパーサーはありません。先に変換してください。
- SAMI(
.smi)、MicroDVD(.sub)、SubViewer(.sbv) - Transcriber(
.trs)、EXMARaLDA、CHAT/CHILDES(.cha) - Montreal Forced Aligner と Gentle のネイティブ出力。これらからは CTM か TextGrid で書き出してください。
- 変わったラッパーに包まれた Azure Speech、Google Speech-to-Text、Whisper API の
verbose_json - 単体の RTTM 話者分離ファイル
単語単位の信頼度は、元データにあれば解析されデータモデルに保持されますが、現時点でそれを表示する画面はありません。
正規化されたターンモデル
上記のすべての形式は、これになります。
{
"audio": "media/interview_01.mp3",
"turns": [
{
"turn_id": "t0",
"speaker": "host",
"start": 0.0,
"end": 6.5,
"text": "Welcome back.",
"words": [{"word": "Welcome", "start": 0.0, "end": 0.4, "confidence": 0.99}],
"confidence": 0.97
}
]
}words と confidence は元データに含まれていた場合にだけ現れます。話者分離されていないターンでは speaker が null になり、アノテーターが埋められるよう選択メニュー付きの Unassigned として表示されます。
YouTube の自動生成字幕は話者情報なしで届くので、アノテーターが選ぶまで各ターンは Unassigned のまま
turn_id はターン単位のアノテーションと話者割り当ての保存キーです。元データに明示的な turn_id または step_id 文字列があればそれを使い、なければ t{index} になります。同じファイルからは常に同じ ID が生成されるので、再読み込みしてもアノテーションは残ります。
Warning: 時間の単位はツールによって異なります。Whisper と Deepgram は浮動小数点の秒を使い、AssemblyAI、whisper.cpp のオフセット、Whisper の TSV 出力は整数のミリ秒を使います。Potato は境界で変換するので、それより後はすべて秒になります。自前の前処理で両者を混同すると、時間が 1000 倍ずれます。
サイドカーファイル
既知の文字起こし拡張子で終わる短い 1 行のパスである値は、テキストとしてではなくディスクから読み込まれます。つまり、音声ファイルの隣に .srt や .json が置かれた、音声認識ツールがすでに作った配置がそのまま前処理なしで使えます。
{
"id": "int_001",
"conversation": {
"audio": "media/int_001.mp3",
"transcript": "media/int_001.srt"
}
}パスは task_dir からの相対で解決され、他の設定パスと同じパスセキュリティ検証を通ります。したがってデータファイルがプロジェクト外を読むことはできません。
認識される拡張子: .srt .vtt .webvtt .json .json3 .srv1 .srv2 .srv3 .ttml .dfxp .xml .ass .ssa .tsv .ctm .TextGrid .eaf .txt
データにファイル名のように見える 1 行の文字起こしが本当に入っている場合は、この推定を切ってください。
display_options:
transcript_is_path: auto # auto (default) | true | false設定
表示: audio_dialogue
音声対話表示はターンを音声と同期した話者バブルとして描き、各ターンに再生ボタンを付けます。
instance_display:
fields:
- key: conversation
type: audio_dialogue
label: "Transcript"
span_target: true
display_options:
audio_key: audio
turns_key: turns
speaker_key: speaker
text_key: text
transcript_is_path: auto
show_timestamps: true
allow_speaker_assignment: auto
scroll_height: 460px| オプション | 既定値 | 動作 |
|---|---|---|
audio_key | audio | 音声の URL またはパスを持つフィールド値のサブキー。 |
turns_key | turns | ターン一覧を持つサブキー。segments も受け付けます。 |
speaker_key / text_key | speaker / text | ターン単位のキー。 |
speakers | [] | {id, name, color, side} のリスト。一覧にない話者には決定的な色と交互の配置が割り当てられます。 |
allow_speaker_assignment | auto | 未分離のターンや修正すべきリストがあるとき、auto はクリックでの割り当てを有効にします。true は常時有効、false は無効。 |
transcript_is_path | auto | フィールド値をサイドカーのパスとして読むかどうか。 |
show_timestamps | true | 各ターンに mm:ss–mm:ss を表示します。 |
scroll_height | 480px | スクロールする文字起こしペインの高さ。 |
playback_rates | [1, 1.25, 1.5, 2] | 速度セレクタの選択肢。 |
スキーマ
2.7.1 以降、speech_transcript、voice_interaction、tiered_annotation はいずれもインスタンスのレコードから直接文字起こしを読み、このページのすべての形式を受け付けます。それ以前は audio_dialogue 表示だけがそうしていました。
annotation_schemes:
- annotation_type: speech_transcript
name: transcript_review
description: "Mark transcription errors"
segments_key: segments # record field holding the transcript
- annotation_type: voice_interaction
name: barge_in
description: "Mark overlaps and barge-in"
turns_key: turns階層アノテーションでは、文字起こしからティアをあらかじめ埋めておけます。アノテーターは音声を一から切り直すのではなく、既存のアライメントを修正することになります。
- annotation_type: tiered_annotation
name: tiers
source_field: audio_url
media_type: audio
transcript_field: asr_output # opt-in; omit to start from a blank timeline
transcript_tier: utterance # defaults to the first tier
tiers:
- name: utterance
labels:
- name: speech
color: "#7c3aed"あらかじめ埋められたアノテーションは、アノテーターが実際に編集するまで書き込まれません。インスタンスを開いただけの人に作業が帰属することはありません。
potato transcripts でデータファイルを作る
変換ツールを音声認識出力のフォルダに向けると、アノテーション可能なデータファイルを書き出します。
# Pair transcripts to media by basename
potato transcripts ./whisper_out --media-dir ./audio -o data/interviews.json
# Media served from elsewhere
potato transcripts './captions/*.vtt' \
--media-url-prefix https://cdn.example.org/audio -o data/talks.json
# What did it detect? Writes nothing.
potato transcripts ./whisper_out --dry-run--dry-run は何も書かずに見つけたものを報告します。Whisper の間違った出力ファイルが入ったフォルダを見つけるいちばん速い方法です。
Scanned 3 file(s):
talk_01.srt SRT 2 turns 5.0s 2 speaker(s): Alice, Bob
talk_02.vtt WebVTT 1 turns 3.0s undiarized
talk_03.json whisper.cpp JSON 1 turns 2.4s undiarized
3 item(s), 4 turn(s).
1 item(s) have no media. Pass --media-dir or --media-url-prefix to enable playback.
| オプション | 用途 |
|---|---|
-o, --output | 書き出し先。--dry-run 以外では必須。 |
--format | json(既定)または jsonl。 |
--media-dir | ベース名で対応付ける素材のフォルダ。 |
--media-url-prefix | ローカルファイルの代わりに使う素材のベース URL。 |
--field | 文字起こしを入れる項目のフィールド。既定は conversation。 |
--id-prefix | 生成される各 ID の前に付ける文字列。 |
--speaker-key | 話者ラベルを持つ元データのキー。 |
-r, --recursive | サブフォルダも辿ります。 |
--dry-run | ファイルごとに判定された形式とターン数を報告します。 |
--emit-config | 対応する config.yaml の断片も出力します。 |
-q, --quiet | ファイルごとの報告を抑制します。 |
項目 ID はファイル名から末尾の素材拡張子を除いたものになるので、Whisper の interview_01.mp3.json は interview_01.mp3 ではなく interview_01 になります。
書き出して戻す
階層アノテーションは ELAN EAF と Praat TextGrid にエクスポートできるので、文字起こしは往復できます。取り込み、Potato でアノテーションし、書き出し、ELAN や Praat で仕上げて、その結果をまた読み込めます。
python -m potato.export --config config.yaml --format eaf --output ./out/
python -m potato.export --config config.yaml --format textgrid --output ./out/どちらのエクスポーターも tiered_annotation の出力を対象とします。他のスキーマは標準の形式で書き出されます。
トラブルシューティング
| 症状 | 何が起きているか |
|---|---|
| すべてが一つの大きなバブルになる | 形式が判定できず、平文段落のフォールバックに落ちています。potato transcripts <file> --dry-run を実行し、plain text と出るなら時間情報は一度も読まれていません。たいてい .json や .srt ではなく、時間情報を持たない Whisper の .txt です。 |
| 話者がなく、すべて Unassigned | 元データに話者ラベルがありません。Whisper 単体は話者分離せず、YouTube の自動生成字幕も同じです。上流で話者分離を実行するか、アノテーターに画面上で割り当ててもらってください。 |
| 時間が 1000 倍ずれている | 上流で秒とミリ秒が混ざりました。whisper.cpp の offsets、AssemblyAI、Whisper の TSV はいずれもミリ秒です。 |
| 文字起こしのパスがそのまま本文として出る | サイドカーファイルが読めず、パスが内容として表示されています。task_dir の下で解決されるか確認してください。サーバーログに具体的な理由が記録されます。 |
| 1 行の文字起こしがファイル名として読まれる | 表示フィールドに transcript_is_path: false を設定してください。 |
サンプルプロジェクト
Potato リポジトリの examples/audio/transcript-formats/ は、六つの形式を並べて表示します。SubRip、WebVTT、Whisper JSON、YouTube json3、Praat TextGrid、Deepgram のそれぞれがサイドカーファイルから読み込まれ、六つとも同じ話者バブルになります。
python potato/flask_server.py start examples/audio/transcript-formats/config.yaml -p 8000同じタスクはダウンロードできるショーケースとしても用意されています: Transcript Format Ingestion。
実装の詳細は元のドキュメントを参照してください。
関連文書
- Whisper の文字起こしをアノテーションする方法、通しの手順
- YouTube の字幕をアノテーションする方法、字幕とその制約について
- 対話アノテーション、話者バブル表示
- 音声アノテーション、波形を一から区切る場合
- アノテーション用データ形式の設計