新增物理合理性過濾 max_accel_kmh: 擋高信心誤讀(暴增不可能加速)
平滑前先剔除『從上一個有效讀數一秒內暴增超過 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) <noreply@anthropic.com>
This commit is contained in:
@@ -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):一段移動要「加速到」此速度才算真起步/行駛而保留(連同前面龜速起點一起留);若整段最高速都低於此(只是排隊龜速、從未真正騎走),整段改判停止剪掉 |
|
||||
|
||||
+20
-2
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user