Download src/compliance.py from rishavk77/ShelfEye: direct link, hf CLI and curl.
- Browser
- Download file 4.62 kB
-
https://huggingface.co/spaces/rishavk77/ShelfEye/resolve/main/src/compliance.py
- Command line
-
hf download hf://spaces/rishavk77/ShelfEye/src/compliance.py
-
curl -L -o compliance.py https://huggingface.co/spaces/rishavk77/ShelfEye/resolve/main/src/compliance.py
4.62 kB
| """Compare detected shelf state against expected planogram.""" | |
| import json | |
| from pathlib import Path | |
| def check_compliance( | |
| pipeline_result: dict, | |
| planogram_path: str = "catalogue/planogram.json", | |
| ) -> dict: | |
| """Compare pipeline result against planogram and flag violations. | |
| Returns a compliance report with: | |
| - compliant: list of products in expected position | |
| - out_of_stock: expected products not found on shelf | |
| - wrong_position: expected products found but in wrong row | |
| - unexpected: detected products not in planogram | |
| - score: compliance percentage | |
| """ | |
| with open(planogram_path) as f: | |
| planogram = json.load(f) | |
| expected_rows = planogram["rows"] | |
| # Build set of all expected SKUs and their expected rows | |
| expected = {} | |
| for row_str, skus in expected_rows.items(): | |
| row = int(row_str) | |
| for sku in skus: | |
| if sku not in expected: | |
| expected[sku] = [] | |
| expected[sku].append(row) | |
| # Build detected SKU -> rows mapping | |
| detected = {} | |
| for p in pipeline_result["products"]: | |
| if p["matched"] and p["sku_id"]: | |
| sku = p["sku_id"] | |
| if sku not in detected: | |
| detected[sku] = [] | |
| detected[sku].append(p["row"]) | |
| compliant = [] | |
| out_of_stock = [] | |
| wrong_position = [] | |
| unexpected = [] | |
| # Check each expected SKU | |
| for sku, expected_row_list in expected.items(): | |
| if sku not in detected: | |
| out_of_stock.append({ | |
| "sku_id": sku, | |
| "expected_rows": expected_row_list, | |
| "violation": "out_of_stock", | |
| }) | |
| else: | |
| detected_rows = detected[sku] | |
| # Check if at least one detection is in an expected row | |
| in_correct_row = any(r in expected_row_list for r in detected_rows) | |
| if in_correct_row: | |
| compliant.append({ | |
| "sku_id": sku, | |
| "expected_rows": expected_row_list, | |
| "detected_rows": detected_rows, | |
| }) | |
| else: | |
| wrong_position.append({ | |
| "sku_id": sku, | |
| "expected_rows": expected_row_list, | |
| "detected_rows": detected_rows, | |
| "violation": "wrong_position", | |
| }) | |
| # Check for products on shelf but not in planogram | |
| expected_skus = set(expected.keys()) | |
| for sku, rows in detected.items(): | |
| if sku not in expected_skus: | |
| unexpected.append({ | |
| "sku_id": sku, | |
| "detected_rows": rows, | |
| "violation": "not_in_planogram", | |
| }) | |
| # Also flag unmatched detections | |
| unmatched = [ | |
| {"bbox": p["bbox"], "row": p["row"], "column": p["column"], "violation": "unrecognized"} | |
| for p in pipeline_result["products"] | |
| if not p["matched"] | |
| ] | |
| total_expected = len(expected) | |
| compliant_count = len(compliant) | |
| score = (compliant_count / total_expected * 100) if total_expected > 0 else 0 | |
| report = { | |
| "planogram": planogram["name"], | |
| "image": pipeline_result["image"], | |
| "compliance_score": round(score, 1), | |
| "total_expected": total_expected, | |
| "summary": { | |
| "compliant": compliant_count, | |
| "out_of_stock": len(out_of_stock), | |
| "wrong_position": len(wrong_position), | |
| "unexpected": len(unexpected), | |
| "unrecognized": len(unmatched), | |
| }, | |
| "compliant": compliant, | |
| "violations": { | |
| "out_of_stock": out_of_stock, | |
| "wrong_position": wrong_position, | |
| "unexpected": unexpected, | |
| "unrecognized": unmatched, | |
| }, | |
| } | |
| print(f"[compliance] {pipeline_result['image']}: score={score:.1f}% " | |
| f"({compliant_count}/{total_expected} compliant, " | |
| f"{len(out_of_stock)} OOS, {len(wrong_position)} misplaced, " | |
| f"{len(unexpected)} unexpected)") | |
| return report | |
| if __name__ == "__main__": | |
| import sys | |
| result_path = sys.argv[1] if len(sys.argv) > 1 else "outputs/result_shelf_03.json" | |
| planogram_path = sys.argv[2] if len(sys.argv) > 2 else "catalogue/planogram.json" | |
| with open(result_path) as f: | |
| result = json.load(f) | |
| report = check_compliance(result, planogram_path) | |
| out_path = result_path.replace("result_", "compliance_") | |
| with open(out_path, "w") as f: | |
| json.dump(report, f, indent=2) | |
| print(f"\nCompliance report saved to {out_path}") | |
| print(json.dumps(report, indent=2)) | |