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,167 @@
1
+ """
2
+ Detection CSV writer for the edge runtime.
3
+
4
+ Produces CSV files with the same column schema as SeaVIsion's DetectionWriter,
5
+ so edge detections can be loaded directly into the validation GUI. The writer
6
+ buffers rows in memory and flushes periodically to balance write performance
7
+ against data safety and memory usage.
8
+
9
+ Dependencies: Only python standard libraries.
10
+ """
11
+
12
+ import csv
13
+ import logging
14
+ from datetime import datetime
15
+ from pathlib import Path
16
+ from typing import Dict, List, Optional, Tuple
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+ # CSV column order — must match seavision.engine.postprocessor.CSV_HEADERS
21
+ CSV_COLUMNS = [
22
+ "source_file",
23
+ "timestamp",
24
+ "frame_number",
25
+ "xc",
26
+ "yc",
27
+ "width",
28
+ "height",
29
+ "confidence",
30
+ "label",
31
+ "track_id",
32
+ ]
33
+
34
+
35
+ class EdgeDetectionWriter:
36
+ """
37
+ Writes detections to CSV files in SeaVision-compatible format.
38
+
39
+ Creates one CSV per recording session (or per video file if processing
40
+ files rather than live camera). Buffers rows in memory and flushes every
41
+ `flush_interval` frames.
42
+
43
+ Attributes:
44
+ output_dir: Directory where CSV files will be saved.
45
+ flush_interval: Number of frames between flushes to disk.
46
+ """
47
+
48
+ def __init__(
49
+ self,
50
+ output_dir: str,
51
+ source_name: str = "camera",
52
+ flush_interval: int = 100,
53
+ ):
54
+ """
55
+ Initialise the writer.
56
+
57
+ Args:
58
+ output_dir: Directory for output files. Created if needed.
59
+ source_name: Name of the source, used in filenames and in
60
+ the source_file column.
61
+ flush_interval: Flush to disk every N frames.
62
+ """
63
+ self.output_dir = Path(output_dir)
64
+ self.output_dir.mkdir(parents=True, exist_ok=True)
65
+
66
+ self.source_name = source_name
67
+ self.flush_interval = flush_interval
68
+
69
+ # Generate a timestamped filename
70
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
71
+ self._csv_path = self.output_dir / f"detections_{timestamp}.csv"
72
+
73
+ self._buffer: List[Dict] = []
74
+ self._file = None
75
+ self._writer = None
76
+ self._frames_since_flush = 0
77
+ self._total_detections = 0
78
+
79
+ # Open and write header
80
+ self._file = open(self._csv_path, "w", newline="")
81
+ self._writer = csv.DictWriter(self._file, fieldnames=CSV_COLUMNS)
82
+ self._writer.writeheader()
83
+ self._file.flush()
84
+
85
+ logger.info("Detection writer opened: %s", self._csv_path)
86
+
87
+ def write(
88
+ self,
89
+ detections: List[Tuple[float, float, float, float, float, int]],
90
+ frame_number: int,
91
+ timestamp: float = 0.0,
92
+ class_names: Optional[Dict[int, str]] = None,
93
+ ) -> None:
94
+ """
95
+ Write detections for a single frame.
96
+
97
+ Args:
98
+ detections: List of (x1, y1, x2, y2, confidence, class_id)
99
+ tuples in pixel coordinates.
100
+ frame_number: Frame index.
101
+ timestamp: Timestamp in seconds from start of source.
102
+ class_names: Optional mapping of class_id to label string.
103
+ """
104
+ for x1, y1, x2, y2, conf, cls_id in detections:
105
+ # Convert xyxy to xc,yc,w,h to match SeaVision's Detection format
106
+ w = x2 - x1
107
+ h = y2 - y1
108
+ xc = x1 + w / 2.0
109
+ yc = y1 + h / 2.0
110
+
111
+ label = ""
112
+ if class_names and cls_id in class_names:
113
+ label = class_names[cls_id]
114
+
115
+ self._buffer.append({
116
+ "source_file": self.source_name,
117
+ "timestamp": f"{timestamp:.3f}",
118
+ "frame_number": frame_number,
119
+ "xc": f"{xc:.1f}",
120
+ "yc": f"{yc:.1f}",
121
+ "width": f"{w:.1f}",
122
+ "height": f"{h:.1f}",
123
+ "confidence": f"{conf:.4f}",
124
+ "label": label,
125
+ "track_id": "",
126
+ })
127
+
128
+ self._total_detections += len(detections)
129
+ self._frames_since_flush += 1
130
+
131
+ if self._frames_since_flush >= self.flush_interval:
132
+ self.flush()
133
+
134
+ def flush(self) -> None:
135
+ """Write buffered rows to disk."""
136
+ if not self._buffer:
137
+ return
138
+
139
+ if self._writer is not None and self._file is not None:
140
+ self._writer.writerows(self._buffer)
141
+ self._file.flush()
142
+ self._buffer.clear()
143
+ self._frames_since_flush = 0
144
+
145
+ def close(self) -> None:
146
+ """Flush remaining buffer and close the file."""
147
+ self.flush()
148
+ if self._file is not None:
149
+ self._file.close()
150
+ self._file = None
151
+
152
+ logger.info(
153
+ "Detection writer closed: %s (%d detections)",
154
+ self._csv_path, self._total_detections,
155
+ )
156
+
157
+ @property
158
+ def csv_path(self) -> Path:
159
+ """Path to the current CSV file."""
160
+ return self._csv_path
161
+
162
+ def __enter__(self):
163
+ return self
164
+
165
+ def __exit__(self, exc_type, exc_val, exc_tb):
166
+ self.close()
167
+ return False
@@ -0,0 +1,52 @@
1
+ """M3B detection package."""
2
+
3
+ from .source import (
4
+ FrameContext,
5
+ VideoMetadata,
6
+ VideoSource,
7
+ LocalVideoSource,
8
+ S3VideoSource,
9
+ discover_local_videos,
10
+ discover_s3_videos,
11
+ )
12
+ from .detectors import Detection, DetectorBase
13
+ from .postprocessor import (
14
+ OutputMode,
15
+ CSVWriterConfig,
16
+ DetectionWriter,
17
+ FramePostprocessor,
18
+ VideoPostprocessor,
19
+ LabelFilterConfig,
20
+ LabelFilter,
21
+ PerFrameNmsConfig,
22
+ PerFrameNmsPostprocessor,
23
+ MotionTrackVideoConfig,
24
+ MotionTrackVideoPostprocessor,
25
+ PostprocessStage,
26
+ build_postprocess_stages,
27
+ )
28
+
29
+ __all__ = [
30
+ "FrameContext",
31
+ "VideoMetadata",
32
+ "VideoSource",
33
+ "Detection",
34
+ "DetectorBase",
35
+ "LocalVideoSource",
36
+ "S3VideoSource",
37
+ "discover_local_videos",
38
+ "discover_s3_videos",
39
+ "OutputMode",
40
+ "CSVWriterConfig",
41
+ "DetectionWriter",
42
+ "FramePostprocessor",
43
+ "VideoPostprocessor",
44
+ "LabelFilterConfig",
45
+ "LabelFilter",
46
+ "PerFrameNmsConfig",
47
+ "PerFrameNmsPostprocessor",
48
+ "MotionTrackVideoConfig",
49
+ "MotionTrackVideoPostprocessor",
50
+ "PostprocessStage",
51
+ "build_postprocess_stages",
52
+ ]
@@ -0,0 +1,90 @@
1
+ """Detection data structures and detector interface.
2
+
3
+ Base classes (Detection, DetectorBase) are imported eagerly.
4
+ Optional detector implementations (SAM3, YOLO) use lazy imports
5
+ to avoid pulling in torch/ultralytics at package initialisation time.
6
+ This allows lightweight consumers (like the GUI) to import Detection
7
+ without triggering heavy dependency loads.
8
+ """
9
+
10
+ from .base import Detection, DetectorBase
11
+
12
+ __all__ = ["Detection", "DetectorBase"]
13
+
14
+ # Lazy-loaded flags — None means "not yet checked"
15
+ _SAM3_AVAILABLE = None
16
+ _YOLO_AVAILABLE = None
17
+
18
+ # Names that trigger lazy loading when accessed
19
+ _SAM3_NAMES = frozenset({
20
+ "SAM3Detector", "SAM3DetectorConfig", "PromptType",
21
+ "HybridStrategy", "PrompterConfig", "PromptConfig",
22
+ "SAM3_AVAILABLE",
23
+ })
24
+ _YOLO_NAMES = frozenset({
25
+ "YOLODetector", "YOLODetectorConfig",
26
+ "YOLO_AVAILABLE",
27
+ })
28
+
29
+
30
+ def __getattr__(name):
31
+ """Lazy import for optional detector implementations."""
32
+ global _SAM3_AVAILABLE, _YOLO_AVAILABLE
33
+
34
+ if name in _SAM3_NAMES:
35
+ if _SAM3_AVAILABLE is None:
36
+ try:
37
+ from .sam3 import (
38
+ SAM3Detector,
39
+ SAM3DetectorConfig,
40
+ PromptType,
41
+ HybridStrategy,
42
+ PrompterConfig,
43
+ PromptConfig,
44
+ )
45
+ _SAM3_AVAILABLE = True
46
+ # Inject into module namespace so subsequent access is fast
47
+ globals().update({
48
+ "SAM3Detector": SAM3Detector,
49
+ "SAM3DetectorConfig": SAM3DetectorConfig,
50
+ "PromptType": PromptType,
51
+ "HybridStrategy": HybridStrategy,
52
+ "PrompterConfig": PrompterConfig,
53
+ "PromptConfig": PromptConfig,
54
+ "SAM3_AVAILABLE": True,
55
+ })
56
+ except Exception:
57
+ _SAM3_AVAILABLE = False
58
+ globals()["SAM3_AVAILABLE"] = False
59
+
60
+ if name == "SAM3_AVAILABLE":
61
+ return _SAM3_AVAILABLE
62
+ if not _SAM3_AVAILABLE:
63
+ raise ImportError(
64
+ f"{name} is not available (SAM3 dependencies not installed)"
65
+ )
66
+ return globals()[name]
67
+
68
+ if name in _YOLO_NAMES:
69
+ if _YOLO_AVAILABLE is None:
70
+ try:
71
+ from .yolo import YOLODetector, YOLODetectorConfig
72
+ _YOLO_AVAILABLE = True
73
+ globals().update({
74
+ "YOLODetector": YOLODetector,
75
+ "YOLODetectorConfig": YOLODetectorConfig,
76
+ "YOLO_AVAILABLE": True,
77
+ })
78
+ except Exception:
79
+ _YOLO_AVAILABLE = False
80
+ globals()["YOLO_AVAILABLE"] = False
81
+
82
+ if name == "YOLO_AVAILABLE":
83
+ return _YOLO_AVAILABLE
84
+ if not _YOLO_AVAILABLE:
85
+ raise ImportError(
86
+ f"{name} is not available (YOLO dependencies not installed)"
87
+ )
88
+ return globals()[name]
89
+
90
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
@@ -0,0 +1,179 @@
1
+ """Base classes for the detectors."""
2
+
3
+ from abc import ABC, abstractmethod
4
+ from dataclasses import dataclass, field
5
+ from typing import Iterator, Optional
6
+ import numpy as np
7
+ from ..source import FrameContext
8
+
9
+ @dataclass
10
+ class Detection:
11
+ """
12
+ Represents a single detection in a video frame.
13
+
14
+ Attributes:
15
+ source_file: Path of the original source file.
16
+ timestamp: Timestamp within the video (seconds from the start).
17
+ frame_number: Frame index within the video.
18
+ xc: X coordinate of the detection center (pixels).
19
+ yc: Y coordinate of the detection center (pixels).
20
+ width: Width of the detection bounding box (pixels).
21
+ height: Height of the detection bounding box (pixels).
22
+ confidence: Confidence score of the detection (0.0 to 1.0), or None
23
+ if not applicable.
24
+ label: Optional class label for the detection.
25
+ track_id: Optional track ID for the detection (if tracking is used).
26
+ mask: Optional Segmentation mask as numpy array (H x W, uint8, 255=object).
27
+ metadata: Optional additional detector-specific data
28
+ """
29
+
30
+ source_file: str
31
+ timestamp: float
32
+ frame_number: int
33
+ xc: float
34
+ yc: float
35
+ width: float
36
+ height: float
37
+ confidence: Optional[float] = None
38
+ label: Optional[str] = None
39
+ track_id: Optional[int] = None
40
+ mask: Optional[np.ndarray] = field(default=None, repr=False)
41
+ metadata: Optional[dict] = field(default=None, repr=False)
42
+
43
+ def to_csv_row(self) -> dict:
44
+ """Convert the detection to a CSV row string. (dictionary)"""
45
+ row = {
46
+ "source_file": self.source_file,
47
+ "timestamp": f"{self.timestamp:.3f}",
48
+ "frame_number": self.frame_number,
49
+ "xc": f"{self.xc:.1f}",
50
+ "yc": f"{self.yc:.1f}",
51
+ "width": f"{self.width:.1f}",
52
+ "height": f"{self.height:.1f}",
53
+ }
54
+
55
+ # Add optional fields if present
56
+ if self.confidence is not None:
57
+ row["confidence"] = f"{self.confidence:.3f}"
58
+ else:
59
+ row["confidence"] = ""
60
+
61
+ if self.label is not None:
62
+ row["label"] = self.label
63
+ else:
64
+ row["label"] = ""
65
+
66
+ if self.track_id is not None:
67
+ row["track_id"] = str(self.track_id)
68
+ else:
69
+ row["track_id"] = ""
70
+
71
+ # Note: mask and metadata are not included in CSV
72
+ # (masks should be saved separately if needed)
73
+
74
+ return row
75
+
76
+ @property
77
+ def bbox(self) -> tuple:
78
+ """Get bounding box as (x1, y1, x2, y2)."""
79
+ x1 = self.xc - self.width / 2
80
+ y1 = self.yc - self.height / 2
81
+ x2 = self.xc + self.width / 2
82
+ y2 = self.yc + self.height / 2
83
+ return (x1, y1, x2, y2)
84
+
85
+ @property
86
+ def area(self) -> float:
87
+ """Get area of the bounding box."""
88
+ return self.width * self.height
89
+
90
+ @classmethod
91
+ def from_bbox(
92
+ cls,
93
+ source_file: str,
94
+ timestamp: float,
95
+ frame_number: int,
96
+ x1: float,
97
+ y1: float,
98
+ x2: float,
99
+ y2: float,
100
+ **kwargs,
101
+ ) -> "Detection":
102
+ """
103
+ Create a Detection from corner-format bounding box.
104
+
105
+ Args:
106
+ source_file: Path of the source file.
107
+ timestamp: Timestamp in seconds.
108
+ frame_number: Frame index.
109
+ x1, y1: Top-left corner.
110
+ x2, y2: Bottom-right corner.
111
+ **kwargs: Optional fields (confidence, label, track_id, etc.)
112
+
113
+ Returns:
114
+ Detection with center-format coordinates.
115
+ """
116
+ xc = (x1 + x2) / 2
117
+ yc = (y1 + y2) / 2
118
+ width = x2 - x1
119
+ height = y2 - y1
120
+
121
+ return cls(
122
+ source_file=source_file,
123
+ timestamp=timestamp,
124
+ frame_number=frame_number,
125
+ xc=xc,
126
+ yc=yc,
127
+ width=width,
128
+ height=height,
129
+ **kwargs,
130
+ )
131
+
132
+
133
+ class DetectorBase(ABC):
134
+ """
135
+ Abstract base class for all detectors.
136
+
137
+ Subclasses must implement the `process_frame` method to analyse individual
138
+ frame and yield detections.
139
+
140
+ The detector maintains internal state between frames (e.g. background
141
+ models, tracking state) and should be reset between video files.
142
+ """
143
+
144
+ @abstractmethod
145
+ def process_frame(
146
+ self,
147
+ frame: np.ndarray,
148
+ context: FrameContext
149
+ ) -> Iterator[Detection]:
150
+ """
151
+ Process a single video frame and yield detections.
152
+
153
+ Args:
154
+ frame: BGR image as a numpy array (H x W x 3).
155
+ context: Metadata about the current frame (FrameContext class).
156
+
157
+ Yields:
158
+ Detection objects for each candidate found in the frame.
159
+ """
160
+ pass
161
+
162
+ def reset(self) -> None:
163
+ """
164
+ Reset detector state for a new video file.
165
+
166
+ Override this method if your detector maintains state that should be
167
+ cleared between video files (e.g. background models, trackers).
168
+ The default implementation does nothing.
169
+ """
170
+ pass
171
+
172
+ def __enter__(self):
173
+ """Context manager entry."""
174
+ return self
175
+
176
+ def __exit__(self, exc_type, exc_val, exc_tb):
177
+ """Context manager exit - performs cleanup."""
178
+ self.reset()
179
+ return False
@@ -0,0 +1,14 @@
1
+ """Motion detection module."""
2
+
3
+ from .detector import MotionDetector, MotionDetectorConfig
4
+ from .stabiliser import FrameStabiliser, StabiliserConfig
5
+ from .background import BackgroundModel, BackgroundConfig
6
+
7
+ __all__ = [
8
+ "MotionDetector",
9
+ "MotionDetectorConfig",
10
+ "FrameStabiliser",
11
+ "StabiliserConfig",
12
+ "BackgroundModel",
13
+ "BackgroundConfig"
14
+ ]
@@ -0,0 +1,92 @@
1
+ """Background subtraction for foreground detection."""
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Optional
5
+ import cv2
6
+ import numpy as np
7
+
8
+
9
+ @dataclass
10
+ class BackgroundConfig:
11
+ """
12
+ Configuration for background subtraction.
13
+
14
+ Attributes:
15
+ history: Number of frames used to build the background model.
16
+ var_threshold:Variance threshold for foreground classification.
17
+ detect_shadows: Whether to detect shadows in the foreground mask.
18
+ learning_rate: Background model learning rate. Use -1 for auto,
19
+ or a value in [0,1] where higher = faster adaptation.
20
+ """
21
+
22
+ history: int = 600 # 1 minute at 10 FPS
23
+ var_threshold: float = 16.0
24
+ detect_shadows: bool = True
25
+ learning_rate: float = -1.0 # Auto
26
+
27
+
28
+ class BackgroundModel:
29
+ """
30
+ Adaptive background subtraction using MOG2.
31
+
32
+ Maintains a per-pixel Gaussian mixture model (GMM) to distinguish forground
33
+ (moving objects) from background (static scene).
34
+
35
+ Example:
36
+ bg_model = BackgroundModel()
37
+
38
+ for frame in stabilised_frames:
39
+ fg_mask = bg_model.apply(frame)
40
+ # mask is binary image where 255 = foreground; 0 = background
41
+ """
42
+
43
+ def __init__(self, config: Optional[BackgroundConfig] = None):
44
+ """
45
+ Initialise the background model.
46
+
47
+ Args:
48
+ config: Configuration for the background model. If None, defaults
49
+ are used.
50
+ """
51
+
52
+ self.config = config or BackgroundConfig()
53
+
54
+ self._subtractor = cv2.createBackgroundSubtractorMOG2(
55
+ history=self.config.history,
56
+ varThreshold=self.config.var_threshold,
57
+ detectShadows=self.config.detect_shadows,
58
+ )
59
+
60
+ def apply(self, frame: np.ndarray) -> np.ndarray:
61
+ """
62
+ Apply background subtraction to a frame.
63
+
64
+ Args:
65
+ frame: Input BGR frame as a numpy array - should already be
66
+ stabilised.
67
+
68
+ Returns:
69
+ Foreground mask as a binary numpy array (uint8) where:
70
+ 255 = foreground; 0 = background.
71
+ """
72
+
73
+ mask = self._subtractor.apply(
74
+ frame,
75
+ learningRate=self.config.learning_rate
76
+ )
77
+
78
+ # Ensure binary output (MOG2 can produce grey values for shadows)
79
+ _, mask = cv2.threshold(mask, 127, 255, cv2.THRESH_BINARY)
80
+
81
+ return mask
82
+
83
+ def reset(self) -> None:
84
+ """
85
+ Reset the background model to initial state.
86
+ """
87
+
88
+ self._subtractor = cv2.createBackgroundSubtractorMOG2(
89
+ history=self.config.history,
90
+ varThreshold=self.config.var_threshold,
91
+ detectShadows=self.config.detect_shadows,
92
+ )