""" Interactive runner for the YOLO object-detection inventory check. Walks you through: 1. Uploading the BASELINE (pickup) photo via a native file-picker dialog 2. Uploading the RETURN photo the same way 3. Detecting objects in both, with a running total per item type 4. Comparing the two counts and reporting exactly what's missing Run: python run_yolo_interactive.py """ import os import json from yolo_detection import detect_objects, draw_detections, compare_with_asymmetric_confidence from inventory_check import compare_inventory def pick_file(window_title): """Open a native file-picker dialog. Falls back to typed path entry if tkinter isn't available (e.g. headless environments).""" try: import tkinter as tk from tkinter import filedialog root = tk.Tk() root.withdraw() root.attributes("-topmost", True) path = filedialog.askopenfilename( title=window_title, filetypes=[("Image files", "*.jpg *.jpeg *.png *.bmp *.webp"), ("All files", "*.*")], ) root.destroy() if path: return path print(" No file selected, please try again.") return pick_file(window_title) except Exception: while True: path = input(f"{window_title}\n Enter the full file path: ").strip().strip('"') if os.path.isfile(path): return path print(f" File not found: {path}") def print_item_counts(label, counts): print(f"\n{label}:") if not counts: print(" (no objects detected with enough confidence)") return total = sum(counts.values()) for name, count in sorted(counts.items(), key=lambda x: -x[1]): print(f" {name}: {count}") print(f" --- total items: {total} ---") def main(): print("=== YOLO Inventory Check ===") item_id = input("Item/kit ID (press Enter for a default): ").strip() or "kit_test_001" print("\nSelect the BASELINE (pickup) photo...") baseline_path = pick_file("Baseline photo (pickup)") print(f" Baseline: {baseline_path}") print("Select the RETURN photo...") return_path = pick_file("Return photo") print(f" Return: {return_path}") print("\nDetecting objects (this may take a few seconds the first time, while the model downloads)...") baseline_detections = detect_objects(baseline_path) return_detections = detect_objects(return_path) baseline_counts, return_counts = compare_with_asymmetric_confidence(baseline_detections, return_detections) print("\n(Note: baseline uses a strict confidence bar to establish the expected inventory;") print(" the return photo uses a more lenient bar to CONFIRM presence, since a physically") print(" unchanged item can naturally score lower confidence from a different angle/lighting -") print(" only items with no detection at all in the return photo count as missing.)") print_item_counts("BASELINE - items detected", baseline_counts) print_item_counts("RETURN - items detected", return_counts) comparison = compare_inventory(baseline_counts, return_counts) print("\n" + "=" * 50) print("RESULT") print("=" * 50) if comparison.unchanged: print("Nothing missing - all item counts match.") else: print("MISSING ITEMS DETECTED:") for name, count in comparison.missing.items(): print(f" - {name}: {count} missing " f"(expected {baseline_counts.get(name, 0)}, found {return_counts.get(name, 0)})") if comparison.extra: print("\nExtra items noticed in return photo (unusual, worth a glance):") for name, count in comparison.extra.items(): print(f" - {name}: +{count}") # save annotated images for visual review out_dir = "yolo_crops" os.makedirs(out_dir, exist_ok=True) baseline_out = os.path.join(out_dir, f"{item_id}_baseline_detected.jpg") return_out = os.path.join(out_dir, f"{item_id}_return_detected.jpg") draw_detections(baseline_path, baseline_detections, baseline_out) draw_detections(return_path, return_detections, return_out) print(f"\nAnnotated images saved:\n {baseline_out}\n {return_out}") result = { "item_id": item_id, "baseline_inventory": baseline_counts, "return_inventory": return_counts, "missing_items": comparison.missing, "extra_items": comparison.extra, "overall_change_detected": not comparison.unchanged, "routing_hint": "agent_4" if not comparison.unchanged else "none", } print("\nFull JSON output:") print(json.dumps(result, indent=2)) if __name__ == "__main__": main()