OTVision 0.6.1__py3-none-any.whl → 0.6.3__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 (38) hide show
  1. OTVision/__init__.py +0 -10
  2. OTVision/application/detect/current_object_detector.py +1 -2
  3. OTVision/application/detect/detected_frame_factory.py +4 -3
  4. OTVision/application/detect/detected_frame_producer.py +1 -2
  5. OTVision/detect/builder.py +1 -1
  6. OTVision/detect/detected_frame_buffer.py +1 -1
  7. OTVision/detect/otdet.py +3 -2
  8. OTVision/detect/yolo.py +2 -2
  9. OTVision/domain/detect_producer_consumer.py +1 -1
  10. OTVision/domain/detection.py +128 -7
  11. OTVision/domain/frame.py +146 -1
  12. OTVision/domain/object_detection.py +1 -2
  13. OTVision/helpers/files.py +10 -2
  14. OTVision/helpers/input_types.py +15 -0
  15. OTVision/track/exporter/__init__.py +0 -0
  16. OTVision/track/exporter/filebased_exporter.py +24 -0
  17. OTVision/track/model/__init__.py +0 -0
  18. OTVision/track/model/filebased/__init__.py +0 -0
  19. OTVision/track/model/filebased/frame_chunk.py +203 -0
  20. OTVision/track/model/filebased/frame_group.py +95 -0
  21. OTVision/track/model/track_exporter.py +119 -0
  22. OTVision/track/model/tracking_interfaces.py +303 -0
  23. OTVision/track/parser/__init__.py +0 -0
  24. OTVision/track/parser/chunk_parser_plugins.py +99 -0
  25. OTVision/track/parser/frame_group_parser_plugins.py +127 -0
  26. OTVision/track/track.py +54 -332
  27. OTVision/track/tracker/__init__.py +0 -0
  28. OTVision/track/tracker/filebased_tracking.py +192 -0
  29. OTVision/track/tracker/tracker_plugin_iou.py +224 -0
  30. OTVision/version.py +1 -1
  31. OTVision/view/view_track.py +1 -1
  32. {otvision-0.6.1.dist-info → otvision-0.6.3.dist-info}/METADATA +8 -6
  33. {otvision-0.6.1.dist-info → otvision-0.6.3.dist-info}/RECORD +35 -23
  34. OTVision/track/iou.py +0 -282
  35. OTVision/track/iou_util.py +0 -140
  36. OTVision/track/preprocess.py +0 -453
  37. {otvision-0.6.1.dist-info → otvision-0.6.3.dist-info}/WHEEL +0 -0
  38. {otvision-0.6.1.dist-info → otvision-0.6.3.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,224 @@
1
+ from dataclasses import dataclass
2
+
3
+ from OTVision.domain.detection import Detection, TrackedDetection, TrackId
4
+ from OTVision.domain.frame import DetectedFrame, FrameNo, TrackedFrame
5
+ from OTVision.track.model.tracking_interfaces import ID_GENERATOR, Tracker
6
+
7
+
8
+ @dataclass(frozen=True)
9
+ class IouParameters:
10
+ sigma_l: float
11
+ sigma_h: float
12
+ sigma_iou: float
13
+ t_min: int
14
+ t_miss_max: int
15
+
16
+
17
+ @dataclass(frozen=True, repr=True)
18
+ class Coordinate:
19
+ x: float
20
+ y: float
21
+
22
+ @staticmethod
23
+ def center_of(detection: Detection) -> "Coordinate":
24
+ return Coordinate(detection.x, detection.y)
25
+
26
+
27
+ @dataclass(frozen=True, repr=True)
28
+ class BoundingBox:
29
+ xmin: float
30
+ ymin: float
31
+ xmax: float
32
+ ymax: float
33
+
34
+ @staticmethod
35
+ def from_xywh(detection: Detection) -> "BoundingBox":
36
+ """Calculates xyxy coordinates from Detection with xywh data:
37
+ pixel values for xcenter, ycenter, width and height.
38
+
39
+ Args:
40
+ detection (Detection): detection to compute BoundingBox for.
41
+
42
+ Returns:
43
+ BoundingBox with pixel coordinates: xmin, ymin, xmay, ymax
44
+ """
45
+ diff_w = detection.w / 2
46
+ diff_h = detection.h / 2
47
+ d = detection
48
+ return BoundingBox(d.x - diff_w, d.y - diff_h, d.x + diff_w, d.y + diff_h)
49
+
50
+ def as_tuple(self) -> tuple[float, float, float, float]:
51
+ return (self.xmin, self.ymin, self.xmax, self.ymax)
52
+
53
+
54
+ @dataclass
55
+ class ActiveIouTrack:
56
+ # TODO check invariant -> at least one element in lists
57
+ id: int
58
+ frame_no: list[FrameNo]
59
+ bboxes: list[BoundingBox]
60
+ center: list[Coordinate]
61
+ conf: list[float]
62
+ classes: list[str]
63
+ max_class: str
64
+ max_conf: float
65
+ first_frame: FrameNo
66
+ last_frame: FrameNo
67
+ track_age: int
68
+
69
+ def __init__(
70
+ self, id: TrackId, frame: "DetectedFrame", detection: "Detection"
71
+ ) -> None:
72
+ self.id = id
73
+ self.frame_no = [frame.no]
74
+ self.bboxes = [BoundingBox.from_xywh(detection)]
75
+ self.center = [Coordinate.center_of(detection)]
76
+ self.conf = [detection.conf]
77
+ self.classes = [detection.label]
78
+ self.max_class = detection.label
79
+ self.max_conf = detection.conf
80
+ self.first_frame = frame.no
81
+ self.last_frame = frame.no
82
+ self.track_age = 0
83
+
84
+ def add_detection(self, frame: DetectedFrame, detection: Detection) -> None:
85
+ self.frame_no.append(frame.no)
86
+ self.bboxes.append(BoundingBox.from_xywh(detection))
87
+ self.center.append(Coordinate.center_of(detection))
88
+ self.conf.append(detection.conf)
89
+ self.classes.append(detection.label)
90
+ self.max_conf = max(self.max_conf, detection.conf)
91
+ self.last_frame = max(self.last_frame, frame.no)
92
+ self.track_age = 0
93
+
94
+ @property
95
+ def frame_span(self) -> int:
96
+ return self.last_frame - self.first_frame
97
+
98
+ def iou_with(self, detection: Detection) -> float:
99
+ return iou(self.bboxes[-1], BoundingBox.from_xywh(detection))
100
+
101
+
102
+ def iou(
103
+ bbox1: BoundingBox,
104
+ bbox2: BoundingBox,
105
+ ) -> float:
106
+ """
107
+ Calculates the intersection-over-union of two bounding boxes.
108
+
109
+ Args:
110
+ bbox1 (BoundingBox): first bounding box.
111
+ bbox2 (BoundingBox): second bounding box.
112
+
113
+ Returns:
114
+ int: intersection-over-onion of bbox1, bbox2
115
+ """
116
+
117
+ (xmin_1, ymin_1, xmax_1, ymax_1) = bbox1.as_tuple()
118
+ (xmin_2, ymin_2, xmax_2, ymax_2) = bbox2.as_tuple()
119
+
120
+ # get the overlap rectangle
121
+ overlap_xmin = max(xmin_1, xmin_2)
122
+ overlap_ymin = max(ymin_1, ymin_2)
123
+ overlap_xmax = min(xmax_1, xmax_2)
124
+ overlap_ymax = min(ymax_1, ymax_2)
125
+
126
+ # check if there is an overlap
127
+ if overlap_xmax - overlap_xmin <= 0 or overlap_ymax - overlap_ymin <= 0:
128
+ return 0
129
+
130
+ # if yes, calculate the ratio of the overlap to each ROI size and the unified size
131
+ size_1 = (xmax_1 - xmin_1) * (ymax_1 - ymin_1)
132
+ size_2 = (xmax_2 - xmin_2) * (ymax_2 - ymin_2)
133
+
134
+ size_intersection = (overlap_xmax - overlap_xmin) * (overlap_ymax - overlap_ymin)
135
+ size_union = size_1 + size_2 - size_intersection
136
+
137
+ return size_intersection / size_union
138
+
139
+
140
+ class IouTracker(Tracker):
141
+
142
+ def __init__(self, parameters: IouParameters):
143
+ super().__init__()
144
+ self.parameters = parameters
145
+ self.active_tracks: list[ActiveIouTrack] = []
146
+
147
+ @property
148
+ def sigma_l(self) -> float:
149
+ return self.parameters.sigma_l
150
+
151
+ @property
152
+ def sigma_h(self) -> float:
153
+ return self.parameters.sigma_h
154
+
155
+ @property
156
+ def sigma_iou(self) -> float:
157
+ return self.parameters.sigma_iou
158
+
159
+ @property
160
+ def t_min(self) -> int:
161
+ return self.parameters.t_min
162
+
163
+ @property
164
+ def t_miss_max(self) -> int:
165
+ return self.parameters.t_miss_max
166
+
167
+ def track_frame(
168
+ self, frame: DetectedFrame, id_generator: ID_GENERATOR
169
+ ) -> TrackedFrame:
170
+
171
+ detections = [d for d in frame.detections if d.conf >= self.sigma_l]
172
+ tracked_detections: list[TrackedDetection] = []
173
+
174
+ finished_track_ids: list[TrackId] = []
175
+ discarded_track_ids: list[TrackId] = []
176
+
177
+ saved_tracks: list[ActiveIouTrack] = []
178
+ updated_tracks: list[ActiveIouTrack] = []
179
+ new_tracks: list[ActiveIouTrack] = []
180
+
181
+ # for each active track, check if detection with best iou match can be added
182
+ for track in self.active_tracks:
183
+ if detections:
184
+ # get det with highest iou
185
+ iou_pairs = (
186
+ (i, det, track.iou_with(det)) for i, det in enumerate(detections)
187
+ )
188
+ best_index, best_match, best_iou = max(iou_pairs, key=lambda p: p[2])
189
+
190
+ if best_iou >= self.sigma_iou:
191
+ track.add_detection(frame, best_match)
192
+ updated_tracks.append(track)
193
+
194
+ del detections[best_index]
195
+ tracked_detections.append(best_match.of_track(track.id, False))
196
+
197
+ # if track was not updated, increase age (time without detection)
198
+ # if max age is reached, check confidence, if sufficient finish that track
199
+ if not updated_tracks or track is not updated_tracks[-1]:
200
+ if track.track_age < self.t_miss_max:
201
+ track.track_age += 1
202
+ saved_tracks.append(track)
203
+ elif track.max_conf >= self.sigma_h and track.frame_span >= self.t_min:
204
+ finished_track_ids.append(track.id)
205
+ else:
206
+ discarded_track_ids.append(track.id)
207
+
208
+ # start new track for each detection that could not be assigned
209
+ for det in detections:
210
+ track_id: TrackId = next(id_generator)
211
+ new_tracks.append(ActiveIouTrack(track_id, frame, det))
212
+ tracked_detections.append(det.of_track(track_id, True))
213
+
214
+ self.active_tracks = updated_tracks + saved_tracks + new_tracks
215
+
216
+ return TrackedFrame(
217
+ no=frame.no,
218
+ occurrence=frame.occurrence,
219
+ source=frame.source,
220
+ detections=tracked_detections,
221
+ image=frame.image,
222
+ finished_tracks=set(finished_track_ids),
223
+ discarded_tracks=set(discarded_track_ids),
224
+ )
OTVision/version.py CHANGED
@@ -1,4 +1,4 @@
1
- __version__ = "v0.6.1"
1
+ __version__ = "v0.6.3"
2
2
 
3
3
 
4
4
  def otdet_version() -> str:
@@ -26,7 +26,7 @@ import tkinter as tk
26
26
  from OTVision.config import CONFIG, PAD
27
27
  from OTVision.helpers.files import get_files, replace_filetype
28
28
  from OTVision.helpers.log import LOGGER_NAME
29
- from OTVision.track.track import main as track
29
+ from OTVision.track.new_track import main as track
30
30
  from OTVision.view.view_helpers import FrameRun
31
31
 
32
32
  log = logging.getLogger(LOGGER_NAME)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: OTVision
3
- Version: 0.6.1
3
+ Version: 0.6.3
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/
@@ -20,17 +20,19 @@ Requires-Dist: av==13.0.0
20
20
  Requires-Dist: fire==0.7.0
21
21
  Requires-Dist: geopandas==1.0.1
22
22
  Requires-Dist: ijson==3.3.0
23
+ Requires-Dist: more-itertools==10.5.0
23
24
  Requires-Dist: moviepy==1.0.3
24
- Requires-Dist: numpy==1.26.4
25
- Requires-Dist: opencv-python==4.9.0.80
26
- Requires-Dist: pandas==2.2.2
25
+ Requires-Dist: numpy==1.26.4; sys_platform == 'win32'
26
+ Requires-Dist: numpy==2.1.1; sys_platform != 'win32'
27
+ Requires-Dist: opencv-python==4.11.0.86
28
+ Requires-Dist: pandas==2.2.3
27
29
  Requires-Dist: pyyaml==6.0.2
28
30
  Requires-Dist: setuptools==74.0.0
29
31
  Requires-Dist: torch==2.3.1
30
32
  Requires-Dist: torchvision==0.18.1
31
- Requires-Dist: tqdm==4.66.5
33
+ Requires-Dist: tqdm==4.67.1
32
34
  Requires-Dist: ujson==5.10.0
33
- Requires-Dist: ultralytics==8.3.74
35
+ Requires-Dist: ultralytics==8.3.94
34
36
  Description-Content-Type: text/markdown
35
37
 
36
38
  # OTVision
@@ -1,7 +1,7 @@
1
- OTVision/__init__.py,sha256=b2goS-TYrN9y8WHmjG22z1oVwCrjV0WtGJE32NJfvJM,1042
1
+ OTVision/__init__.py,sha256=CLnfgTlVHM4_nzDacvy06Z_Crc3hU6usd0mUyEvBf24,781
2
2
  OTVision/config.py,sha256=x_Bh3mrfV434puVchuG0zLsTtjIl-_C0-ZbmRW3tyFM,24371
3
3
  OTVision/dataformat.py,sha256=BHF7qHzyNb80hI1EKfwcdJ9bgG_X4bp_hCXzdg7_MSA,1941
4
- OTVision/version.py,sha256=UIkCw9rQCEG04kSFJmicLwkgMRtldo-soXAr4ZoKy7A,175
4
+ OTVision/version.py,sha256=LsZveHynUdA099dDFIxJcaPj9GVc31eUilqAarmeesE,175
5
5
  OTVision/abstraction/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
6
  OTVision/abstraction/observer.py,sha256=ZFGxUUjI3wUpf5ogXg2yDe-QjCcXre6SxH5zOogOx2U,1350
7
7
  OTVision/abstraction/pipes_and_filter.py,sha256=rzYaWDalXnlMbfpkgI91a0FL0q1llUlCRuIQsidYDv8,940
@@ -13,10 +13,10 @@ OTVision/application/get_config.py,sha256=fRJZUWJvlTed6qriw82-B-05CAqfBma6NQ23Wr
13
13
  OTVision/application/get_current_config.py,sha256=RPVexRhJxY4ul8sU0W2OJaj98eTJxMlP-CiioQZCpEI,299
14
14
  OTVision/application/update_current_config.py,sha256=1p-F00myUofn-S97NMMfJ9SbemJiLKc_FS3ob_27YjM,321
15
15
  OTVision/application/detect/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
- OTVision/application/detect/current_object_detector.py,sha256=dmPbZGIXE49TbBZP-KAZWPgiMEeyZKdiaD2VlLM1SxI,1380
16
+ OTVision/application/detect/current_object_detector.py,sha256=J4U5k1s902agN90FYR4M_gO8MYYa_5XkYGREjStWCxQ,1343
17
17
  OTVision/application/detect/current_object_detector_metadata.py,sha256=xai0UBEzxr-rxXCc8mTmNDECds7mdsw2sem5HZxvQ4Q,1017
18
- OTVision/application/detect/detected_frame_factory.py,sha256=x7FPckcqVVov912ysBiUVvn6eVn3-rqd2rjzXrZBNrY,932
19
- OTVision/application/detect/detected_frame_producer.py,sha256=hUxmA2wx-OXRAjGZieDfsvRQUaJN7IT42R1Po4iUPhA,943
18
+ OTVision/application/detect/detected_frame_factory.py,sha256=Rl6ElFYulgWPK_XZQSXZcT6caF8hvfzMKsOFefYO_pY,963
19
+ OTVision/application/detect/detected_frame_producer.py,sha256=LD4AnQG04YGE68TpxmaRuWRZeCZfhp8oixk6SHTru7c,906
20
20
  OTVision/application/detect/detection_file_save_path_provider.py,sha256=nUyzgR7imrH8PkUl_72kdUDiolPXq1_RQqbpFwLI5Cs,2165
21
21
  OTVision/application/detect/factory.py,sha256=UWQHqpaT6FyhMTzTIBSGR06_Hxo8HKM2-IlSpP_djYA,926
22
22
  OTVision/application/detect/get_detect_cli_args.py,sha256=gezr17im8SwbuXW1suCodWRrFs8lSljNKu76SbWBgkY,265
@@ -25,38 +25,50 @@ OTVision/application/detect/update_detect_config_with_cli_args.py,sha256=NP4asZx
25
25
  OTVision/convert/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
26
  OTVision/convert/convert.py,sha256=vRIFcrAUf9MUWAuvF_q7JQ9aN89B1ouBOCtXrCLuDp8,11013
27
27
  OTVision/detect/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
- OTVision/detect/builder.py,sha256=fOhbspd4TuCJMDR2Vvbc4hOJqyfHTo2f6x7l_wf-IiA,6971
28
+ OTVision/detect/builder.py,sha256=ZFSUnDmx742DqxuEG3N8Hf7ynvE0jLkxvRYAApbB3zA,6967
29
29
  OTVision/detect/cli.py,sha256=qsmbt43j0KAyj8XVuo-QEHxQZJ80DVmpDGZ-d1lE14I,6025
30
30
  OTVision/detect/detect.py,sha256=YaVS-DJXdEmh-OzwE31UPNl2uk7mcFyO_CKKTgMeiuM,1328
31
- OTVision/detect/detected_frame_buffer.py,sha256=SlnwWIqvJsSp-qt9u5Vk094JCnhfpr5luA6A2h5ZRVc,1336
32
- OTVision/detect/otdet.py,sha256=VsprZPBH2hUna9wYQBAQVM9rM4X9L7cD5aNS9N5RFr0,4485
31
+ OTVision/detect/detected_frame_buffer.py,sha256=fLI87CuWucnNuRmOuQQWZBa_7XMGpJOWE9gvack8ZfM,1332
32
+ OTVision/detect/otdet.py,sha256=6yYSNHayzBBVu4PBaMobkh7jBhQKQ6KbnQPOefwoJh8,4508
33
33
  OTVision/detect/otdet_file_writer.py,sha256=9xeBD5I44Yp3DblZWpnOQuBVRWDQ2eObyevmBid2Wyw,4252
34
34
  OTVision/detect/pyav_frame_count_provider.py,sha256=w7p9iM3F2fljV8SD7q491gQhIHANbVczqtalcUiKj-E,453
35
35
  OTVision/detect/timestamper.py,sha256=4wsbkv-ex4zGhPCcYWqQQ2JbqVVPCmVzoT2U6efsy-o,5232
36
36
  OTVision/detect/video_input_source.py,sha256=AoFn4OJvRYSZ63qEhZ_gk0ptPFBTZc37uaptFyPoZNc,8557
37
- OTVision/detect/yolo.py,sha256=h4RTUP5RYTKe4QNy19D_8A6MeNv3lVTDRhShTnOX_as,9845
37
+ OTVision/detect/yolo.py,sha256=YkeE1A-NULsOgx0y2SH16nzBNBYAAd-TdQLzYGfuBH8,9845
38
38
  OTVision/detect/plugin_av/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
39
39
  OTVision/detect/plugin_av/rotate_frame.py,sha256=4wJqTYI2HRlfa4p2Ffap33vLmKIzE_EwFvQraEkQ4R8,1055
40
40
  OTVision/domain/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
41
41
  OTVision/domain/cli.py,sha256=XRIDSv4TJvXxwJw1UVznHSpaeUIRiikCabyGuiwh_2o,1049
42
42
  OTVision/domain/current_config.py,sha256=TKb6Nuei8n2v6k873w0AkDNAnvSwqphdi5h6hlZ4Y5k,274
43
- OTVision/domain/detect_producer_consumer.py,sha256=hWrK3Hh62Opm8t4uWASMw1MTSp1xtZYcE9cQiAfRzGs,859
44
- OTVision/domain/detection.py,sha256=FGI1R8cgTfgL3WOnvsFNMIV2aU7Zu2W4q75T-86yxyg,343
45
- OTVision/domain/frame.py,sha256=SKJlo_Gd9kRjD7JJR6GetBqiiJ7b1_Vn4KH4V6Q1iVA,751
43
+ OTVision/domain/detect_producer_consumer.py,sha256=gD7NwscQZLmCxMDZpZkGql0oMrpGHDBBNvdTXs58Vvw,855
44
+ OTVision/domain/detection.py,sha256=SZLP-87XE3NcTkeYz7GTqp4oPMiqI1P5gILp1_yHtxY,3761
45
+ OTVision/domain/frame.py,sha256=2mMAr6_V9rCzWS0MLbOLtUcj44oGY1fsBEfzZgU4Eww,5962
46
46
  OTVision/domain/input_source_detect.py,sha256=9DzkTg5dh7_KmxE9oxdmxrcTYhvZY8hHLZwhrh7Gz2o,1245
47
- OTVision/domain/object_detection.py,sha256=gJ5Jd5uWGSjdIue-XKddb9krFynLZ59_XctMY4HE1ww,1288
47
+ OTVision/domain/object_detection.py,sha256=k2VoQlaUhnpAUtX-OdbgDdt4qN0o7zjFGA5rZn1gneY,1251
48
48
  OTVision/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
49
49
  OTVision/helpers/date.py,sha256=XK0fmXMombQ2CJ2_RXjk-bc-R8qqXRK2rDmCMBi0_G0,854
50
- OTVision/helpers/files.py,sha256=LMbXuLd7Nid565BtPJxv1SPqkvJhX4BF-yLJ8KMZavo,18118
50
+ OTVision/helpers/files.py,sha256=g_ix1e_ti1ZlW4zOulvkUiupcE8KJV_OHGNGXQTQ71I,18478
51
51
  OTVision/helpers/formats.py,sha256=YLo_QLA2nhVREyv5N-xNW20c4nIL7DIF40E1lrsAyLE,4365
52
+ OTVision/helpers/input_types.py,sha256=d94IYX0e-ersz3bl8xy9SklGUom-LjPUQ-BRsOmpH68,649
52
53
  OTVision/helpers/log.py,sha256=fOSMTXQRQ3_3zzYL8pDlx85IXPwyDsI2WGpK-V_R47Q,4985
53
54
  OTVision/helpers/machine.py,sha256=8Bz_Eg7PS0IL4riOVeJcEIi5D9E8Ju8-JomTkW975p8,2166
54
55
  OTVision/helpers/video.py,sha256=xyI35CiWXqoeGd3HeLhZUPxrLz8GccWyzHusxoweJr4,1480
55
56
  OTVision/track/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
56
- OTVision/track/iou.py,sha256=zpb4TbB34wZjUorhMLqMy6WspfsK-TkYvD75L-_ODM8,9491
57
- OTVision/track/iou_util.py,sha256=mqylrarqiauSewinxOm9CSa6dgVSl8G9bpCv--2m02M,4465
58
- OTVision/track/preprocess.py,sha256=QTKCdVWO8lj3mZ1nqDkKMLomHsiglCMYgqCe-zneick,14194
59
- OTVision/track/track.py,sha256=fKDP6jQ2pDLwxJOiDUYqs9fD3W67fNgwO3l8zUTYOw4,15344
57
+ OTVision/track/track.py,sha256=hrmvwIR4nV6wA8DUuYPSCrDXr7cAP13WXo_gZDBX7hE,5054
58
+ OTVision/track/exporter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
59
+ OTVision/track/exporter/filebased_exporter.py,sha256=7LFoHcqrlNCd0KDQZA5qI2sZFlxecazJ6sBwdVvvdWE,947
60
+ OTVision/track/model/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
61
+ OTVision/track/model/track_exporter.py,sha256=FUuMsdNqWmjqRQasqB6A_0h73NAt-Mt8V93O_ajCHf4,3228
62
+ OTVision/track/model/tracking_interfaces.py,sha256=UCCikNv3RQScyHj0Db9jvDS3RaxZXPKLmS1VAYynZ4U,10785
63
+ OTVision/track/model/filebased/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
64
+ OTVision/track/model/filebased/frame_chunk.py,sha256=rXhQCHXWGJbePy5ZW3JZCdltGz5mZxFdcrW0mgez-2k,6771
65
+ OTVision/track/model/filebased/frame_group.py,sha256=f-hXS1Vc5U_qf2cgNbYVeSTZ3dg5NUJhasOEHuuX1HE,2977
66
+ OTVision/track/parser/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
67
+ OTVision/track/parser/chunk_parser_plugins.py,sha256=ojBv_mCOXABmygxz5mhXzeysur3ECmT4a4VlGkeT11k,3108
68
+ OTVision/track/parser/frame_group_parser_plugins.py,sha256=N5jS6Aq2Aq4gc0ouX0UOBqCUagNLRQRjS_81k_OzZi4,4575
69
+ OTVision/track/tracker/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
70
+ OTVision/track/tracker/filebased_tracking.py,sha256=bYE1_NX08dy3KDrBpD8aTobtQqg9Id8h4WudZgUEZ5Q,6546
71
+ OTVision/track/tracker/tracker_plugin_iou.py,sha256=4e4DUOClwn8cYYyloUj2ux7DTCVeAs7WMptmAeDMNWY,7234
60
72
  OTVision/transform/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
61
73
  OTVision/transform/get_homography.py,sha256=29waW61uzCCB7tlBAS2zck9sliAxZqnjjOa4jOOHHIc,5970
62
74
  OTVision/transform/reference_points_picker.py,sha256=LOmwzCaqbJnXVhaTSaZtkCzTMDamYjbTpY8I99pN0rg,16578
@@ -66,10 +78,10 @@ OTVision/view/view.py,sha256=sBFGfr-lf8lG1BTaQ3Iu46GpZGhaVhSrqg8p5XUPJ4I,3302
66
78
  OTVision/view/view_convert.py,sha256=NUtmrmAAFCYdQS1U2av_eYUScXugQYjhD3e3H0pro6Q,5157
67
79
  OTVision/view/view_detect.py,sha256=E1JP9w5iv44hYkOr86In5d6V3sgn7Ld85KaRcY0oFZU,5951
68
80
  OTVision/view/view_helpers.py,sha256=a5yV_6ZxO5bxsSymOmxdHqzOEv0VFq4wFBopVRGuVRo,16195
69
- OTVision/view/view_track.py,sha256=ioHenonuTJQn8yDzlIDV8Jc1yzWA0GHbC9CotYGtxHQ,5389
81
+ OTVision/view/view_track.py,sha256=vmfMqpbUfnzg_EsWiL-IIKNOApVF09dzSojHpUfYY6M,5393
70
82
  OTVision/view/view_transform.py,sha256=HvRd8g8geKRy0OoiZUDn_oC3SJC5nuXhZf3uZelfGKg,5473
71
83
  OTVision/view/helpers/OTC.ico,sha256=G9kwlDtgBXmXO3yxW6Z-xVFV2q4nUGuz9E1VPHSu_I8,21662
72
- otvision-0.6.1.dist-info/METADATA,sha256=r9EGDVN1iNWPLERQkfjf9udE2b-1sSxtLMshILtz_AI,2244
73
- otvision-0.6.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
74
- otvision-0.6.1.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
75
- otvision-0.6.1.dist-info/RECORD,,
84
+ otvision-0.6.3.dist-info/METADATA,sha256=FNw2TfxIaYGZoPrjXw5XA0DHdnBZYAR2xwd_qjMzn5o,2361
85
+ otvision-0.6.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
86
+ otvision-0.6.3.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
87
+ otvision-0.6.3.dist-info/RECORD,,
OTVision/track/iou.py DELETED
@@ -1,282 +0,0 @@
1
- """
2
- OTVision module to track road users in frames detected by OTVision
3
- """
4
-
5
- # based on IOU Tracker written by Erik Bochinski originally licensed under the
6
- # MIT License, see
7
- # https://github.com/bochinski/iou-tracker/blob/master/LICENSE.
8
-
9
-
10
- # Copyright (C) 2022 OpenTrafficCam Contributors
11
- # <https://github.com/OpenTrafficCam
12
- # <team@opentrafficcam.org>
13
- #
14
- # This program is free software: you can redistribute it and/or modify
15
- # it under the terms of the GNU General Public License as published by
16
- # the Free Software Foundation, either version 3 of the License, or
17
- # (at your option) any later version.
18
- #
19
- # This program is distributed in the hope that it will be useful,
20
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
21
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22
- # GNU General Public License for more details.
23
- #
24
- # You should have received a copy of the GNU General Public License
25
- # along with this program. If not, see <https://www.gnu.org/licenses/>.
26
- from collections import defaultdict
27
- from dataclasses import dataclass
28
- from typing import Iterator
29
-
30
- from tqdm import tqdm
31
-
32
- from OTVision.config import CONFIG
33
- from OTVision.dataformat import (
34
- AGE,
35
- BBOXES,
36
- CENTER,
37
- CLASS,
38
- CONFIDENCE,
39
- DETECTIONS,
40
- FINISHED,
41
- FIRST,
42
- FRAMES,
43
- MAX_CLASS,
44
- MAX_CONF,
45
- START_FRAME,
46
- TRACK_ID,
47
- H,
48
- W,
49
- X,
50
- Y,
51
- )
52
-
53
- from .iou_util import iou
54
-
55
-
56
- class TrackedDetections:
57
- def __init__(
58
- self,
59
- detections: dict[str, dict],
60
- detected_ids: set[int],
61
- active_track_ids: set[int],
62
- ) -> None:
63
- self._detections = detections
64
- self._detected_ids = detected_ids
65
- self._active_track_ids = active_track_ids
66
-
67
- def update_active_track_ids(self, new_active_ids: set[int]) -> None:
68
- self._active_track_ids = {
69
- _id for _id in self._active_track_ids if _id in new_active_ids
70
- }
71
-
72
- def is_finished(self) -> bool:
73
- return len(self._active_track_ids) == 0
74
-
75
-
76
- @dataclass(frozen=True)
77
- class TrackingResult:
78
- tracked_detections: TrackedDetections
79
- active_tracks: list[dict]
80
- last_track_frame: dict[int, int]
81
-
82
-
83
- def make_bbox(obj: dict) -> tuple[float, float, float, float]:
84
- """Calculates xyxy coordinates from dict of xywh.
85
-
86
- Args:
87
- obj (dict): dict of pixel values for xcenter, ycenter, width and height
88
-
89
- Returns:
90
- tuple[float, float, float, float]: xmin, ymin, xmay, ymax
91
- """
92
- return (
93
- obj[X] - obj[W] / 2,
94
- obj[Y] - obj[H] / 2,
95
- obj[X] + obj[W] / 2,
96
- obj[Y] + obj[H] / 2,
97
- )
98
-
99
-
100
- def center(obj: dict) -> tuple[float, float]:
101
- """Retrieves center coordinates from dict.
102
-
103
- Args:
104
- obj (dict): _description_
105
-
106
- Returns:
107
- tuple[float, float]: _description_
108
- """
109
- return obj[X], obj[Y]
110
-
111
-
112
- def id_generator() -> Iterator[int]:
113
- ID: int = 0
114
- while True:
115
- ID += 1
116
- yield ID
117
-
118
-
119
- def track_iou(
120
- detections: list, # TODO: Type hint nested list during refactoring
121
- sigma_l: float = CONFIG["TRACK"]["IOU"]["SIGMA_L"],
122
- sigma_h: float = CONFIG["TRACK"]["IOU"]["SIGMA_H"],
123
- sigma_iou: float = CONFIG["TRACK"]["IOU"]["SIGMA_IOU"],
124
- t_min: int = CONFIG["TRACK"]["IOU"]["T_MIN"],
125
- t_miss_max: int = CONFIG["TRACK"]["IOU"]["T_MISS_MAX"],
126
- previous_active_tracks: list = [],
127
- vehicle_id_generator: Iterator[int] = id_generator(),
128
- ) -> TrackingResult: # sourcery skip: low-code-quality
129
- """
130
- Simple IOU based tracker.
131
- See "High-Speed Tracking-by-Detection Without Using Image Information
132
- by E. Bochinski, V. Eiselein, T. Sikora" for
133
- more information.
134
-
135
- Args:
136
- detections (list): list of detections per frame, usually generated
137
- by util.load_mot
138
- sigma_l (float): low detection threshold.
139
- sigma_h (float): high detection threshold.
140
- sigma_iou (float): IOU threshold.
141
- t_min (float): minimum track length in frames.
142
- previous_active_tracks (list): a list of remaining active tracks
143
- from previous iterations.
144
- vehicle_id_generator (Iterator[int]): provides ids for new tracks
145
-
146
- Returns:
147
- TrackingResult: new detections, a list of active tracks
148
- and a lookup dic for each tracks last detection frame.
149
- """
150
-
151
- _check_types(sigma_l, sigma_h, sigma_iou, t_min, t_miss_max)
152
-
153
- tracks_active: list = []
154
- tracks_active.extend(previous_active_tracks)
155
- # tracks_finished = []
156
-
157
- vehIDs_finished: list = []
158
- new_detections: dict = {}
159
-
160
- for frame_num in tqdm(detections, desc="Tracked frames", unit=" frames"):
161
- detections_frame = detections[frame_num][DETECTIONS]
162
- # apply low threshold to detections
163
- dets = [det for det in detections_frame if det[CONFIDENCE] >= sigma_l]
164
- new_detections[frame_num] = {}
165
- updated_tracks: list = []
166
- saved_tracks: list = []
167
- for track in tracks_active:
168
- if dets:
169
- # get det with highest iou
170
- best_match = max(
171
- dets, key=lambda x: iou(track[BBOXES][-1], make_bbox(x))
172
- )
173
- if iou(track[BBOXES][-1], make_bbox(best_match)) >= sigma_iou:
174
- track[FRAMES].append(int(frame_num))
175
- track[BBOXES].append(make_bbox(best_match))
176
- track[CENTER].append(center(best_match))
177
- track[CONFIDENCE].append(best_match[CONFIDENCE])
178
- track[CLASS].append(best_match[CLASS])
179
- track[MAX_CONF] = max(track[MAX_CONF], best_match[CONFIDENCE])
180
- track[AGE] = 0
181
-
182
- updated_tracks.append(track)
183
-
184
- # remove best matching detection from detections
185
- del dets[dets.index(best_match)]
186
- # best_match[TRACK_ID] = track[TRACK_ID]
187
- best_match[FIRST] = False
188
- new_detections[frame_num][track[TRACK_ID]] = best_match
189
-
190
- # if track was not updated
191
- if not updated_tracks or track is not updated_tracks[-1]:
192
- # finish track when the conditions are met
193
- if track[AGE] < t_miss_max:
194
- track[AGE] += 1
195
- saved_tracks.append(track)
196
- elif (
197
- track[MAX_CONF] >= sigma_h
198
- and track[FRAMES][-1] - track[FRAMES][0] >= t_min
199
- ):
200
- # tracks_finished.append(track)
201
- vehIDs_finished.append(track[TRACK_ID])
202
- # TODO: Alter der Tracks
203
- # create new tracks
204
- new_tracks = []
205
- for det in dets:
206
- vehID = next(vehicle_id_generator)
207
- new_tracks.append(
208
- {
209
- FRAMES: [int(frame_num)],
210
- BBOXES: [make_bbox(det)],
211
- CENTER: [center(det)],
212
- CONFIDENCE: [det[CONFIDENCE]],
213
- CLASS: [det[CLASS]],
214
- MAX_CLASS: det[CLASS],
215
- MAX_CONF: det[CONFIDENCE],
216
- TRACK_ID: vehID,
217
- START_FRAME: int(frame_num),
218
- AGE: 0,
219
- }
220
- )
221
- # det[TRACK_ID] = vehID
222
- det[FIRST] = True
223
- new_detections[frame_num][vehID] = det
224
- tracks_active = updated_tracks + saved_tracks + new_tracks
225
-
226
- # finish all remaining active tracks
227
- # tracks_finished += [
228
- # track
229
- # for track in tracks_active
230
- # if (
231
- # track["max_conf"] >= sigma_h
232
- # and track["frames"][-1] - track["frames"][0] >= t_min
233
- # )
234
- # ]
235
-
236
- # for track in tracks_finished:
237
- # track["max_class"] = pd.Series(track["class"]).mode().iat[0]
238
-
239
- # TODO: #82 Use dict comprehensions in track_iou
240
- # save last occurrence frame of tracks
241
- last_track_frame: dict[int, int] = defaultdict(lambda: -1)
242
-
243
- for frame_num, frame_det in tqdm(
244
- new_detections.items(), desc="New detection frames", unit=" frames"
245
- ):
246
- for vehID, det in frame_det.items():
247
- det[FINISHED] = False
248
- det[TRACK_ID] = vehID
249
- last_track_frame[vehID] = max(frame_num, last_track_frame[vehID])
250
-
251
- # return tracks_finished
252
- # TODO: #83 Remove unnecessary code (e.g. for tracks_finished) from track_iou
253
-
254
- active_track_ids = {t[TRACK_ID] for t in tracks_active}
255
- detected_ids = set(last_track_frame.keys())
256
- return TrackingResult(
257
- TrackedDetections(
258
- detections=new_detections,
259
- detected_ids=detected_ids,
260
- active_track_ids={_id for _id in detected_ids if _id in active_track_ids},
261
- ),
262
- active_tracks=tracks_active,
263
- last_track_frame=last_track_frame,
264
- )
265
- # return new_detections, tracks_active, last_track_frame
266
-
267
-
268
- def _check_types(
269
- sigma_l: float, sigma_h: float, sigma_iou: float, t_min: int, t_miss_max: int
270
- ) -> None:
271
- """Raise ValueErrors if wrong types"""
272
-
273
- if not isinstance(sigma_l, (int, float)):
274
- raise ValueError("sigma_l has to be int or float")
275
- if not isinstance(sigma_h, (int, float)):
276
- raise ValueError("sigma_h has to be int or float")
277
- if not isinstance(sigma_iou, (int, float)):
278
- raise ValueError("sigma_iou has to be int or float")
279
- if not isinstance(t_min, int):
280
- raise ValueError("t_min has to be int")
281
- if not isinstance(t_miss_max, int):
282
- raise ValueError("t_miss_max has to be int")