feat: identity decision contract and session-local observation model
Keeps the model's uncertainty out of the UI: decide_identity() separates raw matcher scores into accepted / ambiguous / unknown / rejected, and only an accepted result can name a SKU. Zero evidence is rejected, never a product. observe.py adds frame-local observations and one-zone scan sessions (draft, finalize, cancel, rescan) with replay, staleness and backpressure rules. pipeline.process_click() now reports the identity level instead of failing when the match is not accepted. Incorporates earlier uncommitted session work in this repository (identity.py, observe.py, yolo_adapter.py, test_observe.py, and the pipeline hunk those needed); yolo_adapter keeps its decoded frame per thread so concurrent sessions cannot swap images. Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
0f1d46f9b3
commit
356f7aa502
5 changed files with 977 additions and 16 deletions
|
|
@ -116,34 +116,31 @@ class ShelfRecognitionPipeline:
|
|||
|
||||
# Classify with selected method
|
||||
cls_result = self.matcher.classify_crop(crop, method=method)
|
||||
if cls_result.get("status") != "success":
|
||||
return {
|
||||
"status": "classification_failed",
|
||||
"target_box": target_box,
|
||||
"message": cls_result.get("message", "Feature matching failed.")
|
||||
}
|
||||
identity_level = cls_result.get("identity_level", "rejected")
|
||||
best_sku = cls_result.get("best_sku")
|
||||
product_info = self.inventory.get_product(best_sku) if best_sku else None
|
||||
|
||||
best_sku = cls_result["best_sku"]
|
||||
product_info = self.inventory.get_product(best_sku)
|
||||
vis_match = None
|
||||
if best_sku:
|
||||
vis_match = self.matcher.generate_match_visualization(crop, best_sku, method=method)
|
||||
|
||||
# Generate match visualization
|
||||
vis_match = self.matcher.generate_match_visualization(crop, best_sku, method=method)
|
||||
|
||||
# Generate annotated shelf image highlighting the selected item
|
||||
annotated_shelf = self.detector.draw_detections(
|
||||
self.current_image, self.current_detections, selected_box_id=target_box["id"]
|
||||
)
|
||||
|
||||
click_status = "success" if identity_level == "accepted" else identity_level
|
||||
return {
|
||||
"status": "success",
|
||||
"status": click_status,
|
||||
"clicked_point": (x, y),
|
||||
"target_box": target_box,
|
||||
"classification": {
|
||||
"method": method,
|
||||
"matched_sku": best_sku,
|
||||
"identity_level": identity_level,
|
||||
"confidence": cls_result["confidence"],
|
||||
"score": cls_result["score"],
|
||||
"top_candidates": cls_result["ranked_candidates"],
|
||||
"message": cls_result.get("message"),
|
||||
},
|
||||
"inventory": product_info,
|
||||
"crop_image": crop,
|
||||
|
|
@ -172,9 +169,10 @@ if __name__ == "__main__":
|
|||
res = pipeline.process_click(cx, cy, method=args.method)
|
||||
print("\n--- Recognition & Inventory Result ---")
|
||||
print(f"Status: {res['status']}")
|
||||
if res['status'] == 'success':
|
||||
prod = res['inventory']
|
||||
print(f"Matched SKU: {res['classification']['matched_sku']} (Score: {res['classification']['score']})")
|
||||
cls = res.get("classification") or {}
|
||||
print(f"Identity: {cls.get('identity_level')} SKU={cls.get('matched_sku')} score={cls.get('score')}")
|
||||
prod = res.get("inventory")
|
||||
if prod:
|
||||
print(f"Product Name: {prod['product_name']}")
|
||||
print(f"Price: {prod['price']} JPY")
|
||||
print(f"In Stock: {prod['number_on']} units (Received: {prod['number_in']}, Sold: {prod['number_out']})")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue