mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-10-18 07:29:45 +00:00
fix(axelera): serialize run() to prevent cross-camera result misattribution
Voyager SDK does not tag results with a per-frame id and can reorder or buffer stream results under sustained load (observed on live with many thousands of frames: camera A started receiving boxes/paths from camera B). With several frames in flight, the FIFO pairing can store a result under a different frame's id. run() now holds a lock so at most one frame is in flight; the next stream result can only belong to the frame just pushed, making pairing exact regardless of SDK reordering. Pipeline throughput stays far below limits (~9ms per inference vs 28ms available per frame at 7x5fps).
This commit is contained in:
parent
8a3483c7a2
commit
71ccf55927
@ -146,6 +146,12 @@ class _AxeleraInference:
|
||||
self._pending_ids: collections.deque = collections.deque()
|
||||
self._results: dict[int, np.ndarray] = {}
|
||||
self._next_id = 0
|
||||
# Serializes run() calls: the SDK stream does not tag results with a
|
||||
# per-frame id and can reorder/buffer results under sustained load
|
||||
# (observed after many thousands of frames), so with several frames in
|
||||
# flight the FIFO pop can misattribute a result to the wrong camera.
|
||||
# With at most one frame in flight the result can only belong to it.
|
||||
self._run_lock = threading.Lock()
|
||||
|
||||
if stream_factory is None:
|
||||
# lazy import: never import the SDK unless the detector is used
|
||||
@ -262,6 +268,14 @@ class _AxeleraInference:
|
||||
|
||||
def run(self, tensor_input, timeout: float = INFERENCE_TIMEOUT) -> np.ndarray:
|
||||
"""Run one inference and return this frame's detections (see detect_raw)."""
|
||||
# At most ONE frame may be in flight at a time: with several frames in
|
||||
# the stream the SDK can reorder/buffer their results (misattribution
|
||||
# across cameras observed after many thousands of frames). Serialized,
|
||||
# the next returned result can only belong to the one frame we pushed.
|
||||
with self._run_lock:
|
||||
return self._run_locked(tensor_input, timeout)
|
||||
|
||||
def _run_locked(self, tensor_input, timeout: float) -> np.ndarray:
|
||||
if tensor_input is None or getattr(tensor_input, "ndim", 0) < 3:
|
||||
raise ValueError(
|
||||
f"Unexpected frame shape: {getattr(tensor_input, 'shape', tensor_input)}"
|
||||
@ -316,7 +330,7 @@ class _AxeleraInference:
|
||||
def stop(self):
|
||||
# cleanup must not raise
|
||||
try:
|
||||
self._frame_q.put_nowait(None)
|
||||
self._frame_q.put_nowait((None, None))
|
||||
except Exception: # noqa: S110, BLE001
|
||||
pass
|
||||
try:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user