Opus 5.5でCSVからグラフ動画を作るには?PythonでMP4に書き出す手順
CSVの検証からOpus 5.5への指示、PythonとFFmpegでの動画出力まで。ダウンロードできる参照プロジェクトで数値と表示を確認します。
Opus 5.5でグラフ動画を作るには、検証済みデータと表示仕様を渡し、Pythonでフレームを描画してFFmpegでエンコードします。 数値そのものはアニメーションさせず、元の値を表示し続けます。棒が伸びる演出を、事業指標が時間とともに増えたという意味にすり替えてはいけません。
このチュートリアルでは、CSVの3項目を表示する12秒、1280 × 720のMP4を作ります。報告書のデータをプレゼンやSNS向け動画にしたい方で、ローカルのコマンドを実行できる方が対象です。データを準備し、Claudeへの指示を作り、グラフを確認して書き出し、完成動画の数値まで検証します。基本設定やほかの制作方法はOpus 5.5の動画制作ガイドを参照してください。
参照動画を確認し、プロジェクトをダウンロードする
編集側で作成した参照プロジェクトを、実際にローカルでレンダリングした動画です。数値は架空、ラベルは英語で、意図的に無音にしています。Ofoxの顧客、売上、サポートの実データではありません。
プロジェクト一式、MP4、完全版プロンプトをダウンロードできます。
検証範囲: 今回はClaude CodeからOpus 5.5を呼び出そうとしましたが、セッションが未ログインのため失敗しました。そのため、参照実装は編集側で作成し、CSV検証とPython/FFmpegのレンダリングをテストしました。今回のOpus出力や、一度の指示で成功した生成例としては扱っていません。プロンプトはご自身の利用権限のあるClaude環境で使えます。また、配布プロジェクトの再現にはモデル呼び出しが不要です。別のスクリーンショット動画のチュートリアルには、その制作時のモデル呼び出し成功記録がありますが、今回の検証を代替するものではありません。
グラフが何を示すのかを先に決める
サンプルは、窓口別の問い合わせ件数を比較するものです。時系列、コンバージョンファネル、成長グラフではありません。この区別は、動きの付け方にも、後から加えるナレーションにも関わります。
| 項目 | 件数 | この例での意味 |
|---|---|---|
| 120 | Emailに割り当てた架空の問い合わせ | |
| Chat | 180 | Chatに割り当てた架空の問い合わせ |
| Docs | 90 | Docsに割り当てた架空の問い合わせ |
合計は390件です。この架空の例では、ChatはEmailより60件多くなっています。しかし、Chatのコンバージョン率が高い、時間を節約できる、利用者の満足度が高いとは言えません。分母や成果の測定値がないためです。
3本の棒はゼロを起点にし、すべて同じ0〜200の尺度を使います。最終的な幅は描画領域の60%、90%、45%です。異なる尺度を読み解かなくても比較できるようにします。差を大きく見せるためだけに軸を途中から始めないでください。
実データでは、出典、抽出日、集計のタイムゾーン、単位、集計ルールをCSVと一緒に記録します。項目間に重複がないかも確認してください。重複があれば、足し合わせた値が重複のない総数を表すとは限りません。モデルに渡す前に個人情報を除き、利用権限のあるデータだけを共有します。
再現用の環境を準備する
アーカイブにはsample.csv、prepare.py、test_prepare.py、render.py、requirements.txt、prompt.txt、READMEが入っています。参照実装はPython 3.9以降、Pillow 11.3.0、H.264エンコーダー(libx264)が使えるFFmpegを利用します。FFmpegの実行ファイルにPATHが通っている必要があります。文字はローカルのTrueTypeフォントで描画します。配布プロジェクトのレンダリングにブラウザーやモデルAPIは不要です。
ローカルのフォルダーに展開し、そのフォルダーでコマンドを実行します。python3 -m venv .venvで仮想環境を作り、macOS/Linuxではsource .venv/bin/activate、Windows PowerShellでは.venv\Scripts\Activate.ps1で有効化してから、インストールと検証を行います。
python3 -m pip install -r requirements.txt
python3 prepare.py
python3 -m unittest test_prepare.py
ffmpeg -version
準備処理の期待値はValidated 3 rows. Total: 390です。違っていたら先に進まず確認します。5つのテストメソッドは、正常な入力、ゼロと境界値、引用符で囲んだラベル内のカンマ、不正な値、形式が崩れた行を検証します。パーサーのテスト成功は、動画のレンダリング成功とは別です。
CHART_FONTには、利用許諾のあるローカルのTrueTypeフォントを指定します。たとえば次のファイルが存在するmacOSでは、以下を使えます。
export CHART_FONT='/System/Library/Fonts/Supplemental/Arial.ttf'
Linuxでは、インストール済みのDejaVu Sansが/usr/share/fonts/truetype/dejavu/DejaVuSans.ttfにある場合があります。Windows PowerShellでは、Arialがあれば$env:CHART_FONT='C:\Windows\Fonts\arial.ttf'で指定できます。Pythonの起動コマンドは環境に合わせ、必要ならpython3をpythonに置き換えます。これらはパスの例であり、全端末にそのフォントがあるという保証ではありません。アーカイブにはフォントを同梱していません。表示言語の文字を含むフォントを選び、ライセンスも確認してください。
FFmpegが見つからない場合は、OSに適した信頼できる方法で導入します。FFmpegのダウンロードページにプラットフォーム別の選択肢があります。Claudeにコードの作成・修正を頼むにはご自身のアクセス権が必要で、プランの利用枠を消費する場合があります。配布Pythonコードの実行はモデルを呼び出しません。このローカルの例から、有料サービスやAPIの互換性まで確認済みとは言えません。
モデルに渡す前にCSVを検証する
ヘッダーとサンプルは次のとおりです。
label,value
Email,120
Chat,180
Docs,90
パーサーはPythonのCSVリーダーを使うため、"Email, shared"のように引用符で囲んだラベルは1セルとして読み取ります。UTF-8はBOMあり・なしの両方に対応します。変更されたヘッダー、重複または空のラベル、余分なセル、3行以外のデータ、数値でない値、負数、整数ではない値、無限大、グラフの上限を超える値は拒否します。
厳しく制限するのは、このレイアウトが「3項目の整数件数」用だからです。欠測値を黙ってゼロにしてはいけません。小数の比率を丸めて件数にしてもいけません。240という値を200で切り詰め、正しい棒として見せることもできません。
別のファイルを確認する場合はpython3 prepare.py my-data.csvを実行します。全行が検証を通った場合だけdata.jsonを書き出します。自分のデータをレンダリングするには、サンプルをバックアップしてsample.csvを置き換え、再検証します。レンダラーは毎回sample.csvを直接読み込んで検証します。 確認用JSONに黙って切り替えることはありません。
検証が失敗しても、古いout/chart.mp4が残っている場合があります。それを新しいレンダリングの成功結果として納品しないでください。レンダラーは一時MP4を書き出し、FFmpegが成功を返した場合だけ最終ファイルを置き換えます。成功時のコマンド出力とout/accepted-data.jsonを成果物と一緒に保存します。
ラベルは24文字以内です。さらに、選択したフォントで200ピクセルより幅が広くなるラベルは、グラフと重ならないようレンダラーが拒否します。略称の意味を説明して短くするか、ラベル列を明示的に設計し直してください。文字数だけでは収まりを保証できず、文字化けや欠けも目視確認が必要です。
Opusに範囲を限定した具体的な指示を渡す
検証済みの行を添付するか、次の仕様と一緒に貼り付けます。ダウンロード用の完全版プロンプトには、参照レイアウトの座標、検証ルール、文字設定も含まれています。以下は、ダウンロード済みプロジェクトを調整するときの短い指示例です。実行条件を揃えやすいよう英語で掲載します。
Edit this existing Python/Pillow and FFmpeg project to make a 12-second chart video.
Read sample.csv with the existing prepare.py parse() validator.
It contains exactly three rows: Email 120, Chat 180, Docs 90.
These are synthetic ticket counts, not real company results.
Keep output at 1280x720, 30fps and 360 RGB frames.
Keep a zero baseline and a shared, fixed maximum of 200.
Keep the exact numeric labels visible and unchanged at every frame.
Reveal bar widths only between frames 30 and 90; hold thereafter.
Use progress=max(0,min(1,(frame-30)/60)) for the reveal.
Do not use random values, network data or new packages.
Keep a permanent label: Synthetic example · tickets · fixed scale 0–200.
State that the reveal is not growth over time.
Preserve CSV validation and do not substitute zero for missing values.
If a value exceeds the maximum, stop and explain; do not clip it.
Return the changed file, explain changes, and give the render command.
Do not invent a test result. List tests that still need to be run.
回答は実行前に確認します。パッケージの変更やファイル操作が依頼に合っているかを見てください。インストール、データのアップロード、上書きはそれぞれ別の操作で、生成された指示に含まれているだけで必要になるわけではありません。この小さなグラフなら既存の依存関係で十分です。
伝えたい内容を変えるなら、先に仕様を変えます。「もっと派手にして」だけでは、守るべき条件がないまま動きだけが増えるかもしれません。「すべての値と尺度を維持し、導入を2秒にして、完成グラフを6秒以上静止表示する」のように指示したほうが具体的です。
タイムラインと固定尺度を確認する
30fpsで360フレームなら12秒です。参照実装では、最初の1秒は棒のない状態を保ち、次の2秒で棒を表示し、残り9秒は完成したグラフを読める状態で保持します。
| フレーム | 時間 | 期待する表示 |
|---|---|---|
| 0〜29 | 0秒〜1秒未満 | タイトル、ラベル、正確な数値、軸を表示。棒の幅はゼロ |
| 30〜89 | 1秒〜3秒未満 | 棒の幅だけが変化。数値ラベルは変わらない |
| 90〜359 | 3秒〜12秒未満 | すべての棒を最終的な長さで保持 |
中心となる計算は短いものです。
progress = max(0, min(1, (frame - 30) / 60))
width = round(value / 200 * 820 * progress)
描画領域の幅は820ピクセルです。最終状態ではEmailが492、Chatが738、Docsが369ピクセルになります。フレーム番号60では、ピクセル単位の丸めを除けば表示が半分まで進んでいますが、数値は元のままです。これは登場演出であり、途中時点の測定結果ではありません。
上下限を設けることで、進行度を0〜1に収めます。この制限がなければ、線形計算により開始前に負の幅、終了後に過大な幅が生じる可能性があります。データの図示では、行き過ぎるアニメーションが実測値より大きい値を示唆しかねません。参照実装では意図的に避けています。
MP4を書き出し、実ファイルを確認する
検証を通り、CHART_FONTを設定したらレンダリングします。
python3 render.py
スクリプトはPillowで360枚のRGBフレームを描き、FFmpegへパイプで渡します。CSVの内容からシェルコマンドを組み立てず、引数リストを使います。エンコーダー設定はlibx264、yuv420p、音声なし、+faststartです。入出力とエンコードの指定はFFmpegのドキュメント、描画処理はPillowのImageDrawリファレンスで確認できます。
期待する出力はout/chart.mp4、確認用の4フレーム、検証を通ったデータのコピーです。実際に使うアプリで再生します。プロセスの正常終了だけでは、文字が読めることや、誤解を招かないことまで確認できません。
FFmpegのツールが入っていれば、別途ファイル情報も確認します。
ffprobe -v error -select_streams v:0 \
-show_entries stream=codec_name,width,height,r_frame_rate,nb_frames \
-show_entries format=duration -of json out/chart.mp4
未変更のサンプルなら、H.264、1280 × 720、フレームレート30/1、長さ12秒が期待値です。コンテナーによってフレーム数のメタデータがない場合もあるので、フィールドがないことを0フレームと解釈しないでください。冒頭、棒の登場、最後の静止部分を再生し、最終数値が120、180、90、尺度が0〜200であることを確認します。このサンプルには意図的に音声トラックを入れていません。
意味を変えずに問題を直す
| 症状 | 最初の確認 | 適切な対処 |
|---|---|---|
prepare.pyで不正な値を指摘される | 欠けたセル、小数の比率、桁区切りのカンマ | データ形式を直す。欠測を黙ってゼロにしない |
| 200を超える値がある | 本当の件数か、単位が違わないか | 検証、棒の幅、目盛り、注記の共通上限を一緒に変更する |
| CSVを変えても数値が変わらない | 作業フォルダーのsample.csvを置き換えたか、処理は成功したか | そのファイルを検証して再実行し、新しいMP4とout/accepted-data.jsonを確認する |
| 長いラベルが拒否される | 使用フォントと対象言語での表示幅 | 意味を説明した略称にするか、ラベル列を再設計して棒の領域も確認する |
ffmpegやlibx264が見つからない | 検索パスとエンコーダー対応 | 信頼できる配布元から適切なFFmpegを導入し、バージョン確認を再実行する |
| MP4に音がない | 構成に音声が含まれるか | この例では無音が正常。必要ならリンク先の音声手順を参考に追加する |
| 棒が伸び続ける | 進行度の上下限、モデルが別の動きを追加していないか | フレームを基準にした上下限付きの計算に戻す |
データ量を増やすには、配列を長くするだけでなく設計を見直す必要があります。10項目なら縦長のレイアウト、グループ化、複数シーンが必要かもしれません。負数には正負を表す軸、百分率には明確な分母と表示規則が必要です。これらは今回の3項目の件数を扱う例では検証していません。
データと動画をセットで引き渡す
検証済みCSV、ソースプロジェクト、完成MP4に、出典、単位、尺度、出力設定、実データか架空データかを記した短い説明を添えます。このサンプルを共有するときは、架空データであることを示すラベルを残してください。会社のロゴに置き換え、実績だと受け取られる状態にしてはいけません。
ナレーションを付ける場合は音声と字幕を同期する手順を参照してください。そちらは別のRemotionプロジェクトを使うため、コマンドをこのPythonアーカイブにそのまま適用することはできません。縦型動画には横型から縦型へのレイアウト変更が参考になります。ラベル配置を変えず寸法だけ変えると読みにくくなります。次にデータではなく製品の説明動画を作るなら、実際のスクリーンショットを使うデモ制作から始めてください。
モデルはアニメーションコードの作成と修正を支援します。説明の信頼性を保つのは、検証済みCSV、明示した尺度、レンダリング後の実ファイル確認です。
よくある質問
- Opus 5.5がMP4を直接生成するのですか?
- この手順ではClaudeがコードの作成や修正を支援し、Pythonがフレームを描画、FFmpegが動画をエンコードします。配布プロジェクトは編集側で作成してローカル検証したもので、今回Opusが生成した成果物ではありません。
- 自分のCSVに差し替えられますか?
- 入力条件を確認したうえで差し替えられます。この例では重複しないラベルを持つ3行と、0〜200の整数件数だけを扱います。行数や値を増やす場合は、レイアウトと尺度も明示的に変更してください。
- 棒が伸びても数値が変わらないのはなぜですか?
- 表示中は常に元の件数を示すためです。棒が伸びる動きは登場演出であり、時系列や成長を表すものではありません。


