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.
- OTVision/__init__.py +0 -10
- OTVision/application/detect/current_object_detector.py +1 -2
- OTVision/application/detect/detected_frame_factory.py +4 -3
- OTVision/application/detect/detected_frame_producer.py +1 -2
- OTVision/detect/builder.py +1 -1
- OTVision/detect/detected_frame_buffer.py +1 -1
- OTVision/detect/otdet.py +3 -2
- OTVision/detect/yolo.py +2 -2
- OTVision/domain/detect_producer_consumer.py +1 -1
- OTVision/domain/detection.py +128 -7
- OTVision/domain/frame.py +146 -1
- OTVision/domain/object_detection.py +1 -2
- OTVision/helpers/files.py +10 -2
- OTVision/helpers/input_types.py +15 -0
- OTVision/track/exporter/__init__.py +0 -0
- OTVision/track/exporter/filebased_exporter.py +24 -0
- OTVision/track/model/__init__.py +0 -0
- OTVision/track/model/filebased/__init__.py +0 -0
- OTVision/track/model/filebased/frame_chunk.py +203 -0
- OTVision/track/model/filebased/frame_group.py +95 -0
- OTVision/track/model/track_exporter.py +119 -0
- OTVision/track/model/tracking_interfaces.py +303 -0
- OTVision/track/parser/__init__.py +0 -0
- OTVision/track/parser/chunk_parser_plugins.py +99 -0
- OTVision/track/parser/frame_group_parser_plugins.py +127 -0
- OTVision/track/track.py +54 -332
- OTVision/track/tracker/__init__.py +0 -0
- OTVision/track/tracker/filebased_tracking.py +192 -0
- OTVision/track/tracker/tracker_plugin_iou.py +224 -0
- OTVision/version.py +1 -1
- OTVision/view/view_track.py +1 -1
- {otvision-0.6.1.dist-info → otvision-0.6.3.dist-info}/METADATA +8 -6
- {otvision-0.6.1.dist-info → otvision-0.6.3.dist-info}/RECORD +35 -23
- OTVision/track/iou.py +0 -282
- OTVision/track/iou_util.py +0 -140
- OTVision/track/preprocess.py +0 -453
- {otvision-0.6.1.dist-info → otvision-0.6.3.dist-info}/WHEEL +0 -0
- {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
OTVision/view/view_track.py
CHANGED
|
@@ -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.
|
|
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.
|
|
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:
|
|
26
|
-
Requires-Dist:
|
|
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.
|
|
33
|
+
Requires-Dist: tqdm==4.67.1
|
|
32
34
|
Requires-Dist: ujson==5.10.0
|
|
33
|
-
Requires-Dist: ultralytics==8.3.
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
19
|
-
OTVision/application/detect/detected_frame_producer.py,sha256=
|
|
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=
|
|
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=
|
|
32
|
-
OTVision/detect/otdet.py,sha256=
|
|
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=
|
|
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=
|
|
44
|
-
OTVision/domain/detection.py,sha256=
|
|
45
|
-
OTVision/domain/frame.py,sha256=
|
|
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=
|
|
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=
|
|
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/
|
|
57
|
-
OTVision/track/
|
|
58
|
-
OTVision/track/
|
|
59
|
-
OTVision/track/
|
|
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=
|
|
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.
|
|
73
|
-
otvision-0.6.
|
|
74
|
-
otvision-0.6.
|
|
75
|
-
otvision-0.6.
|
|
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")
|