video-color-grade について
映像から複数のルックを作成・比較・選択し、LUTを生成して適用するカラーグレーディングスキルです。共通の補正と演出的な色付けを分け、肌色の確認を重視します。
主な機能
- 01映像を分析して基本補正を提案
- 02同じフレームに複数のルックを描画
- 03比較画像で肌色を確認
- 04ルックの選択と理由を記録
- 05ポータブルな`.cube` LUTを生成
- 06音声をコピーしてカラーを適用
活用シーン
- 01映像クリップの色調を比較
- 02肌色を確認しながらルックを選択
- 03LUTを出力してクリップ全体に適用
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ whitetowerai/ cut-as-code --skill video-color-gradeインストール後、Claude Code で/video-color-gradeを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
looks.example.jsonscripts/apply_grade.pyassess.pybake_lut.pygradelib.pymake_clips.pyrender_looks.py詳細
ビデオのカラーグレーディング(評価 → 補正 → ルック作成 → 選択 → LUT 作成と適用)
コードでゼロからカラーグレーディングを作成し、実際の映像に複数の異なる名前付きルックを適用して生成します。明示的な人間の選択、または委任されたエージェントの判断によって1つを選び、持ち運び可能な .cube LUT に焼き込んで、クリップ全体に適用します。プリセットパックは使用しません。
基本的な考え方 — 補正してからスタイルを加える。 各ルックは 補正ベース + クリエイティブレイヤー です。
- 補正ベースは映像を正直に見せます。フラットな LOG/HLG 映像の場合は log→Rec.709 変換、すでに Rec.709 の映像の場合は WB のニュートラル化+露出+控えめなコントラスト調整です。すべてのルックでこのベースを共有するため、「ニュートラル」は意味を保ち、ルックの違いは意図によって生まれます。
- クリエイティブレイヤーは、その上に重ねるルックです(暖色/寒色/ティール&オレンジ/メリハリ/フェードなど)。
肌の色が品質の基準です。 人物が映っている場合、特に肌の色が濃い人では、teal_orange と cool_desat によって緑やグレーの色かぶりが生じがちです。すべてのルックでまず顔を確認してください。中間調の暖かみを保ち、色合いの調整はシャドウ/ハイライト/背景に加え、強い saturation より vibrance を優先します。ホワイトバランスを完全にニュートラルにすると肌の生気が失われるため、調整しすぎないでください。
要件:PATH 上に ffmpeg/ffprobe があること、Python に numpy と Pillow があること。
契約
- 各ルックは
ベース + クリエイティブレイヤーです。すべてのルックでベースを共有します。 - すべてのルックを同じ代表フレームにフル解像度でレンダリングし、ラベルを付けます(公平で条件を統一した比較)。
- クリップ全体への適用では
-c:a copyを使います(音声は再エンコードされないため、A/V 同期は変わりません)。ソースの再生時間、fps、寸法を維持します。 - 納品前にルックの選択を完了します。ユーザーが選択を求めた場合は
humanモードを使い、選択を委任された場合、または自律的な実行を求められた場合は、停止せずにagentモードを使います。どちらのモードでも、判断の根拠を永続的なプランに記録します。
プロジェクトプロトコルのワークフロー
永続的な判断を work/color-grade/grade-plan.json に保存します。
{
"schema_version": 1,
"target": "base-video",
"base": "eq=contrast=1.05",
"looks": [{"name": "clean", "chain": "null"}],
"selected_look": "clean",
"selection_mode": "agent",
"selection_rationale": "Neutral correction preserves natural skin and source lighting.",
"selected_lut": "../../final/selected-color-look.cube",
"evidence_refs": ["media:source"]
}
コンテンツカードやキャプションの色を変更しないよう、デフォルトで base-video を使います。composite は、すでに合成されたピクセルを意図的にグレーディングする場合にのみ有効です。共有の work/understand/media.json と視覚的な根拠を読み込みます。review/02-color-grade/choose-color-look.jpg と review/02-color-grade/skin-tone-check.jpg を生成し、任意で短い selected-look-preview.mp4 も生成します。判断と根拠は review/02-color-grade/selected-look.md に記録します。
ユーザーが明示的に選択した場合は、selection_mode を human に設定します。ユーザーが判断を委任した場合、または中断のないワークフローを求めた場合は、agent に設定し、肌の色、ハイライトの保持、ニュートラルなバランスを優先して慎重に選択します。必ず空でない selection_rationale を記述します。agent モードでは処理を中断しないでください。
選択した LUT を final/selected-color-look.cube に焼き込みます。この LUT を納品する場合は、グレードプランの selected_lut を設定し、共有レンダラーがそのファイルを正確に読み込むようにします。焼き込まれたフルルック LUT には補正ベースがすでに含まれています。レンダラーは base + lut3d ではなく、lut3d のみを適用する必要があります。
based_on に操作の正確な入力リビジョンを記録し、プランまたは選択した入力が変更されたときは整数の revision を増やします。レビュー用ファイルがすべて完成してから操作チェックを pass に設定し、プロトコルの全フィールドを含めます。
{
"target": {"sequence": "main", "scope": "base-video"},
"effects": {
"changes_timeline": false,
"changes_geometry": false,
"changes_video_pixels": true,
"changes_audio": false,
"adds_track": null
},
"check": {
"status": "pass",
"report": "../review/02-color-grade/selected-look.md"
},
"render": {
"kind": "video-filter",
"target": "base-video",
"plan": "color-grade/grade-plan.json"
}
}
共有レンダラーは selected_look が存在することを検証し、タイムライン変換の後、オーバーレイの前にカラーグレーディングを適用して、最終動画を一度だけエンコードします。
python skills/video-understand/scripts/build_render_plan.py .
python skills/video-understand/scripts/render_project.py work/render/render-plan.json
スタンドアロン互換入力
- ソース動画。
baseと名前付きのlooksを含むlooks.json。looks.example.jsonを基にして、assess.pyの報告内容に応じて**baseを調整**します。
既存のすべてのスクリプトは、引き続き従来の looks.json を受け付けます。gradelib.load_spec() は標準形式の grade-plan.json も受け付けるため、移行中もコンタクトシート、クリップ、LUT、スタンドアロン適用の各コマンドを引き続き使用できます。
looks.json のスキーマ
{
"base": "<corrective base: log->Rec.709 conversion, OR WB+exposure+contrast for normal footage>",
"looks": [
{ "name": "clean_neutral", "label": "1 · CLEAN NEUTRAL", "desc": "corrected only", "chain": "eq=contrast=1.02" },
{ "name": "teal_orange", "label": "4 · TEAL & ORANGE", "desc": "cinematic", "chain": "colorbalance=...,eq=..." }
// a look's full ffmpeg filter = base + "," + chain. Set "prepend_base": false to use chain standalone.
]
}
ワークフロー
-
評価する — 評価を見ずに採点しないでください。
python scripts/assess.py SOURCE.mp4 --out work/log と Rec.709 の判定、ホワイトバランスの色かぶり(signalstats の U/V と 128 の比較)、露出(Y)をレポートし、代表フレーム(
work/assess/frame.png)を選び、基本的な調整を提案します。これに応じてlooks.jsonのbaseを再調整してください(例:暖色の色かぶり → 青を少し加える、log → log から 709 への変換をbaseに追加する)。- 代表フレームは顔ではなく、輝度の中央値で選ばれます。 ワンショットのトークヘッド映像では通常、良好な顔のフレームが選ばれますが、Bロールが多い映像や複数ショットの映像では、顔のないフレームが選ばれることがあります。その場合、
face_skin_check.pngは役に立ちません(品質基準は肌です)。--frame-time SECONDSを指定して、顔が映っていることが分かっている場面を選んでください。 - ホワイトバランス/露出の統計値は、映像の長さの 10% 地点にある約 10 秒間の
signalstatsウィンドウ 1 つから取得します。照明が一定であれば問題ありません。照明が変化する映像では、--win-start SECONDS/--win-dur SECONDSで位置を変更したり、長さを延ばしたりしてください。
- 代表フレームは顔ではなく、輝度の中央値で選ばれます。 ワンショットのトークヘッド映像では通常、良好な顔のフレームが選ばれますが、Bロールが多い映像や複数ショットの映像では、顔のないフレームが選ばれることがあります。その場合、
-
選択用にルックをレンダリングします。
python scripts/render_looks.py work/assess/frame.png looks.json --out work/lookswork/looks/looks_compare.png(元映像とすべてのルックをラベル付きでフル解像度表示)と、face_skin_check.png(肌部分を拡大したストリップ)を生成します。両方を確認してください。 肌が基準です。肌が灰色や緑色になるルックは再調整するか除外してから、再レンダリングしてください。被写体が中央から外れている場合は、--face-crop W:H:X:Yを追加してください。 これらの PNG は作業用の中間ファイルとして扱い、承認済みのレビュー用コピーをreview/02-color-grade/choose-color-look.jpgとreview/02-color-grade/skin-tone-check.jpgに保存してください。 -
(任意) 上位 2~3 件のモーションプレビューを作成し、動きを確認できるようにします(音声は保持されます)。
python scripts/make_clips.py SOURCE.mp4 looks.json --out work/clips \ --pick clean_neutral,warm_filmic,teal_orange --with-original --ss 148 --t 8 -
ルックを 1 つ選択します。 ユーザーが選択を求めた場合に限り、コンタクトシートを提示して回答を待ちます。それ以外の場合は、エージェントモードで選択し、判断待ちで中断せずに続行します。納品前に
selected_look、selection_mode、selection_rationaleを記入してください。 -
選択後 — LUT を作成し、クリップ全体に適用します。
python scripts/bake_lut.py looks.json --name CHOSEN \ --out final/selected-color-look.cube --verify-frame work/assess/frame.png python scripts/apply_grade.py SOURCE.mp4 --looks looks.json --name CHOSEN --out out/graded.mp4 # or apply the delivered LUT: --lut final/selected-color-look.cubeapply_grade.pyは再生時間と音声が保持されていることを検証し、ホワイトバランスの変化量(U/V が 128 に近づく方向)を表示して、目視確認用のフレームを出力します。- 適用時に動画は 1 回再エンコードされます(非可逆)。音声には
-c:a copyが使われます。 品質と処理時間は--crf(デフォルトは 18)と--preset(デフォルトはslow)で設定します。小規模または短いクリップならslowで問題ありませんが、1080p/4K の 1 時間の映像では時間が大幅にかかり、進捗も表示されません。納品用には--preset medium(簡易プレビューにはveryfast)に変更し、最終版に限ってslowを使ってください。 - 納品する動画と納品する LUT を一致させてください。 チェーン(
--name)と、焼き込まれた 33³ の.cube(--lut)は、急なカーブでは最大で約 5/255 異なることがあります(bake_lut.pyがこの差をmax|d|として報告します)。ほとんどの作業では見えない差ですが、.cubeを納品し、それに動画を正確に一致させたい場合は、--nameではなく--lut final/selected-color-look.cubeを指定して適用してください。
- 適用時に動画は 1 回再エンコードされます(非可逆)。音声には
設計上の注意点/落とし穴
- クリーン版をグレーディングしてから、その映像にオーバーレイを合成します。 焼き込み済みのキャプション/カードがある動画をグレーディングする必要がある場合、
assess.pyの結果を信用しないでください。グラフィックによって、WB/露出の統計値とフレーム選択が偏ります(上下の暗いスクリーンがYを下げ、明るいキャプション文字やティールのアクセントがU/Vを偏らせ、キャプションの多いフレームが選ばれることがあります)。オーバーレイではなく映像を補正することになるためです。クリーンな映像で調整したbaseを再利用し、グレードによってオーバーレイにも色が付くことを想定してください。そのため、控えめな補正用ルック(clean_neutral)だけを使用します。teal_orange/vintage_fadedを使うと、文字やアクセントの色が目に見えて変わります。 - 関連するすべてのクリップに同じルック/LUTを適用します。 ルックを選んだら、関連するすべてのクリップ(例:クリーン版とオーバーレイ版)で同じ
looks.json/.cubeを再利用します。クリップごとに2段階の選定をやり直さないでください。 - 補正してからスタイルを加えます。
baseは、デッキのS-Log3→Rec.709変換を一般化したものです。通常の映像ではbaseにWB+露出+コントラストを設定し、ログ映像では変換をそこに設定します。 - まず肌を確認します。 暗めの被写体では、
teal_orange/cool_desatが肌色を損ねやすい傾向があります。中間調は暖色に保ち、シャドウ/ハイライト/背景に色を加え、vibranceを使います。U/Vを完全にニュートラルにしないでください。 - ラベルはPILのPNGをffmpegでオーバーレイします。
drawtextは使いません。 Windowsでは、drawtextのfontfileパス(ドライブ名のコロン)が不安定です。フォントはgradelib.pyの先頭で設定します。 - ドライブ名にコロンを含むパスは、パスを取るffmpegのフィルターグラフオプション(
lut3d、metadata=print:file=)で問題を起こします。スクリプトはcwdをファイルのフォルダーにしてffmpegを実行し、ベース名で参照します。パスを取るフィルターを追加する場合も、このパターンを維持してください。 - 出力先は指定した
--outディレクトリです(永続化されます)。システムの一時ディレクトリではありません。 - 顔/肌のストリップには、顔検出ではなく中央上寄りの固定クロップを使います。 被写体がほぼ中央にいない場合は、
render_looks.pyに--face-crop W:H:X:Yを渡してください。そうしないと、肌のストリップに誤った領域が表示されます。 apply_grade.pyの検証用スポットフレームは、動画の長さの20%地点です。 ルックの選定に使用したフレームとは異なります。20%地点に顔がない場合は、別のフレームで肌を目視確認してください(またはassess.pyに--frame-timeを渡し、顔が映ると分かっている場面のフレームを選びます)。- LUTのベイクは正確です。 ここで使うフィルターはすべてピクセル単位のポイント演算だからです。
bake_lut.pyは33³の恒等グリッドを実際の処理チェーンに通し、結果を自己検証します(mean|d|は1/255を大幅に下回るはずです。max|d|が数レベルになるのは補間によるものです)。 - 納品前に必ず選定します。 人間による選定も、委任されたエージェントによる選定も有効ですが、どちらが行われたかとその理由を計画に記録する必要があります。