概要
動画の字幕をテキストに書き起こすプログラムを書きました。
動機
海外アニメで外国語を勉強したい!
「日本のアニメで日本語を勉強した」って言う外国人のかた、多くいらっしゃいますよね。
じゃあ、逆もできるでしょう!
海外アニメを現地語で理解できるようになれば、学習のモチベーションになりそう!
……と思いたったものの、まあ、難しいです。
例えば中国語の場合。
知らない漢字が出てきて、読みがわからないから辞書で引けない。入力ができない。
これ、全部テキスト化できないかな……。そうすれば機械翻訳もしやすいのに。
……と思ったのがきっかけです。
↑ 例えばこんな感じ。現地語字幕がついていると嬉しいですね。やったこと
- 動画の字幕を読み取って、テキスト化するプログラムを作った!
利用した技術
- OCR:Tesseract(テッセラクト)
- オープンソースのOCR(文字認識)ツール。
ローカルで動くので、API制限を気にしなくていいのが良いところ。
ただし精度はそこまで高くない。
- オープンソースのOCR(文字認識)ツール。
- 言語:PHP
- 画像処理:ffmpeg
- データ蓄積:SQLite
コード
<?php
// 設定 ================================================================
$lang = 'jpn'; // OCRで読み取る言語(日本語:jpn)
$fps = 1; // 画像の切り出しのFPS
$bottom = 0.1; // 画像の下側の切り出しの割合
$threshold = 220; // 画像二値化の閾値
// ビデオファイルの存在チェック ================================================================
$videoFile = $_SERVER['argv'][1];
if (!file_exists($videoFile)) {
echo "--- Video File Not Found ---\n";
return;
}
// SQLiteの準備 ================================================================
$dbFile = 'database.sqlite';
if (file_exists($dbFile)) {
unlink($dbFile); // 初期化するため、古いファイルを削除
}
$db = new PDO("sqlite:$dbFile");
$db->exec("CREATE TABLE IF NOT EXISTS transcripts (id INTEGER PRIMARY KEY, timestamp TEXT, content TEXT)");
// 画像の切り出し ================================================================
echo "--- Begin Extracting Frames ---\n";
$frameDir = 'frames/';
mkdir($frameDir, 0777, true);
$crop = "iw:ih*$bottom:0:ih-ih*$bottom"; // 下部分(割合:$bottom)を切り出し
$filters = "format=gray,lutyuv=y='if(gt(val,$threshold),255,0)',negate"; // 閾値:$threshold で二値化、反転
$ffmpegCmd = "ffmpeg -i " . escapeshellarg($videoFile) . " -vf \"fps=$fps,crop=$crop,$filters\" {$frameDir}frame_%04d.png";
shell_exec($ffmpegCmd);
// 画像の読み取り(OCR) ================================================================
echo "--- Begin OCR Process ---\n";
$images = glob($frameDir . "*.png");
sort($images);
foreach ($images as $index => $imagePath) {
// タイムスタンプの計算
$seconds = $index / $fps;
$timestamp = sprintf('%02d:%02d.%01d', floor($seconds / 60), floor($seconds) % 60, ($seconds * 10) % 10);
echo "Processing [$timestamp] $imagePath ... ";
// Tesseract実行
$tesseractCmd = "tesseract " . escapeshellarg($imagePath) . " stdout -l " . $lang . " --psm 6";
$text = shell_exec($tesseractCmd);
$text = trim($text);
$text = str_replace(["\r", "\n", " "], '', $text);
// SQLiteにテキストを保存
if (!empty($text)) {
$stmt = $db->prepare("INSERT INTO transcripts (timestamp, content) VALUES (?, ?)");
$stmt->execute([$timestamp, $text]);
echo "Success.\n";
} else {
echo "No text found.\n";
}
// 処理が終わった画像を削除
unlink($imagePath);
}
// テキストの出力 ================================================================
echo "--- Begin Concatenating Text ---\n";
$stmt = $db->query("SELECT * FROM transcripts ORDER BY id ASC");
$fullText = "";
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
$fullText .= "[{$row['timestamp']}] {$row['content']}\n";
}
file_put_contents('transcript.txt', $fullText);
echo "--- Finished ---\n";
使い方
動画ファイルをローカルにダウンロードしてきて(権利関係に十分ご注意ください)、
$ php transcriber.php ./path/to/video.mp4
のように実行すれば大丈夫です!
利用に際し
利用・改変はご自由にどうぞ!
PHPのインストールとSQLiteの有効化、Tesseractとffmpegのインストールが必要です。
結果(以下余談)
検証に使用した動画は https://www.youtube.com/watch?v=9CvIEF5lsng です。
プログラムの出力
プログラムの出力結果の抜粋です。
[01:52.0] 在風和日施的亞甸大陸上
[01:56.0] 居住著一群以探集木天鞭和碾物維生的嘶貓一族
[02:00.0] 他們每天過著自給自足,快樂又和平的生活
[02:06.0] 但是、有一天
[02:08.0] 亞甸大陸上突然出現三個邪惡又恐怖的族群
[02:12.0] 分別是'磺龍族、麗猴族以及野狗族
[02:20.0] 他俏不斷侵略著嘶嘶們的家園
[02:22.0] 並抓走了許多喵喵
[02:24.0] 誤他們每天都活在不安與害怕之中
[02:28.0] 直到某一天、手持喵神器的三位喵史出現
[02:32.0] 他們吏退了敵人並救回被抓走的同伴
[02:36.0] 而這三位英雄分別是
[02:38.0] 深紅鐮刀的持有者一黑貓九藏
[02:40.0] 手握震晶的磺界帝王一咐磺帝
[02:44.0] 天菜妻的主人、圳花村村長一晴信
[02:48.0] 他們凝蒜了散落在世界各地的啼嘶
[02:52.0] 台敢的對抗敵人
[02:54.0] 並領巡著喵喵們重新建立家園
[02:58.0] 有了他們的守廬
[03:00.0] 喵喵們很快就底復了以往和平的生活
[03:06.0] 就是流傳在亞甸大陸上、,有關我們喵喵一族的歷史
手での書き起こし(比較用)
在風和日麗的亞甸大陸上
居住著一群以採集木天蓼和礦物維生的喵喵一族
他們每天過著自給自足,快樂又和平的生活
但是,有一天...
亞甸大陸上突然出現三個邪惡又恐怖的族群
分別是,魔龍族、魔猴族以及野狗族
他們不斷侵略著喵喵們的家園
並抓走了許多喵喵
讓他們每天都活在不安與害怕之中
直到某一天,手持喵神器的三位喵喵出現
他們擊退了敵人並救回被抓走的同伴
而這三位英雄分別是
深紅鐮刀的持有者―黑貓九藏
手握雷晶的魔界帝王―喵魔帝
天叢雲的主人,櫻花村村長―晴信
他們凝聚了散落在世界各地的喵喵
勇敢的對抗敵人
並領導者喵喵們重新建立家園
有了他們的守護
喵喵們很快就恢復了一往和平的生活
而這單段故事
就是流傳在亞甸大路上,有關我們喵喵一族的歷史
OCRそれ自体の制度は高くありませんが、目的は達成できました!
最後に
APIとつなげれば、精度高く読み取れるかもしれないですし、
自動翻訳もできそうですね!
プログラミングも、外国語も、どちらも勉強頑張りましょう!