""" What does a shelf map buy over track ids? Walks the demo camera once, feeds every frame through both the live session and a ShelfMap, and reports what each counts. Needs a running server on :8077. STEPS=58 python code/bench_shelf_map.py """ import json, sys, time, urllib.request sys.path.insert(0, "code") import cv2, numpy as np from websockets.sync.client import connect from shelf_map import ShelfMap BASE = "http://127.0.0.1:8077" STEPS = int(__import__("os").environ.get("STEPS", "58")) # one walk over image6 at pan 0.02 def fetch(step): url = f"{BASE}/v1/demo/frame?samples_param=image6&step={step}&width=1280&height=720&quality=70&pan=0.02" return urllib.request.urlopen(url, timeout=10).read() shelf = ShelfMap() track_ids, frames, detections = set(), 0, 0 started = time.perf_counter() with connect("ws://127.0.0.1:8077/v1/stream") as ws: ws.recv(timeout=15) for step in range(STEPS): payload = fetch(step) frame = cv2.imdecode(np.frombuffer(payload, np.uint8), cv2.IMREAD_COLOR) ws.send(step.to_bytes(4, "big") + payload) msg = json.loads(ws.recv(timeout=25)) if msg.get("type") != "result": continue frames += 1 tracks = msg["tracks"] detections += len(tracks) for track in tracks: track_ids.add(track["track_id"]) shelf.update(step, frame, tracks) time.sleep(0.05) elapsed = time.perf_counter() - started stats = shelf.stats() print(f"{frames} frames in {elapsed:.1f} s, {detections} detections") print(f" track ids seen (what the live session counts): {len(track_ids)}") print(f" facings on the shelf map (what is actually there): {stats['facings']}") print(f" merges {stats['merges']}, registrations {stats['registrations']}, failures {stats['registration_failures']}") print(f" median registration response {stats['median_response']}") print(f" facings whose pooled identity agrees with itself >= 0.8: {stats['identity_consistent']}") facings = [f for f in shelf.facings() if f.observations >= 3] print(f"\nfacings seen at least 3 times: {len(facings)}") for facing in sorted(facings, key=lambda f: (f.shelf_box[1], f.shelf_box[0]))[:10]: sku, agreement = facing.best_identity() tag_sku, tag_agreement = facing.best_tag_identity() print(f" shelf x={facing.shelf_box[0]:>5} y={facing.shelf_box[1]:>5} obs={facing.observations:>2} " f"tracks={len(facing.seen_track_ids):>2} identity={sku} ({agreement:.2f}) tag={tag_sku} ({tag_agreement:.2f})") # render the map so duplicates are visible rather than inferred import numpy as np facings = shelf.facings() xs = [f.shelf_box[0] for f in facings] + [f.shelf_box[2] for f in facings] ys = [f.shelf_box[1] for f in facings] + [f.shelf_box[3] for f in facings] x0, x1 = min(xs), max(xs) y0, y1 = min(ys), max(ys) scale = 0.6 canvas = np.full((int((y1 - y0) * scale) + 20, int((x1 - x0) * scale) + 20, 3), 20, np.uint8) for facing in facings: bx = [(facing.shelf_box[0] - x0) * scale + 10, (facing.shelf_box[1] - y0) * scale + 10, (facing.shelf_box[2] - x0) * scale + 10, (facing.shelf_box[3] - y0) * scale + 10] colour = (0, 200, 0) if facing.observations >= 5 else (0, 120, 255) cv2.rectangle(canvas, (int(bx[0]), int(bx[1])), (int(bx[2]), int(bx[3])), colour, 1) cv2.imwrite("/tmp/dorayaki-verify/shelf-map.png", canvas) print(f"\nmap canvas {canvas.shape[1]}x{canvas.shape[0]} px covering shelf x {x0}..{x1}, y {y0}..{y1}") from collections import Counter print("observations per facing:", Counter(min(f.observations, 20) for f in facings).most_common(6)) print("facings per y band (100 px):", Counter((f.shelf_box[1] - y0) // 100 for f in facings).most_common(8))