| """ |
| Interactive runner for Agent 1 - Change Detection. |
| |
| Walks you through: |
| 1. Choosing the item type from a list |
| 2. For each required angle, uploading the BASELINE (pickup) photo and the |
| RETURN photo, one pair at a time, via a native file-picker dialog |
| 3. Optionally entering the student's complaint text |
| 4. Running the pipeline and printing a clear summary |
| |
| Run: python run_interactive.py |
| """ |
|
|
| import os |
| import json |
|
|
| from change_detection import run_agent1, ITEM_TYPE_ANGLES |
|
|
|
|
| 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 choose_item_type(): |
| """Print a numbered menu of all known item types (from ITEM_TYPE_ANGLES) and return the chosen key.""" |
| types = list(ITEM_TYPE_ANGLES.keys()) |
| print("\nSelect item type:") |
| for i, t in enumerate(types, 1): |
| print(f" {i}. {t}") |
| while True: |
| choice = input("Enter number: ").strip() |
| if choice.isdigit() and 1 <= int(choice) <= len(types): |
| return types[int(choice) - 1] |
| print("Invalid choice, try again.") |
|
|
|
|
| def main(): |
| """Full interactive flow: item type -> per-angle photo pairs -> optional complaint -> run_agent1() -> print results.""" |
| print("=== Agent 1 - Change Detection ===") |
|
|
| item_id = input("Item ID (e.g. item_laptop_014, press Enter for a default): ").strip() or "item_test_001" |
| item_type = choose_item_type() |
| angles = ITEM_TYPE_ANGLES[item_type] |
|
|
| print(f"\nItem type '{item_type}' requires {len(angles)} angle(s): {', '.join(angles)}") |
| print("For each angle you'll upload the BASELINE (pickup) photo, then the RETURN photo.\n") |
|
|
| baseline_photos = [] |
| return_photos = [] |
|
|
| for angle in angles: |
| print(f"--- Angle: {angle} ---") |
|
|
| print(f"[{angle}] Select the BASELINE (pickup) photo...") |
| base_path = pick_file(f"Baseline photo - angle: {angle}") |
| print(f" Baseline: {base_path}") |
|
|
| print(f"[{angle}] Select the RETURN photo...") |
| return_path = pick_file(f"Return photo - angle: {angle}") |
| print(f" Return: {return_path}\n") |
|
|
| baseline_photos.append({"angle": angle, "path": base_path}) |
| return_photos.append({"angle": angle, "path": return_path}) |
|
|
| complaint_text = input("Optional: student complaint text (press Enter to skip): ").strip() or None |
|
|
| print("\nRunning change detection...\n") |
| result = run_agent1( |
| item_id=item_id, |
| item_type=item_type, |
| baseline_photos=baseline_photos, |
| return_photos=return_photos, |
| complaint_text=complaint_text, |
| crop_output_dir="agent1_crops", |
| ) |
|
|
| print("=" * 50) |
| print("RESULT") |
| print("=" * 50) |
| print(f"Overall change detected : {result['overall_change_detected']}") |
| print(f"Overall change score : {result['overall_change_score']}") |
| print(f"Routing hint : {result['routing_hint']}") |
| print() |
| for a in result["angles"]: |
| print(f"- {a['angle']}: score={a['change_score']}, aligned_ok={a['aligned_ok']}, regions={len(a['regions'])}") |
| for r in a["regions"]: |
| print(f" region: bbox={r['bbox']} area={r['area']} crop={r['crop_path']}") |
|
|
| print("\nFull JSON output:") |
| print(json.dumps(result, indent=2)) |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|