mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-10-18 07:29:45 +00:00
fix(axelera): never attribute primer/keep-alive results to a real request
The SDK can buffer primer results and emit them AFTER the first real frame is already pending; the FIFO pump then popped a real id and stored the skipped/buffered (empty) result under it, dropping the real one. This misattributes detections across cameras even with run() serialized. - eliminate keep-alive frames entirely (Frigate feeds the queue continuously; nothing to keep alive) - a synthetic frame's result can arrive during a pending real id, - drain the primer results by index BEFORE any pending-id bookkeeping, so they can never consume a request id; the first run() waits up to 0.5s for the primer drain.
This commit is contained in:
parent
71ccf55927
commit
13215b0997
@ -51,6 +51,7 @@ AX_DEFAULT_NETWORK = "yolox-s-coco-onnx"
|
||||
|
||||
INFERENCE_TIMEOUT = 5.0
|
||||
MAX_ROWS = 20
|
||||
PRIMER_FRAMES = 3
|
||||
|
||||
|
||||
# ----------------- Utility Functions ----------------- #
|
||||
@ -152,6 +153,9 @@ class _AxeleraInference:
|
||||
# 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()
|
||||
# Set once the SDK has finished emitting the primer results, so they
|
||||
# can never be attributed to a real pending request id.
|
||||
self._primed = threading.Event()
|
||||
|
||||
if stream_factory is None:
|
||||
# lazy import: never import the SDK unless the detector is used
|
||||
@ -177,20 +181,19 @@ class _AxeleraInference:
|
||||
|
||||
def _feed(self):
|
||||
# primer frames: the SDK init pre-buffers several frames before the
|
||||
# source yields (validated on hardware). Their ids are NOT registered,
|
||||
# so their stream results are dropped by the pump.
|
||||
for _ in range(3):
|
||||
# source yields (validated on hardware). Their results are drained by
|
||||
# _pump (by index) and NEVER attributed to a request id.
|
||||
for _ in range(PRIMER_FRAMES):
|
||||
yield np.zeros((self._height, self._width, 3), np.uint8)
|
||||
while True:
|
||||
try:
|
||||
frame_id, frame = self._frame_q.get(timeout=1.0)
|
||||
except queue.Empty:
|
||||
# keep-alive: no request pending, keep the SDK stream fed so the
|
||||
# inference timeout never kills it during quiet periods. The
|
||||
# None id means this frame's result is dropped by the pump.
|
||||
frame_id, frame = None, np.zeros(
|
||||
(self._height, self._width, 3), np.uint8
|
||||
)
|
||||
# Never synthesize keep-alive frames: a synthetic frame's
|
||||
# result can arrive while a real id is pending and steal that
|
||||
# id (misattribution across cameras). In Frigate the queue is
|
||||
# fed continuously, so idling simply waits here.
|
||||
continue
|
||||
if frame is None: # shutdown sentinel from stop()
|
||||
return
|
||||
if frame_id is not None:
|
||||
@ -252,7 +255,14 @@ class _AxeleraInference:
|
||||
def _pump(self):
|
||||
logger.info("axelera: inference stream started")
|
||||
try:
|
||||
for frame_result in self._stream:
|
||||
for run_index, frame_result in enumerate(self._stream):
|
||||
if run_index < PRIMER_FRAMES:
|
||||
# Primer results are drained WITHOUT consuming a request
|
||||
# id — otherwise a buffered primer result could steal a
|
||||
# real id that is already pending (P1 misattribution).
|
||||
if run_index == PRIMER_FRAMES - 1:
|
||||
self._primed.set()
|
||||
continue
|
||||
out = _pad_rows(self._detection_rows(frame_result))
|
||||
if self._pending_ids:
|
||||
# FIFO: the stream returns results in submission order, so
|
||||
@ -299,6 +309,12 @@ class _AxeleraInference:
|
||||
# permanently wedging this detector.
|
||||
raise RuntimeError("Axelera inference thread stopped; restart required.")
|
||||
|
||||
# Wait briefly for the primer results to drain ONCE, so they can never
|
||||
# be attributed to a real request id. After this the pump only ever
|
||||
# sees real frames (keep-alive frames are no longer emitted).
|
||||
if not self._primed.is_set():
|
||||
self._primed.wait(timeout=0.5)
|
||||
|
||||
# Every submitted frame gets a unique id; the pump stores the result
|
||||
# under this id, so we can wait for OUR frame's outcome exclusively.
|
||||
self._next_id += 1
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user