ローカルLLMを使って、動画から summary(要約)と keywords(キーワード)をJSON形式で抽出する処理を実装したいと思います。
以前Qiitaで記事にした「Flutterで意味検索(ベクトル検索)をやってみる」に応用することを念頭に置いています。
実行結果
まず、どんな出力が得られるかを記載しておきます。これはElephants Dreamという動画の意味抽出結果になります。
{
"summary": "この動画は、超現実的で産業的なSF世界観の中での、2人の人物(EmoとPru)もしくはお互いを意識した登場人物たちの緊張感のある対話と葛藤を描いた短編映画『Elephants Dream』の断片的なシーン集です。映像面では、ケーブルやパイプ、巨大な機械構造物が入り乱れる不気味な空間に、デフォルメされた造形の人物たちが配置されています。音声面では、PruがEmoへの指導や支配的な態度を見せ、「機械の音を聞け」と命じたり、目をつぶらせて視覚を遮断させようとするなど、心理的な駆け引きが行われています。中盤から終盤にかけては、バビロンの空中庭園やロドス島の大像といった幻想的なイメージが語られ、最終的には登場人物たちの混乱と、機械的な触手のような構造物に飲み込まれる不気味な結末へと向かいます。最後は、オープンソースの3DソフトBlenderを用いたプロジェクトとしての制作クレジットが表示されます。",
"keywords": [
"Elephants Dream",
"SF",
"超現実的",
"産業的な機械構造",
"心理的な葛藤",
"Blender",
"シュールレアリスム",
"サイバーパンク"
]
}
実装方針
マルチモーダル的な意味抽出を、ローカルLLMとアプリケーションコードによるパイプラインとして実装します。
これには以下のようなメリット・デメリットがあります。
メリット
- 実行環境のスペックを多少下げることができる
- クラウドAPIを使わないため、機密性の高いコンテンツにも適用できる
- VisionとTextでモデルを使い分けることができる
- アプリケーション側で最終的なプロンプトをチューニングできる
デメリット
- フレームを1枚ずつllavaに投げるため、処理時間が長い(1分の動画で数分かかる)
- キーフレームの間隔が固定のため、シーン変化の激しい動画では重要な瞬間を取りこぼす可能性がある
- 音声・映像の説明テキストが長くなるほどプロンプトが膨らみ、モデルの処理精度に影響する
アーキテクチャ
パイプラインは大きく3つのフェーズに分けて実装します。
- 動画の音声をテキスト化する
- 動画のフレームをn秒ごとに画像化し、画像群の意味をテキスト化する
- 1.と2.の結果を統合して動画の意味を求める
モデルの役割分担
今回の実装では以下のようにモデルを分けています。
| モデル | 用途 |
|---|---|
llava:latest |
フレーム画像 → テキスト説明(マルチモーダル) |
llama3.1:latest |
テキスト統合 → JSON生成(テキスト特化) |
最初はllavaだけで最終的なJSON生成まで行っていましたが、日本語テキストの処理に限界があったため、最終の要約・キーワード生成をllama3.1で行うことにしました。
タイムスタンプによるコンテキスト統合
このパイプラインで問題になったのは、当初音声とキーフレーム画像が紐付いていなかったため、適切にサマリーを生成できないことでした。
そこで音声と映像にそれぞれタイムスタンプをつけてコンテキストを強化しました。
faster-whisper はセグメントごとに開始時刻を返すので、これを捨てずに保持するようにします。
def transcribe(audio_path, model_size, language):
model = WhisperModel(model_size, device="cpu", compute_type="int8")
segments, _ = model.transcribe(audio_path)
return [(seg.start, seg.text.strip()) for seg in segments]
キーフレームも同様に、抽出間隔から逆算してタイムスタンプを付与します。
def extract_keyframes(video_path, output_dir, interval):
# ffmpeg で fps=1/interval のフレームを抽出
...
frames = sorted(Path(output_dir).glob("frame_*.jpg"))
return [(i * interval, str(f)) for i, f in enumerate(frames)]
最終的にLLMに渡すプロンプトのイメージ:
TRANSCRIPT (what is being said, with timestamps):
[00:00] 本田が初めて発電機を作ったのは
[00:08] 2階に飾ってあるんですけども
[00:15] ここのところにソニーって書いてあるんです
VISUAL FRAMES (what is being shown, with timestamps):
[00:00] An indoor museum exhibit with display cases and framed photographs on the walls.
[00:15] A close-up of a vintage generator with a label that reads "SONY" on its surface.
[00:30] A person gesturing toward a mechanical display while speaking to camera.
音声なし動画への対応
画面収録や映像のみのコンテンツは音声トラックを持たないため、ffprobe で事前に確認し、音声がなければ文字起こしをスキップしてフレーム解析だけで処理する。
def has_audio_stream(video_path):
result = subprocess.run(
["ffprobe", "-v", "error", "-select_streams", "a",
"-show_entries", "stream=codec_type", "-of", "csv=p=0", video_path],
capture_output=True, text=True,
)
return bool(result.stdout.strip())
ソースコード
コード全体は GitHub で公開しています。READMEに実行方法などを記載していますので、そちらも参考にしてください。
サマリーの正しさについて
肝心の意味抽出精度は、おおよそ要約のためのテキストモデルに使うLLMの性能に依存する結果になるようです。
このセクションでは以下の3つをパラメータとして整合性を検証しました。
- ビジョンモデル (llava, gemma4)
- テキストモデル (llama3.1, llama3.2, gemma4)
- キーフレーム間隔 (10秒, 20秒)
整合性の検証
いくつかのモデルとキーフレーム間隔の組み合わせで、Elephants Dreamのプロットとの整合度をClaude AIに判定してもらったものを以下に記載します。長くなるので実際の出力は省いています。
フォーマット
以下のようなフォーマットで掲載します。見やすさのため、セマンティックを無視してnote記法を使っています。
評価
llava4, llama 3.1, 10秒
整合性評価:非常に低(約10%)
✅ 一致している点
- 何らかの対話が行われている → 大枠のみ合っている
- テクノロジー・工業的な環境 → 世界観として部分的に対応
❌ 不一致・問題点
| 項目 | Summary | Plot(正しい内容) |
|---|---|---|
| 登場人物 | 男性と女性 | 登場するのは老人(Proog)と若い男性(Emo)の2人、女性は登場しない |
| 関係性 | 男性が女性に問いかける | ProogがEmoを一方的に案内・強制する師弟的・支配的関係 |
| 「存在が否定されている」 | 男性側が否定されている | 機械の存在を否定するのはEmo、否定されているのはProogの世界観 |
| 物語の具体的展開 | 完全に欠落 | 電話の罠、奈落、プロジェクターの扉、殴打・気絶など多数のシーンがある |
| 結末 | 完全に欠落 | ProogがEmoを殴打・気絶させ、機械の実在を叫ぶ |
| 全体的な内容 | 非常に曖昧・抽象的 | 一貫した具体的な物語がある |
総評
登場人物の性別からして誤っており(女性キャラクターは存在しない)、物語の具体的な内容がほぼ何も捉えられていません。「何を調べているか明確ではない」という記述自体が、Summaryの生成元モデルが内容を把握できていないことを示しており、信頼性は極めて低いと判断します。
llava4, llama3.1, 20秒
整合性評価:低(約25%)
✅ 一致している点
- EmoとProog(Pru)の会話が中心 → 大枠は合っている
- 暗い場所・機械装置の前にいる → 正確
- 「時計のようなメカニズム、非常に危険」→ Proogの台詞と一致("like clockwork and could destroy them both")
- 機械の実在・信念をめぐる対話がある → 部分的に対応
❌ 不一致・問題点
| 項目 | Summary | Plot(正しい内容) |
|---|---|---|
| 登場人物名 | Pru |
Proog |
| 役割の逆転 | Emoが装置を操作、Pruが説明 | 案内・説明・操作を主導するのはProog |
| 「Emoが動けない」 | 明記されている | そのような描写はない。Emoはむしろ自由に動き回る |
| 「プルプル」 | メカニズムの名称として登場 | 完全な架空の名称(ハルシネーション) |
| 「目に見えないものを信じる」 | Pruが説いている | 逆。Proogは機械の実在と美しさを信じるよう迫る側 |
| 物語の具体的展開 | ほぼ欠落 | 電話の罠、奈落、プロジェクターの扉、殴打・気絶など多数のシーンがある |
| 結末 | 完全に欠落 | ProogがEmoを殴打・気絶させ、機械の実在を叫ぶ |
総評
機械・装置・危険といった世界観の雰囲気は捉えられていますが、登場人物の役割が逆転しており、「プルプル」という実在しない固有名詞も登場しています。物語の核心である二人の衝突・支配関係・結末がほぼ反映されておらず、精度は低いと判断します。
llava4, llama 3.2, 10秒
整合性評価:低〜中(約30%)
✅ 一致している点
- EmoとProog(Pru)が登場 → 名前は部分的に正確
- 「世界は時計仕掛けのようで、一手間違えば破滅する」→ Proogの台詞とほぼ正確に一致("like clockwork and could destroy them both if one wrong move is made")
- 工業的環境・超現実的な景観 → 世界観として正確
❌ 不一致・問題点
| 項目 | Summary | Plot(正しい内容) |
|---|---|---|
| 登場人物名 | Pru |
Proog |
| 「grind him down」 | Emoが削られると表現 | 原文は"destroy them both"、二人とも危険にさらされる |
| 「futuristic spaceships」 | 宇宙船のシーンが登場 | そのような描写は一切ない |
| 「sound design・post-production」 | ストーリーのテーマとして言及 | 制作クレジットの情報が混入していると思われる |
| 物語の具体的展開 | ほぼ欠落 | 電話の罠、奈落、プロジェクターの扉、殴打・気絶など多数のシーンがある |
| 結末 | 完全に欠落 | ProogがEmoを殴打・気絶させ、機械の実在を叫ぶ |
総評
時計仕掛けのくだりだけはプロットと高い一致を見せており、セリフレベルの情報が一部正確に拾えています。しかし「宇宙船」という実在しない要素や、制作クレジット情報(sound design、post-production)がストーリー内容として混入しており、信頼性を大きく損ねています。物語の結末も完全に欠落しています。
llava4, llama3.2, 20秒
整合性評価:低〜中(約35%)
✅ 一致している点
- EmoとProog(Pru)が登場 → 部分的に正確
- 未来的・工業的な機械環境 → 世界観として正確
- 機械・メカニズム・罠(trap)への言及 → 電話の罠のシーンと対応
- 探索・危険の雰囲気 → 概ね正確
❌ 不一致・問題点
| 項目 | Summary | Plot(正しい内容) |
|---|---|---|
| 登場人物名 | Pru |
Proog |
| 「To be continued...が繰り返される」 | テキストとして映像内に登場 | そのような演出はプロットに存在しない |
| 「promotional content」 | プロモーション映像として描写 | 単独の短編映画の本編 |
| 実写・デジタルグラフィックの混在 | 言及あり | プロットには存在しない |
| スチームパンク | テーマとして明記 | プロットにその言及はなく、やや異なる世界観 |
| 関係性・対立構造 | 欠落 | ProogがEmoを支配・強制する関係、最終的に殴打する |
| 物語の具体的展開 | ほぼ欠落 | 奈落、プロジェクターの扉、空中庭園・ロドス像の幻想、殴打・気絶など |
| 結末 | 完全に欠落 | ProogがEmoを殴打・気絶させ、機械の実在を叫ぶ |
総評
世界観の雰囲気(機械・工業・危険・探索)はある程度捉えられていますが、「To be continued...が繰り返される」「実写映像の混在」「プロモーション映像」といった実在しない要素が複数混入しています。二人の関係性・対立構造・結末がまったく反映されておらず、物語の核心には届いていません。
gemma4:31b, llama 3.2, 20秒
整合性評価:低〜中(約35%)
✅ 一致している点
- EmoとProog(Pru)の会話が中心 → 大枠は合っている
- 未来的・工業的・超現実的な環境 → 正確
- 機械部品・電気ケーブルなどの描写 → プロットと対応
- 全体的なトーンが「警戒・警告」 → Proogの行動と概ね一致
- Emoが懐疑的 → 正確
❌ 不一致・問題点
| 項目 | Summary | Plot(正しい内容) |
|---|---|---|
| 登場人物名 | Pru |
Proog |
| 「周囲の音に耳を傾けるよう促す」 | Pruの主な行動として描写 | Proogが促すのは機械の「美しさと完璧さ」の認識であり、「音を聞け」はプロットに明確な根拠がない |
| 「EmoがやがてHidden thingsを見えるようになる」 | 肯定的な成長として描写 | 実際はEmoは最後まで機械の存在を否定し、Proogに殴打・気絶させられる |
| 関係性 | 対等な対話・Pruが穏やかに促す | ProogはEmoを強制・支配し、最終的に暴力を振るう |
| 物語の具体的展開 | ほぼ欠落 | 電話の罠、奈落、プロジェクターの扉、空中庭園・ロドス像、殴打・気絶など |
| 結末 | 完全に欠落 | ProogがEmoを殴打・気絶させ、機械の実在を叫ぶ |
総評
世界観の雰囲気は捉えられていますが、最大の問題は物語の結末と関係性の方向性が真逆である点です。EmoはProogの世界観を受け入れず、Proogは穏やかな導き手ではなく最終的に暴力を行使します。「EmoがやがてHiddenなものを見えるようになる」という成長物語的な読み取りは、プロットの核心と大きく乖離しています。
llava, gemma4:31b, 20秒
整合性評価:中(約55%)
✅ 一致している点
- 『Elephants Dream』CGアニメーション作品 → 正確
- EmoとProog(Pru)の対話が中心 → 大枠は正確
- スチームパンク風の巨大な機械仕掛けの世界観 → 正確
- 複雑な配管・構造物の視覚的描写 → 正確
- 「機械の完璧さと美しさを説くProog vs 存在しないと反論するEmo」→ プロットと高精度で一致
- バビロンの空中庭園・ロドス島の巨像への言及 → 正確
- シュールレアリスム的雰囲気 → 正確
- オープンソース的プロジェクト → 事実として正確
❌ 不一致・問題点
| 項目 | Summary | Plot(正しい内容) |
|---|---|---|
| 登場人物名 |
Pruen、EmoEmo
|
Proog、Emo(誤字・変形が見られる) |
| 「機械の音を聴け」 | Pruenの台詞として明記 | プロットに明確な根拠がない(美しさ・完璧さの話が正確) |
| クレジットロール | 結末として言及 | プロットには存在しない |
| 結末の欠落 | クレジットで締めくくられる | ProogがEmoを殴打・気絶させ、機械の実在を叫ぶ重要シーンがある |
| 「人生の目的」 | テーマとして言及 | プロットに直接的な根拠はない |
総評
対立構造の本質(誰が何を主張しているか)を正確に捉えており、具体的なイメージ(空中庭園・ロドス像)も正しく拾えています。ただし登場人物名にPruen・EmoEmoという誤変形が見られる点、そして結末(殴打・気絶)が完全に欠落している点が大きな減点要因です。
llava, gemma4:31b, 10秒
整合性評価:中(約55%)
✅ 一致している点
- 『Elephants Dream』CGアニメーション → 正確
- EmoとProog(Pru)の対話 → 大枠は正確
- 未来的・工業的・サイバーパンクな世界観 → 正確
- 「機械の美しさと完璧さを理解するよう説くProog vs 拒絶するEmo」→ プロットと高精度で一致
- バビロンの空中庭園・ロドスの巨像への言及 → 正確
- 緊張感のあるやり取り → 正確
❌ 不一致・問題点
| 項目 | Summary | Plot(正しい内容) |
|---|---|---|
| 登場人物名 | Pru |
Proog |
| 「コンピレーションビデオ」 | 断片的シーンの寄せ集めと描写 | 一貫した物語構造を持つ単独の短編映画 |
| 「機械の音に耳を傾ける」 | Pruの台詞として明記 | プロットに明確な根拠がない |
| 「人間のような機械的なキャラクター」 | キャラクター描写として言及 | プロットに機械的キャラクターという描写はない(人間のキャラクター) |
| クレジットロール | 終盤に登場すると明記 | プロットには存在しない |
| 結末の欠落 | クレジットで締めくくられる | ProogがEmoを殴打・気絶させ、機械の実在を叫ぶ |
総評
対立構造の核心(誰が何を主張し、誰が拒絶するか)を正確に捉えており、具体的なイメージも正しく拾えています。しかし「コンピレーションビデオ」という誤った作品分類、クレジットロールの混入、そして最重要シーンである殴打・気絶の結末が完全に欠落している点が大きな減点要因です。前回のSummaryと概ね同水準の評価となります。
gemma4:31b, gemma4:31b, 20秒
中〜高(約65%)
✅ 一致している点
- 短編3DCGアニメーション → 正確
- 超現実的な工業的環境 → 正確
- 巨大なケーブル・複雑な機械装置 → プロットと対応
- 「機械の美しさと完璧さを理解するよう促すProog vs 拒絶するEmo」→ 高精度で一致
- バビロンの空中庭園・ロドスの巨像への言及 → 正確
- 二人の対立的な関係・心理的緊張感 → 正確
- 不気味な空間での駆け引き → 正確
❌ 不一致・問題点
| 項目 | Summary | Plot(正しい内容) |
|---|---|---|
| 登場人物名 | プル(Pru) |
Proog |
| 「機械の音に耳を傾ける」 | Pruの台詞として明記 | プロットに明確な根拠がない(美しさ・完璧さの認識が正確) |
| クレジットへの移行 | 結末として言及 | プロットには存在しない |
| 結末の欠落 | クレジットで締めくくられる | ProogがEmoを殴打・気絶させ、機械の実在を叫ぶ決定的シーンがある |
総評
これまでのSummaryの中でも精度が高い部類です。対立構造・世界観・具体的なイメージをバランスよく捉えており、「心理的な駆け引きと緊張感」という表現も物語の雰囲気に合致しています。減点要因は登場人物名の誤り、クレジットロールの混入、そして結末の欠落に限られており、もし殴打・気絶のシーンが反映されていれば75〜80%に達していたと思われます。
gemma4:31b, gemma4:31b, 10秒
中〜高(約70%)
✅ 一致している点
- 短編映画『Elephants Dream』 → 正確
- EmoとProog(Pru)の緊張感のある対話と葛藤 → 正確
- ケーブル・パイプ・巨大な機械構造物の不気味な空間 → 正確
- Proogの支配的な態度 → 正確(これまでのSummaryで最も正確に関係性を捉えている)
- 目をつぶらせるシーン → プロットと一致("Proog instructs Emo to close his eyes")
- バビロンの空中庭園・ロドス島の大像への言及 → 正確
- Blender・オープンソースプロジェクトへの言及 → 事実として正確
❌ 不一致・問題点
| 項目 | Summary | Plot(正しい内容) |
|---|---|---|
| 登場人物名 | Pru |
Proog |
| 「機械の音を聞け」 | Pruの命令として明記 | プロットに明確な根拠がない |
| 「機械的な触手に飲み込まれる結末」 | 不気味な消滅として描写 | 実際はProogがEmoを殴打・気絶させる。触手に飲み込まれる描写はない |
| 「断片的なシーン集」 | 作品の構成として言及 | 一貫した物語構造を持つ単独の短編映画 |
| クレジットロール | 結末として言及 | プロットには存在しない |
総評
これまでのSummaryの中で最高水準です。特にProogの支配的態度と目をつぶらせるシーンを具体的に捉えている点は他のSummaryにはない精度です。惜しいのは結末で、「機械的な触手に飲み込まれる」という描写がハルシネーションであり、実際の殴打・気絶という結末と異なります。この部分が正確であれば80%超に達していたと思われます。
整合性検証の考察
gemma4:31bについて
gemma4:31b(2026年4月3日時点で最新のマルチモーダルLLM)をビジョン・テキストともに使った場合が一番整合度が良いようです。
gemma4:31bは自分の実行環境(M3 mac, Unified Memory 36GB)だと、ギリギリ動くものです。
llama3.3:latestを試してみたいのですが、手元で動かすのは無理がありそうなので未検証です。
テキストモデルとキーフレーム間隔について
llama3.1,llama3.2では、キーフレーム間隔を10秒にすると20秒の時より精度が落ちます。
検証していませんが、映像情報が多すぎて要約するには性能が足りていないのではないかと推測します。
不整合項目について
各項目で登場人物名の間違いとクレジットロールへの言及がプロットとの相違点として上がっています。
人物名の誤りはwhisperの文字起こしに起因するものであり、クレジットロールはプロットのほうに記載されていないための不整合判定です。
これを除外すると、gemma4:31b, gemma4:31b, 10秒の整合度は約80%になります。
同様に llava, gemma4:31b, 10秒の整合度は約75%という計算になるようです。
このくらいであれば、場合によっては許容可能なのではないでしょうか?
次の取り組みについて
gemma4:31bがギリギリ動かせるサイズなので、それをマルチモーダルLLMとして使って動画の意味を検出させ、今回のパイプライン型アーキテクチャによる意味検出との精度を比較してみたいと思います。
ライセンス
本記事の実行例に使用した動画 Elephants Dream は Blender Foundation が制作したオープンソース映像作品です。
© copyright 2006, Blender Foundation / Netherlands Media Art Institute / www.blender.org
Licensed under Creative Commons Attribution 2.5
最後に
株式会社ボトルキューブではFlutterを使ったお仕事を募集中です。
お問い合わせフォームからご連絡ください。
また、一緒に働く仲間も募集しています。
詳細は採用情報ページをご覧ください。