Netra / backend /app /models /detector.py
chikentikka's picture
plate recog by TrOCR and frontend and some other things updated
f09c2db
Raw
History Blame Contribute Delete
3.19 kB
"""YOLOv8 detection wrapper.
Detects road users (COCO), maps them to display categories, and counts
occupants per vehicle by how much each person box sits inside it.
"""
from collections import Counter
import numpy as np
from app.config import settings
# COCO class id -> (kind, label, display category)
COCO_MAP = {
0: ("person", "person", "Person"),
1: ("vehicle", "bicycle", "Bicycle"),
2: ("vehicle", "car", "Car"),
3: ("vehicle", "motorcycle", "Two-Wheeler"),
5: ("vehicle", "bus", "Public Transport"),
7: ("vehicle", "truck", "Heavy Vehicle"),
9: ("signal", "traffic light", "Signal"),
}
# A person counts as an occupant if at least this fraction sits inside a vehicle.
MIN_CONTAINMENT = 0.2
def _containment(person: list[int], vehicle: list[int]) -> float:
"""Fraction of the person box area that lies inside the vehicle box."""
ix1, iy1 = max(person[0], vehicle[0]), max(person[1], vehicle[1])
ix2, iy2 = min(person[2], vehicle[2]), min(person[3], vehicle[3])
inter = max(0, ix2 - ix1) * max(0, iy2 - iy1)
area = (person[2] - person[0]) * (person[3] - person[1])
return inter / area if area else 0.0
def _assign_occupants(detections: list[dict]) -> None:
"""Attribute each person to the vehicle that best contains them."""
vehicles = [d for d in detections if d["kind"] == "vehicle"]
for person in (d for d in detections if d["kind"] == "person"):
best, best_c = None, MIN_CONTAINMENT
for v in vehicles:
c = _containment(person["bbox"], v["bbox"])
if c > best_c:
best, best_c = v, c
if best is not None:
best["occupants"] += 1
def summarize(detections: list[dict]) -> list[dict]:
"""Road-user counts grouped by category, most common first (no signals)."""
counts = Counter(
d["category"] for d in detections if d["kind"] in ("vehicle", "person")
)
return [{"category": k, "count": v} for k, v in counts.most_common()]
class Detector:
"""Thin YOLOv8 wrapper. The model is loaded lazily on first use."""
def __init__(self):
self._model = None
@property
def model(self):
if self._model is None:
from ultralytics import YOLO
self._model = YOLO(settings.yolo_weights)
return self._model
def detect(self, image: np.ndarray) -> list[dict]:
"""Run inference (conf filter + NMS are built into YOLO)."""
results = self.model(image, conf=settings.confidence_threshold, verbose=False)[0]
detections = []
for i, box in enumerate(results.boxes):
cls = int(box.cls[0])
if cls not in COCO_MAP:
continue
kind, label, category = COCO_MAP[cls]
detections.append({
"id": i,
"kind": kind,
"label": label,
"category": category,
"bbox": [int(v) for v in box.xyxy[0].tolist()],
"confidence": round(float(box.conf[0]), 3),
"occupants": 0,
})
_assign_occupants(detections)
return detections
detector = Detector()