diff --git a/README.md b/README.md index 99b6233..47141e8 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,7 @@ python auto_remove_redlight.py 01.MOV 03.MOV 05.MOV | `stop_seconds` | `4.0` | 連續停車超過幾秒判定為紅燈 | | `speed_threshold` | `0` | 時速 ≤ 此值視為停止 | | `min_confidence` | `0.7` | OCR 信心低於此值視為「讀不到」(交給平滑用前後判斷)| +| `max_accel_kmh` | `25` | 物理合理加速上限(km/h 每秒):機車不可能一秒從 4 衝到 70,超過此加速度的讀數視為高信心誤讀丟棄(只擋暴增,煞車減速不受限)| | `smooth_window` | `5` | 中位數濾波視窗(取樣點數) | | `depart_seconds` | `8.0` | 起步門檻(秒):兩段停車間「持續行駛」需達到(滿)此秒數才算起步成功並保留(含這 8 秒);不足者(起步 N 秒內又停)視為仍在等待,連同短暫蠕動一起剪掉(處理車陣走走停停) | | `depart_min_speed` | `15` | 真起步速度門檻(km/h):一段移動要「加速到」此速度才算真起步/行駛而保留(連同前面龜速起點一起留);若整段最高速都低於此(只是排隊龜速、從未真正騎走),整段改判停止剪掉 | diff --git a/auto_remove_redlight.py b/auto_remove_redlight.py index b78f348..5337cea 100644 --- a/auto_remove_redlight.py +++ b/auto_remove_redlight.py @@ -94,6 +94,9 @@ SPEED_THRESHOLD = 0 # 時速 <= 此值視為「停止」。0 = MIN_CONFIDENCE = 0.70 # OCR 信心低於此值的結果視為「未知」(交給平滑用前後判斷) # 0.7 可濾掉停車時的低信心誤讀(如把 0 看成 70@0.57) MAX_SPEED = 300 # 合理時速上限,超過視為誤判(雜訊),當成未知 +MAX_ACCEL_KMH = 25 # 物理合理加速上限(km/h 每秒)。機車不可能一秒從 4 衝到 70, + # 超過此加速度的讀數視為誤讀丟棄(擋高信心誤讀,如 4->70)。 + # 只限制「暴增」;煞車減速很快,不受此限。 # --- 平滑濾波(關鍵!解決停車時 OCR 在 0 與雜訊間跳動的問題)---- # 停車時 OCR 多數讀到 0,但偶爾蹦出單格雜訊(6、60…)或空值,會把「連續停車」 @@ -323,13 +326,27 @@ def smooth_speeds(samples: List[Sample], window: int) -> List[int]: if n == 0: return [] + spd: List[Optional[int]] = [s.speed for s in samples] + + # 物理合理性過濾: 剔除「從上一個有效讀數一秒內暴增」的不可能加速(多為高信心誤讀, + # 如 4 -> 70)。只擋暴增;煞車減速很快故不限制下降。 + last_v: Optional[int] = None + last_i = 0 + for i in range(n): + if spd[i] is None: + continue + if last_v is not None and spd[i] - last_v > MAX_ACCEL_KMH * (i - last_i): + spd[i] = None # 不可能的暴增 → 視為誤讀丟棄 + continue + last_v, last_i = spd[i], i + + # 中位數(只對有效讀數;空白跳過,整個視窗都讀不到才當 0) half = max(0, window // 2) smoothed: List[int] = [0] * n for i in range(n): lo = max(0, i - half) hi = min(n, i + half + 1) - vals = sorted(samples[k].speed for k in range(lo, hi) - if samples[k].speed is not None) + vals = sorted(v for v in spd[lo:hi] if v is not None) smoothed[i] = vals[len(vals) // 2] if vals else 0 # 全讀不到 → 視為停止 return smoothed @@ -1042,6 +1059,7 @@ _CONFIG_KEYS = { "stop_seconds": "STOP_SECONDS", "speed_threshold": "SPEED_THRESHOLD", "min_confidence": "MIN_CONFIDENCE", + "max_accel_kmh": "MAX_ACCEL_KMH", "smooth_window": "SMOOTH_WINDOW", "depart_seconds": "DEPART_SECONDS", "depart_min_speed": "DEPART_MIN_SPEED", diff --git a/config.json b/config.json index aff6580..77b5ad1 100644 --- a/config.json +++ b/config.json @@ -10,6 +10,7 @@ "stop_seconds": 4.0, "speed_threshold": 0, "min_confidence": 0.7, + "max_accel_kmh": 25, "smooth_window": 7, "depart_seconds": 8.0, "depart_min_speed": 15,