Communitygithub.com

johnnyfekete/video-framing

在 DaVinci Resolve Studio 中自動為談話性鏡頭裁切構圖,如同專業剪接師般去除雜亂邊緣、加入推近效果,並產生新的時間線版本。

video-framing 是什麼?

這項技能專為 DaVinci Resolve Studio 設計,旨在自動化處理談話性影片的構圖問題。它會分析每個攝影機設置中的臉部、眼睛、手勢等元素,自動裁切出乾淨、置中的畫面,去除不必要的背景雜物(如家電、多餘椅子),同時保留有意的場景佈置(如植物、花朵)。此外,它能在明顯的跳剪處加入推近鏡頭(punch-in),並在關鍵台詞處緩慢推入(push-in),所有操作都在來源素材的縮放預算內完成。技能透過 resolve-bridge 創建新的時間線版本,並生成詳細報告供使用者審閱。支援 macOS 平台,需要 resolve-bridge、ffmpeg、ffprobe 和 Swift。當使用者反映畫面雜亂、鬆散、業餘,跳剪過於明顯,主講者不在畫面中央,或要求推近、推入或清理裁切時,即可使用此技能。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/johnnyfekete/resolve-video-skills/tree/HEAD/skills/video-framing

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

video-framing 是做什麼的?

請在執行 video-tighten(它會隱藏 tight 產生的跳剪)之後及 video-color 之前執行此技能。需要在其旁邊安裝 resolve-bridge 技能(它會在 ../resolve-bridge 中尋找,或設定 RVS_BRIDGE),以及 ffmpeg、ffprobe 和 swift(macOS 已內建;Apple Vision 用於測量臉部和手部)。轉錄需要 uv(本機 Whisper)或在 RVS_TRANSCRIBE_CMD 中設定轉錄命令;如果轉錄失敗,prepare 會繼續執行並發出警告,但轉錄文本將不完整。

以 python3 <此技能資料夾>/scripts/framing.py <指令> --workdir <資料夾> 執行腳本。指令有:prepare、solve、apply、pushes。

腳本負責測量和計算。由您做出判斷(什麼是雜物、哪些設置是靜態的、哪些台詞需要推近)。**切勿停下來請求批准。**使用者之後會進行審閱。對於您不確定的判斷,仍請給出您的最佳答案並繼續建置;將它加入 decisions.json 的 issues 清單(參閱如何決定),apply 會將其轉換為核對清單中的問題。reason 欄位僅用於報告中。

工作流程

  1. 確認所在位置。執行 python3 <resolve-bridge>/scripts/rvs.py --workdir <資料夾> status。如果尚未追蹤任何時間線,請先採用(adopt)時間線(參閱 resolve-bridge)。

  2. 準備。不更改 Resolve 中的任何內容:

    python3 scripts/framing.py prepare --workdir <資料夾>
    

    如果 Resolve 已開啟,且被追蹤的時間線有尚未記錄的手動編輯,prepare 會首先透過 resolve-bridge 將其記錄為一個新版本(manual,「framing 前的手動編輯」)並告知您。通知使用者。如果 Resolve 已開啟,且其縮放模式會導致 apply 拒絕執行(參閱它對時間線做了什麼),prepare 會在開始時列印警告,以便使用者在長時間運行前進行更改。

    找到鏡頭前的主體,將其分組為攝影機設置,測量臉部、眼睛、身體和手部(約每秒 4 個樣本),並寫入:

    • generated/framing/setups/<id>.jpg:每個設置一個靜止幀,帶有標記的 10% 網格(兩個軸上皆為 0.0 到 1.0);
    • .edit/framing/transcript.md:MM:SS.ss-MM:SS.ss <設置> <文本>,在時間線時間中,每個鏡頭前說話的句子一行。每個鏡頭前的片段僅包含其自身來源範圍內的單詞(Whisper 的單詞時間),因此跨片段不會重複,時間為第一個和最後一個單詞的時間。直接從這些行中選取推近範圍;
    • .edit/framing/decisions.json:一個範本,每個設置為空(重新運行會保留未更改項目的設置的決定),context.json 包含預算和靜止幀。

    首次 prepare 很慢: 測量大約需要每秒 1.5 秒的來源時間,且 Whisper 為每個來源檔案運行一次。請在背景執行,並預期在 5 分鐘的影片上需要數十分鐘。它會為每個項目和來源列印進度行(已刷新,因此重新導向的日誌會顯示)。後續運行會快取並迅速;技能更新若改變測量方式,則會再次測量一次。

    它會為每個設置列印一行(項目、縮放預算、靜止幀)以及已帶有手動構圖的項目數量:這些項目將保持原樣,其列表位於 context.json 和 REPORT.md 中。

  3. 決定。查看每個設置的靜止幀並填寫 decisions.json(參閱如何決定)。然後選取推近台詞。

  4. 求解。一個不會更改 Resolve 中任何內容且從不連接到它的測試運行。它始終從被追蹤狀態中當前版本的已記錄計劃(透過 resolve-bridge 採用的時間線)運行,因此自上次記錄版本以來在 Resolve 中進行的手動編輯不會包含在內:prepare 和 apply 會先記錄它們。

    python3 scripts/framing.py solve --workdir <資料夾>
    

    寫入 generated/framing/REPORT.md、每個設置的 setups/<id>_before_after.jpg 和每個標記項目的 flags/<n>.jpg,以及 .edit/framing/solution.json。無效的 decisions.json(缺少標籤、框超出 0..1 範圍)會終止並附上原因和退出碼 2。

  5. 自行檢查預覽。開啟每張前後對比圖像和標記圖像。每個預覽顯示一個幀:該設置的第一個項目,或標記項目,在其中間幀。其他項目或其他時間點的手勢不在其中,因此請檢查每個標記項目的預覽,而不僅是設置的預覽,並閱讀標記文字。對於畫面中位置較低的手(向鏡頭方向伸展、在底部邊緣附近做手勢),即使顯示的一個幀看起來沒問題,也請考慮設定 "bottom_hands": "gesture"。版面:左半邊是完整的來源,帶有綠色邊框的實際畫格、紅色的「排除區」和藍色的「保留區」(推近的結束幀為青色;關於跳過的推近或放棄的推入的標記上,被拒絕的嘗試為橙色);右半邊是觀眾看到的結果,帶有三分線。尋找規則禁止的情況:沿著畫面邊緣的切線、被裁切的手(如果一隻手在底部邊緣被裁切,同時它正向鏡頭方向伸展或做手勢,而不是放在膝蓋或桌子上,請在該設置上設定 "bottom_hands": "gesture" 並重新求解)、頭部過於靠近頂部、邊緣仍有雜物、選定的植物被裁切。如果畫面看起來不對,修正您自己的決定(移動框、更改標籤、放棄推近)並重新求解。重複此過程,直到預覽正確,或剩餘的問題是只有人才能判斷的。也請閱讀報告的標記和放棄的推近:報告會在每個標記下方提供其原始詳細資訊(框座標、項目鍵);核對清單中的問題會獲得簡短易懂的文字。

  6. 套用。需要開啟 Resolve 並載入專案。

    python3 scripts/framing.py apply --workdir <資料夾>
    

    再次求解,然後透過 resolve-bridge 分岔(fork)出 <base>_vNN_framing(等級、效果和手動編輯保持不變),設定縮放、平移和傾斜(推近片段獲得其起始幀),封存先前的版本並儲存專案。記錄該步驟後,每個標記和您在 issues 清單中的每個條目都會成為 EDIT_CHECKLIST.md 中的一個問題,並根據片段名稱和時間碼定位。標記會先合併:一個設置中所有跳過的推近會變成一個問題,並統計原因;一個設置上相同的標記會變成一個。告知使用者新的時間線名稱、問題 ID 以及報告的位置。退出碼 2 表示預期的失敗,並附帶訊息(無追蹤的時間線、frame 未校準的縮放模式等)。

  7. 在檢閱器中為推入設定關鍵影格。Resolve 的 Scripting API 無法建立關鍵影格,因此 apply 會將每個推入片段保留在其起始幀,寫入 .edit/framing/keyframes_todo.json,並為每個推入新增一個核對清單問題,附帶確切的值。如果您有電腦控制功能,請立即執行;否則告知使用者這些是他們的問題(每個片段約需一分鐘),並且這些問題會在使用者的下一次手動編輯同步後,由 pushes check 標記為已完成。

    python3 scripts/framing.py pushes begin --workdir <資料夾>     # 分岔出 <base>_vNN_pushins,列出推入
    python3 scripts/framing.py pushes goto 1 start --workdir <資料夾>
    python3 scripts/framing.py pushes finish --workdir <資料夾>    # 檢查關鍵影格,儲存,關閉問題
    

    對於每個推入,在 Resolve 編輯頁面的檢閱器中(影片 > 變形)。背景應用程式控制無法輸入到 Resolve 中,因此這需要全螢幕控制:

    1. pushes goto N start。腳本會移動播放頭,檢閱器會跟隨。點擊片段可能會將播放頭移動一個影格,因此之後不要點擊時間線。檢查檢閱器是否命名了該片段。
    2. 點擊縮放和位置每一行右側的關鍵影格菱形。它們會變成紅色。數值已經是起始幀。
    3. pushes goto N end。雙擊縮放 X,然後全選,輸入結束縮放值並按 Return(縮放 Y 會在連結時跟隨)。對位置 X 和 Y 執行相同操作。新的關鍵影格會自動出現。
    4. 緩動:右鍵點擊每個紅色菱形。在結束處,選擇淡入。在開始處,一旦結束存在,選擇淡入和淡出。關鍵影格的選單僅提供其相鄰影格允許的緩動,且勾選標記不會總是更新,因此請檢查結果:在中間點,縮放應接近兩個值的平均值,在四分之一處應低於直線值。
    5. 切勿在帶有關鍵影格的片段上透過腳本設定屬性:Resolve 會刪除其所有關鍵影格。

    pushes finish 會讀回關鍵影格(透過 FCPXML 匯出)並比較影格和數值。如果缺少一個或錯誤,它會指出是哪個並保持該步驟開啟。--partial 會無論如何儲存並將這些問題保持開啟。pushes check 執行相同的檢查但不儲存。apply --pushes fusion 改為製作 Fusion Transform 推入(舊方法,完全腳本化)。

如何決定

decisions.json:

{
  "setups": {
    "A": {
      "items": ["..."],
      "label": "plain",
      "reason": "坐在桌子旁,正面朝向,靜止。右邊邊緣有微波爐和磁鐵,最左邊有空椅子;植物保留。",
      "keep_out": [[0.00, 0.45, 0.12, 1.00], [0.86, 0.30, 1.00, 0.80]],
      "keep_in":  [[0.60, 0.20, 0.74, 0.62]]
    },
    "B": {"items": ["..."], "label": "dynamic", "reason": "坐在櫃檯上,高角度", "keep_out": [], "keep_in": []}
  },
  "reclassify": {"V1@1234": "broll"},
  "push_ins": [
    {"start_s": 41.2, "end_s": 47.0, "text": "寫作就是思考。",
     "reason": "論點,第一次被直白地說出"}
  ],
  "issues": [
    {"text": "右邊邊緣的植物與咖啡桌一起被裁切;不確定它是否是雜物",
     "where": "S04_IMG_1234.MOV 在 00:14.00"}
  ]
}

框是 [左, 上, 右, 下],在設置靜止幀上標準化為 0..1(從其網格讀取)。start_s 和 end_s 是來自 transcript.md 的時間線秒數。items 由 prepare 填寫;請保留。

  • 「排除區」框(keep-out):靠近邊緣、沒有人選擇顯示的所有東西。家電、磁鐵、空椅子、燈、電線、恆溫器、門框、沙發的一小角。求解器會在可能的情況下將其裁切掉,而不會裁剪到任何手勢。
  • 「保留區」框(keep-in):主講者選擇的場景佈置。植物、花朵、花瓶。求解器會完整保留它們,或清楚地裁切掉它們,絕不留下小片。框住重要的部分(花束、植物的葉子),而不是延伸到來源邊緣:觸碰到來源邊緣的框永遠無法在裁切內部保持完整。
  • 為每個設置標記為 plain 或 dynamic。plain 是正面、視線水平且靜止的(坐在桌子旁)。dynamic 是高低角度、不尋常的位置(坐在櫃檯上)或強烈的對角線。動態設置不會獲得推近。
  • 原因(reason):每個設置一行,總是如此。會放入報告中。
  • 底部手勢(bottom_hands)(可選):在設置上設定 "bottom_hands": "gesture"。預設情況下,觸碰來源底部(從其高度的 93% 向下)的手被視為休息中:畫面可以在它們之上停止。向鏡頭方向伸展的手也會觸碰底部邊緣,而該規則會將其裁切掉。如果前後對比或標記預覽顯示一隻手在底部邊緣被裁切,同時它在伸展或做手勢,請在該設置上設定此選項並重新求解:這些手隨後會被完全計算在內,並且畫面會更寬鬆。
  • 問題(issues):您不確定的每個判斷(雜物或選定的、要保留哪兩個東西中的哪一個、您幾乎選取的台詞),並套用您的最佳答案。text 說明您決定了什麼以及應該檢查的內容;where 是片段名稱和時間碼(來自 context.json),例如 S04_IMG_1234.MOV at 00:14.00。兩者都必須非空。當 apply 完成時,它們會變成核對清單中的問題。
  • **重新分類(reclassify)**僅用於更正分組:腳本視為鏡頭前但實際上是 B-roll 的項目(broll),或相反(on_camera)。鍵是 V<軌道>@<起始影格>(例如 V1@1234):這些是 .edit/framing/context.json 中 items 的鍵,它也提供每個項目的片段名稱和時間碼。與任何項目都不匹配的鍵將被忽略,因此請對照 context.json 進行檢查。所有項目都被重新分類為 broll 的設置不需要標籤。
  • 推近台詞(push-in lines) 來自腳本和 transcript.md:論點、論證的轉折、妙語、行動呼籲。不是填充內容,不是鋪陳句子。將它們保持在 3 到 8 秒長,且至少間隔 30 秒,放在沒有 B-roll 覆蓋主講者的靜態設置上,並為每個寫下原因。少而精勝過弱的推近。求解器會放棄任何違反規則的推近,並在報告中說明是哪條規則(並在其標記預覽中以橙色顯示被拒絕的推近)。推近以時間線秒數保存,因此 solve 會檢查每個推近的 text 是否與當前時間線的轉錄文本相符:當該台詞不再在其範圍內被說出時(自您選取以來時間線已更改),該推近將被放棄,並附帶一個問題以重新選取它。從 transcript.md 複製 text,以便檢查能夠找到它。如果推近的範圍沒有快取的轉錄文本,它將被保留,且報告會說明未經檢查。

構圖規則

這些是腳本強制執行的規則。列出原因是為了讓您可以判斷規則未涵蓋的鏡頭。

基礎畫面,每個設置一個

  • 眼睛位於上三分線或其正上方,每個片段都有小而均等的頭頂空間。*眼睛靠近上三分線讀起來自然且投入;牆壁空間太多看起來業餘。裁切到前額或觸碰到頭髮的畫面看起來像是個錯誤。*當畫面中低處的手將畫面下拉並將眼睛推到三分線以上時,畫面會向後傾斜,以保持頭頂上方約 10% 的畫面高度(根據臉部大小估計),使用相同的縮放;在手勢、雜物或關節沒有留下空間的地方,僅由眼線決定。
  • 直接交流時以臉部為中心,最多偏移寬度的 5% 以避開雜物。直視鏡頭意味著對稱;偏離中心的臉部除非鏡頭明顯如此構圖,否則看起來像是個錯誤。
  • 底部邊緣永遠不會落在關節上(下巴、脖子、手肘、手腕、腰部、膝蓋、腳)。*關節處的裁切看起來像是意外的截肢,且觀眾會注意到。*在構圖中包括雙手的截肢是正常的,只要手勢可見即可。

仍然片段

  • 縮放、平移和傾斜以拍攝物體為中心。*主體應指向它(偏離中心最多 10%),使框架線與特徵(角落、邊緣)對齊,且眼睛大致位於上方。*儲存靜止幀足以檢查位置。

推近(punch-in)

  • 當兩個仍然片段之間的跳剪在剪輯點處具有相似的眼部位置和鏡頭大小,且第二個片段至少有 0.5 秒長時,才會加入:第一個片段的最後 0.2 秒縮放至第二個片段在剪輯點處的眼部位置(隱藏跳躍)。*人眼在方向上需要一個原因;推近提供了這個原因,而這樣的跳剪看起來有意為之。*跳剪兩側差異太大的鏡頭會保持不變。
  • 當一個設置的片段攜帶手動構圖時,跳過該設置的推近。手動構圖是刻意的;覆蓋它會失去調整。
  • 如果第二個片段太短(< 0.5 秒)或第一個片段太小(< 3 秒),則跳過推近,並附帶原因。
  • 當推近需要超過 115% 的來源縮放時,跳過它(推近會放大至 140%,但 115% 以上的裁切加上輸入縮放會使雜亂的來源看起來是假的)。此限制在 context.json 的 punch_zoom_budget 中。

推入(push-in)

  • 從 1.0 縮放開始,在 3–8 秒內平穩放大(1–1.15 範圍內)。*從 1.0 開始保證我們在來源解析度內。*始終使用相同的開始和結束縮放。
  • 將結束畫面放在與靜態構圖相同的位置,但結束眼線與起始眼線相同(偏移不超過其高度的一半)。眼球運動中斷了我們剛才建立的平穩感。
  • 透過中心進行(當起始和結束畫面水平居中時)。如果起始畫面偏離中心,則水平和垂直移動。掃視運動解決了移動的物體,但平坦的橫向移動看起來像是幻燈片轉場。
  • 僅限於在相機覆蓋範圍內沒有剪輯的設置(兩個片段之間沒有轉場,也沒有 B-roll)。*B-roll 覆蓋了講話者,因此推入不可見。*如果 prepare 或自那時以來的時間線變更引入了 B-roll,則當前版本的 B-roll 會決定。
  • 當設置的講話者在推入期間移動到另一個設置時,放棄推入(「跨設置移動」)。*主體在推入期間不應離開畫面;他們在不同的取景框中。*一個設置中的安靜片段可以具有推入。當來源顯示講話者站立並轉身離開——他們的身體轉向側面——時,放棄推入(「講話者轉身離開」):轉身的長鏡頭看起來像剪輯。當相機位置本身不是推入(沒有平行移動、升降或橫搖)時,目標設置在兩個片段中具有相同的鼻子角度。
  • 當最終畫面達到 1080p 邊界(水平或垂直)時,退出推入並將其作為問題提出。推入不應跑到來源之外。
  • 當設置的腿放手勢檢測到向下的手部運動或向下的目光時,跳過推入(「向下的手勢」)。向下看的講話者正在閱讀,應保持靜止。

背景雜物

  • 裁切掉所有靠近邊緣的未選定物體,使用在求解中允許的最大縮放,直到等效於來源縮放 2.0(或 context.json 中的更小值;這是對於給定設置,構成可信 4K 縮放的最大裁切)。*當需要 2.0 倍以上才能移除預先存在的物體(如檯燈)時,保持原始構圖,因為更大的裁切看起來是假的。*背景中的大幅物體會移動相機朝向,直到裁切掉或包含該物體。
  • 如果一個物體從邊緣伸入(檯燈、管道、窗框)且無法拉伸到 2.0 倍以上,則在該側設定最小裁切。空調通風口、天花板感應器和消防灑水器總是會被裁切掉(透過在求解中標記它們邊界)。
  • 將保留的物體置於畫面邊界內,如果保留它們會使畫面在邊緣處留下空白空間,則將其放大。

保持設定

  • bottom_hands: gesture:當設定時,手部完全包含在內,裁切上升到手的頂部。
  • 不在裝置中:具有正視圖的正面,眼睛朝前,臉部有陰影(如果可能,使臉部更亮)。

它對時間線做了什麼

apply 透過 resolve-bridge 建立一個名為 <base>_vNN_framing 的新版本,其中 NN 是編號。它設定片段屬性:

  • 縮放(Zoom X)、位置 X 和位置 Y(「更換大小和裁剪」樣式)。對於推入片段,僅設定起始幀,但 pushes finish 會增加結束關鍵影格。
  • 來源模式至「裁切並調整大小」,縮放至「將所有設定為適應」。縮放模式必須與此相符。 prepare 會檢查 Resolve 中的項目設定;如果模式不相符,它會列印警告且 apply 會失敗。
  • 保持所有等級和效果不變。
  • 片段的位置(時間碼點)不受影響。

用途

  • 使用者說構圖看起來很雜亂、鬆散、業餘。
  • 跳剪太明顯。
  • 主講者偏離中心。
  • 想要推近、推入或清理裁切。

在這些情況下使用此技能:

  • 回應使用者對鏡頭構圖的抱怨。
  • 修復無人機鏡頭?不:它是為談話性鏡頭設計的。
  • 在 video-tighten 之後使用。

環境

  • RVS_BRIDGE:指向 resolve-bridge 資料夾的路徑。預設值:../resolve-bridge。
  • RVS_TRANSCRIBE_CMD:用於轉錄的命令。預設值:使用 uv run whisper(本機 Whisper)。
  • RVS_OPENAI_KEY:OpenAI API 金鑰,用於轉錄。可選。
  • RVS_OPENAI_MODEL:OpenAI 模型名稱。預設值:whisper-1。
  • RVS_FFMPEG:ffmpeg 路徑。預設值:ffmpeg。
  • RVS_FFPROBE:ffprobe 路徑。預設值:ffprobe。
  • RVS_PYTHON:Python 3 路徑。預設值:python3。
  • RVS_SWIFT:Swift 路徑。預設值:swift。
  • RVS_PUSHIN_MIN_GAP_S:推入之間的最小間隔(以秒為單位)。預設值:30。
  • RVS_PUSHIN_MAX_DURATION_S:推入的最大持續時間(以秒為單位)。預設值:8。
  • RVS_PUSHIN_MIN_DURATION_S:推入的最小持續時間(以秒為單位)。預設值:3。

以下內容是關於名稱、任務、設定和技能的機器可讀元資料。對於人類來說,只需注意它說明了該技能如何在系統層級配置。

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

相關技能