0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Radeon 8060SでLTX-2.5の動画・音声生成をCLI化する — 写真から競馬場を走るデロリアンを生成

0
Posted at

AMD Ryzen AI MAX+ 395の内蔵GPU、Radeon 8060S(gfx1151)で、LTX-2.5による音声付き動画生成を動かしました。 ComfyUIは使わず、PythonとDiffusersを使ったCLIから実行します。

テキストからの生成に加え、写真を開始画像として指定する機能も実装しました。試した題材は「競馬の馬群の先頭をデロリアンが走る映像」です。レース風景と車の写真を事前に合成し、その1枚から768×512、24 fps、121フレーム(約5秒)、48 kHz音声付きのMP4を書き出せました。

この記事は2026年9月25〜26日の実機検証記録です。導入方法と使い方に加え、48 GiBのGPUメモリで動かすために分けた処理や、デコードが長時間完了しなかった際の復旧方法を紹介します。

コードはGitHubで公開しています。記事の対象はコミット929bb48です。

使用環境

項目 構成
本体 GMKtec NucBox EVO X2
CPU AMD Ryzen AI MAX+ 395
GPU Radeon 8060S Graphics / gfx1151
メモリ 96 GB統合メモリ、BIOSでGPU側に48 GBを割り当て
OS側メモリ 約45 GiB
OS Ubuntu 26.04
Python 3.12
PyTorch 2.11.0+rocm7.13.0
Diffusers 0.40.0.dev0(Git版)
Transformers 5.15.0
パッケージ管理 uv

PyTorchのバージョンは、このプロジェクトの仮想環境で確認したものです。システム全体のROCmバージョンとは区別しています。Diffusersの取得元コミットはuv.lockに固定されており、今回の環境ではd5baa4fb548294f47dbca49890abd4b291204c60です。

8060Sは統合メモリを使うGPUですが、今回の構成でGPU側に見えている容量は48 GiBです。96 GBすべてをGPUメモリとして使える前提にはしていません。他のGPUやメモリ構成では未検証です。

作成したCLI

コマンドはltx25 generateとltx25 decodeの2つです。

コマンド 処理
ltx25 generate --prompt "..." テキストから動画・音声を生成
ltx25 generate --image image.png --prompt "..." 画像を開始状態として動画・音声を生成
ltx25 decode --input saved.latents.pt --output result.mp4 保存済みlatentから映像・音声を書き出す

画像指定の有無で、LTX2PipelineとLTX2ImageToVideoPipelineを切り替えます。これらのパイプラインはDiffusers公式ドキュメントでも説明されています。

今回のCLIはdistilledモデルの8ステップ推論を使います。画像の自動合成、既存動画への物体追加、開始・終了画像の補間は実装していません。

1. セットアップ

uvと、ホスト側でGPUを利用できるAMDドライバ環境を用意してから実行します。

git clone https://github.com/kotetsuy/LTX-25-cli.git
cd LTX-25-cli

unset HSA_OVERRIDE_GFX_VERSION
uv sync --locked
uv run python scripts/check_env.py

check_env.pyはGPUの認識、BF16行列演算、必要なDiffusersクラスの存在を確認します。ROCm環境でもPyTorchのGPU API名はtorch.cudaです。

このプロジェクトのpyproject.tomlは、torch・torchvision・torchaudioをAMDのgfx1151専用indexから取得する設定です。

[[tool.uv.index]]
name = "rocm-gfx1151"
url = "https://repo.amd.com/rocm/whl/gfx1151/"

開発時には汎用wheelでhipErrorInvalidImageが発生しました。今回はネイティブgfx1151ビルドを使うため、HSA_OVERRIDE_GFX_VERSIONは設定しません。依存関係を再現する際は、個別に最新版へ更新するのではなく、まずuv sync --lockedで同梱のロックファイルを使います。

2. モデルをダウンロードする

uv run python scripts/download_models.py --dry-run
uv run python scripts/download_models.py

取得元はLightricks/LTX-2.5-Diffusers、保存先はmodels/ltx-2.5-diffusers/です。

モデル用に約67 GiB、そのほかにPython環境・ダウンロードキャッシュ・生成物の空き容量が必要です。取得スクリプトはシャードのindexを参照し、重複した重みや通常使わないtransformer_full/を除外します。--dry-runもindex確認のためネットワークへアクセスします。

アクセス制限で取得できない場合は、モデルの利用条件を確認し、承認されたHugging Faceアカウントでログインします。

uv run hf auth login

--full-transformerや--loraで追加の重みを取得する機能もありますが、今回の生成手順では使用しません。

3. テキストから動画を生成する

まず、検証時に使ったMIOpen設定を用意します。

mkdir -p miopen-cache
export MIOPEN_FIND_MODE=FAST
export MIOPEN_USER_DB_PATH="$PWD/miopen-cache"

この環境ではgfx1151用のMIOpen perf DBが読めない警告が出ています。上の設定は探索モードとユーザーDBの保存先を指定するもので、すべての停止やメモリ不足を防ぐ保証はありません。

続いて動画を生成します。

uv run ltx25 generate \
  --prompt "A cinematic shot of a red fox walking through a snowy forest at dawn, the camera tracking alongside, snow crunching underfoot." \
  --width 768 --height 512 --frames 121 --fps 24 \
  --seed 42 --offload none \
  --output outputs/fox.mp4

幅・高さは32の倍数、フレーム数は8n+1、fpsは正の整数で指定します。121 / 24なので、出力時間は約5.04秒です。VAEの空間・時間分割デコードは標準で有効です。

出力先がoutputs/fox.mp4の場合、次のファイルを使います。

ファイル 内容
fox.embeds.pt テキストの埋め込みキャッシュ
fox.latents.pt デコード前の映像・音声latentと生成条件
fox.mp4 音声付き動画
fox.metrics.json 一括生成完了時の時間・メモリ計測

同名の出力は上書きされるので、結果を比較したい場合は--outputを変更します。

4. 写真から「競馬場を走るデロリアン」を生成する

先に開始画像を1枚作る

元の素材は、デロリアンの写真と、横から撮影した競馬レースの写真の2枚です。

今回は別の画像編集機能でレース写真の左側を広げ、馬群の前に左向きのデロリアンを配置しました。車の大きさ、地面との接地、影を合わせた合成画像をinputs/race_delorean.pngとして保存しています。

車の写真 + レース写真
        ↓ 事前に画像編集で合成
開始画像1枚
        ↓ ltx25 generate --image
音声付き動画

2枚の写真を合成する処理は、このCLIには含まれていません。 --imageには、最初のフレームとして使いたい完成画像を1枚渡します。画像を作る際は、車と馬の進行方向を揃え、進行方向に余白を持たせると構図を指定しやすくなります。

入力写真・合成画像・生成動画はGit管理から除外しているため、リポジトリをcloneしても付属しません。以下のコマンドを試す場合は、自分で開始画像を用意してください。

開始画像と動きの指示を渡す

uv run ltx25 generate \
  --image inputs/race_delorean.png \
  --prompt "A silver DeLorean drives toward the left ahead of galloping racehorses on a dirt racetrack. The car wheels rotate as the car moves forward, keeping a steady lead. The horses gallop behind it, kicking up light dust. The camera tracks alongside smoothly, keeping the car and leading horses in view. Natural engine sound and rhythmic hoofbeats." \
  --width 768 --height 512 --frames 121 --fps 24 \
  --seed 42 --offload none \
  --output outputs/race_delorean.mp4

プロンプトでは、車と馬の動きだけでなく、カメラが横から追従することも指定しました。画像入力でもテキスト生成時と同じモデル一式を使い、追加のモデルダウンロードは不要でした。

画像の縦横比と出力サイズが違う場合は、--image-fitで調整できます。

指定 動作
--image-fit contain(標準) 画像全体を残し、余った部分を黒い余白にする
--image-fit cover 画面を埋めるよう拡大・縮小し、はみ出す部分を中央基準で切り取る

どちらも縦横比を維持します。写真のEXIF回転を反映し、透過部分は黒背景に変換します。アニメーション画像の場合は最初のフレームを使います。

今回の結果では、途中と最後のフレームで車が馬群の前にいる構図を確認できました。一方で、車体や馬の細部には生成による変化があります。入力画像は開始状態の指定であり、元写真の細部や特定の動きを最後まで完全に維持する仕組みではありません。

5. デコードが終わらない場合はlatentから再開する

今回の画像入力テストでは、8ステップの推論とlatent保存までは完了しました。しかし、その後のデコードが数分経っても終了しなかったため、実行中のプロセスを終了しました。

保存されたoutputs/race_delorean.latents.ptを使い、次のコマンドでデコードだけを別プロセスで実行すると、MP4を書き出せました。

mkdir -p miopen-cache
MIOPEN_FIND_MODE=FAST MIOPEN_USER_DB_PATH="$PWD/miopen-cache" \
  uv run ltx25 decode \
    --input outputs/race_delorean.latents.pt \
    --output outputs/race_delorean.mp4

最初の画像入力テストでは上記のMIOpen設定を明示せずに実行し、再試行時に設定を変更しました。MIOpen設定の変更とプロセス分離を同時に行っているため、どちらが改善に寄与したかは切り分けできていません。 記事前半の手順では、この再試行時の設定を先に用意していますが、その条件で画像入力から一括完走するかは別途検証が必要です。

再デコードには元画像もテキストエンコーダーも不要です。generateを再実行すると推論からやり直すので、保存済みlatentの書き出しにはdecodeを使います。再試行前には、先のプロセスが終了しGPUメモリが解放されていることを確認します。

なぜ処理を分けるのか

今回の環境では、BF16のモデル全体を一度にロードする構成はメモリに収まりませんでした。そこで、必要な部品だけを段階的にロードしています。

  1. テキストエンコーダーを別プロセスで動かし、埋め込みをファイルへ保存する。
  2. そのプロセスを終了してメモリを解放する。
  3. 動画生成に必要な部品だけをロードし、8ステップの推論を行う。
  4. 映像・音声latentを保存し、ファイルをfsyncする。
  5. Transformerとconnectorsを解放してからデコードする。

テキスト側は約23 GiB、生成側は約43 GiBの重みをロードする構成です。CPUオフロードを使えば必ず安全になるわけではなく、開発中にはOS側メモリの圧迫とクラッシュを経験しました。そのため、このマシンでは--offload noneを使っています。

また、VAEには空間タイリングと時間方向の分割デコードを有効にしています。

pipe.vae.enable_tiling()
pipe.vae.use_framewise_decoding = True

distilled推論では、DISTILLED_SIGMA_VALUESを渡して8ステップを指定しています。単にnum_inference_steps=8を設定する構成ではありません。CLIにはステップ数変更用の引数を設けていません。

実測結果

テキスト入力は2026年9月25日のCLI一括生成、画像入力は翌日のデロリアンのテストです。同じ条件を繰り返して測定したベンチマークではありません。

計測項目 テキスト入力 画像入力
解像度・フレーム数 768×512・121 768×512・121
推論ステップ 8 8
generate:denoise区間 120.2秒 161.0秒
同区間のピークGPUメモリ 44.93 GiB 45.50 GiB
デコード 一括処理で17.8秒 別プロセスで映像15.3秒+音声1.6秒
MP4書き出し 0.9秒 1.0秒
一括処理の計測区間合計 182.8秒 デコードを中断したため集計なし

画像入力側のgenerate:denoiseには、パイプライン内部の画像前処理やVAEエンコードなども含まれます。そのため、この値だけで純粋な推論速度の差を比較することはできません。

ピークGPUメモリはPyTorchが計測した割り当て済みメモリであり、システム全体の使用量ではありません。計測区間合計も、プロセス起動などを含む実際の経過時間とは異なります。

画像入力の最終MP4はPyAVで読み戻し、次を確認しました。

項目 結果
映像 全121フレームをデコード
解像度 768×512
フレームレート 24 fps
映像時間 約5.04秒
音声 48 kHz、全音声フレームをデコード

テストと実装ファイル

GPUを使わずに実行できるテストも用意しました。

uv run python -m unittest discover -s tests -v

2026年9月26日時点で8件が通過しています。引数検証、画像の読み込み、余白・切り抜き・EXIF回転、パイプライン選択、画像の引き渡しなどを確認します。パイプライン呼び出しのテストはモックを使用しており、実機での動画生成確認とは別です。

主な実装は以下のファイルにあります。

ファイル 役割
src/ltx25_cli/cli.py 引数解析・入力検証
src/ltx25_cli/images.py 開始画像の読み込みとサイズ調整
src/ltx25_cli/runner.py 埋め込み生成・推論・デコード・計測
scripts/download_models.py モデルの選択的ダウンロード
scripts/check_env.py GPU・ライブラリの確認

古い実装のscripts/t2v_smoke.pyには現在のメモリ対策が揃っていないため、通常はltx25コマンドを使います。HANDOFF.mdには過去の調査記録も残っていますが、現在の使い方はREADMEを参照してください。

今後確認したいこと

テキストからの一括生成と、画像からの推論・保存済みlatentの再デコードまで確認できました。今後は、画像入力でも一括処理が安定して完走する条件を調べ、MIOpen設定とプロセス分離の効果を個別に確認したいと考えています。

画質についても、車体の形状や馬の動きがどの程度維持されるか、開始画像とプロンプトを変えながら検証する余地があります。

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?