fix(edge): identity-aware GHOST dedup keyed on track_id (FIX 4)
edge/engine.py keyed the ghost cooldown as (GHOST, round(x), round(y)) — purely spatial, ignoring track identity. Two distinct workers collapsing at ~the same pixel within the cooldown window would suppress each other's ghost event, potentially masking a second CRITICAL incident. edge/detectors/ghost.py GhostIncidentDetector.update now includes the candidate's track_id in each emitted dict. edge/engine.py keys the cooldown as (EventType.GHOST, track_id) and sets the emitted Event's track_id accordingly, matching how FALL/NO_MOTION/IMMOBILITY/zone events are already keyed per-track. Tests: tests/test_ghost.py asserts ghosts[0]["track_id"] == 1 on the existing reappear-timeout test.
This commit is contained in:
parent
f46ed40088
commit
f7266f5980
3 changed files with 5 additions and 3 deletions
|
|
@ -42,7 +42,7 @@ class GhostIncidentDetector:
|
|||
out = []
|
||||
for tid, (spatial, state, _height, lost_at) in list(self._candidates.items()):
|
||||
if now - lost_at >= self.cfg.reappear_s:
|
||||
out.append({"spatial": spatial, "last_state": state,
|
||||
out.append({"track_id": tid, "spatial": spatial, "last_state": state,
|
||||
"waited_s": round(now - lost_at, 1)})
|
||||
self._candidates.pop(tid, None)
|
||||
return out
|
||||
|
|
|
|||
|
|
@ -67,9 +67,10 @@ class EventEngine:
|
|||
events.append(self._mk(etype, now, tr.track_id, zone_id=hit["zone_id"]))
|
||||
|
||||
for g in self.ghost.update(active_tracks, now):
|
||||
key = (EventType.GHOST, round(g["spatial"][0]), round(g["spatial"][1]))
|
||||
key = (EventType.GHOST, g["track_id"])
|
||||
if self._allow(key, now):
|
||||
events.append(self._mk(EventType.GHOST, now, spatial=g["spatial"],
|
||||
events.append(self._mk(EventType.GHOST, now, track_id=g["track_id"],
|
||||
spatial=g["spatial"],
|
||||
metrics={"last_state": g["last_state"],
|
||||
"waited_s": g["waited_s"]}))
|
||||
return events
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ def test_lost_dangerous_track_becomes_ghost_after_wait():
|
|||
assert len(ghosts) == 1
|
||||
assert ghosts[0]["last_state"] == "FALLEN"
|
||||
assert ghosts[0]["spatial"] == (110.0, 130.0) # bbox 중심
|
||||
assert ghosts[0]["track_id"] == 1
|
||||
|
||||
def test_non_dangerous_lost_track_is_ignored():
|
||||
d = GhostIncidentDetector(GhostConfig(reappear_s=1.0))
|
||||
|
|
|
|||
Loading…
Reference in a new issue