File size: 3,247 Bytes
04a5678
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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

import gradio as gr
from PIL import Image
from typing import Tuple, Any, Dict, List

# Lazy imports to speed cold start
clf = None
yolo_severity = None

def _load_models():
    global clf, yolo_severity
    if clf is None:
        from transformers import pipeline
        # Image classification (damage types)
        clf = pipeline("image-classification", model="beingamit99/car_damage_detection")
    if yolo_severity is None:
        from ultralytics import YOLO
        # YOLOv8 severity detector (Light/Moderate/Severe)
        yolo_severity = YOLO("nezahatkorkmaz/car-damage-level-detection-yolov8")

def analyze(img: Image.Image) -> Tuple[Dict[str, Any], Image.Image, Any]:
    """
    Returns:
      - JSON summary (gate + top labels + detections)
      - Overlay image with boxes
      - Raw YOLO JSON (string or dict)
    """
    _load_models()

    # --- Gate using classifier ---
    # If model has a 'no_damage' label use it; otherwise treat max score < 0.5 as "no damage"
    preds = sorted(clf(img), key=lambda x: x["score"], reverse=True)
    top = preds[0] if preds else {"label": "unknown", "score": 0.0}
    label_lower = top["label"].lower()
    if "no" in label_lower and "damage" in label_lower:
        gate = False
    else:
        gate = top["score"] >= 0.5

    if not gate:
        return {"gate": "No visible damage", "classification_top": top}, img, {"detections": []}

    # --- Top-3 labels for type ---
    top3 = [{"label": p["label"], "score": float(p["score"])} for p in preds[:3]]

    # --- YOLO severity boxes ---
    yres = yolo_severity.predict(img)
    result = yres[0]
    plotted = result.plot()  # numpy array with drawn boxes

    dets = []
    if result.boxes is not None and len(result.boxes) > 0:
        # class names if available
        names = result.names if hasattr(result, "names") else {}
        for i in range(len(result.boxes)):
            b = result.boxes[i]
            xyxy = b.xyxy[0].tolist()
            conf = float(b.conf[0].item())
            cls_id = int(b.cls[0].item())
            cls_name = names.get(cls_id, str(cls_id))
            dets.append({
                "bbox_xyxy": [float(x) for x in xyxy],
                "confidence": conf,
                "class_id": cls_id,
                "class_name": cls_name
            })

    summary = {"gate": "Damaged", "classification_top3": top3, "detections": dets}
    try:
        raw_json = result.tojson()  # string
    except Exception:
        raw_json = {"error": "tojson failed"}

    from PIL import Image as _Image
    return summary, _Image.fromarray(plotted), raw_json

demo = gr.Interface(
    fn=analyze,
    inputs=gr.Image(type="pil", label="Upload a car photo"),
    outputs=[
        gr.JSON(label="Results (gate + top labels + detections)"),
        gr.Image(label="Detections Overlay"),
        gr.JSON(label="Raw YOLO JSON")
    ],
    title="Car Damage Inspector",
    description=(
        "Fast, open-source car damage analysis.\n"
        "- Step 1: Classify damage type (ViT).\n"
        "- Step 2: Detect severity with YOLOv8 (boxes).\n"
        "Models: beingamit99/car_damage_detection, nezahatkorkmaz/car-damage-level-detection-yolov8."
    ),
)

if __name__ == "__main__":
    demo.launch()