feat: add bounded crop evidence and keyed gallery cache
This commit is contained in:
parent
69de9ea418
commit
0d0e9f82b9
8 changed files with 1065 additions and 41 deletions
|
|
@ -502,5 +502,179 @@ class ClassificationBudgetTests(unittest.TestCase):
|
|||
engine.shutdown()
|
||||
|
||||
|
||||
|
||||
class SmallBoxDetector(FakeDetector):
|
||||
"""A facing too small for the frame crop to decide, so only a crop can."""
|
||||
|
||||
def __init__(self, box=None):
|
||||
super().__init__()
|
||||
self.boxes = [{
|
||||
"id": 0, "box": list(box or [100, 100, 130, 200]), "confidence": 0.9,
|
||||
"class_id": 0, "width": 30, "height": 100, "center": [115, 150],
|
||||
}]
|
||||
|
||||
|
||||
def make_crop_session(matcher=None, settings=None, detector=None):
|
||||
detector = detector or SmallBoxDetector()
|
||||
engine = RecognitionEngine(
|
||||
detector, matcher or FakeMatcher(), FakeInventory(),
|
||||
settings=__import__("engine").EngineSettings(classify_workers=2),
|
||||
)
|
||||
session = LiveSession("crop", engine, settings=settings or LiveSettings(method="hybrid"))
|
||||
session.refresh_catalog()
|
||||
session.enable_crops()
|
||||
return session, engine, detector
|
||||
|
||||
|
||||
def requested_crop(result):
|
||||
for track in result["tracks"]:
|
||||
if "crop_request" in track:
|
||||
return track["crop_request"]
|
||||
return None
|
||||
|
||||
|
||||
class CropLaneTests(unittest.TestCase):
|
||||
"""A full-resolution crop is extra evidence for one track, nothing else."""
|
||||
|
||||
def test_a_protocol_v1_client_is_never_asked(self):
|
||||
session, engine, _ = make_crop_session()
|
||||
session.enable_crops # noqa: B018 - documents that crops start disabled
|
||||
session.settings.crops = False
|
||||
process(session, 1)
|
||||
result = process(session, 2)
|
||||
self.assertIsNone(requested_crop(result))
|
||||
self.assertEqual(result["stats"]["crops"]["requested"], 0)
|
||||
engine.shutdown()
|
||||
|
||||
def test_accepted_crop_labels_the_track_it_was_asked_about(self):
|
||||
session, engine, _ = make_crop_session()
|
||||
process(session, 1)
|
||||
request = requested_crop(process(session, 2))
|
||||
self.assertIsNotNone(request, "a box the frame crop cannot use must be asked about")
|
||||
self.assertEqual(request["frame_id"], 2)
|
||||
|
||||
reply = session.submit_crop(request["request_id"], request["track_id"],
|
||||
request["frame_id"], frame_bytes())
|
||||
self.assertEqual(reply["type"], "crop_ack")
|
||||
self.assertTrue(wait_for(lambda: process(session, 3)["stats"]["crops"]["accepted"] == 1))
|
||||
engine.shutdown()
|
||||
|
||||
def test_the_label_says_it_was_measured_on_a_crop(self):
|
||||
session, engine, _ = make_crop_session()
|
||||
process(session, 1)
|
||||
request = requested_crop(process(session, 2))
|
||||
session.submit_crop(request["request_id"], request["track_id"],
|
||||
request["frame_id"], frame_bytes())
|
||||
self.assertTrue(wait_for(lambda: "crop_request" not in process(session, 3)["tracks"][0]))
|
||||
labelled = [t for t in process(session, 4)["tracks"] if t["identity"]["state"] == "recognized"]
|
||||
self.assertTrue(labelled, "the crop should settle the track the frame could not")
|
||||
self.assertEqual(labelled[0]["identity"]["sku_id"], "sku_a")
|
||||
self.assertEqual(labelled[0]["identity"]["source"], "crop")
|
||||
self.assertEqual(labelled[0]["identity"]["attempts"], 1)
|
||||
engine.shutdown()
|
||||
|
||||
def test_a_crop_can_arrive_after_the_next_frame(self):
|
||||
session, engine, _ = make_crop_session()
|
||||
process(session, 1)
|
||||
request = requested_crop(process(session, 2))
|
||||
process(session, 3)
|
||||
reply = session.submit_crop(request["request_id"], request["track_id"],
|
||||
request["frame_id"], frame_bytes())
|
||||
self.assertEqual(reply["type"], "crop_ack")
|
||||
self.assertTrue(wait_for(lambda: session._crops_accepted == 1))
|
||||
engine.shutdown()
|
||||
|
||||
def test_a_track_is_asked_once(self):
|
||||
session, engine, _ = make_crop_session()
|
||||
process(session, 1)
|
||||
process(session, 2)
|
||||
result = process(session, 3)
|
||||
self.assertIsNone(requested_crop(result), "one crop per track object")
|
||||
self.assertEqual(result["stats"]["crops"]["requested"], 1)
|
||||
engine.shutdown()
|
||||
|
||||
def test_a_crop_for_another_frame_is_refused(self):
|
||||
session, engine, _ = make_crop_session()
|
||||
process(session, 1)
|
||||
request = requested_crop(process(session, 2))
|
||||
reply = session.submit_crop(request["request_id"], request["track_id"], 999, frame_bytes())
|
||||
self.assertEqual(reply["code"], "CROP_MISMATCH")
|
||||
result = process(session, 3)
|
||||
self.assertNotEqual(result["tracks"][0]["identity"]["state"], "recognized")
|
||||
self.assertEqual(result["stats"]["crops"]["rejected"], 1)
|
||||
self.assertEqual(result["stats"]["crops"]["accepted"], 0)
|
||||
engine.shutdown()
|
||||
|
||||
def test_a_crop_for_another_track_is_refused(self):
|
||||
session, engine, _ = make_crop_session()
|
||||
process(session, 1)
|
||||
request = requested_crop(process(session, 2))
|
||||
reply = session.submit_crop(request["request_id"], request["track_id"] + 41,
|
||||
request["frame_id"], frame_bytes())
|
||||
self.assertEqual(reply["code"], "CROP_MISMATCH")
|
||||
self.assertEqual(process(session, 3)["stats"]["crops"]["accepted"], 0)
|
||||
engine.shutdown()
|
||||
|
||||
def test_an_oversized_crop_is_refused(self):
|
||||
session, engine, _ = make_crop_session(settings=LiveSettings(max_crop_bytes=2048))
|
||||
session.enable_crops(2048)
|
||||
process(session, 1)
|
||||
request = requested_crop(process(session, 2))
|
||||
reply = session.submit_crop(request["request_id"], request["track_id"],
|
||||
request["frame_id"], b"\xff\xd8" + b"\x00" * 4096)
|
||||
self.assertEqual(reply["code"], "CROP_TOO_LARGE")
|
||||
self.assertEqual(process(session, 3)["stats"]["crops"]["accepted"], 0)
|
||||
engine.shutdown()
|
||||
|
||||
def test_an_undecodable_crop_is_refused(self):
|
||||
session, engine, _ = make_crop_session()
|
||||
process(session, 1)
|
||||
request = requested_crop(process(session, 2))
|
||||
reply = session.submit_crop(request["request_id"], request["track_id"],
|
||||
request["frame_id"], b"not an image")
|
||||
self.assertEqual(reply["code"], "CROP_UNDECODABLE")
|
||||
self.assertEqual(process(session, 3)["stats"]["crops"]["accepted"], 0)
|
||||
engine.shutdown()
|
||||
|
||||
def test_a_crop_cannot_be_replayed(self):
|
||||
session, engine, _ = make_crop_session()
|
||||
process(session, 1)
|
||||
request = requested_crop(process(session, 2))
|
||||
first = session.submit_crop(request["request_id"], request["track_id"],
|
||||
request["frame_id"], frame_bytes())
|
||||
self.assertEqual(first["type"], "crop_ack")
|
||||
again = session.submit_crop(request["request_id"], request["track_id"],
|
||||
request["frame_id"], frame_bytes())
|
||||
self.assertEqual(again["code"], "CROP_UNKNOWN_REQUEST")
|
||||
engine.shutdown()
|
||||
|
||||
def test_an_unknown_request_is_refused(self):
|
||||
session, engine, _ = make_crop_session()
|
||||
process(session, 1)
|
||||
reply = session.submit_crop("c-nope", 1, 1, frame_bytes())
|
||||
self.assertEqual(reply["code"], "CROP_UNKNOWN_REQUEST")
|
||||
engine.shutdown()
|
||||
|
||||
def test_an_unanswered_request_expires_and_frees_the_track(self):
|
||||
session, engine, _ = make_crop_session(settings=LiveSettings(crop_ttl_frames=1))
|
||||
process(session, 1)
|
||||
request = requested_crop(process(session, 2))
|
||||
process(session, 3)
|
||||
# The request was made on frame 2 with a one-frame budget, so by frame 4
|
||||
# it is out of date and the track it held is free again.
|
||||
result = process(session, 4)
|
||||
self.assertEqual(result["stats"]["crops"]["rejected"], 1, "an unanswered request is counted")
|
||||
late = session.submit_crop(request["request_id"], request["track_id"],
|
||||
request["frame_id"], frame_bytes())
|
||||
self.assertEqual(late["code"], "CROP_UNKNOWN_REQUEST")
|
||||
engine.shutdown()
|
||||
|
||||
def test_the_client_states_the_smaller_crop_limit(self):
|
||||
session, engine, _ = make_crop_session()
|
||||
self.assertEqual(session.enable_crops(4096)["max_crop_bytes"], 4096)
|
||||
self.assertEqual(session.capabilities()["crop"], True)
|
||||
engine.shutdown()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue