少し前のエントリでYouTubeから音声データをダウンロードするといった内容のことをやっていたのですが、それならその音声データをWhisper
に入力し、生成された音声解析のテキストデータをYouTubeの字幕ファイルに変えてみたらどうなるかなと思っていました。Whisper
のログ出力はほぼそのままでも字幕ファイルフォーマットであるSRTファイル
に近い出力フォーマットになっているからです。
ただ、そのまま取り出した文字データはタイムコードのないモノになるので、後で処理を行うと面倒なことになります。今回はWhisper
を使用し、ログファイルとして出力されたデータをファイル化してYouTubeでも使用可能な字幕データにしてやろうという試みです。
Whisperとは?
Whisper
は```OpenAI社のは音声認識と機械翻訳のサービスになります。詳細は以下のGitHubを見てもらえればと思います。
使用方法も結構簡単で
$ pip install git+https://github.com/openai/whisper.git
を実行することでPythonのライブラリだけでなく、コマンドからも実行することができるようになります。今回はPythonから使用します。先程のGitHubのドキュメントにもあるように、音声ファイルと音声モデルを指定することで音声認識されたデータが出力されます。
GitHubにあるサンプル
import whisper model = whisper.load_model("base") result = model.transcribe("audio.mp3") print(result["text"])
このプログラムではbase
という認識モデルを使用して、audio.mp3
を解析しテキストファイルを作成しています。このプログラムのアプトプットでは解析時はタイムコードとともに解析したテキストのログと、解析後のテキスト全文を出力しています。コードとしてprint(result["text"])
となっている部分がテキストの全文となります。せっかく出力されているログの部分はコードとしては現れず標準出力(stdout
)として画面表示されています。。本当に使いたいのはこの標準出力の部分なのです。
ということで、Pythonのコードで標準エラーの出力をファイルを取得し、そのデータを変換して字幕ファイルに変換します。
YouTubeで対応している字幕フォーマットがいくつかあります。
YouTubeに対応した字幕ファイルフォーマット
SubRip(.srt)形式
これは非常に一般的な字幕フォーマットで、プレーンテキストファイルです。各字幕エントリには、開始および終了時間と字幕テキストが含まれています。
srtファイルのサンプル
1 00:00:02,000 --> 00:00:04,000 こんにちは、皆さん 2 00:00:05,000 --> 00:00:07,000 今日は天気が良いです
SubViewer(.sub)形式
SubViewerフォーマット
もプレーンテキストフォーマットで、各エントリには開始および終了時間、および字幕テキストが含まれています。
subファイルのサンプル
00:00:02,00 00:00:04,00 こんにちは、皆さん 00:00:05,00 00:00:07,00 今日は天気が良いです
SBV (YouTube)(.sbv)形式
これはYouTube固有のフォーマットで、開始および終了時間、および字幕テキストを含むエントリがあります。上記との違いはタイムコードの桁や小数点の表記だけのように感じます。
sbvファイルのサンプル
0:00:02.000,0:00:04.000 こんにちは、皆さん 0:00:05.000,0:00:07.000 今日は天気が良いです
今回使用する字幕ファイルのフォーマット
この3つから出力形式を選ぶのですが、srtファイル
はファイル内にシーケンス番号があるので、あとから編集する可能性を考えるとちょっと面倒です(ちなみにシーケンス番号が飛び飛びになっていても問題はないようですが)。
ということで、今回はSubViewerフォーマット
へ変換してみることにします。
作成方針
今回は2つの処理プログラムを組み合わせて処理を行っていきます。
- 音声ファイルから
Whisper
で音声解析を行ってテキストファイルを出力する - 音声解析したテキストデータを
SubViewerフォーマット
に変換する
あとはこの2つの処理をシェルスクリプトとして実行するようにまとめます。
音声ファイルからWhisper
で音声解析を行ってテキストファイルを出力
このプログラムは引数に指定された音声ファイルをもとに解析結果のログを出力します。
create_audio_to_whisperlog.py
import sys import argparse import whisper def process_audio_to_text(input_audio_file, output_text_file): # Load the Whisper model model = whisper.load_model("small") # Options: tiny, base, small, medium, large original_stdout = sys.stdout # Save the result to the output text file with open(output_text_file, 'w', encoding='utf-8') as f: sys.stdout = f # Transcribe the audio file to text result = model.transcribe(input_audio_file, fp16=False, verbose=True, language='ja') sys.stdout = original_stdout def main(): # Create a parser for parsing command-line arguments parser = argparse.ArgumentParser(description='Transcribe an audio file to text.') parser.add_argument('input_audio_file', type=str, help='Path to the input audio file.') parser.add_argument('output_text_file', type=str, help='Path to the output text file.') # Parse the arguments args = parser.parse_args() # Call the function to process the audio file and transcribe it to text process_audio_to_text(args.input_audio_file, args.output_text_file) if __name__ == '__main__': main()
上記のプログラムのポイントは標準出力であるstdout
の内容をファイルに保存している点と
model.transcribe()
の引数にverbose=True
を明示的に追記している点です、。この関数の引数は指定しなければ、verbose=True
となり解析結果を標準出力に出力してくれますが、Falseを設定していると何も出力されないので注意してください。
また、今回はWhisperの音声解析モデルをsmall
を使用しています。モデルにはサイズ・解析の品質レベルによってtiny
、base
、small
、medium
、large
があります。tinyが一番小さく品質が低く、large
が一番大きく品質が高くなります。品質の高いモデルは解析の時間が必要となりますので、どれがいいかは使ってみて比較が必要かなと思います。
ポイントの箇所を取り出してみました
original_stdout = sys.stdout # ※標準出力をいったん保存 # Save the result to the output text file with open(output_text_file, 'w', encoding='utf-8') as f: sys.stdout = f # ※標準出力をファイルに保存する指定 # Transcribe the audio file to text result = model.transcribe(input_audio_file, fp16=False, verbose=True, language='ja') sys.stdout = original_stdout # ※標準出力をもとに戻す
音声解析したテキストデータをSubViewerフォーマット
に変換
このプログラムは引数に指定されたWhisperの解析結果ををもとに字幕ファイルを生成します。 タイムコードも表示されているテキストデータの変換なのでそれほど難しい処理ではありません。
transform_whisperlog_to_subfile.py
import argparse def transform_multiple_lines(input_text): entries = input_text.strip().split("\n") output_entries = [transform_subtitle(entry) for entry in entries] return "\n\n".join(output_entries) def transform_subtitle(input_text): lines = input_text.strip().split("\n") output_lines = [] for line in lines: time_codes, text = line.split("] ") start_time, end_time = time_codes[1:].split(" --> ") output_time = f"00:{start_time},00:{end_time}" output_lines.extend([output_time, text]) return "\n".join(output_lines) def main(): parser = argparse.ArgumentParser(description="Transform subtitle format") parser.add_argument("input_file", help="Path to the input file") parser.add_argument("output_file", help="Path to the output file") args = parser.parse_args() with open(args.input_file, "r", encoding="utf-8") as infile: input_text = infile.read() transformed_text = transform_multiple_lines(input_text) with open(args.output_file, "w", encoding="utf-8") as outfile: outfile.write(transformed_text) if __name__ == "__main__": main()
変換を行うシェルスクリプト
上記2つのpythonプログラムをもとに、実行のシェルスクリプト(入力ファイルをaudio.mp3として、中間ファイルをwhisper.logを生成し、出力する字幕ファイルをoutput.subとしています)を作成します。スクリプト内ではpythonのプログラムを&&
でつないでいますが、これは1つ目のプログラムが正常終了する場合にのみ2つ目のプログラムが実行されるというような動作をします。
audio2sub.sh
#!/bin/bash python3 ./create_audio_to_whisperlog.py audio.mp3 whisper.log \ && python3 ./transform_whisperlog_to_subfile.py whisper.log output.sub
【参考】
このスクリプトを作成後に実行権限を付与します。
chmod 755 audio2sub.sh
実行!
実行は以下のように行います。 (注意)入力する音声ファイルaudio.mp3がスクリプトファイルと同じパスにある必要があります。
$ ./audio2sub.sh
実行すると中間ファイルとして音声解析を行った中間ファイルwhisper.log
が生成されます。
今回は天気予報を読み上げの音声データを使ってみました。
whisper.logの内容
$ cat whisper.log [00:00.000 --> 00:04.000] 10月5日木曜日の全国の天気をお伝えします。 [00:04.000 --> 00:07.000] まずは天気図から見ていきましょう。 [00:07.000 --> 00:12.000] 定期圧や全線の通過に伴い、北日本や北陸では雨・風が強まります。 [00:12.000 --> 00:14.000] 荒れた天気に警戒が必要です。
中間ファイルの生成が成功すると字幕ファイルoutput.sub
が生成されます。
出力される字幕ファイルoutput.subの内容
$ cat output.sub 00:00:00.000,00:00:04.000 10月5日木曜日の全国の天気をお伝えします。 00:00:04.000,00:00:07.000 まずは天気図から見ていきましょう。 00:00:07.000,00:00:12.000 定期圧や全線の通過に伴い、北日本や北陸では雨・風が強まります。 00:00:12.000,00:00:14.000 荒れた天気に警戒が必要です。
YouTubeに字幕ファイルを取り込む
字幕ファイルができれば、あとは字幕ファイルの取り込みになります。
YouTubeのファイルの管理画面(YouTube Studio
)から字幕ファイルのアップロードができます(公開後でも字幕を付けることができます)。
画面を開いて画面の右下にある【字幕】ボタンをクリックします。
ボタン部分の拡大
字幕追加のダイアログが開き、【ファイルをアップロード】をクリックすると・・・
クリックするとタイムコードありか、タイムコードなしか選択ダイアログが表示されます。今回のSubViewerフォーマット
の字幕ファイルにはタイムコードが含まれているので【タイムコードあり】を選択して、【続行】をクリックします。
すると、字幕がタイムコード順にならんでプレビューを行うことができます。データの編集もこの画面で行うこともできるので便利ですね。
あとは【完了】ボタンをクリックすればOKです。
おわりに
音声ファイルからWhisper
を使用して、YouTubeの字幕ファイルを生成し、動画につけるということをやってみました。Whisper
の入力のできるの音声ファイルの容量は25MByteではありますが、音声ファイル自体で25MByteはかなり長時間になると思いますので、ほぼ大丈夫なのではないかと思います。超えてしまう場合には音声の品質などを変えるとかチャンネルを減らすとかも工夫はできそうです。
かなりの品質で字幕をつくることができるので非常にいいですよね~ということで終わりたいと思います。
ちなみに、Whisperのサンプルコードをpipeでフィルタプログラムに通せばもっと楽に実装できます😁