"""Detector factory: build a Detector from AppConfig. Kept separate from ``app.py`` so both the app bootstrap and the UI can build detectors without an import cycle. Raises ``ValueError`` (not ``SystemExit``) on bad config so the GUI can show the message instead of exiting. YOLO-only, but multi-model: every path in ``config.detector_models`` (ticked under models/yolo//) becomes a YoloDetector tagged with its category, and they run together via :class:`~.multi.MultiYoloDetector`. The classic-CV detector and the ``combined`` composite were removed (noisy/approximate on real footage). """ from __future__ import annotations from pathlib import Path from ...config import AppConfig from .base import Detector from .registry import category_of def build_detector(config: AppConfig) -> Detector: models = [m for m in config.detector_models if Path(m).is_file()] if not models: raise ValueError( "Не выбрана ни одна модель детекции.\n" "Положите веса YOLO в models/yolo/<категория>/ (например models/yolo/mosaic/) " "и отметьте их галочкой в меню «Модели». См. README." ) # lazy imports: YoloDetector pulls in torch/ultralytics only when a detect runs from .multi import MultiYoloDetector from .yolo import YoloDetector nms_iou = config.nms_iou if config.cross_model_nms else None return MultiYoloDetector( [YoloDetector(m, config.detection, label=category_of(m)) for m in models], nms_iou=nms_iou, )