OTVision 0.6.3__py3-none-any.whl → 0.6.5__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.
- OTVision/abstraction/defaults.py +15 -0
- OTVision/application/buffer.py +2 -1
- OTVision/application/config.py +475 -0
- OTVision/application/config_parser.py +280 -0
- OTVision/application/configure_logger.py +1 -1
- OTVision/application/detect/factory.py +1 -1
- OTVision/application/detect/update_detect_config_with_cli_args.py +2 -1
- OTVision/application/get_config.py +2 -1
- OTVision/application/get_current_config.py +1 -1
- OTVision/application/track/__init__.py +0 -0
- OTVision/application/track/get_track_cli_args.py +20 -0
- OTVision/application/track/update_current_track_config.py +42 -0
- OTVision/application/track/update_track_config_with_cli_args.py +52 -0
- OTVision/application/update_current_config.py +1 -1
- OTVision/config.py +61 -668
- OTVision/convert/convert.py +3 -3
- OTVision/detect/builder.py +27 -20
- OTVision/detect/cli.py +3 -3
- OTVision/detect/detected_frame_buffer.py +6 -0
- OTVision/detect/file_based_detect_builder.py +19 -0
- OTVision/detect/otdet.py +54 -1
- OTVision/detect/otdet_file_writer.py +3 -2
- OTVision/detect/rtsp_based_detect_builder.py +37 -0
- OTVision/detect/rtsp_input_source.py +199 -0
- OTVision/detect/timestamper.py +1 -1
- OTVision/detect/video_input_source.py +3 -3
- OTVision/detect/yolo.py +17 -1
- OTVision/domain/cli.py +31 -1
- OTVision/domain/current_config.py +1 -1
- OTVision/domain/frame.py +9 -0
- OTVision/domain/object_detection.py +6 -1
- OTVision/domain/serialization.py +12 -0
- OTVision/domain/time.py +13 -0
- OTVision/helpers/files.py +14 -15
- OTVision/plugin/__init__.py +0 -0
- OTVision/plugin/yaml_serialization.py +20 -0
- OTVision/track/builder.py +132 -0
- OTVision/track/cli.py +128 -0
- OTVision/track/exporter/filebased_exporter.py +2 -1
- OTVision/track/id_generator.py +15 -0
- OTVision/track/model/track_exporter.py +2 -1
- OTVision/track/model/tracking_interfaces.py +6 -6
- OTVision/track/parser/frame_group_parser_plugins.py +35 -5
- OTVision/track/track.py +54 -133
- OTVision/track/tracker/filebased_tracking.py +8 -7
- OTVision/track/tracker/tracker_plugin_iou.py +14 -9
- OTVision/transform/transform.py +2 -2
- OTVision/version.py +1 -1
- otvision-0.6.5.dist-info/METADATA +182 -0
- {otvision-0.6.3.dist-info → otvision-0.6.5.dist-info}/RECORD +52 -35
- otvision-0.6.3.dist-info/METADATA +0 -49
- {otvision-0.6.3.dist-info → otvision-0.6.5.dist-info}/WHEEL +0 -0
- {otvision-0.6.3.dist-info → otvision-0.6.5.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
def value_or_default[T](value: T | None, default: T) -> T:
|
|
2
|
+
"""
|
|
3
|
+
Returns the provided value if it is not None; otherwise, returns a default value.
|
|
4
|
+
|
|
5
|
+
Args:
|
|
6
|
+
value (T | None): The value to be evaluated.
|
|
7
|
+
default (T): The fallback value if 'value' is None.
|
|
8
|
+
|
|
9
|
+
Returns:
|
|
10
|
+
T: The provided value or the default value if the original is None.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
if value is not None:
|
|
14
|
+
return value
|
|
15
|
+
return default
|
OTVision/application/buffer.py
CHANGED
|
@@ -22,7 +22,8 @@ class Buffer[T, SUBJECT_TYPE, OBSERVING_TYPE](Observable[SUBJECT_TYPE], Filter[T
|
|
|
22
22
|
return self._buffer
|
|
23
23
|
|
|
24
24
|
def _reset_buffer(self) -> None:
|
|
25
|
-
self._buffer
|
|
25
|
+
del self._buffer
|
|
26
|
+
self._buffer = list()
|
|
26
27
|
|
|
27
28
|
def on_flush(self, event: OBSERVING_TYPE) -> None:
|
|
28
29
|
buffered_elements = self._get_buffered_elements()
|
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from datetime import datetime, timedelta
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
AVAILABLE_WEIGHTS = "AVAILABLEWEIGHTS"
|
|
6
|
+
CALIBRATIONS = "CALIBRATIONS"
|
|
7
|
+
COL_WIDTH = "COLWIDTH"
|
|
8
|
+
CONF = "CONF"
|
|
9
|
+
CONVERT = "CONVERT"
|
|
10
|
+
DEFAULT_FILETYPE = "DEFAULT_FILETYPE"
|
|
11
|
+
DELETE_INPUT = "DELETE_INPUT"
|
|
12
|
+
ROTATION = "ROTATION"
|
|
13
|
+
DETECT = "DETECT"
|
|
14
|
+
DETECTIONS = "DETECTIONS"
|
|
15
|
+
FILETYPES = "FILETYPES"
|
|
16
|
+
FONT = "FONT"
|
|
17
|
+
FONT_SIZE = "FONTSIZE"
|
|
18
|
+
FPS_FROM_FILENAME = "FPS_FROM_FILENAME"
|
|
19
|
+
FRAME_WIDTH = "FRAMEWIDTH"
|
|
20
|
+
GUI = "GUI"
|
|
21
|
+
HALF_PRECISION = "HALF_PRECISION"
|
|
22
|
+
INPUT_FPS = "INPUT_FPS"
|
|
23
|
+
IMG = "IMG"
|
|
24
|
+
IMG_SIZE = "IMGSIZE"
|
|
25
|
+
IOU = "IOU"
|
|
26
|
+
LAST_PATHS = "LAST PATHS"
|
|
27
|
+
LOCATION_X = "LOCATION_X"
|
|
28
|
+
LOCATION_Y = "LOCATION_Y"
|
|
29
|
+
NORMALIZED = "NORMALIZED"
|
|
30
|
+
OTC_ICON = "OTC ICON"
|
|
31
|
+
OUTPUT_FPS = "OUTPUT_FPS"
|
|
32
|
+
OUTPUT_FILETYPE = "OUTPUT_FILETYPE"
|
|
33
|
+
OVERWRITE = "OVERWRITE"
|
|
34
|
+
PATHS = "PATHS"
|
|
35
|
+
RUN_CHAINED = "RUN_CHAINED"
|
|
36
|
+
EXPECTED_DURATION = "EXPECTED_DURATION"
|
|
37
|
+
REFPTS = "REFPTS"
|
|
38
|
+
SEARCH_SUBDIRS = "SEARCH_SUBDIRS"
|
|
39
|
+
SIGMA_H = "SIGMA_H"
|
|
40
|
+
SIGMA_IOU = "SIGMA_IOU"
|
|
41
|
+
SIGMA_L = "SIGMA_L"
|
|
42
|
+
T_MIN = "T_MIN"
|
|
43
|
+
T_MISS_MAX = "T_MISS_MAX"
|
|
44
|
+
TRACK = "TRACK"
|
|
45
|
+
TRACKS = "TRACKS"
|
|
46
|
+
TRANSFORM = "TRANSFORM"
|
|
47
|
+
UNDISTORT = "UNDISTORT"
|
|
48
|
+
VID = "VID"
|
|
49
|
+
VID_ROTATABLE = "VID_ROTATABLE"
|
|
50
|
+
VIDEOS = "VIDEOS"
|
|
51
|
+
WEIGHTS = "WEIGHTS"
|
|
52
|
+
WINDOW = "WINDOW"
|
|
53
|
+
YOLO = "YOLO"
|
|
54
|
+
LOG = "LOG"
|
|
55
|
+
LOG_LEVEL_CONSOLE = "LOG_LEVEL_CONSOLE"
|
|
56
|
+
LOG_LEVEL_FILE = "LOG_LEVEL_FILE"
|
|
57
|
+
LOG_DIR = "LOG_DIR"
|
|
58
|
+
START_TIME = "START_TIME"
|
|
59
|
+
DETECT_END = "DETECT_END"
|
|
60
|
+
DETECT_START = "DETECT_START"
|
|
61
|
+
DATETIME_FORMAT = "%Y-%m-%d_%H-%M-%S"
|
|
62
|
+
DEFAULT_EXPECTED_DURATION: timedelta = timedelta(minutes=15)
|
|
63
|
+
"""Default length of a video is 15 minutes."""
|
|
64
|
+
STREAM = "STREAM"
|
|
65
|
+
STREAM_SAVE_DIR = "SAVE_DIR"
|
|
66
|
+
STREAM_NAME = "NAME"
|
|
67
|
+
STREAM_SOURCE = "SOURCE"
|
|
68
|
+
FLUSH_BUFFER_SIZE = "FLUSH_BUFFER_SIZE"
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@dataclass(frozen=True)
|
|
72
|
+
class _LogConfig:
|
|
73
|
+
log_level_console: str = "WARNING"
|
|
74
|
+
log_level_file: str = "DEBUG"
|
|
75
|
+
|
|
76
|
+
def to_dict(self) -> dict:
|
|
77
|
+
return {
|
|
78
|
+
LOG_LEVEL_CONSOLE: self.log_level_console,
|
|
79
|
+
LOG_LEVEL_FILE: self.log_level_file,
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@dataclass(frozen=True)
|
|
84
|
+
class _DefaultFiletype:
|
|
85
|
+
video: str = ".mp4"
|
|
86
|
+
image: str = ".jpg"
|
|
87
|
+
detect: str = ".otdet"
|
|
88
|
+
track: str = ".ottrk"
|
|
89
|
+
refpts: str = ".otrfpts"
|
|
90
|
+
|
|
91
|
+
def to_dict(self) -> dict:
|
|
92
|
+
return {
|
|
93
|
+
VID: self.video,
|
|
94
|
+
IMG: self.image,
|
|
95
|
+
DETECT: self.detect,
|
|
96
|
+
TRACK: self.track,
|
|
97
|
+
REFPTS: self.refpts,
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@dataclass(frozen=True)
|
|
102
|
+
class _VideoFiletypes:
|
|
103
|
+
avi: str = ".avi"
|
|
104
|
+
mkv: str = ".mkv"
|
|
105
|
+
mov: str = ".mov"
|
|
106
|
+
mp4: str = ".mp4"
|
|
107
|
+
|
|
108
|
+
def to_list(self) -> list:
|
|
109
|
+
return [
|
|
110
|
+
self.avi,
|
|
111
|
+
self.mkv,
|
|
112
|
+
self.mov,
|
|
113
|
+
self.mp4,
|
|
114
|
+
]
|
|
115
|
+
|
|
116
|
+
def rotatable_to_list(self) -> list:
|
|
117
|
+
return [self.mov, self.mp4]
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
@dataclass(frozen=True)
|
|
121
|
+
class _ImageFiletypes:
|
|
122
|
+
jpg: str = ".jpg"
|
|
123
|
+
jpeg: str = ".jpeg"
|
|
124
|
+
png: str = ".png"
|
|
125
|
+
|
|
126
|
+
def to_list(self) -> list:
|
|
127
|
+
return [self.jpg, self.jpeg, self.png]
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
@dataclass(frozen=True)
|
|
131
|
+
class _Filetypes:
|
|
132
|
+
video_filetypes: _VideoFiletypes = _VideoFiletypes()
|
|
133
|
+
image_filetypes: _ImageFiletypes = _ImageFiletypes()
|
|
134
|
+
detect: str = _DefaultFiletype.detect
|
|
135
|
+
track: str = _DefaultFiletype.track
|
|
136
|
+
refpts: str = _DefaultFiletype.refpts
|
|
137
|
+
transform: str = ".gpkg"
|
|
138
|
+
|
|
139
|
+
def to_dict(self) -> dict:
|
|
140
|
+
return {
|
|
141
|
+
VID: self.video_filetypes.to_list(),
|
|
142
|
+
VID_ROTATABLE: self.video_filetypes.rotatable_to_list(),
|
|
143
|
+
IMG: self.image_filetypes.to_list(),
|
|
144
|
+
DETECT: [self.detect],
|
|
145
|
+
TRACK: [self.track],
|
|
146
|
+
REFPTS: [self.refpts],
|
|
147
|
+
TRANSFORM: [self.transform],
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
@dataclass(frozen=True)
|
|
152
|
+
class _LastPaths:
|
|
153
|
+
videos: list = field(default_factory=list)
|
|
154
|
+
detections: list = field(default_factory=list)
|
|
155
|
+
tracks: list = field(default_factory=list)
|
|
156
|
+
calibrations: list = field(default_factory=list)
|
|
157
|
+
refpts: list = field(default_factory=list)
|
|
158
|
+
|
|
159
|
+
def to_dict(self) -> dict:
|
|
160
|
+
return {
|
|
161
|
+
VIDEOS: self.videos,
|
|
162
|
+
DETECTIONS: self.detections,
|
|
163
|
+
TRACKS: self.tracks,
|
|
164
|
+
CALIBRATIONS: self.calibrations,
|
|
165
|
+
REFPTS: self.refpts,
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
@dataclass(frozen=True)
|
|
170
|
+
class ConvertConfig:
|
|
171
|
+
paths: list[str] = field(default_factory=list)
|
|
172
|
+
run_chained: bool = True
|
|
173
|
+
output_filetype: str = _VideoFiletypes.mp4
|
|
174
|
+
input_fps: float = 20.0
|
|
175
|
+
output_fps: float = 20.0
|
|
176
|
+
fps_from_filename: bool = True
|
|
177
|
+
delete_input: bool = False
|
|
178
|
+
rotation: int = 0
|
|
179
|
+
overwrite: bool = True
|
|
180
|
+
|
|
181
|
+
def to_dict(self) -> dict:
|
|
182
|
+
return {
|
|
183
|
+
PATHS: self.paths,
|
|
184
|
+
RUN_CHAINED: self.run_chained,
|
|
185
|
+
OUTPUT_FILETYPE: self.output_filetype,
|
|
186
|
+
INPUT_FPS: self.input_fps,
|
|
187
|
+
OUTPUT_FPS: self.output_fps,
|
|
188
|
+
FPS_FROM_FILENAME: self.fps_from_filename,
|
|
189
|
+
DELETE_INPUT: self.delete_input,
|
|
190
|
+
ROTATION: self.rotation,
|
|
191
|
+
OVERWRITE: self.overwrite,
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
@dataclass(frozen=True)
|
|
196
|
+
class _YoloWeights:
|
|
197
|
+
yolov8s: str = "yolov8s"
|
|
198
|
+
yolov8m: str = "yolov8m"
|
|
199
|
+
yolov8l: str = "yolov8l"
|
|
200
|
+
yolov8x: str = "yolov8x"
|
|
201
|
+
|
|
202
|
+
def to_list(self) -> list:
|
|
203
|
+
return [self.yolov8s, self.yolov8m, self.yolov8l, self.yolov8x]
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
@dataclass(frozen=True)
|
|
207
|
+
class YoloConfig:
|
|
208
|
+
"""Represents the configuration for the YOLO model.
|
|
209
|
+
|
|
210
|
+
Attributes:
|
|
211
|
+
weights (str): Path to YOLO model weights.
|
|
212
|
+
available_weights (_YoloWeights): List of available default YOLO model weights.
|
|
213
|
+
conf (float): Confidence threshold.
|
|
214
|
+
iou (float): Intersection over union threshold.
|
|
215
|
+
img_size (int): Size of the input image.
|
|
216
|
+
chunk_size (int): Chunk size for processing.
|
|
217
|
+
normalized (bool): Whether to normalize the bounding boxes.
|
|
218
|
+
"""
|
|
219
|
+
|
|
220
|
+
weights: str = _YoloWeights.yolov8s
|
|
221
|
+
available_weights: _YoloWeights = _YoloWeights()
|
|
222
|
+
conf: float = 0.25
|
|
223
|
+
iou: float = 0.45
|
|
224
|
+
img_size: int = 640
|
|
225
|
+
chunk_size: int = 1
|
|
226
|
+
normalized: bool = False
|
|
227
|
+
|
|
228
|
+
def to_dict(self) -> dict:
|
|
229
|
+
return {
|
|
230
|
+
WEIGHTS: self.weights,
|
|
231
|
+
AVAILABLE_WEIGHTS: self.available_weights.to_list(),
|
|
232
|
+
CONF: self.conf,
|
|
233
|
+
IOU: self.iou,
|
|
234
|
+
IMG_SIZE: self.img_size,
|
|
235
|
+
NORMALIZED: self.normalized,
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
@dataclass(frozen=True)
|
|
240
|
+
class DetectConfig:
|
|
241
|
+
"""Represents the configuration for the `detect` command.
|
|
242
|
+
|
|
243
|
+
Attributes:
|
|
244
|
+
paths (list[Path]): List of files to be processed.
|
|
245
|
+
run_chained (bool): Whether to run chained commands.
|
|
246
|
+
yolo_config (YoloConfig): Configuration for the YOLO model.
|
|
247
|
+
expected_duration (timedelta | None): Expected duration of the video.
|
|
248
|
+
`None` if unknown.
|
|
249
|
+
overwrite (bool): Whether to overwrite existing files.
|
|
250
|
+
half_precision (bool): Whether to use half precision.
|
|
251
|
+
detect_start (int | None): Start frame for detection expressed in seconds.
|
|
252
|
+
Value `None` marks the start of the video.
|
|
253
|
+
detect_end (int | None): End frame for detection expressed in seconds.
|
|
254
|
+
Value `None` marks the end of the video.
|
|
255
|
+
|
|
256
|
+
"""
|
|
257
|
+
|
|
258
|
+
@property
|
|
259
|
+
def confidence(self) -> float:
|
|
260
|
+
"""Gets the confidence level set in the YOLO configuration.
|
|
261
|
+
|
|
262
|
+
Returns:
|
|
263
|
+
float: The intersection over union threshold value.
|
|
264
|
+
"""
|
|
265
|
+
return self.yolo_config.conf
|
|
266
|
+
|
|
267
|
+
@property
|
|
268
|
+
def weights(self) -> str:
|
|
269
|
+
return self.yolo_config.weights
|
|
270
|
+
|
|
271
|
+
@property
|
|
272
|
+
def iou(self) -> float:
|
|
273
|
+
return self.yolo_config.iou
|
|
274
|
+
|
|
275
|
+
@property
|
|
276
|
+
def img_size(self) -> int:
|
|
277
|
+
return self.yolo_config.img_size
|
|
278
|
+
|
|
279
|
+
@property
|
|
280
|
+
def normalized(self) -> bool:
|
|
281
|
+
return self.yolo_config.normalized
|
|
282
|
+
|
|
283
|
+
paths: list[str] = field(default_factory=list)
|
|
284
|
+
run_chained: bool = True
|
|
285
|
+
yolo_config: YoloConfig = YoloConfig()
|
|
286
|
+
expected_duration: timedelta | None = None
|
|
287
|
+
overwrite: bool = True
|
|
288
|
+
half_precision: bool = False
|
|
289
|
+
start_time: datetime | None = None
|
|
290
|
+
detect_start: int | None = None
|
|
291
|
+
detect_end: int | None = None
|
|
292
|
+
|
|
293
|
+
def to_dict(self) -> dict:
|
|
294
|
+
expected_duration = (
|
|
295
|
+
int(self.expected_duration.total_seconds())
|
|
296
|
+
if self.expected_duration is not None
|
|
297
|
+
else None
|
|
298
|
+
)
|
|
299
|
+
return {
|
|
300
|
+
PATHS: [str(p) for p in self.paths],
|
|
301
|
+
RUN_CHAINED: self.run_chained,
|
|
302
|
+
YOLO: self.yolo_config.to_dict(),
|
|
303
|
+
EXPECTED_DURATION: expected_duration,
|
|
304
|
+
OVERWRITE: self.overwrite,
|
|
305
|
+
HALF_PRECISION: self.half_precision,
|
|
306
|
+
START_TIME: self.start_time,
|
|
307
|
+
DETECT_START: self.detect_start,
|
|
308
|
+
DETECT_END: self.detect_end,
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
@dataclass(frozen=True)
|
|
313
|
+
class _TrackIouConfig:
|
|
314
|
+
sigma_l: float = 0.27
|
|
315
|
+
sigma_h: float = 0.42
|
|
316
|
+
sigma_iou: float = 0.38
|
|
317
|
+
t_min: int = 5
|
|
318
|
+
t_miss_max: int = 51
|
|
319
|
+
|
|
320
|
+
def to_dict(self) -> dict:
|
|
321
|
+
return {
|
|
322
|
+
SIGMA_L: self.sigma_l,
|
|
323
|
+
SIGMA_H: self.sigma_h,
|
|
324
|
+
SIGMA_IOU: self.sigma_iou,
|
|
325
|
+
T_MIN: self.t_min,
|
|
326
|
+
T_MISS_MAX: self.t_miss_max,
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
@dataclass(frozen=True)
|
|
331
|
+
class TrackConfig:
|
|
332
|
+
@property
|
|
333
|
+
def sigma_l(self) -> float:
|
|
334
|
+
return self.iou.sigma_l
|
|
335
|
+
|
|
336
|
+
@property
|
|
337
|
+
def sigma_h(self) -> float:
|
|
338
|
+
return self.iou.sigma_h
|
|
339
|
+
|
|
340
|
+
@property
|
|
341
|
+
def sigma_iou(self) -> float:
|
|
342
|
+
return self.iou.sigma_iou
|
|
343
|
+
|
|
344
|
+
@property
|
|
345
|
+
def t_min(self) -> int:
|
|
346
|
+
return self.iou.t_min
|
|
347
|
+
|
|
348
|
+
@property
|
|
349
|
+
def t_miss_max(self) -> int:
|
|
350
|
+
return self.iou.t_miss_max
|
|
351
|
+
|
|
352
|
+
paths: list[str] = field(default_factory=list)
|
|
353
|
+
run_chained: bool = True
|
|
354
|
+
iou: _TrackIouConfig = _TrackIouConfig()
|
|
355
|
+
overwrite: bool = True
|
|
356
|
+
|
|
357
|
+
def to_dict(self) -> dict:
|
|
358
|
+
return {
|
|
359
|
+
PATHS: [str(p) for p in self.paths],
|
|
360
|
+
RUN_CHAINED: self.run_chained,
|
|
361
|
+
IOU: self.iou.to_dict(),
|
|
362
|
+
OVERWRITE: self.overwrite,
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
@dataclass(frozen=True)
|
|
367
|
+
class _UndistortConfig:
|
|
368
|
+
overwrite: bool = False
|
|
369
|
+
|
|
370
|
+
def to_dict(self) -> dict:
|
|
371
|
+
return {OVERWRITE: self.overwrite}
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
@dataclass(frozen=True)
|
|
375
|
+
class _TransformConfig:
|
|
376
|
+
paths: list[str] = field(default_factory=list)
|
|
377
|
+
run_chained: bool = True
|
|
378
|
+
overwrite: bool = True
|
|
379
|
+
|
|
380
|
+
def to_dict(self) -> dict:
|
|
381
|
+
return {
|
|
382
|
+
PATHS: [str(p) for p in self.paths],
|
|
383
|
+
RUN_CHAINED: self.run_chained,
|
|
384
|
+
OVERWRITE: self.overwrite,
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
@dataclass(frozen=True)
|
|
389
|
+
class _GuiWindowConfig:
|
|
390
|
+
location_x: int = 0
|
|
391
|
+
location_y: int = 0
|
|
392
|
+
|
|
393
|
+
def to_dict(self) -> dict:
|
|
394
|
+
return {LOCATION_X: self.location_x, LOCATION_Y: self.location_y}
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
@dataclass(frozen=True)
|
|
398
|
+
class _GuiConfig:
|
|
399
|
+
otc_icon: str = str(Path(__file__).parents[0] / r"view" / r"helpers" / r"OTC.ico")
|
|
400
|
+
font: str = "Open Sans"
|
|
401
|
+
font_size: int = 12
|
|
402
|
+
window_config: _GuiWindowConfig = _GuiWindowConfig()
|
|
403
|
+
frame_width: int = 80
|
|
404
|
+
col_width: int = 50
|
|
405
|
+
|
|
406
|
+
def to_dict(self) -> dict:
|
|
407
|
+
return {
|
|
408
|
+
OTC_ICON: self.otc_icon,
|
|
409
|
+
FONT: self.font,
|
|
410
|
+
FONT_SIZE: self.font_size,
|
|
411
|
+
WINDOW: self.window_config.to_dict(),
|
|
412
|
+
FRAME_WIDTH: self.frame_width,
|
|
413
|
+
COL_WIDTH: self.col_width,
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
@dataclass(frozen=True)
|
|
418
|
+
class StreamConfig:
|
|
419
|
+
name: str
|
|
420
|
+
source: str
|
|
421
|
+
save_dir: Path
|
|
422
|
+
flush_buffer_size: int
|
|
423
|
+
|
|
424
|
+
def to_dict(self) -> dict:
|
|
425
|
+
return {
|
|
426
|
+
STREAM_NAME: self.name,
|
|
427
|
+
STREAM_SOURCE: self.source,
|
|
428
|
+
STREAM_SAVE_DIR: str(self.save_dir),
|
|
429
|
+
FLUSH_BUFFER_SIZE: self.flush_buffer_size,
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
@dataclass
|
|
434
|
+
class Config:
|
|
435
|
+
"""Represents the OTVision config file.
|
|
436
|
+
|
|
437
|
+
Provides methods to parse in a custom config file from a dict or a YAML file.
|
|
438
|
+
Updates the default configuration with the custom config.
|
|
439
|
+
"""
|
|
440
|
+
|
|
441
|
+
log: _LogConfig = _LogConfig()
|
|
442
|
+
search_subdirs: bool = True
|
|
443
|
+
default_filetype: _DefaultFiletype = _DefaultFiletype()
|
|
444
|
+
filetypes: _Filetypes = _Filetypes()
|
|
445
|
+
last_paths: _LastPaths = _LastPaths()
|
|
446
|
+
convert: ConvertConfig = ConvertConfig()
|
|
447
|
+
detect: DetectConfig = DetectConfig()
|
|
448
|
+
track: TrackConfig = TrackConfig()
|
|
449
|
+
undistort: _UndistortConfig = _UndistortConfig()
|
|
450
|
+
transform: _TransformConfig = _TransformConfig()
|
|
451
|
+
gui: _GuiConfig = _GuiConfig()
|
|
452
|
+
stream: StreamConfig | None = None
|
|
453
|
+
|
|
454
|
+
def to_dict(self) -> dict:
|
|
455
|
+
"""Returns the OTVision config as a dict.
|
|
456
|
+
|
|
457
|
+
Returns:
|
|
458
|
+
dict: The OTVision config.
|
|
459
|
+
"""
|
|
460
|
+
data = {
|
|
461
|
+
LOG: self.log.to_dict(),
|
|
462
|
+
SEARCH_SUBDIRS: self.search_subdirs,
|
|
463
|
+
DEFAULT_FILETYPE: self.default_filetype.to_dict(),
|
|
464
|
+
FILETYPES: self.filetypes.to_dict(),
|
|
465
|
+
LAST_PATHS: self.last_paths.to_dict(),
|
|
466
|
+
CONVERT: self.convert.to_dict(),
|
|
467
|
+
DETECT: self.detect.to_dict(),
|
|
468
|
+
TRACK: self.track.to_dict(),
|
|
469
|
+
UNDISTORT: self.undistort.to_dict(),
|
|
470
|
+
TRANSFORM: self.transform.to_dict(),
|
|
471
|
+
GUI: self.gui.to_dict(),
|
|
472
|
+
}
|
|
473
|
+
if self.stream is not None:
|
|
474
|
+
data[STREAM] = self.stream.to_dict()
|
|
475
|
+
return data
|