seavision-python 0.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. seavision/__init__.py +3 -0
  2. seavision/defaults.py +38 -0
  3. seavision/edge/__init__.py +38 -0
  4. seavision/edge/bundle.py +189 -0
  5. seavision/edge/capture.py +107 -0
  6. seavision/edge/config.py +219 -0
  7. seavision/edge/postprocess.py +185 -0
  8. seavision/edge/runtime.py +416 -0
  9. seavision/edge/writer.py +167 -0
  10. seavision/engine/__init__.py +52 -0
  11. seavision/engine/detectors/__init__.py +90 -0
  12. seavision/engine/detectors/base.py +179 -0
  13. seavision/engine/detectors/motion/__init__.py +14 -0
  14. seavision/engine/detectors/motion/background.py +92 -0
  15. seavision/engine/detectors/motion/detector.py +218 -0
  16. seavision/engine/detectors/motion/stabiliser.py +174 -0
  17. seavision/engine/detectors/motion/tracker.py +174 -0
  18. seavision/engine/detectors/sam3/__init__.py +23 -0
  19. seavision/engine/detectors/sam3/config.py +309 -0
  20. seavision/engine/detectors/sam3/detector.py +1316 -0
  21. seavision/engine/detectors/sam3/native.py +490 -0
  22. seavision/engine/detectors/yolo/__init__.py +12 -0
  23. seavision/engine/detectors/yolo/config.py +39 -0
  24. seavision/engine/detectors/yolo/detector.py +236 -0
  25. seavision/engine/export/__init__.py +24 -0
  26. seavision/engine/export/config.py +160 -0
  27. seavision/engine/export/exporter.py +286 -0
  28. seavision/engine/export/validation.py +314 -0
  29. seavision/engine/postprocessor.py +738 -0
  30. seavision/engine/source/__init__.py +16 -0
  31. seavision/engine/source/base.py +87 -0
  32. seavision/engine/source/discovery.py +154 -0
  33. seavision/engine/source/local.py +155 -0
  34. seavision/engine/source/s3.py +231 -0
  35. seavision/engine/visualiser/__init__.py +62 -0
  36. seavision/engine/visualiser/annotator.py +382 -0
  37. seavision/engine/visualiser/config.py +127 -0
  38. seavision/engine/visualiser/loader.py +558 -0
  39. seavision/engine/visualiser/visualiser.py +402 -0
  40. seavision/engine/visualiser/writer.py +245 -0
  41. seavision/gui/__init__.py +0 -0
  42. seavision/gui/app.py +30 -0
  43. seavision/gui/main_window.py +891 -0
  44. seavision/gui/shared/__init__.py +0 -0
  45. seavision/gui/shared/conversion.py +55 -0
  46. seavision/gui/shared/session_utils.py +37 -0
  47. seavision/gui/validation/__init__.py +0 -0
  48. seavision/gui/validation/button_styles.py +144 -0
  49. seavision/gui/validation/detection_detail.py +164 -0
  50. seavision/gui/validation/detection_rect_item.py +486 -0
  51. seavision/gui/validation/detection_table.py +563 -0
  52. seavision/gui/validation/interactive_frame_scene.py +340 -0
  53. seavision/gui/validation/interactive_frame_view.py +252 -0
  54. seavision/gui/validation/s3_browser.py +645 -0
  55. seavision/gui/validation/seekable_source.py +262 -0
  56. seavision/gui/validation/session.py +376 -0
  57. seavision/gui/validation/tab.py +2503 -0
  58. seavision/gui/validation/transport_bar.py +172 -0
  59. seavision/gui/validation/validation_model.py +482 -0
  60. seavision/gui/validation/video_cache.py +215 -0
  61. seavision/gui/validation/video_list.py +140 -0
  62. seavision/gui/validation/video_viewer.py +150 -0
  63. seavision/gui/validation/video_worker.py +422 -0
  64. seavision/pipeline.py +856 -0
  65. seavision/resources/default.yaml +139 -0
  66. seavision/run_edge.py +96 -0
  67. seavision/run_export.py +176 -0
  68. seavision/run_pipeline.py +388 -0
  69. seavision_python-0.1.1.dist-info/METADATA +286 -0
  70. seavision_python-0.1.1.dist-info/RECORD +73 -0
  71. seavision_python-0.1.1.dist-info/WHEEL +5 -0
  72. seavision_python-0.1.1.dist-info/entry_points.txt +5 -0
  73. seavision_python-0.1.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,490 @@
1
+ """Native SAM3 detector using the official Transformers API.
2
+
3
+ This module provides a DetectorBase-compatible wrapper around Meta's
4
+ "facebook/sam3" image segmentation model exposed via the Hugging Face
5
+ Transformers library. It is intended as an alternative to the
6
+ Ultralytics-based SAM3 integration used by :class:`SAM3Detector`.
7
+
8
+ Key characteristics:
9
+
10
+ - Uses :class:`transformers.Sam3Model` and :class:`transformers.Sam3Processor`
11
+ directly ("native" API), mirroring the behaviour of the official
12
+ SAM3 demo and documentation.
13
+ - Operates on video streams frame-by-frame, but treats each frame as an
14
+ independent image segmentation problem and then assigns simple track IDs
15
+ across frames using IoU-based matching.
16
+ - Supports TEXT prompts only ("promptable concept segmentation"), using the
17
+ same :class:`SAM3DetectorConfig` and :class:`PromptConfig` as the existing
18
+ Ultralytics-backed detector.
19
+ - Emits :class:`engine.detectors.base.Detection` objects, so it integrates
20
+ seamlessly with the rest of the SeaVision pipeline (writers, visualisers
21
+ and post-processors).
22
+
23
+ Notes
24
+ -----
25
+ - This implementation focuses on correctness and API parity rather than
26
+ maximum performance. For each frame, it runs the SAM3 model once per
27
+ text prompt, similar to how the official demo handles single-text queries.
28
+ - The tracking implementation is deliberately simple (greedy IoU matching)
29
+ and is intended to provide stable track IDs for downstream consumers
30
+ (e.g. motion-based filters), rather than fully replicating SAM3's
31
+ internal video-tracking behaviour.
32
+ """
33
+
34
+ from __future__ import annotations
35
+
36
+ import logging
37
+ from typing import Dict, Iterator, List, Optional
38
+
39
+ import numpy as np
40
+ import torch
41
+ from PIL import Image
42
+
43
+ from ...source import FrameContext
44
+ from ..base import Detection, DetectorBase
45
+ from .config import SAM3DetectorConfig, PromptConfig, PromptType
46
+
47
+ logger = logging.getLogger(__name__)
48
+
49
+
50
+ try: # Optional dependency: Transformers with SAM3 support
51
+ from transformers import Sam3Model, Sam3Processor # type: ignore
52
+
53
+ HF_SAM3_AVAILABLE = True
54
+ except ImportError: # pragma: no cover - optional dependency guard
55
+ Sam3Model = None # type: ignore[assignment]
56
+ Sam3Processor = None # type: ignore[assignment]
57
+ HF_SAM3_AVAILABLE = False
58
+
59
+
60
+ class SAM3NativeDetector(DetectorBase):
61
+ """Native SAM3 detector using the Hugging Face Transformers API.
62
+
63
+ This detector provides promptable concept segmentation for video frames
64
+ using the official ``facebook/sam3`` checkpoint. It accepts the same
65
+ :class:`SAM3DetectorConfig` as the Ultralytics-based :class:`SAM3Detector`
66
+ and produces :class:`Detection` objects in the same format so it can be
67
+ dropped into existing SeaVision pipelines.
68
+
69
+ Differences vs :class:`SAM3Detector` (Ultralytics backend):
70
+
71
+ - Uses :class:`Sam3Model` + :class:`Sam3Processor` (image PCS) rather
72
+ than Ultralytics' ``SAM3VideoSemanticPredictor``.
73
+ - Currently supports **TEXT prompts only** (one or more concepts), using
74
+ per-frame image segmentation rather than the SAM3 video model.
75
+ - Implements a simple IoU-based tracker to maintain ``track_id``
76
+ continuity across frames, instead of relying on SAM3's native video
77
+ tracking state.
78
+
79
+ Parameters
80
+ ----------
81
+ config:
82
+ :class:`SAM3DetectorConfig` instance. The following fields are used:
83
+
84
+ - ``prompts`` / ``prompt_config.text_prompts``: list of text
85
+ concepts, e.g. ``["fish", "seal"]``.
86
+ - ``confidence_threshold``: minimum score for keeping a mask.
87
+ - ``min_mask_area`` / ``max_mask_area``: area filters (in pixels).
88
+ - ``device``: ``"cuda"``, ``"cpu"``, etc.
89
+ - ``output_masks`` / ``output_labels``: control Detection outputs.
90
+
91
+ Raises
92
+ ------
93
+ ImportError
94
+ If the ``transformers`` package with SAM3 support is not installed.
95
+ ValueError
96
+ If the prompt configuration is missing or uses a non-TEXT mode.
97
+ """
98
+
99
+ # IoU threshold for associating detections with existing tracks
100
+ _TRACK_IOU_THRESHOLD: float = 0.5
101
+ # Maximum age (in frames) for keeping tracks without updates
102
+ _MAX_TRACK_AGE_FRAMES: int = 30
103
+
104
+ def __init__(self, config: Optional[SAM3DetectorConfig] = None):
105
+ if not HF_SAM3_AVAILABLE:
106
+ raise ImportError(
107
+ "SAM3NativeDetector requires the 'transformers' package with "
108
+ "SAM3 support. Install with: pip install 'transformers>=4.47' "
109
+ "and ensure the facebook/sam3 checkpoint is available."
110
+ )
111
+
112
+ self.config: SAM3DetectorConfig = config or SAM3DetectorConfig()
113
+
114
+ # Ensure we have a TEXT prompt configuration
115
+ prompt_cfg: Optional[PromptConfig] = self.config.prompt_config
116
+ if prompt_cfg is None:
117
+ if self.config.prompts:
118
+ prompt_cfg = PromptConfig(
119
+ prompt_type=PromptType.TEXT,
120
+ text_prompts=self.config.prompts,
121
+ )
122
+ self.config.prompt_config = prompt_cfg
123
+ else:
124
+ raise ValueError(
125
+ "SAM3NativeDetector requires text prompts. Provide "
126
+ "SAM3DetectorConfig(prompts=[...]) or a PromptConfig "
127
+ "with prompt_type=TEXT."
128
+ )
129
+
130
+ if prompt_cfg.prompt_type != PromptType.TEXT:
131
+ raise ValueError(
132
+ "SAM3NativeDetector currently supports only TEXT prompts. "
133
+ f"Got prompt_type={prompt_cfg.prompt_type!r}."
134
+ )
135
+
136
+ if not prompt_cfg.text_prompts:
137
+ raise ValueError(
138
+ "No text prompts specified. Provide text_prompts in the "
139
+ "PromptConfig or use the 'prompts' shorthand in "
140
+ "SAM3DetectorConfig."
141
+ )
142
+
143
+ self._device = torch.device(self.config.device)
144
+
145
+ # Lazily loaded native SAM3 components
146
+ self._model: Optional[Sam3Model] = None # type: ignore[assignment]
147
+ self._processor: Optional[Sam3Processor] = None # type: ignore[assignment]
148
+
149
+ # Video / tracking state
150
+ self._current_source_file: Optional[str] = None
151
+ self._frame_count: int = 0
152
+
153
+ # Simple track state: track_id -> {"bbox": (x1,y1,x2,y2), "label": str, "last_frame": int}
154
+ self._tracks: Dict[int, Dict] = {}
155
+ self._next_track_id: int = 0
156
+
157
+ logger.info("SAM3NativeDetector initialised")
158
+ logger.info(" Backend : Hugging Face Sam3Model (image PCS)")
159
+ logger.info(" Device : %s", self._device)
160
+ logger.info(" Prompts : %s", prompt_cfg.text_prompts)
161
+
162
+ # ------------------------------------------------------------------
163
+ # Model loading and video state management
164
+ # ------------------------------------------------------------------
165
+ def _ensure_model_loaded(self) -> None:
166
+ """Lazily load the native SAM3 model and processor.
167
+
168
+ By default this loads the official ``facebook/sam3`` checkpoint from
169
+ Hugging Face. You can override the model identifier or use a fully
170
+ offline local path by setting ``SAM3DetectorConfig.checkpoint`` to
171
+ either a local directory or a different model ID.
172
+
173
+ If the environment variable ``HF_TOKEN`` is set, it will be passed to
174
+ ``from_pretrained`` to authenticate against gated repositories.
175
+ """
176
+
177
+ if self._model is not None and self._processor is not None:
178
+ return
179
+
180
+ # Allow overriding the model via config.checkpoint. This can be either
181
+ # a local path (for offline use) or a Hugging Face model ID.
182
+ model_name_or_path = getattr(self.config, "checkpoint", None) or "facebook/sam3"
183
+
184
+ # Optional token for gated repos
185
+ import os
186
+
187
+ hf_token = os.environ.get("HF_TOKEN")
188
+
189
+ logger.info(
190
+ "Loading native SAM3 model '%s' on %s (token: %s)",
191
+ model_name_or_path,
192
+ self._device,
193
+ "set" if hf_token else "not set",
194
+ )
195
+
196
+ model_kwargs = {"token": hf_token} if hf_token else {}
197
+
198
+ # Use default dtype from the checkpoint; mixed precision can be
199
+ # controlled externally if desired.
200
+ self._model = Sam3Model.from_pretrained( # type: ignore[call-arg]
201
+ model_name_or_path,
202
+ **model_kwargs,
203
+ ).to(self._device)
204
+ self._processor = Sam3Processor.from_pretrained( # type: ignore[call-arg]
205
+ model_name_or_path,
206
+ **model_kwargs,
207
+ )
208
+
209
+ self._model.eval()
210
+ logger.info("Native SAM3 model loaded successfully")
211
+
212
+ def _on_video_change(self, context: FrameContext) -> None:
213
+ """Reset tracking state when the video source changes.
214
+
215
+ This is called automatically when ``context.source_file`` differs
216
+ from the previously seen source. It clears the internal track
217
+ dictionary and frame counter, ensuring that detections from
218
+ different videos are not linked together.
219
+ """
220
+
221
+ if self._current_source_file is not None:
222
+ logger.debug(
223
+ "Video changed from %s to %s",
224
+ self._current_source_file,
225
+ context.source_file,
226
+ )
227
+
228
+ self._current_source_file = context.source_file
229
+ self._frame_count = 0
230
+ self._tracks.clear()
231
+ self._next_track_id = 0
232
+
233
+ # ------------------------------------------------------------------
234
+ # Core DetectorBase implementation
235
+ # ------------------------------------------------------------------
236
+ def process_frame(
237
+ self,
238
+ frame: np.ndarray,
239
+ context: FrameContext,
240
+ ) -> Iterator[Detection]:
241
+ """Process a single video frame and yield detections.
242
+
243
+ For each configured text prompt, this method runs the native SAM3
244
+ image model to obtain segmentation masks for matching concepts.
245
+ Masks are converted into :class:`Detection` objects, filtered by
246
+ area and confidence, and then assigned simple track IDs based on
247
+ IoU with tracks from previous frames.
248
+ """
249
+
250
+ self._ensure_model_loaded()
251
+
252
+ # Reset state when the video source changes
253
+ if context.source_file != self._current_source_file:
254
+ self._on_video_change(context)
255
+
256
+ prompt_cfg = self.config.prompt_config
257
+ assert prompt_cfg is not None # validated in __init__
258
+ text_prompts = prompt_cfg.text_prompts
259
+
260
+ all_detections: List[Detection] = []
261
+
262
+ # Run SAM3 once per text concept, similar to the HF demo's usage.
263
+ for text in text_prompts:
264
+ detections_for_prompt = self._run_single_prompt(frame, context, text)
265
+ all_detections.extend(detections_for_prompt)
266
+
267
+ # Assign / update track IDs based on IoU with existing tracks
268
+ self._assign_tracks(all_detections, context.frame_number)
269
+
270
+ # Yield detections for this frame
271
+ for det in all_detections:
272
+ yield det
273
+
274
+ self._frame_count += 1
275
+
276
+ # ------------------------------------------------------------------
277
+ # Prompt processing helpers
278
+ # ------------------------------------------------------------------
279
+ def _run_single_prompt(
280
+ self,
281
+ frame: np.ndarray,
282
+ context: FrameContext,
283
+ text_prompt: str,
284
+ ) -> List[Detection]:
285
+ """Run SAM3 for a single text prompt on the given frame.
286
+
287
+ Parameters
288
+ ----------
289
+ frame:
290
+ BGR image as a ``numpy.ndarray`` with shape ``(H, W, 3)``.
291
+ context:
292
+ Frame metadata (timestamp, frame_number, source_file, etc.).
293
+ text_prompt:
294
+ Single natural-language concept to segment (e.g. ``"fish"``).
295
+
296
+ Returns
297
+ -------
298
+ list of :class:`Detection`
299
+ One Detection per accepted mask instance for this prompt.
300
+ """
301
+
302
+ assert self._processor is not None
303
+ assert self._model is not None
304
+
305
+ # Convert BGR (OpenCV-style) to RGB for the processor
306
+ rgb = frame[:, :, ::-1]
307
+ pil_image = Image.fromarray(rgb)
308
+
309
+ # Build model inputs using the native processor
310
+ model_inputs = self._processor(
311
+ images=pil_image,
312
+ text=text_prompt,
313
+ return_tensors="pt",
314
+ ).to(self._device)
315
+
316
+ with torch.no_grad():
317
+ outputs = self._model(**model_inputs)
318
+
319
+ # Post-process to obtain per-instance masks and scores
320
+ processed = self._processor.post_process_instance_segmentation(
321
+ outputs,
322
+ threshold=self.config.confidence_threshold,
323
+ mask_threshold=0.5,
324
+ target_sizes=model_inputs.get("original_sizes").tolist(),
325
+ )[0]
326
+
327
+ masks = processed.get("masks")
328
+ scores = processed.get("scores")
329
+
330
+ if masks is None or scores is None:
331
+ return []
332
+
333
+ # Convert to numpy for geometric processing
334
+ masks_np = masks.cpu().numpy()
335
+ scores_np = scores.cpu().numpy()
336
+
337
+ detections: List[Detection] = []
338
+
339
+ for mask_arr, score in zip(masks_np, scores_np):
340
+ conf = float(score)
341
+ if conf < self.config.confidence_threshold:
342
+ continue
343
+
344
+ # Binary mask (True where object present)
345
+ binary_mask = mask_arr > 0.5
346
+ if not binary_mask.any():
347
+ continue
348
+
349
+ # Bounding box from mask extents
350
+ rows = np.any(binary_mask, axis=1)
351
+ cols = np.any(binary_mask, axis=0)
352
+
353
+ if not rows.any() or not cols.any():
354
+ continue
355
+
356
+ y_indices = np.where(rows)[0]
357
+ x_indices = np.where(cols)[0]
358
+ y1, y2 = y_indices[0], y_indices[-1]
359
+ x1, x2 = x_indices[0], x_indices[-1]
360
+
361
+ width = x2 - x1
362
+ height = y2 - y1
363
+ area = width * height
364
+
365
+ if area < self.config.min_mask_area:
366
+ continue
367
+ if area > self.config.max_mask_area:
368
+ continue
369
+
370
+ # Optionally include the raw mask in the Detection
371
+ mask_out = None
372
+ if self.config.output_masks:
373
+ mask_out = (binary_mask.astype(np.uint8) * 255)
374
+
375
+ det = Detection.from_bbox(
376
+ source_file=context.source_file,
377
+ timestamp=context.timestamp,
378
+ frame_number=context.frame_number,
379
+ x1=float(x1),
380
+ y1=float(y1),
381
+ x2=float(x2),
382
+ y2=float(y2),
383
+ confidence=conf,
384
+ label=text_prompt if self.config.output_labels else None,
385
+ mask=mask_out,
386
+ )
387
+
388
+ detections.append(det)
389
+
390
+ return detections
391
+
392
+ # ------------------------------------------------------------------
393
+ # Tracking helpers
394
+ # ------------------------------------------------------------------
395
+ @staticmethod
396
+ def _bbox_iou(bbox_a: tuple, bbox_b: tuple) -> float:
397
+ """Compute IoU between two ``(x1, y1, x2, y2)`` boxes."""
398
+
399
+ ax1, ay1, ax2, ay2 = bbox_a
400
+ bx1, by1, bx2, by2 = bbox_b
401
+
402
+ inter_x1 = max(ax1, bx1)
403
+ inter_y1 = max(ay1, by1)
404
+ inter_x2 = min(ax2, bx2)
405
+ inter_y2 = min(ay2, by2)
406
+
407
+ if inter_x2 <= inter_x1 or inter_y2 <= inter_y1:
408
+ return 0.0
409
+
410
+ inter_area = (inter_x2 - inter_x1) * (inter_y2 - inter_y1)
411
+ area_a = max((ax2 - ax1) * (ay2 - ay1), 0.0)
412
+ area_b = max((bx2 - bx1) * (by2 - by1), 0.0)
413
+
414
+ union = area_a + area_b - inter_area
415
+ if union <= 0.0:
416
+ return 0.0
417
+
418
+ return float(inter_area / union)
419
+
420
+ def _assign_tracks(self, detections: List[Detection], frame_number: int) -> None:
421
+ """Assign or update track IDs for the detections in this frame.
422
+
423
+ The algorithm is a simple greedy IoU-based tracker:
424
+
425
+ - For each detection, find the best matching existing track with the
426
+ same label and IoU above :attr:`_TRACK_IOU_THRESHOLD`.
427
+ - If a match is found, reuse the track ID and update its state.
428
+ - Otherwise, create a new track ID for the detection.
429
+ - Tracks that have not been updated for more than
430
+ :attr:`_MAX_TRACK_AGE_FRAMES` frames are pruned.
431
+ """
432
+
433
+ if not detections and not self._tracks:
434
+ return
435
+
436
+ # Prune very old tracks
437
+ for track_id in list(self._tracks.keys()):
438
+ last_frame = self._tracks[track_id]["last_frame"]
439
+ if frame_number - last_frame > self._MAX_TRACK_AGE_FRAMES:
440
+ del self._tracks[track_id]
441
+
442
+ for det in detections:
443
+ best_track_id: Optional[int] = None
444
+ best_iou = 0.0
445
+
446
+ det_bbox = det.bbox
447
+ det_label = det.label
448
+
449
+ for track_id, track_state in self._tracks.items():
450
+ if det_label is not None and track_state["label"] != det_label:
451
+ continue
452
+
453
+ iou = self._bbox_iou(det_bbox, track_state["bbox"])
454
+ if iou > best_iou:
455
+ best_iou = iou
456
+ best_track_id = track_id
457
+
458
+ if best_track_id is not None and best_iou >= self._TRACK_IOU_THRESHOLD:
459
+ # Reuse existing track
460
+ det.track_id = best_track_id
461
+ self._tracks[best_track_id]["bbox"] = det_bbox
462
+ self._tracks[best_track_id]["last_frame"] = frame_number
463
+ else:
464
+ # Start a new track
465
+ new_id = self._next_track_id
466
+ self._next_track_id += 1
467
+
468
+ det.track_id = new_id
469
+ self._tracks[new_id] = {
470
+ "bbox": det_bbox,
471
+ "label": det_label,
472
+ "last_frame": frame_number,
473
+ }
474
+
475
+ # ------------------------------------------------------------------
476
+ # Lifecycle helpers
477
+ # ------------------------------------------------------------------
478
+ def reset(self) -> None:
479
+ """Reset detector and tracking state.
480
+
481
+ This clears the current video context, frame counter and internal
482
+ track dictionary. The underlying SAM3 model and processor remain
483
+ loaded so that subsequent videos do not pay the model load cost
484
+ again.
485
+ """
486
+
487
+ self._current_source_file = None
488
+ self._frame_count = 0
489
+ self._tracks.clear()
490
+ self._next_track_id = 0
@@ -0,0 +1,12 @@
1
+ """
2
+ YOLO-based object detector for SeaVision.
3
+
4
+ This package provides:
5
+ - YOLODetectorConfig: configuration for the YOLO detector.
6
+ - YOLODetector: DetectorBase-compatible wrapper around Ultralytics YOLO.
7
+ """
8
+
9
+ from .config import YOLODetectorConfig
10
+ from .detector import YOLODetector
11
+
12
+ __all__ = ["YOLODetector", "YOLODetectorConfig"]
@@ -0,0 +1,39 @@
1
+ """Configuration dataclass for the YOLO detector."""
2
+
3
+ from dataclasses import dataclass
4
+ from typing import List, Optional
5
+
6
+ @dataclass
7
+ class YOLODetectorConfig:
8
+ """
9
+ Configuration for the YOLO detector.
10
+
11
+ Attributes:
12
+ model_path: Path to the YOLO model weights.
13
+ device: Device to run the model on (e.g., 'cpu', 'cuda', or 'mps').
14
+ imgsz: Inference image size (e.g., 640), passed to the YOLO model.
15
+ conf_threshold: Minimum confidence score for detections.
16
+ iou_threshold: IoU threshold for non-max suppression.
17
+ max_detections: Maximum number of detections per image.
18
+ classes: Optional list of class indices to keep. If None, keep all
19
+ classes.
20
+ output_labels: Whether to map class indicies to human-readable labels
21
+ using the model's built-in names.
22
+ half: Whether to use half precision (float16) for inference. Only
23
+ applicable on compatible devices (e.g., CUDA).
24
+ verbose: Whether to print detailed logs during inference (default True).
25
+ """
26
+
27
+ model_path: str = ""
28
+ device: str = "cpu"
29
+ imgsz: int = 640
30
+
31
+ conf_threshold: float = 0.25
32
+ iou_threshold: float = 0.45
33
+ max_detections: int = 300
34
+
35
+ classes: Optional[List[int]] = None
36
+ output_labels: bool = True
37
+
38
+ half: bool = False
39
+ verbose: bool = True