From 9c077284db6f551fbf5cf1363aa28781c21697af Mon Sep 17 00:00:00 2001 From: JianMiau Date: Wed, 3 Jun 2026 23:12:26 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=89=A9=E7=90=86=E5=90=88?= =?UTF-8?q?=E7=90=86=E6=80=A7=E9=81=8E=E6=BF=BE=20max=5Faccel=5Fkmh:=20?= =?UTF-8?q?=E6=93=8B=E9=AB=98=E4=BF=A1=E5=BF=83=E8=AA=A4=E8=AE=80(?= =?UTF-8?q?=E6=9A=B4=E5=A2=9E=E4=B8=8D=E5=8F=AF=E8=83=BD=E5=8A=A0=E9=80=9F?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 平滑前先剔除『從上一個有效讀數一秒內暴增超過 max_accel_kmh(25)』的讀數, 這類多為高信心 OCR 誤讀(如 4->70->76 conf 0.76~0.78),會造成假性提早起步。 只擋暴增,煞車減速不受限。實測擋掉 02 的 19:18 70/76 誤讀。 註: 雜訊路口的邊界精度受平滑視窗 ±3s 限制,仍有約 5 秒誤差(OCR 品質的物理極限)。 本次調整由 Claude Opus 4.8 (1M context) 協助處理。 Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 1 + auto_remove_redlight.py | 22 ++++++++++++++++++++-- config.json | 1 + 3 files changed, 22 insertions(+), 2 deletions(-) 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,