File size: 4,695 Bytes
0dc87cd | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | """ShelfEye Gradio Demo β AI-Powered Retail Shelf Intelligence."""
import sys
import json
import tempfile
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent / "src"))
import gradio as gr
import numpy as np
from detector import detect_products
from cropper import crop_detections
from embedder import embed_catalogue, embed_crops
from matcher import match_detections
from position import assign_positions
from compliance import check_compliance
from visualizer import draw_detections
def analyze_shelf(image_path: str, use_vlm: bool, clip_threshold: float):
if image_path is None:
return None, "Upload a shelf image to begin."
dets = detect_products(image_path)
dets = crop_detections(image_path, dets)
dets = match_detections(dets, clip_threshold=clip_threshold, use_vlm=use_vlm)
dets = assign_positions(dets)
annotated = draw_detections(image_path, dets)
annotated_rgb = annotated[:, :, ::-1]
# Build pipeline result for compliance
products = []
for d in dets:
products.append({
"sku_id": d.get("sku_id"),
"product_name": d.get("product_name", "unknown"),
"confidence": round(d.get("similarity", d.get("confidence", 0)), 4),
"bbox": [int(c) for c in d["bbox"]],
"row": d["row"],
"column": d["column"],
"matched": d.get("matched", False),
"match_method": d.get("match_method", "none"),
})
matched_count = sum(1 for p in products if p["matched"])
result = {
"image": Path(image_path).name,
"total_products": len(products),
"matched": matched_count,
"unmatched": len(products) - matched_count,
"products": products,
}
# Compliance check
planogram_path = Path("catalogue/planogram.json")
if planogram_path.exists():
compliance = check_compliance(result, str(planogram_path))
else:
compliance = {"note": "No planogram found. Skipping compliance check."}
result_json = json.dumps(result, indent=2)
compliance_json = json.dumps(compliance, indent=2)
summary = (
f"**Detected:** {len(products)} products\n\n"
f"**Matched:** {matched_count} ({matched_count/len(products)*100:.0f}%)\n\n"
f"**Unmatched:** {len(products) - matched_count}\n\n"
)
if "compliance_score" in compliance:
summary += (
f"**Compliance Score:** {compliance['compliance_score']}%\n\n"
f"**Out of Stock:** {compliance['summary']['out_of_stock']}\n\n"
f"**Wrong Position:** {compliance['summary']['wrong_position']}\n\n"
f"**Unexpected:** {compliance['summary']['unexpected']}"
)
return annotated_rgb, summary, result_json, compliance_json
with open("catalogue/catalogue.json") as f:
catalogue = json.load(f)
catalogue_info = "\n".join(f"- {p['product_name']} ({p['brand']})" for p in catalogue)
with gr.Blocks(title="ShelfEye β AI Shelf Intelligence", theme=gr.themes.Soft()) as demo:
gr.Markdown(
"# ShelfEye β AI-Powered Retail Shelf Intelligence\n"
"Upload a retail shelf image to detect products, match them against a catalogue, "
"and check planogram compliance.\n\n"
"**Pipeline:** Grounding DINO (detection) β CLIP (matching) β Qwen2.5-VL (fallback) β Compliance Engine"
)
with gr.Row():
with gr.Column(scale=1):
input_image = gr.Image(type="filepath", label="Upload Shelf Image")
with gr.Accordion("Settings", open=False):
use_vlm = gr.Checkbox(value=True, label="Enable VLM Fallback (Qwen2.5-VL)")
clip_threshold = gr.Slider(0.5, 0.9, value=0.65, step=0.05, label="CLIP Match Threshold")
run_btn = gr.Button("Analyze Shelf", variant="primary")
gr.Markdown("### Catalogue (20 products)")
gr.Markdown(catalogue_info)
with gr.Column(scale=2):
output_image = gr.Image(label="Annotated Result")
summary_md = gr.Markdown(label="Summary")
with gr.Row():
result_json = gr.Code(label="Detection Result (JSON)", language="json")
compliance_json = gr.Code(label="Compliance Report (JSON)", language="json")
run_btn.click(
fn=analyze_shelf,
inputs=[input_image, use_vlm, clip_threshold],
outputs=[output_image, summary_md, result_json, compliance_json],
)
gr.Markdown(
"---\n"
"Built with Grounding DINO, CLIP, Qwen2.5-VL | "
"[GitHub](https://github.com/rixav77/ShelfEye)"
)
if __name__ == "__main__":
demo.launch(server_name="0.0.0.0", server_port=7860)
|