import gradio as gr import torch import numpy as np from PIL import Image from ultralytics import YOLO import cv2 # ─── Load Model ─────────────────────────────────────────────────────────────── MODEL_PATH = "yolov8s_best.pt" CLASS_NAMES = [ "D00 — Longitudinal Crack", "D10 — Transverse Crack", "D20 — Alligator Crack", "D40 — Pothole", ] CLASS_COLORS = { "D00 — Longitudinal Crack": "#3498db", "D10 — Transverse Crack": "#2ecc71", "D20 — Alligator Crack": "#e67e22", "D40 — Pothole": "#e74c3c", } COUNTRY_POTHOLE = { "🇮🇳 India": "⬛⬛⬛⬛⬛ Very High", "🇳🇴 Norway": "⬛⬛⬛⬛ High", "🇺🇸 United States": "⬛⬛⬛ Moderate", "🇨🇿 Czech Republic": "⬛⬛⬛ Moderate", "🇨🇳 China": "⬛⬛ Low-Moderate", "🇯🇵 Japan": "⬛ Low", } model = YOLO(MODEL_PATH) # ─── Inference Function ─────────────────────────────────────────────────────── def detect(image, conf_thresh, iou_thresh, country): img_np = np.array(image) results = model.predict( source = img_np, conf = conf_thresh, iou = iou_thresh, imgsz = 640, device = 0 if torch.cuda.is_available() else "cpu", verbose = False, )[0] # ── Annotated image ─────────────────────────────────────────────────────── annotated = results.plot() annotated_rgb = cv2.cvtColor(annotated, cv2.COLOR_BGR2RGB) # ── Count detections ────────────────────────────────────────────────────── class_counts = {name: 0 for name in CLASS_NAMES} total = len(results.boxes) pothole_count = 0 for box in results.boxes: cls_id = int(box.cls.item()) class_counts[CLASS_NAMES[cls_id]] += 1 if cls_id == 3: pothole_count += 1 # ── Detection table ─────────────────────────────────────────────────────── det_table = "### 📊 Detection Summary\n\n" det_table += "| Class | Count | % of Total |\n|---|---|---|\n" for name, count in class_counts.items(): pct = count / max(total, 1) * 100 color_dot = "🔵" if "D00" in name else "🟢" if "D10" in name else "🟠" if "D20" in name else "🔴" det_table += f"| {color_dot} {name} | {count} | {pct:.1f}% |\n" det_table += f"\n**Total Detections: {total}**" # ── Pothole report box ──────────────────────────────────────────────────── pct_pothole = pothole_count / max(total, 1) * 100 if pct_pothole > 30: severity = "🔴 CRITICAL — Immediate repair needed" elif pct_pothole > 10: severity = "🟠 MODERATE — Schedule maintenance" elif pothole_count > 0: severity = "🟡 LOW — Monitor road surface" else: severity = "🟢 NONE DETECTED — Road surface OK" pothole_box = f"""### 🕳️ Pothole (D40) Report | Field | Value | |---|---| | Pothole Count | **{pothole_count}** | | % of Detections | **{pct_pothole:.1f}%** | | Severity | {severity} | | Avg Confidence | {"N/A" if pothole_count == 0 else f"{np.mean([b.conf.item() for b in results.boxes if int(b.cls.item())==3]):.2f}"} | """ # ── Country pothole context ─────────────────────────────────────────────── country_info = COUNTRY_POTHOLE.get(country, "—") country_box = f"""### 🌍 Country Context — {country} | Field | Value | |---|---| | Selected Country | {country} | | Pothole Density (RDD2022) | {country_info} | | Dominant Damage Types | {"D00 / D10" if "Japan" in country else "D40 / D20" if "India" in country else "D10 / D40"} | | Collection Method | {"Smartphone" if "India" in country or "Japan" in country or "Czech" in country else "Street View" if "United" in country else "Camera / Drone"} | """ return Image.fromarray(annotated_rgb), det_table, pothole_box, country_box # ─── Gradio UI ──────────────────────────────────────────────────────────────── with gr.Blocks( title="🛣️ Road Damage Detector — RDD2022", theme=gr.themes.Soft(primary_hue="blue"), css=""" .title-box { text-align: center; padding: 20px; } .result-box { border-radius: 10px; padding: 10px; } footer { display: none !important; } """ ) as demo: # Header gr.HTML("""
YOLOv8s · Trained on RDD2022 · 4 Damage Classes · 6 Countries
🔵 D00 Longitudinal 🟢 D10 Transverse 🟠 D20 Alligator 🔴 D40 Pothole