OTVision 0.6.6__py3-none-any.whl → 0.6.8__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/application/config.py +18 -0
- OTVision/application/config_parser.py +17 -9
- OTVision/application/detect/update_detect_config_with_cli_args.py +16 -0
- OTVision/application/event/__init__.py +0 -0
- OTVision/application/event/new_video_start.py +9 -0
- OTVision/application/video/__init__.py +0 -0
- OTVision/application/video/generate_video.py +15 -0
- OTVision/config.py +2 -0
- OTVision/detect/builder.py +27 -8
- OTVision/detect/cli.py +44 -1
- OTVision/detect/detected_frame_producer.py +14 -0
- OTVision/detect/detected_frame_producer_factory.py +43 -0
- OTVision/detect/file_based_detect_builder.py +39 -3
- OTVision/detect/rtsp_based_detect_builder.py +35 -3
- OTVision/detect/rtsp_input_source.py +135 -34
- OTVision/detect/video_input_source.py +38 -6
- OTVision/detect/yolo.py +9 -1
- OTVision/domain/cli.py +10 -0
- OTVision/domain/input_source_detect.py +1 -3
- OTVision/domain/time.py +2 -2
- OTVision/domain/video_writer.py +30 -0
- OTVision/plugin/ffmpeg_video_writer.py +300 -0
- OTVision/plugin/generate_video.py +24 -0
- OTVision/version.py +1 -1
- {otvision-0.6.6.dist-info → otvision-0.6.8.dist-info}/METADATA +6 -6
- {otvision-0.6.6.dist-info → otvision-0.6.8.dist-info}/RECORD +28 -20
- OTVision/application/detect/detected_frame_producer.py +0 -24
- {otvision-0.6.6.dist-info → otvision-0.6.8.dist-info}/WHEEL +0 -0
- {otvision-0.6.6.dist-info → otvision-0.6.8.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from enum import IntEnum, StrEnum
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from subprocess import PIPE, Popen, TimeoutExpired
|
|
5
|
+
from threading import Thread
|
|
6
|
+
from typing import Callable, Generator
|
|
7
|
+
|
|
8
|
+
import ffmpeg
|
|
9
|
+
from numpy import ndarray
|
|
10
|
+
|
|
11
|
+
from OTVision.application.event.new_video_start import NewVideoStartEvent
|
|
12
|
+
from OTVision.detect.detected_frame_buffer import FlushEvent
|
|
13
|
+
from OTVision.domain.frame import Frame, FrameKeys
|
|
14
|
+
from OTVision.domain.video_writer import VideoWriter
|
|
15
|
+
from OTVision.helpers.log import LOGGER_NAME
|
|
16
|
+
from OTVision.helpers.machine import ON_WINDOWS
|
|
17
|
+
|
|
18
|
+
VideoSaveLocationStrategy = Callable[[str], str]
|
|
19
|
+
|
|
20
|
+
BUFFER_SIZE_100MB = 10**8
|
|
21
|
+
DEFAULT_CRF = 23
|
|
22
|
+
VIDEO_SAVE_FILE_POSTFIX = "_processed"
|
|
23
|
+
|
|
24
|
+
log = logging.getLogger(LOGGER_NAME)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class VideoCodec(StrEnum):
|
|
28
|
+
"""Enum of possible video codecs to be used with ffmpeg
|
|
29
|
+
|
|
30
|
+
Attributes:
|
|
31
|
+
H264_SOFTWARE: Software-based H.264 encoder.
|
|
32
|
+
H264_NVENC: NVIDIA GPU-based H.264 encoder.
|
|
33
|
+
H264_QSV: Intel Quick Sync Video encoder.
|
|
34
|
+
H264_VAAPI: Intel/AMD GPU-based H.264 encoder.
|
|
35
|
+
H264_VIDEOTOOLBOX: macOS hardware encoder.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
H264_SOFTWARE = "libx264"
|
|
39
|
+
H264_NVENC = "h264_nvenc"
|
|
40
|
+
H264_QSV = "h264_qsv"
|
|
41
|
+
H264_VAAPI = "h264_vaapi"
|
|
42
|
+
H264_VIDEOTOOLBOX = "h264_videotoolbox"
|
|
43
|
+
|
|
44
|
+
@staticmethod
|
|
45
|
+
def as_list() -> list[str]:
|
|
46
|
+
return list(VideoCodec.__members__.values())
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class VideoFormat(StrEnum):
|
|
50
|
+
RAW = "rawvideo"
|
|
51
|
+
MP4 = "mp4"
|
|
52
|
+
|
|
53
|
+
@staticmethod
|
|
54
|
+
def as_list() -> list[str]:
|
|
55
|
+
return list(VideoFormat.__members__.values())
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class PixelFormat(StrEnum):
|
|
59
|
+
YUV420P = "yuv420p" # compatible with most players for H.264
|
|
60
|
+
RGB24 = "rgb24"
|
|
61
|
+
BGR24 = "bgr24"
|
|
62
|
+
|
|
63
|
+
@staticmethod
|
|
64
|
+
def as_list() -> list[str]:
|
|
65
|
+
return list(PixelFormat.__members__.values())
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class EncodingSpeed(StrEnum):
|
|
69
|
+
ULTRA_FAST = "ultrafast"
|
|
70
|
+
SUPER_FAST = "superfast"
|
|
71
|
+
VERY_FAST = "veryfast"
|
|
72
|
+
FASTER = "faster"
|
|
73
|
+
FAST = "fast"
|
|
74
|
+
MEDIUM = "medium"
|
|
75
|
+
SLOW = "slow"
|
|
76
|
+
SLOWER = "slower"
|
|
77
|
+
VERY_SLOW = "veryslow"
|
|
78
|
+
|
|
79
|
+
@staticmethod
|
|
80
|
+
def as_list() -> list[str]:
|
|
81
|
+
return list(EncodingSpeed.__members__.values())
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class ConstantRateFactor(IntEnum):
|
|
85
|
+
"""Adjust quality/size (lower means better quality/larger file).
|
|
86
|
+
Attributes:
|
|
87
|
+
LOSSLESS: Perfect quality, massive file size.
|
|
88
|
+
HIGH_QUALITY: Visually lossless for most eyes.
|
|
89
|
+
GOOD: High quality, slightly compressed.
|
|
90
|
+
DEFAULT: x264 default; good balance of size and quality.
|
|
91
|
+
COMPACT: Acceptable quality for small screens or streaming.
|
|
92
|
+
LOW_QUALITY: Noticeable compression artifacts; smaller file.
|
|
93
|
+
WORST_ACCEPTABLE: Very low quality; only for previews or constrained storage.
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
LOSSLESS = 0
|
|
97
|
+
HIGH_QUALITY = 18
|
|
98
|
+
GOOD = 20
|
|
99
|
+
DEFAULT = 23
|
|
100
|
+
COMPACT = 26
|
|
101
|
+
LOW_QUALITY = 28
|
|
102
|
+
WORST_ACCEPTABLE = 35
|
|
103
|
+
|
|
104
|
+
@staticmethod
|
|
105
|
+
def as_list() -> list[str]:
|
|
106
|
+
return list(ConstantRateFactor.__members__.keys())
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class FfmpegVideoWriter(VideoWriter):
|
|
110
|
+
|
|
111
|
+
@property
|
|
112
|
+
def _current_video_metadata(self) -> NewVideoStartEvent:
|
|
113
|
+
if self.__current_video_metadata is None:
|
|
114
|
+
raise ValueError("FfmpegVideoWriter is not configured yet.")
|
|
115
|
+
return self.__current_video_metadata
|
|
116
|
+
|
|
117
|
+
@property
|
|
118
|
+
def _ffmpeg_process(self) -> Popen:
|
|
119
|
+
if self.__ffmpeg_process is None:
|
|
120
|
+
raise ValueError("FfmpegVideoWriter is not initialized yet.")
|
|
121
|
+
return self.__ffmpeg_process
|
|
122
|
+
|
|
123
|
+
@property
|
|
124
|
+
def is_open(self) -> bool:
|
|
125
|
+
return self.__ffmpeg_process is not None
|
|
126
|
+
|
|
127
|
+
@property
|
|
128
|
+
def is_closed(self) -> bool:
|
|
129
|
+
return self.is_open is False
|
|
130
|
+
|
|
131
|
+
def __init__(
|
|
132
|
+
self,
|
|
133
|
+
save_location_strategy: VideoSaveLocationStrategy,
|
|
134
|
+
encoding_speed: EncodingSpeed = EncodingSpeed.FAST,
|
|
135
|
+
input_format: VideoFormat = VideoFormat.RAW,
|
|
136
|
+
output_format: VideoFormat = VideoFormat.MP4,
|
|
137
|
+
input_pixel_format: PixelFormat = PixelFormat.RGB24,
|
|
138
|
+
output_pixel_format: PixelFormat = PixelFormat.YUV420P,
|
|
139
|
+
output_video_codec: VideoCodec = VideoCodec.H264_SOFTWARE,
|
|
140
|
+
constant_rate_factor: ConstantRateFactor = ConstantRateFactor.LOSSLESS,
|
|
141
|
+
) -> None:
|
|
142
|
+
if ON_WINDOWS:
|
|
143
|
+
log.warning(
|
|
144
|
+
"Writing every frame into a new video is not supported on Windows."
|
|
145
|
+
)
|
|
146
|
+
self._save_location_strategy = save_location_strategy
|
|
147
|
+
self._encoding_speed = encoding_speed
|
|
148
|
+
self._input_format = input_format
|
|
149
|
+
self._output_format = output_format
|
|
150
|
+
self._input_pixel_format = input_pixel_format
|
|
151
|
+
self._output_pixel_format = output_pixel_format
|
|
152
|
+
self._output_video_codec = output_video_codec
|
|
153
|
+
self.__ffmpeg_process: Popen | None = None
|
|
154
|
+
self.__current_video_metadata: NewVideoStartEvent | None = None
|
|
155
|
+
self._constant_rate_factor = constant_rate_factor
|
|
156
|
+
log.info(
|
|
157
|
+
"FFmpeg video writer settings: "
|
|
158
|
+
f"video_codec='{self._output_video_codec.value}', "
|
|
159
|
+
f"encoding_speed='{self._encoding_speed.value}', "
|
|
160
|
+
f"crf='{self._constant_rate_factor.value}'"
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
def open(self, output: str, width: int, height: int, fps: float) -> None:
|
|
164
|
+
self.__ffmpeg_process = self.__create_ffmpeg_process(
|
|
165
|
+
output_file=output,
|
|
166
|
+
width=width,
|
|
167
|
+
height=height,
|
|
168
|
+
fps=fps,
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
def write(self, image: ndarray) -> None:
|
|
172
|
+
try:
|
|
173
|
+
if self._ffmpeg_process.stdin:
|
|
174
|
+
self._ffmpeg_process.stdin.write(image.tobytes())
|
|
175
|
+
self._ffmpeg_process.stdin.flush()
|
|
176
|
+
except BrokenPipeError:
|
|
177
|
+
# Check if the process is still running
|
|
178
|
+
if self._ffmpeg_process.poll() is not None:
|
|
179
|
+
# Process has terminated, get the error message
|
|
180
|
+
stderr = (
|
|
181
|
+
self._ffmpeg_process.stderr.read()
|
|
182
|
+
if self._ffmpeg_process.stderr
|
|
183
|
+
else b""
|
|
184
|
+
)
|
|
185
|
+
log.info(stderr.decode("utf-8", errors="ignore"))
|
|
186
|
+
raise RuntimeError(
|
|
187
|
+
"ffmpeg process terminated unexpectedly: "
|
|
188
|
+
f"{stderr.decode('utf-8', errors='ignore')}"
|
|
189
|
+
)
|
|
190
|
+
raise # Re-raise the original exception if the process is still running
|
|
191
|
+
|
|
192
|
+
def close(self) -> None:
|
|
193
|
+
if self.__ffmpeg_process is not None:
|
|
194
|
+
process_to_cleanup = self._ffmpeg_process
|
|
195
|
+
self.__ffmpeg_process = None # Immediately mark as closed
|
|
196
|
+
|
|
197
|
+
# Close stdin synchronously (fast operation)
|
|
198
|
+
try:
|
|
199
|
+
if process_to_cleanup.stdin:
|
|
200
|
+
process_to_cleanup.stdin.flush()
|
|
201
|
+
process_to_cleanup.stdin.close()
|
|
202
|
+
except Exception as cause:
|
|
203
|
+
log.debug(f"Error closing stdin: {cause}")
|
|
204
|
+
|
|
205
|
+
# Handle cleanup in background thread
|
|
206
|
+
def cleanup_process() -> None:
|
|
207
|
+
try:
|
|
208
|
+
# Just check if it's still running and wait for it
|
|
209
|
+
if process_to_cleanup.poll() is None:
|
|
210
|
+
# Still running, close stdin and wait
|
|
211
|
+
try:
|
|
212
|
+
if (
|
|
213
|
+
process_to_cleanup.stdin
|
|
214
|
+
and not process_to_cleanup.stdin.closed
|
|
215
|
+
):
|
|
216
|
+
process_to_cleanup.stdin.close()
|
|
217
|
+
except Exception as cause:
|
|
218
|
+
log.error(f"Error closing stdin: {cause}")
|
|
219
|
+
|
|
220
|
+
# Simple wait with timeout
|
|
221
|
+
try:
|
|
222
|
+
process_to_cleanup.wait(timeout=5.0)
|
|
223
|
+
except TimeoutExpired:
|
|
224
|
+
process_to_cleanup.kill()
|
|
225
|
+
try:
|
|
226
|
+
process_to_cleanup.wait(timeout=1.0)
|
|
227
|
+
except TimeoutExpired:
|
|
228
|
+
log.error("Could not kill FFmpeg process")
|
|
229
|
+
|
|
230
|
+
# Log return code if we care
|
|
231
|
+
if process_to_cleanup.returncode != 0:
|
|
232
|
+
log.debug(
|
|
233
|
+
f"FFmpeg ended with code: {process_to_cleanup.returncode}"
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
except Exception as e:
|
|
237
|
+
log.debug(f"Cleanup completed with minor issues: {e}")
|
|
238
|
+
|
|
239
|
+
# Start cleanup in daemon thread (won't prevent program exit)
|
|
240
|
+
cleanup_thread = Thread(target=cleanup_process, daemon=True)
|
|
241
|
+
cleanup_thread.start()
|
|
242
|
+
|
|
243
|
+
self.__current_video_metadata = None
|
|
244
|
+
|
|
245
|
+
def notify_on_flush_event(self, event: FlushEvent) -> None:
|
|
246
|
+
self.close()
|
|
247
|
+
|
|
248
|
+
def notify_on_new_video_start(self, event: NewVideoStartEvent) -> None:
|
|
249
|
+
self.__current_video_metadata = event
|
|
250
|
+
self.open(event.output, event.width, event.height, event.fps)
|
|
251
|
+
|
|
252
|
+
def __create_ffmpeg_process(
|
|
253
|
+
self, output_file: str, width: int, height: int, fps: float
|
|
254
|
+
) -> Popen:
|
|
255
|
+
save_file = self._save_location_strategy(output_file)
|
|
256
|
+
cmd = (
|
|
257
|
+
ffmpeg.input(
|
|
258
|
+
"pipe:0",
|
|
259
|
+
format=self._input_format.value,
|
|
260
|
+
framerate=fps,
|
|
261
|
+
pix_fmt=self._input_pixel_format.value,
|
|
262
|
+
s=f"{width}x{height}",
|
|
263
|
+
)
|
|
264
|
+
.output(
|
|
265
|
+
save_file,
|
|
266
|
+
pix_fmt=self._output_pixel_format.value,
|
|
267
|
+
vcodec=self._output_video_codec.value,
|
|
268
|
+
preset=self._encoding_speed.value,
|
|
269
|
+
crf=self._constant_rate_factor.value,
|
|
270
|
+
format=self._output_format.value,
|
|
271
|
+
)
|
|
272
|
+
.overwrite_output()
|
|
273
|
+
.compile()
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
process = Popen(
|
|
277
|
+
cmd,
|
|
278
|
+
stdin=PIPE,
|
|
279
|
+
stderr=PIPE,
|
|
280
|
+
bufsize=BUFFER_SIZE_100MB,
|
|
281
|
+
)
|
|
282
|
+
log.info(f"Writing new video file to '{save_file}'.")
|
|
283
|
+
return process
|
|
284
|
+
|
|
285
|
+
def filter(
|
|
286
|
+
self, pipe: Generator[Frame, None, None]
|
|
287
|
+
) -> Generator[Frame, None, None]:
|
|
288
|
+
for frame in pipe:
|
|
289
|
+
if (image := frame.get(FrameKeys.data)) is not None:
|
|
290
|
+
self.write(image)
|
|
291
|
+
yield frame
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def append_save_suffix_to_save_location(given: str) -> str:
|
|
295
|
+
filepath = Path(given)
|
|
296
|
+
return str(Path(filepath).with_stem(f"{filepath.stem}{VIDEO_SAVE_FILE_POSTFIX}"))
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def keep_original_save_location(given: str) -> str:
|
|
300
|
+
return given
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from functools import cached_property
|
|
2
|
+
|
|
3
|
+
from OTVision.application.video.generate_video import GenerateVideo
|
|
4
|
+
from OTVision.detect.file_based_detect_builder import FileBasedDetectBuilder
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class GenerateVideoBuilder(FileBasedDetectBuilder):
|
|
8
|
+
@cached_property
|
|
9
|
+
def generate_video(self) -> GenerateVideo:
|
|
10
|
+
return GenerateVideo(
|
|
11
|
+
input_source=self.input_source, video_writer=self.video_file_writer
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
def build_generate_video(self) -> GenerateVideo:
|
|
15
|
+
self.register_observers()
|
|
16
|
+
return self.generate_video
|
|
17
|
+
|
|
18
|
+
def register_observers(self) -> None:
|
|
19
|
+
self.input_source.subject_new_video_start.register(
|
|
20
|
+
self.video_file_writer.notify_on_new_video_start
|
|
21
|
+
)
|
|
22
|
+
self.input_source.subject_flush.register(
|
|
23
|
+
self.video_file_writer.notify_on_flush_event
|
|
24
|
+
)
|
OTVision/version.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: OTVision
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.8
|
|
4
4
|
Summary: OTVision is a core module of the OpenTrafficCam framework to perform object detection and tracking.
|
|
5
5
|
Project-URL: Homepage, https://opentrafficcam.org/
|
|
6
6
|
Project-URL: Documentation, https://opentrafficcam.org/overview/
|
|
@@ -17,22 +17,22 @@ Classifier: Operating System :: OS Independent
|
|
|
17
17
|
Classifier: Programming Language :: Python :: 3
|
|
18
18
|
Requires-Python: >=3.11
|
|
19
19
|
Requires-Dist: av==13.0.0
|
|
20
|
+
Requires-Dist: ffmpeg-python==0.2.0
|
|
20
21
|
Requires-Dist: fire==0.7.0
|
|
21
22
|
Requires-Dist: geopandas==1.0.1
|
|
22
23
|
Requires-Dist: ijson==3.3.0
|
|
23
|
-
Requires-Dist: more-itertools==10.
|
|
24
|
+
Requires-Dist: more-itertools==10.7.0
|
|
24
25
|
Requires-Dist: moviepy==1.0.3
|
|
25
26
|
Requires-Dist: numpy==1.26.4; sys_platform == 'win32'
|
|
26
27
|
Requires-Dist: numpy==2.1.1; sys_platform != 'win32'
|
|
27
28
|
Requires-Dist: opencv-python==4.10.0.84
|
|
28
29
|
Requires-Dist: pandas==2.2.3
|
|
29
30
|
Requires-Dist: pyyaml==6.0.2
|
|
30
|
-
Requires-Dist:
|
|
31
|
-
Requires-Dist:
|
|
32
|
-
Requires-Dist: torchvision==0.18.1
|
|
31
|
+
Requires-Dist: torch==2.7.1
|
|
32
|
+
Requires-Dist: torchvision==0.22.1
|
|
33
33
|
Requires-Dist: tqdm==4.67.1
|
|
34
34
|
Requires-Dist: ujson==5.10.0
|
|
35
|
-
Requires-Dist: ultralytics==8.3.
|
|
35
|
+
Requires-Dist: ultralytics==8.3.159
|
|
36
36
|
Description-Content-Type: text/markdown
|
|
37
37
|
|
|
38
38
|
# OTVision
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
OTVision/__init__.py,sha256=CLnfgTlVHM4_nzDacvy06Z_Crc3hU6usd0mUyEvBf24,781
|
|
2
|
-
OTVision/config.py,sha256=
|
|
2
|
+
OTVision/config.py,sha256=D4NIio27JG9hZk7yHI6kNKiMxKeKa_MGfrKNDdEH370,5389
|
|
3
3
|
OTVision/dataformat.py,sha256=BHF7qHzyNb80hI1EKfwcdJ9bgG_X4bp_hCXzdg7_MSA,1941
|
|
4
|
-
OTVision/version.py,sha256=
|
|
4
|
+
OTVision/version.py,sha256=8pR24rKY8pgm_Tctl-E9a4RhSGvh-bTk4sm-pPiNync,175
|
|
5
5
|
OTVision/abstraction/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
6
|
OTVision/abstraction/defaults.py,sha256=ftETDe25gmr563RPSbG6flcEiNiHnRb0iXK1Zj_zdNg,442
|
|
7
7
|
OTVision/abstraction/observer.py,sha256=ZFGxUUjI3wUpf5ogXg2yDe-QjCcXre6SxH5zOogOx2U,1350
|
|
8
8
|
OTVision/abstraction/pipes_and_filter.py,sha256=rzYaWDalXnlMbfpkgI91a0FL0q1llUlCRuIQsidYDv8,940
|
|
9
9
|
OTVision/application/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
10
|
OTVision/application/buffer.py,sha256=eoK3Rfvc_72cQD3EAASzmxhznjmnIFFh3LX9fqAzXyU,1166
|
|
11
|
-
OTVision/application/config.py,sha256=
|
|
12
|
-
OTVision/application/config_parser.py,sha256
|
|
11
|
+
OTVision/application/config.py,sha256=tTFVo2iJt3zCJ_p06FCeQOJ_NbuwmPd60ttrh-6fANI,13670
|
|
12
|
+
OTVision/application/config_parser.py,sha256=-k-FlqH4Cb5q_OD2u5Aa12-Ca8RtKPbpaNcadmGUhXk,9610
|
|
13
13
|
OTVision/application/configure_logger.py,sha256=1TzHB-zm7vGTPtUp7m28ne4WxOyiUYeChLZU-ZPyOVQ,623
|
|
14
14
|
OTVision/application/frame_count_provider.py,sha256=zN_75IM-w9Xlc5rT8OArhiWhPHR8mUfFhdzhSmQQuaM,693
|
|
15
15
|
OTVision/application/get_config.py,sha256=kFRTFQ1eLzBZCIkLrsf0wk7pWIlnUQRXvoZsl0QttVM,829
|
|
@@ -19,44 +19,50 @@ OTVision/application/detect/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMp
|
|
|
19
19
|
OTVision/application/detect/current_object_detector.py,sha256=J4U5k1s902agN90FYR4M_gO8MYYa_5XkYGREjStWCxQ,1343
|
|
20
20
|
OTVision/application/detect/current_object_detector_metadata.py,sha256=xai0UBEzxr-rxXCc8mTmNDECds7mdsw2sem5HZxvQ4Q,1017
|
|
21
21
|
OTVision/application/detect/detected_frame_factory.py,sha256=sW_l0xaPz44_lPEmCPKz4Xg1Mv4ZGKN9CyBCG_iN8dQ,1007
|
|
22
|
-
OTVision/application/detect/detected_frame_producer.py,sha256=LD4AnQG04YGE68TpxmaRuWRZeCZfhp8oixk6SHTru7c,906
|
|
23
22
|
OTVision/application/detect/detection_file_save_path_provider.py,sha256=nUyzgR7imrH8PkUl_72kdUDiolPXq1_RQqbpFwLI5Cs,2165
|
|
24
23
|
OTVision/application/detect/factory.py,sha256=UCnLtgpWdNqwwjW0v2yzKF9Gacx6gewjTyy43wXs2Jg,938
|
|
25
24
|
OTVision/application/detect/get_detect_cli_args.py,sha256=gezr17im8SwbuXW1suCodWRrFs8lSljNKu76SbWBgkY,265
|
|
26
25
|
OTVision/application/detect/timestamper.py,sha256=us9l1GaPnSfMrUZQ5UEDBPX787ypeUiUtpOZm3JpgM0,653
|
|
27
|
-
OTVision/application/detect/update_detect_config_with_cli_args.py,sha256=
|
|
26
|
+
OTVision/application/detect/update_detect_config_with_cli_args.py,sha256=tRT4jB-ynhZ24HvA7c6pyvNDcwIs_M6w-o4k2hCONPQ,4393
|
|
27
|
+
OTVision/application/event/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
|
+
OTVision/application/event/new_video_start.py,sha256=Ydkao4v3fagJUvXASkvxgmplRb7PYY88cmj-bSxmzfk,148
|
|
28
29
|
OTVision/application/track/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
29
30
|
OTVision/application/track/get_track_cli_args.py,sha256=uebHGDB6xzttak_Z4Nos08Ay69QKT2JypfHwn1dLpIM,553
|
|
30
31
|
OTVision/application/track/update_current_track_config.py,sha256=pCFNuGl6rqpsUUGm2i1kjLSuuITVXv2R79Xf81n45as,1667
|
|
31
32
|
OTVision/application/track/update_track_config_with_cli_args.py,sha256=5MDr2ih4xHzZ0kI6TSQ2C4mcWsPXW2PB80kA22-M_5I,2273
|
|
33
|
+
OTVision/application/video/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
34
|
+
OTVision/application/video/generate_video.py,sha256=4zazQoRdwefF_R-sUcnbP-23NTWgn_aYdQJrhtJ5Ni4,513
|
|
32
35
|
OTVision/convert/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
33
36
|
OTVision/convert/convert.py,sha256=UUfzpbtMlxlJgKE6XeT5nyNqK26-K02bQDhq3o7KrXE,11050
|
|
34
37
|
OTVision/detect/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
35
|
-
OTVision/detect/builder.py,sha256=
|
|
36
|
-
OTVision/detect/cli.py,sha256=
|
|
38
|
+
OTVision/detect/builder.py,sha256=ubmxSICTR5wEPhqrLfuH3Bq0fvA_1qxRjn_z9NXa33c,7723
|
|
39
|
+
OTVision/detect/cli.py,sha256=3yf0G_B9cVm2CAz7AFzG10Cctv4ogMOxRHWEg8SZHss,7562
|
|
37
40
|
OTVision/detect/detect.py,sha256=YaVS-DJXdEmh-OzwE31UPNl2uk7mcFyO_CKKTgMeiuM,1328
|
|
38
41
|
OTVision/detect/detected_frame_buffer.py,sha256=TrLbImpvCm1B09Z3c000e2uDO5WguyVwoMmFAqH8Zvk,1505
|
|
39
|
-
OTVision/detect/
|
|
42
|
+
OTVision/detect/detected_frame_producer.py,sha256=yO60w4YHOum2DkiinA6mi2aaHSOmZxXRykhox2CMsZY,544
|
|
43
|
+
OTVision/detect/detected_frame_producer_factory.py,sha256=QeBvjF6MneHiaTPYelTrzJWTpYxWYWDcVBzFYNb1PUQ,1646
|
|
44
|
+
OTVision/detect/file_based_detect_builder.py,sha256=G72GFhF2BCXO3fwfC9pXkbJPhRFfU6RphLRQs3ugCUQ,2315
|
|
40
45
|
OTVision/detect/otdet.py,sha256=-8rZGY9NVRAwIHeVcNCjm665SmnW5UVIO_PSKdHegDA,6274
|
|
41
46
|
OTVision/detect/otdet_file_writer.py,sha256=idgPKwhgEd18HT7HshaT304JPlSw0kT4Isn2gWJRaSk,4304
|
|
42
47
|
OTVision/detect/pyav_frame_count_provider.py,sha256=w7p9iM3F2fljV8SD7q491gQhIHANbVczqtalcUiKj-E,453
|
|
43
|
-
OTVision/detect/rtsp_based_detect_builder.py,sha256=
|
|
44
|
-
OTVision/detect/rtsp_input_source.py,sha256=
|
|
48
|
+
OTVision/detect/rtsp_based_detect_builder.py,sha256=P47nKU2C1PJQmokgacv04DzJ-7eG6CUbqqg3bdQhXTQ,2662
|
|
49
|
+
OTVision/detect/rtsp_input_source.py,sha256=1us8zziXGeggNROWQWrx7N35XztGwhEhpbbMNHQScvs,10268
|
|
45
50
|
OTVision/detect/timestamper.py,sha256=VvDTzHu9fTI7qQL9x775Gc27r47R8D5Pb040ffwO04k,5288
|
|
46
|
-
OTVision/detect/video_input_source.py,sha256=
|
|
47
|
-
OTVision/detect/yolo.py,sha256=
|
|
51
|
+
OTVision/detect/video_input_source.py,sha256=LCmElr8r03n4OqQlzhas9soq5bZ76Q9vXQZ8jk5IxME,10106
|
|
52
|
+
OTVision/detect/yolo.py,sha256=7NOH6Tbr1ILkp_u7AdWNmnU3wLem6u7oSMhHAexZaXs,10632
|
|
48
53
|
OTVision/detect/plugin_av/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
49
54
|
OTVision/detect/plugin_av/rotate_frame.py,sha256=4wJqTYI2HRlfa4p2Ffap33vLmKIzE_EwFvQraEkQ4R8,1055
|
|
50
55
|
OTVision/domain/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
51
|
-
OTVision/domain/cli.py,sha256=
|
|
56
|
+
OTVision/domain/cli.py,sha256=INiw1dxQNO4XI6GGCIMhX2G08ig68FNS04fdtMt3gNs,2061
|
|
52
57
|
OTVision/domain/current_config.py,sha256=Q38NCktoGNU1Z_miXNoJXLH8-NDbVszwVOMGR1aAwWM,286
|
|
53
58
|
OTVision/domain/detect_producer_consumer.py,sha256=gD7NwscQZLmCxMDZpZkGql0oMrpGHDBBNvdTXs58Vvw,855
|
|
54
59
|
OTVision/domain/detection.py,sha256=SZLP-87XE3NcTkeYz7GTqp4oPMiqI1P5gILp1_yHtxY,3761
|
|
55
60
|
OTVision/domain/frame.py,sha256=Hv1v9cegfhVGgl2MB4uKYbvngfkOLrvES_w4gD0HtMo,6410
|
|
56
|
-
OTVision/domain/input_source_detect.py,sha256=
|
|
61
|
+
OTVision/domain/input_source_detect.py,sha256=BNhzxJBurKrM7g5cFrBigJGNmTdIQfVOy9V0pIqsZQ8,1107
|
|
57
62
|
OTVision/domain/object_detection.py,sha256=kyrTbP9sZBKtGo54vCNfluDMM8wpWZST9Oqf8m8Q1y4,1394
|
|
58
63
|
OTVision/domain/serialization.py,sha256=S7gb648z_W8U3Fb6TSk7hVU4qHlGwOZ7D6FeYSLXQwM,257
|
|
59
|
-
OTVision/domain/time.py,sha256=
|
|
64
|
+
OTVision/domain/time.py,sha256=_6a4zDbhXU7DmK7PdBYWRrrO2yQ4D68qtSYLTwnwWMQ,302
|
|
65
|
+
OTVision/domain/video_writer.py,sha256=iYt-QXu8jaNSIwdbpEjc5hsEtHveUgqbmxme7iZNVLA,910
|
|
60
66
|
OTVision/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
61
67
|
OTVision/helpers/date.py,sha256=XK0fmXMombQ2CJ2_RXjk-bc-R8qqXRK2rDmCMBi0_G0,854
|
|
62
68
|
OTVision/helpers/files.py,sha256=G7zoOHzWIYrMmkjgHJHkZbh2hcGtnwZomuspthG2GsE,18444
|
|
@@ -66,6 +72,8 @@ OTVision/helpers/log.py,sha256=fOSMTXQRQ3_3zzYL8pDlx85IXPwyDsI2WGpK-V_R47Q,4985
|
|
|
66
72
|
OTVision/helpers/machine.py,sha256=8Bz_Eg7PS0IL4riOVeJcEIi5D9E8Ju8-JomTkW975p8,2166
|
|
67
73
|
OTVision/helpers/video.py,sha256=xyI35CiWXqoeGd3HeLhZUPxrLz8GccWyzHusxoweJr4,1480
|
|
68
74
|
OTVision/plugin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
75
|
+
OTVision/plugin/ffmpeg_video_writer.py,sha256=1e8FVrdNopTOtgn2QOicz_lTXriwLXoJ9CPtAv9hlL4,10469
|
|
76
|
+
OTVision/plugin/generate_video.py,sha256=Jxk8iQBP8YhobRIRJ535yW3gx0h4d7H8oz0rULRPgcc,840
|
|
69
77
|
OTVision/plugin/yaml_serialization.py,sha256=LjJ_QLJPClRwsaw7ooagWT7LBW08OvSb527jbex1qIQ,557
|
|
70
78
|
OTVision/track/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
71
79
|
OTVision/track/builder.py,sha256=kHGV8Bb7Uh_SbVP1-tT5p68yzBqiWEeLmjM_P6wnE3A,4868
|
|
@@ -98,7 +106,7 @@ OTVision/view/view_helpers.py,sha256=a5yV_6ZxO5bxsSymOmxdHqzOEv0VFq4wFBopVRGuVRo
|
|
|
98
106
|
OTVision/view/view_track.py,sha256=vmfMqpbUfnzg_EsWiL-IIKNOApVF09dzSojHpUfYY6M,5393
|
|
99
107
|
OTVision/view/view_transform.py,sha256=HvRd8g8geKRy0OoiZUDn_oC3SJC5nuXhZf3uZelfGKg,5473
|
|
100
108
|
OTVision/view/helpers/OTC.ico,sha256=G9kwlDtgBXmXO3yxW6Z-xVFV2q4nUGuz9E1VPHSu_I8,21662
|
|
101
|
-
otvision-0.6.
|
|
102
|
-
otvision-0.6.
|
|
103
|
-
otvision-0.6.
|
|
104
|
-
otvision-0.6.
|
|
109
|
+
otvision-0.6.8.dist-info/METADATA,sha256=fj6FWmTovYBzy7TPOR3jtVWfkSQW9vmcZiBDybboTlY,6265
|
|
110
|
+
otvision-0.6.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
111
|
+
otvision-0.6.8.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
112
|
+
otvision-0.6.8.dist-info/RECORD,,
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
from typing import Generator
|
|
2
|
-
|
|
3
|
-
from OTVision.abstraction.pipes_and_filter import Filter
|
|
4
|
-
from OTVision.domain.detect_producer_consumer import DetectedFrameProducer
|
|
5
|
-
from OTVision.domain.frame import DetectedFrame, Frame
|
|
6
|
-
from OTVision.domain.input_source_detect import InputSourceDetect
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class SimpleDetectedFrameProducer(DetectedFrameProducer):
|
|
10
|
-
|
|
11
|
-
def __init__(
|
|
12
|
-
self,
|
|
13
|
-
input_source: InputSourceDetect,
|
|
14
|
-
detection_filter: Filter[Frame, DetectedFrame],
|
|
15
|
-
detected_frame_buffer: Filter[DetectedFrame, DetectedFrame],
|
|
16
|
-
) -> None:
|
|
17
|
-
self._input_source = input_source
|
|
18
|
-
self._detection_filter = detection_filter
|
|
19
|
-
self._detected_frame_buffer = detected_frame_buffer
|
|
20
|
-
|
|
21
|
-
def produce(self) -> Generator[DetectedFrame, None, None]:
|
|
22
|
-
return self._detected_frame_buffer.filter(
|
|
23
|
-
self._detection_filter.filter(self._input_source.produce())
|
|
24
|
-
)
|
|
File without changes
|
|
File without changes
|