西川和久の不定期コラム
Qwen3.8で動画解析→生成AI動画プロンプト化してみた。LM Studioも対応
2026年9月21日 06:06
Xでは9月に入っても、8月初旬に公開された生成AI動画モデル「MiniMax-H3」に関する話題の勢いは止まらない。そこで今回はどうやってプロンプトを書くか?をちょっと違った角度からご紹介したい。
お題は“既存動画から生成AI動画用プロンプトを作る”
最近、筆者は生成AI画像にしても、生成AI動画にしても、自分でプロンプトは書かず、エージェント=AIに「こんな感じで!」と指示、プロンプトを生成してもらっている。
画像に関してはベタ英文でも行けるので、まだ自分で書けなくはないものの、動画に関してはプロンプトにいろいろなルールがあり、それに合わせないとうまく動かない。さらにT2VA(テキストで指示)に加え、FL2VA(最初/最後のフレームを画像で指定)、Ref2VA(複数のリファレンス画像を使用)と、モードによって書き方も変わったりする。
公式ガイドを丸暗記、プロンプト展開する根性もなく(笑)、エージェントに公式ガイドのURLを渡し、まとめてmd化、それを参照しつつ筆者の指示をプロンプト化……筆者はできたプロンプトをコピペするだけな感じだ。
そんなある日、ふっと思ったのが“既存動画から、MiniMax-H3やLTX-2.5用のプロンプトを自動生成できないだろうか?”……だった。
以前からVisionで画像解析できるのは知っていたが、実はモデルによっては動画解析もできるのだ。であれば作ってみるか!が今回のお題となる。
動画Vision対応LLMの使い方
動画Vision対応のLLMは、主要どころだと「Qwen3.6」や「Qwen3.8」系および「Gemma4」系ならOK。DeepSeekは最近、「DeepSeek-V4-Flash-Vision-Exp」でVision対応はしたものの動画は未対応。今回はもともとDGX Spark互換機で動かしていたvLLM + Qwen3.8-Flash-Nextで試すことにした。
まず動作確認。やってることは単純で、ffmpegを使い指定動画を480pに縮小(1080p/720pなど大きいままだと重くなる)。それをcurlでOpenAI API形式を使い送ってるだけだ。お題の動画はこんな感じ。六本木カフェ巡りVlog的な内容になっている。
手順は以下の通り。
ffmpeg -y -i test/test.mp4 -vf "scale=-2:480" -c:v libx264 -crf 28 test_480p.mp4
curl -X POST "http://192.168.11.100:8000/v1/chat/completions" \
-H "Content-Type: application/json" \
-d "{
\"model\": \"Qwen/Qwen3.8-Flash-Next\",
\"messages\": [{
\"role\": \"user\",
\"content\": [
{\"type\": \"text\", \"text\": \"この動画の内容を解析してください\"},
{\"type\": \"video_url\", \"video_url\": {\"url\": \"data:video/mp4;base64,$(base64 -i test_480p.mp4 | tr -d '\n')\"}}
]
}]
}"
するとこんなjsonが戻る。
{"id":"chatcmpl-a97f30d928ad852e","object":"chat.completion","created":1789660693,"model":"Qwen/Qwen3.8-Flash-Next","choices":[{"index":0,"message":{"role":"assistant","content":"この動画の内容は、若い女性がカフェでスイーツを食べる様子です。彼女は白い服を着ており、まず店に入ります。その後、店員と会話をして、ケーキとアイスカフェオレを頼んでいるようです。最後に、彼女はケーキを一口食べて、美味しいという表情を浮かべています。","refusal":null,"annotations":null,"audio":null,"function_call":null,"reasoning":null},"logprobs":null,"finish_reason":"stop","stop_reason":null,"token_ids":null,"routed_experts":null}],"service_tier":null,"system_fingerprint":"vllm-0.29.0-ed12bfe0","usage":{"prompt_tokens":5995,"total_tokens":6059,"completion_tokens":64,"prompt_tokens_details":null,"completion_tokens_details":{"reasoning_tokens":0}},"prompt_logprobs":null,"prompt_token_ids":null,"prompt_text":null,"kv_transfer_params":null,"ec_transfer_params":null,"metrics":null}
ご覧のように解析できている。ただセリフや音は対象外、動きのみ。別途これを解決しないとプロンプトは作れない。そこで登場するのが以下2つ。
1つ目は、mlx-whisper(Apple製mlxフレームワークの一部、モデルはmlx-community/whisper-large-v3-turbo)によるASRだ。ただこれはApple Silicon専用なので、Macでしか動かない点に注意。PC(CUDA)の場合は、openai-whisperやfaster-whisperが使えるようだが未確認。インストールと使い方は簡単!
pip install mlx-whisper
import mlx_whisper
import json
result = mlx_whisper.transcribe(
"test/test.mp4",
path_or_hf_repo="mlx-community/whisper-large-v3-turbo",
)
print(json.dumps(result, ensure_ascii=False, indent=2))
{
"text": "今日は六本木をブラブラしてるんだけどいいお店ないかな?わ、ここ!入ってみよう!いらっしゃいませーすすみません、いちごのケーキ一つくださいはい、かしこまりましたうん!おいしい!ここ、やばい、店員さんイケメン",
"segments": [
{
"id": 0, "seek": 0, "start": 0.0, "end": 2.82,
"text": "今日は六本木をブラブラしてるんだけどいいお店ないかな?",
"tokens": [50365, 43791, 30566, 8802, 41437, 5998, 28889, 11353, 28889, 11353, 8822, 4895, 26983, 21891, 13806, 6117, 22469, 9311, 24939, 30, 50506],
"temperature": 0.0, "avg_logprob": -0.2417571518447373,
"compression_ratio": 1.5257731958762886, "no_speech_prob": 1.775446629992405e-11
},
.
. (長くなるので省略)
.
{
"id": 7, "seek": 0, "start": 12.06, "end": 14.64,
"text": "ここ、やばい、店員さんイケメン",
"tokens": [50968, 23509, 1231, 7355, 42176, 1231, 22469, 20992, 15567, 8040, 32639, 21647, 4824, 51097],
"temperature": 0.0, "avg_logprob": -0.2417571518447373,
"compression_ratio": 1.5257731958762886, "no_speech_prob": 1.775446629992405e-11
}
],
"language": "ja"
}
バッチリ時系列と共にセリフが取れている。ただ環境音などセリフ以外の音も欲しいので、もう1つ、panns-inference(PANNs、AudioSetの527クラス分類モデルCnn14)を使う。こちらは「音楽が鳴っているか」「環境音は何か」といった、セリフ以外の音の手がかり抽出用だ。
pip install panns-inference
import wave, numpy as np
from panns_inference import AudioTagging
from panns_inference.config import labels
with wave.open("test_audio_32k.wav", "rb") as w:
data = np.frombuffer(w.readframes(w.getnframes()), dtype=np.int16).astype(np.float32) / 32768.0
audio = data[None, :] # (1, samples)
model = AudioTagging(checkpoint_path=".panns/Cnn14_mAP=0.431.pth", device="cpu")
clipwise_output, _ = model.inference(audio)
probs = clipwise_output[0]
order = np.argsort(probs)[::-1][:10]
for i in order:
print(f"{probs[i]:.3f} {labels[i]}")
0.851 Speech
0.312 Inside, small room
0.224 Babbling
0.156 Child speech, kid speaking
0.093 Giggle
0.062 Baby cry, infant cry
0.050 Whimper
0.047 Baby laughter
0.037 Belly laugh
0.030 Crying, sobbing
「Child speech」や「Baby cry」など、ん?的なものも出ているが、取得はできている。ここまでネタが揃えば、後はAIに作って!でOK(笑)。
実は今回もう1つ試したことがある。日頃この手のコードはClaude Code(Sonnet 5)に任せているが、今回はdeepseek-harnessと、Qwen3.8-27Bを使用。Claude Codeがリミット中など使えないときに代替えできるか?のテストも兼ねた。結果はOK。
integrated_multimodal_description:
[Shot 1] Live-action, vlog style, handheld selfie medium close-up. A young woman with long brown hair, wearing a white lace top, cream cardigan, and plaid skirt, walks down a sunny street in Roppongi. The camera shakes slightly as she moves forward, capturing storefronts and pedestrians in the background. She looks around casually and speaks to the camera with a clear, casual voice (S1): <d>[Japanese] 今日は六本木をブラブラしてるんだけどいいお店ないかな?</d>
[Shot 2] At 00:03.50, the camera pans right as the woman spots a cafe entrance. Her expression brightens with excitement. She turns and walks through the glass door. The young male staff member inside (S2) greets her politely: <d>[Japanese] いらっしゃいませーす</d>
[Shot 3] At 00:06.00, the shot cuts to a static handheld view inside the cafe. The woman stands at the counter facing two young male staff members in white shirts. She orders: <d>[Japanese] すみません、いちごのケーキ一つください</d> The staff member (S2) responds: <d>[Japanese] はい、かしこまりました</d>
[Shot 4] At 00:09.00, the shot cuts to a static medium shot of the woman sitting at a wooden table. A strawberry shortcake and an iced drink are in front of her. She takes a bite with a spoon, chews, and nods happily, saying: <d>[Japanese] うん!</d> She takes another bite and exclaims with delight: <d>[Japanese] おいしい!</d>
[Shot 5] At 00:12.00, the shot cuts back to a handheld selfie view on the street. The woman is walking again, smiling broadly and talking to the camera with a playful tone: <d>[Japanese] ここ、やばい、店員さんイケメン</d>
overall_soundscape: Ambient city noise including distant traffic and footsteps on pavement. The sound of a door opening and closing, followed by the quiet hum of a cafe interior with coffee machines and soft chatter. The clink of a spoon against a plate and the rustle of clothing as the subject moves.
non_diegetic_music: N/A
このプロンプトを使い、実際MiniMax-H3で生成するとこのような動画が出力された。完全コピーではないものの、なかなかいい感じなのが分かる。
テストでほかにもいろいろな動画を試したところ、得意なのはこの手の緩い系。苦手なのは、動きが速い戦闘シーンやタイポグラフィで目まぐるしく次々変わるような動画。この辺りはVisionが追いつかないのだろう。
これでお題の“動画Visionを使って動画生成用のプロンプトを作る”は完了。ただ実際運用すると問題が発生した……。
動画対応Visionは動かせる環境を選ぶ
ここまでは順調だったのだが、実際にこのツールをいろいろな環境で動かそうとして、1つ壁にぶつかった。それは“LLM自体は動画入力に対応していても、動かすサーバー側が対応しているとは限らない”ということだ。
具体的に書くと、ローカルLLMを動かす一般的なllama.cpp(とそれを使ったLM StudioなどのGUIラッパー)は、Vision対応モデルの画像入力なら普通に扱える。しかし動画そのもの(video_url)を受け付けるAPIには対応していない。つまり、たまたま最初に試したvLLMはOKで、ほかのAI PCで動作するLM Studioでは使えないのだ。
以降は今回のお題からは脱線するが、より多くの環境で使えるように……の改良版となる。
そこで、動画Visionが使えないなら、フレームを抜き出し、画像Visionに渡せばいいのでは……と考えた。動画Vision対応版では、映像解析(Pass1)とプロンプト生成(Pass2)の2段階で処理しており、フェード検出、ASR、音声タグは共通。異なるのはPass1の入力方法だけだ。方法は以下の通り。
- ffmpegのfpsフィルタで、コンテンツ範囲から等間隔に(既定1秒に1枚、最大32枚)jpgフレームを抜き出す
- 各フレームの直前に「Frame at 3.00s」のようなタイムスタンプ見出しを添えて、image_urlの配列としてVision LLMに渡す
実際に同じ“六本木カフェVlog”を、Pass1(フレーム15枚+文字起こし+音声タグ)で実行すると解析結果は以下のようになる。
{
"style": "live-action, vlog, handheld selfie, urban, casual",
"shots": [
{ "index": 1, "time_range": "0.0s-3.0s",
"subjects": "Young woman with reddish-brown hair, wearing a white lace camisole, white knit cardigan, and a pastel plaid skirt. She is holding the camera with her right hand.",
"actions": "The woman walks down a city sidewalk while talking to the camera. She looks around, scanning the shops on the left.",
"environment": "City street in Roppongi, daytime, overcast lighting. Shops and trees line the street." },
{ "index": 3, "time_range": "5.0s-8.0s",
"subjects": "The woman (back/side profile) at the counter. Two male baristas in white shirts and black aprons standing behind the counter.",
"actions": "The woman stands at the counter talking to the staff. The baristas smile and listen.",
"environment": "Cafe interior, counter with coffee machines and pastries, warm lighting, white tiled wall." }
],
"speakers": [
{ "id": "S1", "description": "Young female, on-screen, bright and energetic tone, fast speaking rate, Japanese accent" },
{ "id": "S2", "description": "Male, on-screen (barista), polite and calm tone, standard speaking rate, Japanese accent" }
],
"dialogue": [
{ "time": "0.0s", "speaker": "S1", "text": "今日は六本木をブラブラしてるんだけどいいお店ないかな?", "confidence": "high" },
{ "time": "3.16s", "speaker": "S1", "text": "わ、ここ!入ってみよう!", "confidence": "high" },
{ "time": "4.94s", "speaker": "S2", "text": "いらっしゃいませーす", "confidence": "high" },
{ "time": "6.0s", "speaker": "S1", "text": "すみません、いちごのケーキ一つください", "confidence": "high" },
{ "time": "8.08s", "speaker": "S2", "text": "はい、かしこまりました", "confidence": "high" },
{ "time": "9.0s", "speaker": "S1", "text": "うん!", "confidence": "high" },
{ "time": "11.16s","speaker": "S1", "text": "おいしい!", "confidence": "high" },
{ "time": "12.06s","speaker": "S1", "text": "ここ、やばい、店員さんイケメン", "confidence": "high" }
]
}
※長くなるのでshot 1・3のみ抜粋。実際は5ショット分すべてにcomposition/camera_motionまで含まれる)
ショット割り、話者、セリフの内容自体はASRの文字起こし(合体させたテキスト)がそのまま反映されたもので、フレーム画像側はそこに「誰がどんな見た目で、どんな構図、動作をしているか」という視覚情報を足す。両者を合わせることで、動画まるごと版と遜色ない解析結果が組み立てられている。
ここからPass2(プロンプト生成)を経て最終的なH3プロンプトになる流れは動画版と共通。できたプロンプトをMiniMax-H3で生成した結果が以下の動画となる。
いかがだろうか。動画対応Visionで解析した場合と変わらない仕上がりになっているのが分かる。この方法であれば、LM Studioが使えるので多くの環境で利用可能だ。
動画Vision版と比較すると、1.0fpsで15枚抽出するため、1秒未満の細かい動き(カメラ揺れの詳細など)は取りこぼしやすい。一方、ペイロード(データ)は動画版より軽い(mp4丸ごとbase64 vs jpegの集合)のがメリットとなる。
いずれにしてもここまでプロンプト化されていれば、被写体、衣装、背景、セリフなどを変えるのも容易。後はお好みで!となる。
とはいえ、できた動画2つ観れば分かるように、構図や動き、登場人物の特徴などが異なり、完コピではない。当たらずと雖も遠からず的な映像となる。
もっと似せるには、構図を固定するControlNetを使ったり、それこそリファレンスとして元動画を入れる(MiniMax-H3は対応)という荒技もある。特に後者は、元動画のまま人物や衣装、背景だけを変更できる。使い方によっては問題になりそうな手法だ。
いずれにしても今回はVisionで動画を解析したい!が主な目的だったので、これについては十分達成している。
制限事項として、LLM側で扱える動画の長さはモデルによって異なり、MiniMax H3は最大15秒となる。これを超える尺に関しては、分割して生成、最後に連結など、オペレーションが面倒になる。対象動画は、10~15秒程度にするのがコツといえよう。
以上のように、画像/動画Vision対応LLMを使えば、短い尺なら解析することができた。その内容をMiniMax-H3に整形、生成/出力した結果はご覧の通り。若干機能追加したものを、GitHubに公開している。また、ComfyUIカスタムノードもaroslanov氏によって移植され公開されたので興味のある人はぜひ試してほしい。









![WATCH ME [Explicit] 製品画像:1位](https://m.media-amazon.com/images/I/311P5J1e4cL._SL160_.jpg)






![【Amazon.co.jp限定】 伊藤園 磨かれて、澄みきった日本の水 2L 8本 ラベルレス [ ケース ] [ 水 ] [ ペットボトル ] [ 箱買い ] [ ストック ] [ 水分補給 ] 製品画像:5位](https://m.media-amazon.com/images/I/41m8ly-SllL._SL160_.jpg)




