Files
JianMiau c4a3fdde97 新增 min_real_zeros(真紅燈需≥N個真實0) + keep_after_stop 改 0
- min_real_zeros(3): 一段停止要含至少 N 個『真實讀到的 GPS=0』才算紅燈剪掉,
  讀不到(??)不算 → 濾掉『整段都讀不到』的假停車(OCR 失敗區,如 02 的 09:48/20:06)
- keep_after_stop 2->0: 不留卡達緩衝,直接剪到停車結束點(偵測到開始動那一刻)
實測 02: 假停車濾掉剩 9 段紅燈;#3 剪到 03:20、#9 剪到 19:17(真正起步點)。

本次調整由 Claude Opus 4.8 (1M context) 協助處理。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 00:02:05 +08:00

172 lines
8.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Redlight Remover — 機車第一人稱影片自動去紅燈工具
針對「安全帽第一人稱機車影片」,自動偵測等紅燈的停車片段並剪掉,輸出無損的去紅燈影片。
程式用 OCR 讀取畫面上的 GPS 時速表,當時速持續為 0 超過設定秒數即判定為紅燈,再用 FFmpeg 無損切割拼接。
---
## 功能特色
- 🎯 **自動偵測紅燈**:逐秒 OCR 辨識時速,連續停車超過門檻即視為等紅燈
- ✂️ **無損剪輯**:用 FFmpeg `-c copy` 串流複製,畫質零損失、速度快
- 🧹 **抗雜訊平滑**:中位數濾波 + 補空值 + 相鄰段合併,解決停車時 OCR 在 0 與雜訊間跳動的問題
-**GPU 加速**:支援 NVIDIA CUDAOCR 大幅加速
- 🖱️ **互動選單**:批次檔啟動,↑↓ 選資料夾、空白鍵勾選影片
- ⚙️ **設定檔**:所有參數集中在 `config.json`,免改程式碼
- 📊 **辨識紀錄**:每支影片輸出 `*_speeds.csv`,方便檢查 OCR 結果
---
## 環境需求
- **Python 3.x**
- **FFmpeg / ffprobe**(需加入系統 PATH
- Python 套件:`easyocr``opencv-python``numpy`
- (選用)NVIDIA 顯卡 + CUDA 版 PyTorch 以啟用 GPU
### 安裝
```bat
pip install -r requirements.txt
```
FFmpeg 可用 winget 安裝:
```bat
winget install Gyan.FFmpeg
```
啟用 GPU(需 NVIDIA 顯卡),安裝對應 CUDA 版 PyTorch,例如:
```bat
pip install --force-reinstall torch torchvision --index-url https://download.pytorch.org/whl/cu128
```
---
## 使用方法
### 方式一:互動批次檔(推薦)
雙擊 `啟動.bat`,依選單操作:
1.`[1]` 預覽 ROI:先確認時速表框選位置(第一次使用建議先做)
2.`[2]` 開始處理:
- ↑↓ 移動、Enter 選擇要處理的資料夾
- ↑↓ 移動、**空白鍵勾選**要去紅燈的影片、Enter 開始
- 處理完成,成品輸出到該資料夾的 `no_redlight\` 子資料夾
### 方式二:命令列
```bat
:: 預覽 ROI(確認時速表位置)
python auto_remove_redlight.py 20260516 --preview
:: 處理整個資料夾
python auto_remove_redlight.py 20260516
:: 處理單一影片
python auto_remove_redlight.py E:\videos\20260516\01.MOV
:: 處理多支指定影片
python auto_remove_redlight.py 01.MOV 03.MOV 05.MOV
```
---
## 設定檔 `config.json`
所有常用參數集中於此,每次執行會自動讀取(檔案不存在時會自動產生預設值)。
| 參數 | 預設值 | 說明 |
|------|--------|------|
| `roi` | `[3300, 1770, 300, 140]` | 時速表在畫面上的位置 `[x, y, 寬, 高]` |
| `sample_interval` | `1.0` | 取樣間隔(秒),每幾秒辨識一次;越小越精準但越慢 |
| `progress_every` | `60` | 進度回報間隔(秒),純顯示用,不影響效能 |
| `stop_seconds` | `4.0` | 連續停車超過幾秒判定為紅燈 |
| `speed_threshold` | `0` | 時速 ≤ 此值視為停止 |
| `min_confidence` | `0.5` | OCR 信心低於此值視為「讀不到」(當 0);誤讀改由物理過濾與短移動判定處理 |
| `max_accel_kmh` | `25` | 物理合理加速上限(km/h 每秒):機車不可能一秒從 4 衝到 70,超過此加速度的讀數視為高信心誤讀丟棄(只擋暴增,煞車減速不受限)|
| `depart_seconds` | `8.0` | 起步門檻(秒):一段移動要持續達到(滿)此秒數、且其中沒有 ≥`stop_seconds` 的 GPS=0,才算「穩定起步」而保留;不足者(短暫蠕動/排隊龜速/誤讀)併入停止一起剪掉 |
| `cut_before_stop` | `2.0` | 進入紅燈端(速度到 0)移除起點再往前幾秒,連減速進站一起砍 |
| `keep_after_stop` | `0.0` | 綠燈起步端(速度從 0 開始跑)移除終點提早幾秒。`0` = 剪到停車結束點(偵測到開始動的那一刻),不留卡達緩衝 |
| `min_real_zeros` | `3` | 真紅燈門檻:一段停止裡至少要有幾個「真實讀到的 GPS=0」才算紅燈而剪掉。讀不到(`??`)不算數——避免整段都讀不到被誤當停車剪掉 |
| `min_keep` | `0.8` | 保留片段短於此秒數即丟棄 |
| `use_gpu` | `true` | 是否使用 GPU(無 CUDA 時自動退回 CPU |
| `reencode` | `false` | `false` = 無損快剪;`true` = 重新編碼,切點精準到幀但較慢 |
| `transition` | `true` | 剪接點是否加轉場(淡出淡入 + 恆定功率交叉淡化)。**開啟會強制重新編碼**,有 NVIDIA 顯卡時自動用 NVENC 加速 |
| `transition_duration` | `1.0` | 轉場長度(秒),畫面與聲音共用以維持同步 |
| `video_transition` | `fadeblack` | 畫面轉場類型:`fadeblack`=淡出到黑再淡入;`fade`=交叉溶接;其餘見 FFmpeg `xfade` 文件 |
| `video_quality` | `30` | 重新編碼畫質(NVENC `-rc vbr -cq`x264 `-crf`)。數字越大檔案越小、畫質越低。參考(4K 來源約 54Mbps):`28`≈高畫質(檔大)/`30`≈接近原檔/`33`≈省空間/`36`≈更小 |
| `volume_boost` | `true` | **音量增益**(Adobe PR「增益/音量」):是否放大音量 |
| `volume_boost_percent` | `30` | 音量增加幅度(%),`30` = 放大成 130%(×1.3 ≈ +2.3dB |
| `hard_limiter` | `true` | **強制壓限**Adobe PR「強制壓限/Hard Limiter」):拉大音量後把峰值壓在 0dB 以下,避免破音 |
| `lowpass` | `true` | **低通**Adobe PR「低通/Lowpass」):濾掉高頻刺耳聲(風切/嘶聲) |
| `lowpass_hz` | `15000` | 低通截止頻率(Hz |
| `base_dir` | `E:\\videos` | 影片根目錄 |
> 參數優先順序:**命令列參數 > config.json > 程式內建預設**
### 關於轉場與無損
- `transition: false` → 使用 FFmpeg `-c copy` 串流複製,**畫質無損、速度快**,但剪接點是硬切。
- `transition: true` → 在每個剪接點加上**畫面淡出淡入**(`xfade`)與**聲音恆定功率交叉淡化**`acrossfade``c1=qsin:c2=qsin`)。此模式必須重新編碼(非無損),4K 影片較耗時,建議搭配 NVIDIA 顯卡用 NVENC 加速。
### 關於音訊處理
音量增益、強制壓限、低通三項(命名參考 Adobe Premiere Pro)會在重新編碼時一併套用,處理順序為 **音量增益 → 低通 → 強制壓限**(壓限擺最後當煞車)。
由於需要重新編碼,這些音訊處理**僅在 `transition: true`(重編模式)時生效**;若 `transition: false`(無損快剪)則音訊維持原樣不處理。
---
## 輸出結果
成品放在來源資料夾的 `no_redlight\` 子資料夾,原始影片不會被改動:
```
<影片資料夾>\no_redlight\
├─ 01_no_redlight.MOV ← 去紅燈後的成品
├─ 01_speeds.csv ← 每秒辨識時速紀錄(檢查用)
├─ 01_cut_log.txt ← 剪輯紀錄:哪些片段被剪掉、各自原因
└─ ...
```
> `*_cut_log.txt` 會列出每段被剪掉的時間範圍與原因(例如「等紅燈/停車約 42 秒」「停車至影片結尾」),以及保留下來的行駛片段,方便核對。
---
## 運作原理
1. 用 OpenCV 每隔 N 秒抽一幀
2. 裁切出畫面上時速表的固定像素區域(ROI)
3. 用 EasyOCR 只辨識數字,讀出當下時速
4. 整理每秒速度(**讀不到當 0** + **物理過濾**剔除不可能的暴增誤讀),再用兩段判定:**Pass A** 把 <`stop_seconds` 的停止併入移動(OCR 瞬斷不算紅燈)、**Pass B** 把 <`depart_seconds` 的移動併入停止(沒撐滿就不算穩定起步),不做中位數平滑以免糊掉邊界
5. 時速持續為 0 超過門檻 → 判定為等紅燈,標記移除(走走停停會合併、結尾停車砍到底)
6. 用 FFmpeg 切割保留片段並拼接(可選淡出淡入轉場 + 音量處理),輸出去紅燈影片
7. 另輸出 `*_cut_log.txt` 剪輯紀錄,記下哪些片段被剪、原因
---
## 注意事項
- 不同影片的儀表疊加位置可能不同,換來源時請先用 `--preview` 確認並調整 `roi`
- 無損模式切點會對齊最近的關鍵幀,實際剪接點可能 ±1~2 秒(對去紅燈用途通常足夠)
- 程式只處理資料夾第一層的影片,不會遞迴子資料夾
---
## 檔案說明
| 檔案 | 用途 |
|------|------|
| `auto_remove_redlight.py` | 主程式 |
| `啟動.bat` | 互動式啟動器(Big5 編碼) |
| `_bat_src.txt` | 啟動器的 UTF-8 原始碼 |
| `_convert.ps1` | 把 `_bat_src.txt` 轉成 Big5 的 `啟動.bat` |
| `config.json` | 參數設定檔 |
| `requirements.txt` | Python 套件清單 |
---
> 本工具由 **Claude Opus 4.8 (1M context)** 協助開發與處理。