影片調色 (Video Color Grade) (評估 → 校正 → 風格 → 選擇 → LUT + 套用)
從頭以程式碼編寫調色流程,在真實素材上生成數種具名風格,透過明確的人工選擇或委派代理判斷選定一種,接著烘焙出可攜式的 .cube LUT 並套用於完整片段。不使用預設套件。
核心概念 — 先校正,再風格化。 每一種風格都是 校正基底 + 創意分層:
- 校正基底 讓素材變得 真實 — 對於平坦的 LOG/HLG 素材,它是 log→Rec.709 轉換;對於已是 Rec.709 的素材,它是白平衡中和 + 曝光 + 適度對比。 每種風格共用此基底,因此「中性」始終有意義,而不同風格的差異來自於 意圖。
- 創意分層 是疊加在基底之上的風格 (暖色 / 冷色 / 青橙 / 鮮明 / 褪色…)。
膚色是品質標竿。 在人物身上 — 尤其是較深膚色 — teal_orange 和 cool_desat 經常造成綠色/灰色偏色。務必先以臉部判斷每一種風格;保持中間調溫暖,將色調推向陰影/高光/背景,偏好使用 vibrance 而非原始 saturation。不要將白平衡完全推向中性,否則膚色會失去生氣。
需求:ffmpeg/ffprobe 位於 PATH,Python 搭配 numpy + Pillow。
合約
- 每種風格 =
基底 + 創意分層;基底由所有風格共用。 - 所有風格都渲染在同一個代表性影格上 (公平、可控的比較),附上標籤,以完整解析度呈現。
- 完整套用使用
-c:a copy(音訊永不重新編碼 → 影音同步一致),並保持來源的時長、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": "中性校正保留了自然的膚色與原始光線。",
"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 已包含校正基底;渲染器必須單獨套用 lut3d,而不是 base + 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
獨立相容性輸入
- 來源影片。
- 一個
looks.json(base+ 具名looks);從looks.example.json開始,並根據assess.py的報告重新調整base。
所有現有腳本繼續接受舊版的 looks.json。gradelib.load_spec() 也接受標準的 grade-plan.json,因此在遷移期間,聯絡表、片段、LUT 和獨立套用命令保持相容。
looks.json 結構
{
"base": "<校正基底:log→Rec.709 轉換,或一般素材的 WB+曝光+對比>",
"looks": [
{ "name": "clean_neutral", "label": "1 · 乾淨中性", "desc": "僅校正", "chain": "eq=contrast=1.02" },
{ "name": "teal_orange", "label": "4 · 青橙色", "desc": "電影感", "chain": "colorbalance=...,eq=..." }
// 一個風格的完整 ffmpeg filter = base + "," + chain。將 "prepend_base": false 設為 true 可獨立使用 chain。
]
}
工作流程
-
評估 — 絕不盲目調色。
python scripts/assess.py SOURCE.mp4 --out work/報告 log 與 Rec.709 的關係、白平衡色偏 (signalstats U/V 與 128 的比較)、曝光 (Y),挑選一個代表性影格 (
work/assess/frame.png),並建議基底調整。據此重新調整looks.jsonbase(例如暖色偏 → 加入一點藍色;log → 將 log→709 轉換放入base)。- 代表性影格是根據中間亮度選取,而非臉部。 在單一鏡頭的 talking-head 上,這通常會落在一個好的臉部影格,但在 B-roll 為主 / 多鏡頭素材上,它可能會選到一個沒有臉部的影格 — 此時
face_skin_check.png就沒有用處 (膚色是品質標竿)。使用--frame-time SECONDS強制指定一個已知有臉的時刻。 - WB/曝光統計來自約 10 秒的
signalstats視窗,位於時長 10% 的位置 — 對於恆定光源來說沒問題;對於光線會變化的素材,使用--win-start SECONDS/--win-dur SECONDS移動/延長視窗。
- 代表性影格是根據中間亮度選取,而非臉部。 在單一鏡頭的 talking-head 上,這通常會落在一個好的臉部影格,但在 B-roll 為主 / 多鏡頭素材上,它可能會選到一個沒有臉部的影格 — 此時
-
渲染風格以供選擇。
python scripts/render_looks.py work/assess/frame.png looks.json --out work/looks產生
work/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 -
選擇一種風格。 僅在使用者要求選擇時才呈現聯絡表並等待。否則以 agent 模式選取,並繼續流程,不做決策暫停。在交付前寫入
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 # 或使用交付的 LUT 套用:--lut final/selected-color-look.cubeapply_grade.py會驗證時長/音訊是否保持,印出 WB 偏移 (U/V 趨向 128),並產生一個檢查影格以供目視。- 套用會對影片重新編碼一次 (有損);音訊使用
-c:a copy。 品質/時間由--crf(預設 18) 和--preset(預設slow) 控制。slow對於小/短片段還行,但在 1080p/4K 長達一小時的素材上,這是一個龐大且無聲的時間成本 — 對於交付降至--preset medium(或veryfast用於快速預覽),僅在最終版本使用slow。 - 使交付的影片與交付的 LUT 匹配: 鏈 (
--name) 和烘焙的 33³.cube(--lut) 在陡峭曲線上的差異可達約 5/255 (bake_lut.py會報告此max|d|)。對大部分工作來說不可見,但如果你要交付.cube並且希望渲染的影片與之完全匹配,請使用--lut final/selected-color-look.cube套用,而不是--name。
- 套用會對影片重新編碼一次 (有損);音訊使用
設計備註 / 陷阱
- 先對乾淨的剪輯調色,再將合成疊加層疊加在調色後的影片上。 如果你必須對已含有燒錄字幕/卡片的影片進行調色,請勿相信
assess.py:它的 WB/曝光統計和影格選取會被圖形干擾 (暗色頂部/底部遮幅會拉低 Y,明亮的字幕文字/青色強調色會偏移 U/V,且影格選取可能落在一個字幕繁多的影格上) — 你等於是在校正疊加層,而不是素材。重複使用在乾淨素材上調整好的base,並預期調色會影響疊加層的顏色,因此僅使用微妙/校正性質的風格 (clean_neutral);teal_orange/vintage_faded會明顯地重新著色文字/強調色。 - 一種風格/LUT 涵蓋所有相關片段。 一旦你選定了一種風格,就在每個相關片段 (例如乾淨剪輯 + 加上疊加層的版本) 上重複使用相同的
looks.json/.cube— 不要為每個片段重新執行兩階段的選擇流程。 - 先校正,再風格化。 基底是典範中 S-Log3→Rec.709 步驟的一般化。對於一般素材,基底是 WB+曝光+對比;對於 log 素材,則將轉換放在那裡。
- 膚色優先。
teal_orange/cool_desat是較深膚色主體上常見的問題 — 保持中間調溫暖,將色調推向陰影/高光/背景,使用vibrance。不要完全中和 U/V。 - 標籤是以 PIL 生成的 PNG,由 ffmpeg 疊加 — 絕不使用
drawtext。 在 Windows 上,drawtext的fontfile路徑 (磁碟機冒號) 不可靠。字型在gradelib.py頂部設定。 - 磁碟機冒號路徑會破壞接受路徑的 ffmpeg filtergraph 選項 (
lut3d、metadata=print:file=)。腳本以cwd= 檔案所在資料夾的方式執行 ffmpeg,並以基本名稱引用它。對於任何新的接受路徑的 filter,請保持此模式。 - 輸出會落入你傳入的
--out目錄 (持久化),而非系統暫存目錄。 - 臉部/膚色條使用固定的居中上半部裁切,而非臉部偵測。 如果主體沒有大致居中,請向
render_looks.py傳遞--face-crop W:H:X:Y,否則膚色條會顯示錯誤的區域。 apply_grade.py的驗證檢查影格位於時長的 20% — 這是一個與選擇風格時不同的影格。如果該 20% 的位置沒有臉部,請改為目視檢查另一個影格的膚色 (或向assess.py傳遞--frame-time,使選定的影格是一個已知有臉的時刻)。- LUT 烘焙是精確的,因為這裡的每個 filter 都是逐像素的點運算:
bake_lut.py將一個 33³ 的單位網格推送通過實際鏈,並與之進行自我檢查 (mean|d|應遠低於 1/255;幾個位階的max|d|僅是內插誤差)。 - 交付前務必完成選擇。 人工選擇和委派代理選擇都是有效的,但計畫必須記錄發生的是哪一種以及原因。