この記事は Zenn に投稿した記事 の転載です。最新版・コメントは Zenn 側をご覧ください。
ローカルの Wan2.2 I2V で、静止画に「本物のカメラワーク」を足そうとして踏んだ落とし穴を全部書きます。M1 Max 64GB・ComfyUI で、1枚の風景写真から 5 秒のカメラ飛行クリップを作り、Web に載せるまでの実録です。出す数字はすべて実測(ログの秒数・ffprobe・ブラウザ計測)で、憶測は書きません。
先に持ち帰ってほしい 4 点:
- Apple Silicon の統合メモリでは、生成時間は作業量に比例しない。ある解像度を境に崖から落ちる。
- 長時間生成を中断したら、次を投げる前に ComfyUI を再起動する。中断した回の GPU 確保が残り、次の生成が「最初から崖の上」で始まる。
- I2V のプロンプトで「絵を再描写」してはいけない。写真は最初のフレーム。書くべきは「加える動き」だけ。再描写すると別のシーンに漂流する。
-
SeedVR2 動画アップスケーラは Apple Silicon では完走しない(
conv3d未実装)。代替は ffmpeg lanczos。
先に断っておくと、本記事は設定と実測の実録です。消費電力や、LTX をさらに追い込んだ場合の到達点は未計測(1 リトライで打ち切り)。出す数字は生成ログ・
ffprobe・ブラウザの seek 計測など、実際に測った値だけです。
崖①:解像度を「ちょっと」上げただけで 27 秒/step → 1693 秒/step
同じワークフロー(Wan2.2 I2V A14B Q5_K_M・MoE 2 エキスパート・Lightning 4step LoRA)で、解像度とフレーム数だけ変えて回した実測がこれです。
| 解像度×フレーム | 画素×frames | 対最小比 | 実測 s/step | 時間比 | 結果 |
|---|---|---|---|---|---|
| 480×320×25f | 3,840,000 | 1.0× | 27.6 / 26.5 | 1× | 正常(全体 182.7 秒で完走) |
| 720×480×81f | 27,993,600 | 7.29× | 461(high) / 615(low) | 17× | 完走 |
| 864×576×81f | 40,310,784 | 10.50× | 1693 → 2317(悪化) | 61×→84× | 破綻・41 分で中断 |
作業量は 7.3 倍・10.5 倍なのに、時間は 17 倍・61 倍。線形ではありません。61 倍の側で何が起きていたかというと、スワップが 21.9GB / 22.5GB(空き 539MB)まで埋まり、スラッシングしていました。統合メモリはある一線を越えると、計算ではなくディスクとの往復に時間を溶かします。864×576 は、この機体の Wan2.2 A14B には無理をさせすぎでした。
崖②:中断が「次の生成」を殺す
ここが一番ハマった罠です。864×576 を 41 分で中断し、720×480 に落として投げ直したら、同じ設定なのに 1 step 目が 28 分かかりました。理由を実測で追うと、GPU の確保メモリがこうなっていました。
| タイミング | GPU Alloc system memory |
|---|---|
| 864×576 を中断した直後 | 72,276,721,664 B(約 72.3GB・物理 64GB を超過) |
| ComfyUI プロセスを停止した後 | 42,425,729,024 B(約 42.4GB)=約 30GB 解放 |
| 再起動直後の MPS 空き(ログ) | 52.70GB free / 64.00GB total |
中断した回の確保が解放されず、物理を超えたまま次の生成が始まっていた=最初から崖の上。ComfyUI を再起動して 42GB に戻したら、720×480 は 461 秒/step の正常速度で走りました。ここに気づかないと「同じ設定なのに遅い」を無限に踏みます。教訓は単純で、長時間生成を中断したら、次を投げる前にプロセスを再起動する。なお、この確保が解放されない原因が ComfyUI 側の解放漏れか、中断シグナルの扱いか、MPS の統合メモリ管理かは、今回は特定していません(観測した事実=「再起動で 30GB 戻り、速度が正常化した」まで)。
崖③:プロンプトで「絵を再描写」すると別世界に飛ぶ
Wan2.2 と対抗馬の LTX-2.3 を、同じキーフレーム・同じ趣旨のプロンプト・同尺(81 フレーム)で比較しました。ところが LTX の 1 周目は、入力の滝の写真を捨てて、ヤシのプランテーションと山と空という無関係な風景に化けました。

LTX-2.3・1 周目の失敗。frame0(左端)は元の滝。以降が別の風景に漂流している。
これを「LTX の負け」と書くのは不正確でした。原因は私のプロンプトです。元画像は地上の滝なのに、私は aerial cinematic shot flying over rainforest canopy(上空から樹冠の上を飛ぶ)と書いていました。Wan は画像を優先して矛盾を吸収し、LTX はテキストに従っただけです。
なぜこうなるのか、一次情報が的確に言語化していました。
Your photo is the first frame. If you spend your prompt re-describing the picture, you have told the model nothing about what should change, and you get generic drift.(写真は最初のフレーム。プロンプトで絵を再描写すると「何を変えるか」を何も伝えていないことになり、別物への漂流が起きる)
— segmind / VEED の Wan I2V プロンプトガイドより
I2V では画像が最初のフレームで、被写体も場所も光もモデルは既に見ています。だからプロンプトの全語は「加える動き」に使うべきで、絵の描写や、写っていない視点(地上写真に aerial)を足すと、それを作るために画面を作り替える=漂流します。修正はこうしました。
- プロンプトを画像の内容(地上・滝・苔むした岩・渓流)に合わせ、動きだけを書く
-
--image <path> 0 1.0で frame0 を強度 1.0 でアンカー(1 周目は強度指定なしだった) - cfg を 2.3 → 3.0 に上げて追従を強める(※「動かない時は cfg を下げる」の逆。症状が「動かない」ではなく「離脱」の時は逆に振るのが正しい)
公平な条件で再比較:数値だけでは決められない
条件を直して LTX を回し直し、Wan と並べました。上段が Wan2.2、下段が LTX-2.3、左から frame 0 / 20 / 40 / 60 / 80 です。

公平な条件での再比較。上=Wan2.2 / 下=LTX-2.3。frame 0/20/40/60/80。
フレーム 0 との差(256×171 にリサイズし RGB 平均絶対差。小さいほど元画像を保持)と、隣接サンプル間の変化量(大きいほど動いている)を測りました。
| frame | frame0乖離 Wan | frame0乖離 LTX | 変化量 Wan | 変化量 LTX |
|---|---|---|---|---|
| 10 | 0.0455 | 0.1239 | 0.0455 | 0.1239 |
| 20 | 0.0826 | 0.1352 | 0.0730 | 0.0626 |
| 40 | 0.1387 | 0.1385 | 0.1139 | 0.0832 |
| 60 | 0.1828 | 0.1395 | 0.1204 | 0.0833 |
| 80 | 0.2042 | 0.1420 | 0.1305 | 0.1025 |
ここが本記事の肝です。最終乖離だけ見ると Wan(0.204) > LTX(0.142) で、Wan の方が「元画像から離れている」ように見えます。だがこの指標は「遠くまで旅した」と「別物になった」を区別できません。数値単独では判定できず、フレームの目視と併せて初めて意味を持ちます。
- LTX:frame10 で一気に跳ね(0.124)、以降ほぼ横ばい = 早々に別の滝へ乗り換えてから、あまり動かない
- Wan:連続的に増加し、変化量も加速 = 同じ滝を保ったまま奥へ進み続ける
判定:優劣ではなく用途で割れる
| Wan2.2 I2V A14B | LTX-2.3 (MLX q8) | |
|---|---|---|
| 入力シーンの保持 | ◎ 連続的に保つ | △ 別の場所へ移る |
| 解像感 | ○ | ◎ 明確にシャープ |
| 生成解像度 | 720×480 | 896×576 |
| サンプリング所要 | 約 36 分 | 約 20 分(68.75 秒/step×16) |
「1 枚絵から綺麗な風景動画を作る」なら LTX-2.3 が勝ちます。絵はシャープで、解像度も高く、生成も速い。一方、「元の風景を保ったまま潜る」なら Wan2.2。今回の用途はスクロールで世界の中へ入っていく演出で、動画が読み込めない時のフォールバックが同じ静止画の 2.5D パララックスでした。動画が別の場所を映していると切り替わった瞬間に破綻するので、シーン保持が要件=Wan2.2 を採用。好みではなく統合上の制約です。

採用した Wan2.2 クリップ。同じ滝を保ったまま、手前の枝が流れて奥へ進む。
崖④:SeedVR2 は Apple Silicon で完走しない(1 時間 42 分を溶かした)
本命の仕上げは、低解像度で生成して専用アップスケーラ SeedVR2 で 1080 に上げる計画でした。これが完走しません。
NotImplementedError: convolution_overrideable not implemented → F.conv3d
[INFO] Prompt executed in 01:42:03 ← 出力ゼロ
VAE デコーダが 3D 畳み込み(conv3d)を使い、MPS がこの演算子を実装していません。メモリ不足ではないので、解像度もバッチも下げても直りません。SeedVR2LoadDiTModel / SeedVR2LoadVAEModel とも device の選択肢は ['mps'] のみで CPU に逃がす経路もありません。厄介なのは、エンコード段(≒104 秒/batch×27=47 分)も DiT アップスケール本体(≒20 秒/batch×27=9 分)も完走すること。順調に見えて最後のデコードで全部無になります。
ただし、生成物は無傷でした。パイプラインを「生の出力を、上げ処理より先に保存する」順序にしていたためです。これは 864×576 で 41 分ぶんを丸ごと失った反省から入れた設計変更で、二度目の事故(1 時間 42 分)でサンプリング結果を守りました。多段パイプラインでは、重い後処理より前に必ず素の出力を保存する。
代替は ffmpeg lanczos です。生成ネイティブが 720 幅なので、それを超える拡大は偽の解像度に容量を払うだけ。実測で 960=3.8MB / 1152=4.8MB / 1280=5.5MB となり、960 幅(1.33 倍)を採用しました。
Web に載せる:スクロールでフレーム送りするなら全 I フレーム
このクリップは、スクロール量に合わせて動画の currentTime を動かす(フレームを送る)形で Web ページに載せました。ここで効くのがエンコード設定です。同じ素材を GOP 違いで書き出し、Chromium で seek を 82 回繰り返して遅延を測りました。
| エンコード | avg | p95 | max | サイズ |
|---|---|---|---|---|
| 全 I フレーム(-g 1) | 20.6ms | 22.9ms | 40.3ms | 6,081KB |
| GOP=10 | 48.3ms | 66.7ms | 71.9ms | 4,514KB |
| GOP=30 | 74.5ms | 126.3ms | 271.1ms | 4,150KB |
GOP が長いと seek のたびに直前のキーフレームから再デコードが走り、GOP=30 は最悪 271ms=目に見えて引っかかります。全 I フレームなら常に 20ms 前後で安定し、スクロールに追従します。容量は増えますが、葉の細かい絵柄はフレーム間予測が効かず差が小さいので、滑らかさを取る方が得。容量は GOP を伸ばさず、crf と幅で詰めるのが正解です。
実ページで、スクロール進捗(シーン内 lA)と再生位置が一致するかも測りました。lA=0.05→0.251s / 0.2→0.983s / 0.35→1.76s=理論値(lA×5.06 秒)とほぼ完全に一致。フレーム送りは狙いどおり動いています。
まとめ:ローカル動画生成の「見えない崖」を測る
クラウド API と違い、ローカルの動画生成は自分のハードの限界がそのまま結果に出ます。今回測って分かったのは、時間は作業量に比例しない・中断は次を殺す・プロンプトは絵を再描写しない・アップスケーラは動くとは限らないという 4 つの崖でした。どれも「動いた」と「狙いどおり動いた」の間にある落とし穴で、実測しないと踏みます。最終的に、同じ滝を保ったまま森の奥へ潜っていく 5 秒のクリップが、Web ページの入口で流れています。