fix: include latest shelf tag in live inspection

This commit is contained in:
kiyo9w 2026-09-28 13:11:06 +09:00
parent 2894979a35
commit 06f9fb4819
2 changed files with 65 additions and 1 deletions

View file

@ -191,6 +191,10 @@ class LiveSession:
self._shift_map = None self._shift_map = None
self._compliance_tracker = None self._compliance_tracker = None
self._last_compliance: Dict[str, Any] = {"violations": 0, "by_rule": {}, "rows": []} self._last_compliance: Dict[str, Any] = {"violations": 0, "by_rule": {}, "rows": []}
# Settled tag + reconciliation from the last result, keyed by track.
# Inspect copies these; it does not re-run OCR.
self._settled_tags: Dict[int, Dict[str, Any]] = {}
self._settled_tag_seq = 0
self._label_memory: "collections.deque[Dict[str, Any]]" = collections.deque( self._label_memory: "collections.deque[Dict[str, Any]]" = collections.deque(
maxlen=LABEL_MEMORY_MAX maxlen=LABEL_MEMORY_MAX
) )
@ -293,6 +297,8 @@ class LiveSession:
self._latest_frame_id = None self._latest_frame_id = None
self._frame_origin = [0.0, 0.0] self._frame_origin = [0.0, 0.0]
self._shift_map = None self._shift_map = None
self._settled_tags = {}
self._settled_tag_seq = 0
# ------------------------------------------------------------------ frame path # ------------------------------------------------------------------ frame path
def process_frame(self, frame_id: int, payload: bytes) -> Dict[str, Any]: def process_frame(self, frame_id: int, payload: bytes) -> Dict[str, Any]:
@ -439,6 +445,17 @@ class LiveSession:
if tag_summary is not None: if tag_summary is not None:
summary["price_tags"] = tag_summary["tags"] summary["price_tags"] = tag_summary["tags"]
summary["compliance"] = tag_summary["compliance"] summary["compliance"] = tag_summary["compliance"]
settled: Dict[int, Dict[str, Any]] = {}
for payload in track_payload:
snapshot: Dict[str, Any] = {}
if "price_tag" in payload:
snapshot["price_tag"] = payload["price_tag"]
if "reconciliation" in payload:
snapshot["reconciliation"] = payload["reconciliation"]
if snapshot:
settled[int(payload["track_id"])] = snapshot
self._settled_tags = settled
self._settled_tag_seq = seq
self._submit_classification(submissions, method, generation) self._submit_classification(submissions, method, generation)
self._run_tag_passes(image, tag_bands) self._run_tag_passes(image, tag_bands)
@ -912,7 +929,16 @@ class LiveSession:
if track is None or image is None: if track is None or image is None:
raise KeyError("TRACK_NOT_IN_VIEW") raise KeyError("TRACK_NOT_IN_VIEW")
box = list(track.raw_box) box = list(track.raw_box)
return self.engine.inspect(image, box, method=chosen_method) # Snapshot is the frame current at tap time. engine.inspect runs
# outside the lock, so a newer frame may land first; the reply still
# carries the tag from the frame the tap matched, not that later one.
settled = None
if self._settled_tag_seq == self._latest_seq:
settled = self._settled_tags.get(track_id)
result = self.engine.inspect(image, box, method=chosen_method)
if settled:
result.update(settled)
return result
# ------------------------------------------------------------------ catalog cache # ------------------------------------------------------------------ catalog cache
def refresh_catalog(self) -> None: def refresh_catalog(self) -> None:

View file

@ -384,6 +384,44 @@ class ClassificationBudgetTests(unittest.TestCase):
process(session, 1) process(session, 1)
with self.assertRaises(KeyError): with self.assertRaises(KeyError):
session.inspect(999) session.inspect(999)
def test_inspect_includes_settled_tag_from_the_latest_frame(self):
session, engine, _, _ = make_session()
tag = {"state": "readable", "price": 150, "box": [100, 270, 200, 320]}
class Tagged:
def plan(self, boxes, image_shape, shelf_offset=None):
return []
def attach(self, tracks):
by_track = {int(track["track_id"]): dict(tag) for track in tracks}
return by_track, list(by_track.values()), {"readable": len(by_track)}
def stats(self):
return {}
session._tags = Tagged()
result = process(session, 1)
track = result["tracks"][0]
track_id = track["track_id"]
self.assertEqual(track["price_tag"]["price"], 150)
self.assertIn("reconciliation", track)
inspected = session.inspect(track_id)
self.assertEqual(inspected["price_tag"], track["price_tag"])
self.assertEqual(inspected["reconciliation"], track["reconciliation"])
self.assertIn("identity", inspected)
session._tags = None
bare = process(session, 2)
self.assertNotIn("price_tag", bare["tracks"][0])
inspected_bare = session.inspect(track_id)
self.assertNotIn("price_tag", inspected_bare)
self.assertNotIn("reconciliation", inspected_bare)
session.reset()
with self.assertRaises(KeyError):
session.inspect(track_id)
engine.shutdown() engine.shutdown()
def test_close_stops_the_loop(self): def test_close_stops_the_loop(self):