""" path_utils.py ------------- Centralised path handling for the Pothole Detection Streamlit app. """ from pathlib import Path # ── Root of the project ──────────────────────────────────────────────────────── ROOT = Path(__file__).resolve().parent.parent def get_available_models() -> dict: """ Find and categorize trained weights in the project's local 'models' directory. Returns ------- dict : { "🛠️ Crack Model (run_name)": Path, "🕳️ Pothole Model (run_name)": Path } """ weights_parent = ROOT / "models" models = {} if not weights_parent.exists(): return models for run_dir in weights_parent.iterdir(): if run_dir.is_dir(): best_pt = run_dir / "best.pt" if best_pt.exists(): name = run_dir.name if "crack" in name.lower(): label = f"🛠️ Crack Model ({name})" else: label = f"🕳️ Pothole Model ({name})" models[label] = best_pt return models def get_model_path(variant: str) -> Path: # This is now just returning the Path directly since get_available_models returns Paths return Path(variant) def get_training_artifact(model_path: Path, filename: str) -> Path: """Return the Path to a specific training plot/artifact, searching common synonyms.""" base_dir = model_path.parent # Define common naming synonyms across YOLO versions synonyms = { "PR_curve.png": ["PR_curve.png", "BoxPR_curve.png"], "results.png": ["results.png"], "confusion_matrix.png": ["confusion_matrix_normalized.png", "confusion_matrix.png"], "val_batch0_pred.jpg": ["val_batch0_pred.jpg"], "val_batch0_labels.jpg": ["val_batch0_labels.jpg"] } if filename in synonyms: for syn in synonyms[filename]: p = base_dir / syn if p.exists(): return p # Fallback to exact filename return base_dir / filename def get_sample_path(filename: str, is_video: bool = False, is_crack: bool = False) -> Path: """Find a sample file, prioritizing cloud-accessible 'assets/' folder.""" if is_video: sub = "videos" elif is_crack: sub = "images/cracks" else: sub = "images" # Priority 1: Cloud Assets (included in Git) p = ROOT / "assets" / "samples" / sub / filename if p.exists(): return p # Priority 2: Local Data Folders (Git ignored) if is_video: p = ROOT / "data" / "samples" / "videos" / filename else: p = ROOT / "data" / "combined" / "train" / "images" / filename if not p.exists(): p = ROOT / "data" / "combined" / "test" / "images" / filename if not p.exists() and is_crack: p = ROOT / "data" / "bc_projekt" / "test" / "images" / filename return p def model_info(model_path: Path) -> dict: # Static info based on model size (deduced from filename or parent) name = str(model_path).lower() if "yolo11s" in name: return {"name": "YOLO11s", "map50": "74.1%", "speed_ms": "~8ms"} return {"name": "YOLO11n", "map50": "66.7%", "speed_ms": "~4ms"}