File size: 4,547 Bytes
36dac13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b39045a
36dac13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b39045a
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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import spaces  # MUST come before torch / any CUDA-touching import
import os
os.environ.setdefault("PYTORCH_CUDA_ALLOC_CONF", "expandable_segments:True")

import tempfile
import gradio as gr
import numpy as np
import PIL.Image as Image
from huggingface_hub import hf_hub_download
from ultralytics import YOLO

# VisDrone class names (10 classes detected by this model)
VISDRONE_CLASSES = [
    "pedestrian", "people", "bicycle", "car", "van",
    "truck", "tricycle", "awning-tricycle", "bus", "motor",
]

# Load model weights at module scope — ZeroGPU intercepts .to("cuda")
weights_path = hf_hub_download(repo_id="dronefreak/visdrone-yolov26n", filename="best.pt")
model = YOLO(weights_path)


@spaces.GPU(duration=30)
def detect(
    image,
    conf_threshold: float = 0.25,
    iou_threshold: float = 0.7,
    show_labels: bool = True,
    show_conf: bool = True,
    imgsz: int = 640,
):
    """Detect objects in an aerial/drone image using YOLOv26n fine-tuned on VisDrone.

    Args:
        image: Input image (PIL or numpy array).
        conf_threshold: Minimum confidence score for detections (0–1).
        iou_threshold: IoU NMS threshold (0–1).
        show_labels: Whether to draw class labels on the output.
        show_conf: Whether to draw confidence scores on the output.
        imgsz: Inference image size in pixels.
    """
    results = model.predict(
        source=image,
        conf=conf_threshold,
        iou=iou_threshold,
        imgsz=imgsz,
        verbose=False,
    )

    # Build detection summary
    detection_counts = {}
    if results[0].boxes is not None and len(results[0].boxes) > 0:
        cls_ids = results[0].boxes.cls.cpu().numpy().astype(int)
        for cid in cls_ids:
            name = VISDRONE_CLASSES[cid] if cid < len(VISDRONE_CLASSES) else str(cid)
            detection_counts[name] = detection_counts.get(name, 0) + 1

    summary_lines = [f"**Total detections:** {sum(detection_counts.values())}"]
    for name, count in sorted(detection_counts.items(), key=lambda x: -x[1]):
        summary_lines.append(f"- {name}: {count}")
    summary = "\n".join(summary_lines)

    # Annotated image
    annotated = results[0].plot(labels=show_labels, conf=show_conf)
    annotated_pil = Image.fromarray(annotated[..., ::-1])

    return annotated_pil, summary


CSS = """
#col-container { max-width: 1200px; margin: 0 auto; }
.dark .gradio-container { color: var(--body-text-color); }
"""

with gr.Blocks(title="YOLOv26n VisDrone Detection") as demo:
    gr.Markdown(
        """
        # YOLOv26n VisDrone Object Detection

        A lightweight 2.6M-parameter YOLOv26n model fine-tuned on the [VisDrone](https://github.com/VisDrone/VisDrone-Dataset) benchmark for aerial/drone imagery.
        It detects 10 classes: *pedestrian, people, bicycle, car, van, truck, tricycle, awning-tricycle, bus, motor*.

        Upload a drone/aerial image and run detection, or try one of the examples below.
        """
    )

    with gr.Row():
        with gr.Column():
            input_image = gr.Image(type="pil", label="Input Image")
            with gr.Accordion("Detection Settings", open=False):
                conf_slider = gr.Slider(minimum=0.0, maximum=1.0, value=0.25, step=0.01, label="Confidence Threshold")
                iou_slider = gr.Slider(minimum=0.0, maximum=1.0, value=0.7, step=0.01, label="IoU (NMS) Threshold")
                imgsz_radio = gr.Radio(choices=[320, 640, 1024], value=640, label="Inference Image Size")
                labels_checkbox = gr.Checkbox(value=True, label="Show Labels")
                conf_show_checkbox = gr.Checkbox(value=True, label="Show Confidence Scores")
            detect_btn = gr.Button("Detect Objects", variant="primary")

        with gr.Column():
            output_image = gr.Image(type="pil", label="Detection Result")
            detection_summary = gr.Markdown(label="Detection Summary")

    detect_btn.click(
        fn=detect,
        inputs=[input_image, conf_slider, iou_slider, labels_checkbox, conf_show_checkbox, imgsz_radio],
        outputs=[output_image, detection_summary],
    )

    gr.Examples(
        examples=[
            ["examples/0000001_02999_d_0000005.jpg"],
            ["examples/0000002_00005_d_0000014.jpg"],
            ["examples/0000006_00159_d_0000001.jpg"],
        ],
        inputs=[input_image],
        outputs=[output_image, detection_summary],
        fn=detect,
        cache_examples=True,
        cache_mode="lazy",
    )

demo.launch(mcp_server=True, theme=gr.themes.Citrus(), css=CSS)