OTVision 0.6.5__py3-none-any.whl → 0.6.6__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.
@@ -20,6 +20,7 @@ class DetectedFrameFactory:
20
20
 
21
21
  return DetectedFrame(
22
22
  source=frame[FrameKeys.source],
23
+ output=frame[FrameKeys.output],
23
24
  no=frame[FrameKeys.frame],
24
25
  occurrence=frame[FrameKeys.occurrence],
25
26
  detections=detections,
@@ -62,7 +62,7 @@ class OtdetFileWriter:
62
62
  detect_config = config.detect
63
63
 
64
64
  actual_frames = len(event.frames)
65
- if (expected_duration := detect_config.expected_duration) is not None:
65
+ if expected_duration := detect_config.expected_duration:
66
66
  actual_fps = actual_frames / expected_duration.total_seconds()
67
67
  else:
68
68
  actual_fps = actual_frames / source_metadata.duration.total_seconds()
@@ -31,6 +31,7 @@ RETRY_SECONDS = 1
31
31
 
32
32
  class Counter:
33
33
  def __init__(self, start_value: int = 0) -> None:
34
+ self._start_value = start_value
34
35
  self.__counter = start_value
35
36
 
36
37
  def increment(self) -> None:
@@ -39,6 +40,9 @@ class Counter:
39
40
  def get(self) -> int:
40
41
  return self.__counter
41
42
 
43
+ def reset(self) -> None:
44
+ self.__counter = self._start_value
45
+
42
46
 
43
47
  class RtspInputSource(InputSourceDetect):
44
48
 
@@ -68,6 +72,10 @@ class RtspInputSource(InputSourceDetect):
68
72
  def flush_buffer_size(self) -> int:
69
73
  return self.stream_config.flush_buffer_size
70
74
 
75
+ @property
76
+ def fps(self) -> float:
77
+ return self.config.convert.output_fps
78
+
71
79
  def __init__(
72
80
  self,
73
81
  subject: Subject[FlushEvent],
@@ -82,6 +90,9 @@ class RtspInputSource(InputSourceDetect):
82
90
  self._get_current_config = get_current_config
83
91
  self._current_stream: str | None = None
84
92
  self._current_video_capture: VideoCapture | None = None
93
+ self._stream_start_time: datetime = self._datetime_provider.provide()
94
+ self._current_video_start_time = self._stream_start_time
95
+ self._outdated = True
85
96
 
86
97
  @property
87
98
  def _video_capture(self) -> VideoCapture:
@@ -106,20 +117,30 @@ class RtspInputSource(InputSourceDetect):
106
117
  return self._current_video_capture
107
118
 
108
119
  def produce(self) -> Generator[Frame, None, None]:
109
- start_time = self._datetime_provider.provide()
120
+ self._stream_start_time = self._datetime_provider.provide()
121
+ self._current_video_start_time = self._stream_start_time
110
122
  while not self.should_stop():
111
123
  if (frame := self._read_next_frame()) is not None:
112
124
  self._frame_counter.increment()
125
+ occurrence = self._datetime_provider.provide()
126
+
127
+ if self._outdated:
128
+ self._current_video_start_time = occurrence
129
+ self._outdated = False
113
130
 
114
131
  yield Frame(
115
132
  data=convert_frame_to_rgb(frame), # YOLO expects RGB
116
133
  frame=self.current_frame_number,
117
134
  source=self.rtsp_url,
118
- occurrence=self._datetime_provider.provide(),
135
+ output=self.create_output(),
136
+ occurrence=occurrence,
119
137
  )
120
138
  if self.flush_condition_met():
121
- self._notify(start_time)
122
- self._notify(start_time)
139
+ self._notify()
140
+ self._outdated = True
141
+ self._frame_counter.reset()
142
+
143
+ self._notify()
123
144
 
124
145
  def _init_video_capture(self, source: str) -> VideoCapture:
125
146
  cap = VideoCapture(source)
@@ -152,24 +173,16 @@ class RtspInputSource(InputSourceDetect):
152
173
  def flush_condition_met(self) -> bool:
153
174
  return self.current_frame_number % self.flush_buffer_size == 0
154
175
 
155
- def _notify(self, start_time: datetime) -> None:
176
+ def _notify(self) -> None:
156
177
  frame_width = int(self._video_capture.get(CAP_PROP_FRAME_WIDTH))
157
178
  frame_height = int(self._video_capture.get(CAP_PROP_FRAME_HEIGHT))
158
- fps = self.config.convert.output_fps
159
- _start_time = calculate_start_time(
160
- start_time, self.current_frame_number, fps, self.flush_buffer_size
161
- )
162
179
  frames = (
163
180
  self.flush_buffer_size
164
181
  if self.current_frame_number % self.flush_buffer_size == 0
165
182
  else self.current_frame_number % self.flush_buffer_size
166
183
  )
167
- duration = timedelta(seconds=round(frames / fps))
168
- output_filename = (
169
- f"{self.stream_config.name}_FR{round(fps)}"
170
- f"_{_start_time.strftime(DATETIME_FORMAT)}.mp4"
171
- )
172
- output = str(self.stream_config.save_dir / output_filename)
184
+ duration = timedelta(seconds=round(frames / self.fps))
185
+ output = self.create_output()
173
186
  self._subject.notify(
174
187
  FlushEvent.create(
175
188
  source=self.rtsp_url,
@@ -177,22 +190,17 @@ class RtspInputSource(InputSourceDetect):
177
190
  duration=duration,
178
191
  source_width=frame_width,
179
192
  source_height=frame_height,
180
- source_fps=fps,
181
- start_time=_start_time,
193
+ source_fps=self.fps,
194
+ start_time=self._current_video_start_time,
182
195
  )
183
196
  )
184
197
 
185
-
186
- def calculate_start_time(
187
- start: datetime, current_frame_number: int, fps: float, flush_buffer_size: int
188
- ) -> datetime:
189
- offset_in_frames = (
190
- current_frame_number // flush_buffer_size - 1
191
- ) * flush_buffer_size
192
- if offset_in_frames == 0:
193
- return start
194
- offset_in_seconds = offset_in_frames / fps
195
- return start + timedelta(seconds=offset_in_seconds)
198
+ def create_output(self) -> str:
199
+ output_filename = (
200
+ f"{self.stream_config.name}_FR{round(self.fps)}"
201
+ f"_{self._current_video_start_time.strftime(DATETIME_FORMAT)}.mp4"
202
+ )
203
+ return str(self.stream_config.save_dir / output_filename)
196
204
 
197
205
 
198
206
  def convert_frame_to_rgb(frame: ndarray) -> ndarray:
@@ -61,6 +61,7 @@ class VideoTimestamper(Timestamper):
61
61
  data=frame[FrameKeys.data],
62
62
  frame=frame[FrameKeys.frame],
63
63
  source=frame[FrameKeys.source],
64
+ output=frame[FrameKeys.output],
64
65
  occurrence=occurrence,
65
66
  )
66
67
 
@@ -116,6 +116,7 @@ class VideoSource(InputSourceDetect):
116
116
  FrameKeys.data: rotated_image,
117
117
  FrameKeys.frame: frame_number,
118
118
  FrameKeys.source: str(video_file),
119
+ FrameKeys.output: str(video_file),
119
120
  }
120
121
  )
121
122
  else:
@@ -124,6 +125,7 @@ class VideoSource(InputSourceDetect):
124
125
  FrameKeys.data: None,
125
126
  FrameKeys.frame: frame_number,
126
127
  FrameKeys.source: str(video_file),
128
+ FrameKeys.output: str(video_file),
127
129
  }
128
130
  )
129
131
  counter += 1
@@ -206,8 +208,9 @@ class VideoSource(InputSourceDetect):
206
208
  def __add_occurrence(self, timestamper: Timestamper, frame: dict) -> Frame:
207
209
  updated = timestamper.stamp(frame)
208
210
  return Frame(
209
- data=updated["data"],
210
- frame=updated["frame"],
211
- source=updated["source"],
212
- occurrence=updated["occurrence"],
211
+ data=updated[FrameKeys.data],
212
+ frame=updated[FrameKeys.frame],
213
+ source=updated[FrameKeys.source],
214
+ output=updated[FrameKeys.output],
215
+ occurrence=updated[FrameKeys.occurrence],
213
216
  )
OTVision/domain/frame.py CHANGED
@@ -20,6 +20,7 @@ class FrameKeys:
20
20
  frame: Literal["frame"] = "frame"
21
21
  source: Literal["source"] = "source"
22
22
  occurrence: Literal["occurrence"] = "occurrence"
23
+ output: Literal["output"] = "output"
23
24
 
24
25
 
25
26
  class Frame(TypedDict):
@@ -35,6 +36,7 @@ class Frame(TypedDict):
35
36
  data: Optional[ndarray]
36
37
  frame: int
37
38
  source: str
39
+ output: str
38
40
  occurrence: datetime
39
41
 
40
42
 
@@ -49,6 +51,7 @@ class DetectedFrame:
49
51
  no (FrameNo): Frame number.
50
52
  occurrence (datetime): Time stamp, at which frame was recorded.
51
53
  source (str): Source from where frame was obtained, e.g. video file path.
54
+ output (str): Output file name, e.g. video file name.
52
55
  detections (Sequence[Detection]): A sequence of Detections occurring in frame.
53
56
  image (Optional[ndarray]): Optional image data of frame.
54
57
  """
@@ -56,6 +59,7 @@ class DetectedFrame:
56
59
  no: FrameNo
57
60
  occurrence: datetime
58
61
  source: str
62
+ output: str
59
63
  detections: Sequence[Detection]
60
64
  image: Optional[ndarray] = None
61
65
 
@@ -64,6 +68,7 @@ class DetectedFrame:
64
68
  no=self.no,
65
69
  occurrence=self.occurrence,
66
70
  source=self.source,
71
+ output=self.output,
67
72
  detections=self.detections,
68
73
  image=None,
69
74
  )
@@ -153,6 +158,7 @@ class TrackedFrame(DetectedFrame):
153
158
  no=self.no,
154
159
  occurrence=self.occurrence,
155
160
  source=self.source,
161
+ output=self.output,
156
162
  finished_tracks=self.finished_tracks,
157
163
  detections=detections,
158
164
  image=self.image,
@@ -62,6 +62,7 @@ class JsonChunkParser(ChunkParser):
62
62
  no=int(key) + frame_offset,
63
63
  occurrence=occurrence,
64
64
  source=str(file),
65
+ output=str(file),
65
66
  detections=detections,
66
67
  image=None,
67
68
  )
@@ -222,6 +222,7 @@ class IouTracker(Tracker):
222
222
  no=frame.no,
223
223
  occurrence=frame.occurrence,
224
224
  source=frame.source,
225
+ output=frame.output,
225
226
  detections=tracked_detections,
226
227
  image=frame.image,
227
228
  finished_tracks=set(finished_track_ids),
OTVision/version.py CHANGED
@@ -1,4 +1,4 @@
1
- __version__ = "v0.6.5"
1
+ __version__ = "v0.6.6"
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.5
3
+ Version: 0.6.6
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/
@@ -1,7 +1,7 @@
1
1
  OTVision/__init__.py,sha256=CLnfgTlVHM4_nzDacvy06Z_Crc3hU6usd0mUyEvBf24,781
2
2
  OTVision/config.py,sha256=0ecnI0N2rS2q0Ld6gBpK4iU2iyuUw683XWjj4g-L1m4,5336
3
3
  OTVision/dataformat.py,sha256=BHF7qHzyNb80hI1EKfwcdJ9bgG_X4bp_hCXzdg7_MSA,1941
4
- OTVision/version.py,sha256=pPG0EpzyxpxNsBhpPGGgivl4nunilLlKyXa5gBTSYYo,175
4
+ OTVision/version.py,sha256=TePRHn7igC5gC-d_U37x7TlN7c1dsVfR5nB0hJk7lY0,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
@@ -18,7 +18,7 @@ OTVision/application/update_current_config.py,sha256=iW1rpCClTHn8tnmVSpLVxdEB0nh
18
18
  OTVision/application/detect/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
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
- OTVision/application/detect/detected_frame_factory.py,sha256=Rl6ElFYulgWPK_XZQSXZcT6caF8hvfzMKsOFefYO_pY,963
21
+ OTVision/application/detect/detected_frame_factory.py,sha256=sW_l0xaPz44_lPEmCPKz4Xg1Mv4ZGKN9CyBCG_iN8dQ,1007
22
22
  OTVision/application/detect/detected_frame_producer.py,sha256=LD4AnQG04YGE68TpxmaRuWRZeCZfhp8oixk6SHTru7c,906
23
23
  OTVision/application/detect/detection_file_save_path_provider.py,sha256=nUyzgR7imrH8PkUl_72kdUDiolPXq1_RQqbpFwLI5Cs,2165
24
24
  OTVision/application/detect/factory.py,sha256=UCnLtgpWdNqwwjW0v2yzKF9Gacx6gewjTyy43wXs2Jg,938
@@ -38,12 +38,12 @@ OTVision/detect/detect.py,sha256=YaVS-DJXdEmh-OzwE31UPNl2uk7mcFyO_CKKTgMeiuM,132
38
38
  OTVision/detect/detected_frame_buffer.py,sha256=TrLbImpvCm1B09Z3c000e2uDO5WguyVwoMmFAqH8Zvk,1505
39
39
  OTVision/detect/file_based_detect_builder.py,sha256=C6BqVuknbJzX-B4N4nSwJgEpt9Nf79Oen8H6so9AflU,754
40
40
  OTVision/detect/otdet.py,sha256=-8rZGY9NVRAwIHeVcNCjm665SmnW5UVIO_PSKdHegDA,6274
41
- OTVision/detect/otdet_file_writer.py,sha256=l4NRWwMywiho4KOhrzxpJqAklyzE6nOtxpkt1kyy3Ss,4318
41
+ OTVision/detect/otdet_file_writer.py,sha256=idgPKwhgEd18HT7HshaT304JPlSw0kT4Isn2gWJRaSk,4304
42
42
  OTVision/detect/pyav_frame_count_provider.py,sha256=w7p9iM3F2fljV8SD7q491gQhIHANbVczqtalcUiKj-E,453
43
43
  OTVision/detect/rtsp_based_detect_builder.py,sha256=5-jg4ivJhiWi3PVIILVThorDNKg-i4Z-rFqZ2n01RDY,1322
44
- OTVision/detect/rtsp_input_source.py,sha256=U0yY-t56wxyh1j-7R48Dbrb4bTGYnCwIAiuHZjM2EGw,6568
45
- OTVision/detect/timestamper.py,sha256=lexX7zahtoyg0tnyu5lkiqHvauewqBCLe0DPXBR10YY,5244
46
- OTVision/detect/video_input_source.py,sha256=vaf1RfoeXnOqrk76xvcrX_UsAseNKeKXkg73Aurj3NI,8555
44
+ OTVision/detect/rtsp_input_source.py,sha256=0BGMTFZqAf_8qjha_3BrNhjDoS-ejyNlU_Z9h3DqG54,6865
45
+ OTVision/detect/timestamper.py,sha256=VvDTzHu9fTI7qQL9x775Gc27r47R8D5Pb040ffwO04k,5288
46
+ OTVision/detect/video_input_source.py,sha256=GLzG4LeZNYcOE1tHyebL51HxBHzJOVaDfJKsLiAtu4A,8775
47
47
  OTVision/detect/yolo.py,sha256=Ksj8X7DZmONalaMB_iz-AtXwhEk4Fu7nZNzrXpqfhQw,10451
48
48
  OTVision/detect/plugin_av/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
49
49
  OTVision/detect/plugin_av/rotate_frame.py,sha256=4wJqTYI2HRlfa4p2Ffap33vLmKIzE_EwFvQraEkQ4R8,1055
@@ -52,7 +52,7 @@ OTVision/domain/cli.py,sha256=VNZZ_zc1x6RzndQlOFp5HSeWTg_VAiF1G3IhYymdrzA,1781
52
52
  OTVision/domain/current_config.py,sha256=Q38NCktoGNU1Z_miXNoJXLH8-NDbVszwVOMGR1aAwWM,286
53
53
  OTVision/domain/detect_producer_consumer.py,sha256=gD7NwscQZLmCxMDZpZkGql0oMrpGHDBBNvdTXs58Vvw,855
54
54
  OTVision/domain/detection.py,sha256=SZLP-87XE3NcTkeYz7GTqp4oPMiqI1P5gILp1_yHtxY,3761
55
- OTVision/domain/frame.py,sha256=qHduCRbBTgzGLIuu7MlLWvhzphPlD3V0nrjlEApmr00,6211
55
+ OTVision/domain/frame.py,sha256=Hv1v9cegfhVGgl2MB4uKYbvngfkOLrvES_w4gD0HtMo,6410
56
56
  OTVision/domain/input_source_detect.py,sha256=9DzkTg5dh7_KmxE9oxdmxrcTYhvZY8hHLZwhrh7Gz2o,1245
57
57
  OTVision/domain/object_detection.py,sha256=kyrTbP9sZBKtGo54vCNfluDMM8wpWZST9Oqf8m8Q1y4,1394
58
58
  OTVision/domain/serialization.py,sha256=S7gb648z_W8U3Fb6TSk7hVU4qHlGwOZ7D6FeYSLXQwM,257
@@ -81,11 +81,11 @@ OTVision/track/model/filebased/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5
81
81
  OTVision/track/model/filebased/frame_chunk.py,sha256=rXhQCHXWGJbePy5ZW3JZCdltGz5mZxFdcrW0mgez-2k,6771
82
82
  OTVision/track/model/filebased/frame_group.py,sha256=f-hXS1Vc5U_qf2cgNbYVeSTZ3dg5NUJhasOEHuuX1HE,2977
83
83
  OTVision/track/parser/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
84
- OTVision/track/parser/chunk_parser_plugins.py,sha256=ojBv_mCOXABmygxz5mhXzeysur3ECmT4a4VlGkeT11k,3108
84
+ OTVision/track/parser/chunk_parser_plugins.py,sha256=X86W_TBQN20ldZIEuv63FSvWBacG0wEaD0YkC9jcJVg,3142
85
85
  OTVision/track/parser/frame_group_parser_plugins.py,sha256=TWnGhM-N7ldN8LHZ1YYecEjn4xo2o91PyOGgr4Jzh9M,5479
86
86
  OTVision/track/tracker/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
87
87
  OTVision/track/tracker/filebased_tracking.py,sha256=H3WYbsSca-67898diBoixpLjBqQDnOSiqnbvvySE6fc,6576
88
- OTVision/track/tracker/tracker_plugin_iou.py,sha256=PQOB3fXlNKSTEGK7HFfaUfTRgXSY1ZhlVmjk1VWugFU,7484
88
+ OTVision/track/tracker/tracker_plugin_iou.py,sha256=AecE4CXRf4qUdN3_AvSFcsW4so-zDUGAVXqzfjSb-i0,7517
89
89
  OTVision/transform/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
90
90
  OTVision/transform/get_homography.py,sha256=29waW61uzCCB7tlBAS2zck9sliAxZqnjjOa4jOOHHIc,5970
91
91
  OTVision/transform/reference_points_picker.py,sha256=LOmwzCaqbJnXVhaTSaZtkCzTMDamYjbTpY8I99pN0rg,16578
@@ -98,7 +98,7 @@ OTVision/view/view_helpers.py,sha256=a5yV_6ZxO5bxsSymOmxdHqzOEv0VFq4wFBopVRGuVRo
98
98
  OTVision/view/view_track.py,sha256=vmfMqpbUfnzg_EsWiL-IIKNOApVF09dzSojHpUfYY6M,5393
99
99
  OTVision/view/view_transform.py,sha256=HvRd8g8geKRy0OoiZUDn_oC3SJC5nuXhZf3uZelfGKg,5473
100
100
  OTVision/view/helpers/OTC.ico,sha256=G9kwlDtgBXmXO3yxW6Z-xVFV2q4nUGuz9E1VPHSu_I8,21662
101
- otvision-0.6.5.dist-info/METADATA,sha256=-YE_U1NlesErB101C72im0uWFlf_7ItDmD0YIg5cVvY,6262
102
- otvision-0.6.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
103
- otvision-0.6.5.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
104
- otvision-0.6.5.dist-info/RECORD,,
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,,