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.
@@ -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,4 +1,4 @@
1
- __version__ = "v0.6.6"
1
+ __version__ = "v0.6.8"
2
2
 
3
3
 
4
4
  def otdet_version() -> str:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: OTVision
3
- Version: 0.6.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.5.0
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: setuptools==74.0.0
31
- Requires-Dist: torch==2.3.1
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.94
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=0ecnI0N2rS2q0Ld6gBpK4iU2iyuUw683XWjj4g-L1m4,5336
2
+ OTVision/config.py,sha256=D4NIio27JG9hZk7yHI6kNKiMxKeKa_MGfrKNDdEH370,5389
3
3
  OTVision/dataformat.py,sha256=BHF7qHzyNb80hI1EKfwcdJ9bgG_X4bp_hCXzdg7_MSA,1941
4
- OTVision/version.py,sha256=TePRHn7igC5gC-d_U37x7TlN7c1dsVfR5nB0hJk7lY0,175
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=FFNmwiwZnAqDax8xIJhVhW_wK1FKrhaSzBgYXjcAoq0,13080
12
- OTVision/application/config_parser.py,sha256=CLBTEKDqREbEpf6-N7ZDPu8EV4OgWbaGfAJAPWLbhwA,9162
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=_8vaFfqqtq1tjj8twArqPKOmQBiCzdBnEKhSRNjX_yQ,3769
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=P2RbTTffetz0gBfNKKYBOKuXEJyGRk3HDyclYAXBGxY,7166
36
- OTVision/detect/cli.py,sha256=_fB_4UH_AhxUFxKDgzErieiviUZ1u51Zb66YRIXUXVE,6052
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/file_based_detect_builder.py,sha256=C6BqVuknbJzX-B4N4nSwJgEpt9Nf79Oen8H6so9AflU,754
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=5-jg4ivJhiWi3PVIILVThorDNKg-i4Z-rFqZ2n01RDY,1322
44
- OTVision/detect/rtsp_input_source.py,sha256=0BGMTFZqAf_8qjha_3BrNhjDoS-ejyNlU_Z9h3DqG54,6865
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=GLzG4LeZNYcOE1tHyebL51HxBHzJOVaDfJKsLiAtu4A,8775
47
- OTVision/detect/yolo.py,sha256=Ksj8X7DZmONalaMB_iz-AtXwhEk4Fu7nZNzrXpqfhQw,10451
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=VNZZ_zc1x6RzndQlOFp5HSeWTg_VAiF1G3IhYymdrzA,1781
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=9DzkTg5dh7_KmxE9oxdmxrcTYhvZY8hHLZwhrh7Gz2o,1245
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=gVjhWNRhYKDp11CcCYHlymlWbhGEIFjWlUwVDat77eY,327
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.6.dist-info/METADATA,sha256=RzNTAEFsKAPXOo7ze8bsD_wrxIt7RtPSP6C_uWgZ7QA,6262
102
- otvision-0.6.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
103
- otvision-0.6.6.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
104
- otvision-0.6.6.dist-info/RECORD,,
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
- )