Johnntirs's picture
Room Visualizer backend (Docker)
0cdb4e8
Raw
History Blame Contribute Delete
1.92 kB
"""Object detection via YOLOv8, with a graceful no-ML fallback.
When the optional ML stack (torch + ultralytics) is available and enabled, this
detects furniture-like objects so their floor footprints can be treated as
blocked space. Otherwise it returns an empty list and the floor is treated as
unobstructed — the rest of the pipeline still runs.
Grounding DINO is intentionally OUT for the MVP (YOLO covers detection).
"""
from __future__ import annotations
import functools
from dataclasses import dataclass, field
from pathlib import Path
from ..config import settings
from .ml_runtime import ml_available
# COCO labels that occupy floor space and matter for placement.
FURNITURE_LABELS = {
"bed", "couch", "chair", "dining table", "tv", "potted plant",
"refrigerator", "toilet", "sink", "bench", "vase", "book", "clock",
}
@dataclass
class Detection:
label: str
confidence: float
box: list[float] = field(default_factory=list) # [x1, y1, x2, y2] in pixels
@functools.lru_cache(maxsize=1)
def _load_model():
from ultralytics import YOLO
return YOLO(settings.YOLO_MODEL)
def detect_objects(image_path: Path, conf_threshold: float = 0.30) -> list[Detection]:
"""Detect objects in an image. Returns [] when ML is unavailable/failing."""
if not ml_available():
return []
try:
model = _load_model()
result = model(str(image_path), verbose=False)[0]
names = result.names
out: list[Detection] = []
for b in result.boxes:
conf = float(b.conf)
if conf < conf_threshold:
continue
label = str(names[int(b.cls)])
xyxy = [float(v) for v in b.xyxy[0].tolist()]
out.append(Detection(label=label, confidence=conf, box=xyxy))
return out
except Exception:
# Missing weights / runtime error -> degrade gracefully.
return []