Communitygithub.com

layyyback/网球回合剪辑

將一段很長的網球練習影片(對牆練習、發球機、對打皆可)根據擊球聲音自動剪成一個個完整的回合,去處撿球、走動、停頓的片段。

网球回合剪辑 是什麼?

此技能專為自動化網球練習影片的回合剪輯而設計,透過分析音頻中的擊球聲擷取完整回合,而非依賴畫面動態變化,因為撿球和走動的畫面變動往往比打球更大。它使用 Python 指令稿 `scripts/rally_cut.py`,依賴 ffmpeg、ffprobe 和 numpy,支援 macOS、Linux 等常見平台。主要工作流程包括:先用 `probe` 指令檢查影片品質並找出可能的原始高畫質檔案;再用 `analyze` 指令根據擊球聲間隔(預設大於 6 秒視為回合結束)識別回合;接著用 `sheets` 指令生成每回合的開頭和結尾縮圖,供使用者視覺核對;最後用 `cut` 指令以 H.264 重新編碼輸出獨立 mp4 影片,確保方向正確且不因關鍵幀間隔導致剪輯誤差。它還內建 `align` 指令,可在更換原始高畫質影片時對齊時間軸,避免重複計算。此技能也適用於乒乓球、羽毛球、壁球等擊球聲清晰的練習影片,使用者可調整 `--k`(音量閾值)和 `--gap`(回合間隔秒數)等參數來適應不同環境噪音。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/layyyback/tennis-rally-cut/tree/HEAD/tennis-rally-cut

在你喜歡的 AI 中提問

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

說明文件

網球回合剪輯(tennis-rally-cut)

沉澱自 2026-11-08 的一次真實剪輯:兩段對牆練習影片,剪出 14 個回合。 核心思路:聽聲音找回合,看畫面做核對。

用戶要的是什麼(硬規則)

  • 一個回合 = 從第一拍到球打死。整個回合放進一個片段。
  • 不許把回合剪碎。 連續對牆打了 10 拍,就是一個片段,不能拆成幾個 2 拍、3 拍。
  • 撿球、走動、停頓、拿手機、收機器,都剪掉。
  • 原影片不動。成品放在原影片同目錄的 剪輯/ 資料夾。

為什麼用聲音不用畫面

  • 每一拍都有「拍子擊球」和「球撞牆/落地」兩聲脆響。撿球、走路時沒有。
  • 畫面動靜不行:撿球走動時畫面變化比打球還大,分不出來。
  • 畫面只用來抽查核對。

工具

腳本:scripts/rally_cut.py。只依賴 ffmpeg、ffprobe 和 numpy。

子命令作用
probe看解析度、碼率、方向;提示是否被聊天軟體壓縮
analyze找擊球聲,串成回合,寫 <名字>.segments.json
sheets每個回合開頭、結尾各拼一張幀圖,用來核對
cut按回合剪成獨立 mp4,寫 片段清單.txt
align換了高清原片時,算新舊兩版的時間偏移

下文 RC 指腳本的絕對路徑。中間檔案放工作階段的臨時目錄,不要放進用戶的影片目錄。

流程

1. 先查畫質

python3 -I $RC probe <影片...>
  • 出現「疑似被聊天軟體壓縮」時,先告訴用戶。剪輯本身幾乎不損失畫質,原片糊成品就糊。
  • 微信傳來的影片,原片常在微信快取裡,檔名同名加 _raw 後綴: ~/Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files/<帳號>/msg/video/<年-月>/<名字>_raw.mp4 用戶在微信裡點「查看原影片」之後才會下載。可用 mdfind 按加入時間搜: mdfind -onlyin ~ 'kMDItemContentTypeTree == "public.movie" && kMDItemDateAdded >= $time.iso(<日期>)'
  • 找到高清原片後,用 align 確認偏移為 0,就能直接沿用已經算好的切割點。

2. 找回合

python3 -I $RC analyze <影片...> --work <臨時目錄>

它會列印每秒擊球聲數量的時間條和回合列表。預設參數:

參數預設含義
--k2.5比背景響幾倍算一聲。環境吵就調大
--gap6兩聲間隔超過 6 秒才算回合結束。這是「不剪碎」的關鍵
--min-hits6少於 6 聲的串當雜訊丟掉(腳步、收手機)
--pre / --post1 / 1.5回合前後多留的秒數,保證揮拍和落點完整

--gap 的取捨:

  • 4 秒會把「球死後幾秒內立刻重新開球」拆成兩段。
  • 6 秒把它們留在一起。用戶要求不剪碎,所以預設 6。
  • 用戶明確說「每次重新開球都單獨一段」時,才改成 4。

3. 看畫面核對(不可跳過)

python3 -I $RC sheets <影片> --segments <json> --work <臨時目錄>/sheets

逐張看圖,重點查:

  • 影片最後一個回合:結尾常有人走向鏡頭、拿起手機,聲音會被當成擊球。看到就把 end 改到最後一拍後約 1.5 秒。
  • 只有個位數擊球聲的短回合:可能是雜訊。畫面裡沒人在揮拍就刪掉。
  • 回合之間 5 秒左右的空檔:確認是球死重開,而不是漏檢。

直接編輯 segments.json 裡的 segments 陣列:改 start/end,或刪整條。

4. 剪

python3 -I $RC cut <影片> --segments <json> --out <影片目錄>/剪輯 --label A
  • 多個來源影片用不同 --label(A、B…),檔名形如 影片A_03_2m54s-3m22s.mp4,時間是原片中的起止。
  • 必須重新編碼(預設 H.264、CRF 17)。原片關鍵幀常隔 2~3 秒,直接拷貝會切偏。
  • 豎拍影片的方向資訊 ffmpeg 會自動處理,成品就是正的。
  • 目標目錄已有舊成品時,先改名成 剪輯_舊版 備份,不要直接覆蓋或刪除。
  • 1080p 60 幀的片子編碼較慢,放背景跑。

5. 驗收再彙報

  • 用 ffprobe 核每個成品的時長、解析度、幀率。
  • 從一兩個成品裡截幀看一眼:方向正、畫面清楚。
  • 彙報時給:片段數、每段時長範圍、成品目錄(附 Finder 能點開的 file:// 連結,中文路徑要百分號編碼)。
  • 寫明拿不準的片段(例如擊球聲很少的短回合),讓用戶自己決定刪不刪。
  • 寫明只抽查了畫面,沒有逐段從頭看到尾。

踩過的坑

坑後果做法
迴圈裡調 ffmpeg 沒加 -nostdinffmpeg 吃掉清單後續行,切割點錯亂、崩潰腳本已加
時間按「格數×10ms」換算一格其實是 220 個採樣點,7 分鐘漂 1 秒腳本按採樣點換算
用畫面動靜判斷打球撿球比打球動得還厲害只用聲音判斷
用壓縮過的轉發影片成品發糊,用戶以為是剪壞了第 1 步先 probe

相關技能