supervisely 6.73.418__py3-none-any.whl → 6.73.419__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 (50) hide show
  1. supervisely/api/entity_annotation/figure_api.py +89 -45
  2. supervisely/nn/inference/inference.py +61 -45
  3. supervisely/nn/inference/instance_segmentation/instance_segmentation.py +1 -0
  4. supervisely/nn/inference/object_detection/object_detection.py +1 -0
  5. supervisely/nn/inference/session.py +4 -4
  6. supervisely/nn/model/model_api.py +31 -20
  7. supervisely/nn/model/prediction.py +11 -0
  8. supervisely/nn/model/prediction_session.py +33 -6
  9. supervisely/nn/tracker/__init__.py +1 -2
  10. supervisely/nn/tracker/base_tracker.py +44 -0
  11. supervisely/nn/tracker/botsort/__init__.py +1 -0
  12. supervisely/nn/tracker/botsort/botsort_config.yaml +31 -0
  13. supervisely/nn/tracker/botsort/osnet_reid/osnet.py +566 -0
  14. supervisely/nn/tracker/botsort/osnet_reid/osnet_reid_interface.py +88 -0
  15. supervisely/nn/tracker/botsort/tracker/__init__.py +0 -0
  16. supervisely/nn/tracker/{bot_sort → botsort/tracker}/basetrack.py +1 -2
  17. supervisely/nn/tracker/{utils → botsort/tracker}/gmc.py +51 -59
  18. supervisely/nn/tracker/{deep_sort/deep_sort → botsort/tracker}/kalman_filter.py +71 -33
  19. supervisely/nn/tracker/botsort/tracker/matching.py +202 -0
  20. supervisely/nn/tracker/{bot_sort/bot_sort.py → botsort/tracker/mc_bot_sort.py} +68 -81
  21. supervisely/nn/tracker/botsort_tracker.py +259 -0
  22. supervisely/project/project.py +1 -1
  23. {supervisely-6.73.418.dist-info → supervisely-6.73.419.dist-info}/METADATA +3 -1
  24. {supervisely-6.73.418.dist-info → supervisely-6.73.419.dist-info}/RECORD +29 -42
  25. supervisely/nn/tracker/bot_sort/__init__.py +0 -21
  26. supervisely/nn/tracker/bot_sort/fast_reid_interface.py +0 -152
  27. supervisely/nn/tracker/bot_sort/matching.py +0 -127
  28. supervisely/nn/tracker/bot_sort/sly_tracker.py +0 -401
  29. supervisely/nn/tracker/deep_sort/__init__.py +0 -6
  30. supervisely/nn/tracker/deep_sort/deep_sort/__init__.py +0 -1
  31. supervisely/nn/tracker/deep_sort/deep_sort/detection.py +0 -49
  32. supervisely/nn/tracker/deep_sort/deep_sort/iou_matching.py +0 -81
  33. supervisely/nn/tracker/deep_sort/deep_sort/linear_assignment.py +0 -202
  34. supervisely/nn/tracker/deep_sort/deep_sort/nn_matching.py +0 -176
  35. supervisely/nn/tracker/deep_sort/deep_sort/track.py +0 -166
  36. supervisely/nn/tracker/deep_sort/deep_sort/tracker.py +0 -145
  37. supervisely/nn/tracker/deep_sort/deep_sort.py +0 -301
  38. supervisely/nn/tracker/deep_sort/generate_clip_detections.py +0 -90
  39. supervisely/nn/tracker/deep_sort/preprocessing.py +0 -70
  40. supervisely/nn/tracker/deep_sort/sly_tracker.py +0 -273
  41. supervisely/nn/tracker/tracker.py +0 -285
  42. supervisely/nn/tracker/utils/kalman_filter.py +0 -492
  43. supervisely/nn/tracking/__init__.py +0 -1
  44. supervisely/nn/tracking/boxmot.py +0 -114
  45. supervisely/nn/tracking/tracking.py +0 -24
  46. /supervisely/nn/tracker/{utils → botsort/osnet_reid}/__init__.py +0 -0
  47. {supervisely-6.73.418.dist-info → supervisely-6.73.419.dist-info}/LICENSE +0 -0
  48. {supervisely-6.73.418.dist-info → supervisely-6.73.419.dist-info}/WHEEL +0 -0
  49. {supervisely-6.73.418.dist-info → supervisely-6.73.419.dist-info}/entry_points.txt +0 -0
  50. {supervisely-6.73.418.dist-info → supervisely-6.73.419.dist-info}/top_level.txt +0 -0
@@ -1,127 +0,0 @@
1
- import lap # pylint: disable=import-error
2
- import numpy as np
3
- from cython_bbox import bbox_overlaps as bbox_ious # pylint: disable=import-error
4
- from scipy.spatial.distance import cdist
5
-
6
- from supervisely.nn.tracker.utils import kalman_filter
7
-
8
-
9
- def linear_assignment(cost_matrix, thresh):
10
- if cost_matrix.size == 0:
11
- return (
12
- np.empty((0, 2), dtype=int),
13
- tuple(range(cost_matrix.shape[0])),
14
- tuple(range(cost_matrix.shape[1])),
15
- )
16
- matches, unmatched_a, unmatched_b = [], [], []
17
- cost, x, y = lap.lapjv(cost_matrix, extend_cost=True, cost_limit=thresh)
18
- for ix, mx in enumerate(x):
19
- if mx >= 0:
20
- matches.append([ix, mx])
21
- unmatched_a = np.where(x < 0)[0]
22
- unmatched_b = np.where(y < 0)[0]
23
- matches = np.asarray(matches)
24
- return matches, unmatched_a, unmatched_b
25
-
26
-
27
- def ious(atlbrs, btlbrs):
28
- """
29
- Compute cost based on IoU
30
- :type atlbrs: list[tlbr] | np.ndarray
31
- :type atlbrs: list[tlbr] | np.ndarray
32
-
33
- :rtype ious np.ndarray
34
- """
35
- ious = np.zeros((len(atlbrs), len(btlbrs)), dtype=float)
36
- if ious.size == 0:
37
- return ious
38
-
39
- ious = bbox_ious(
40
- np.ascontiguousarray(atlbrs, dtype=float), np.ascontiguousarray(btlbrs, dtype=float)
41
- )
42
-
43
- return ious
44
-
45
-
46
- def iou_distance(atracks, btracks):
47
- """
48
- Compute cost based on IoU
49
- :type atracks: list[STrack]
50
- :type btracks: list[STrack]
51
-
52
- :rtype cost_matrix np.ndarray
53
- """
54
-
55
- if (len(atracks) > 0 and isinstance(atracks[0], np.ndarray)) or (
56
- len(btracks) > 0 and isinstance(btracks[0], np.ndarray)
57
- ):
58
- atlbrs = atracks
59
- btlbrs = btracks
60
- else:
61
- atlbrs = [track.tlbr for track in atracks]
62
- btlbrs = [track.tlbr for track in btracks]
63
- _ious = ious(atlbrs, btlbrs)
64
- cost_matrix = 1 - _ious
65
-
66
- return cost_matrix
67
-
68
-
69
- def embedding_distance(tracks, detections, metric="cosine"):
70
- """
71
- :param tracks: list[STrack]
72
- :param detections: list[BaseTrack]
73
- :param metric:
74
- :return: cost_matrix np.ndarray
75
- """
76
-
77
- cost_matrix = np.zeros((len(tracks), len(detections)), dtype=float)
78
- if cost_matrix.size == 0:
79
- return cost_matrix
80
- det_features = np.asarray([track.curr_feat for track in detections], dtype=float)
81
- track_features = np.asarray([track.smooth_feat for track in tracks], dtype=float)
82
-
83
- cost_matrix = np.maximum(
84
- 0.0, cdist(track_features, det_features, metric)
85
- ) # / 2.0 # Nomalized features
86
- return cost_matrix
87
-
88
-
89
- def fuse_motion(kf, cost_matrix, tracks, detections, only_position=False, lambda_=0.98):
90
- if cost_matrix.size == 0:
91
- return cost_matrix
92
- gating_dim = 2 if only_position else 4
93
- gating_threshold = kalman_filter.chi2inv95[gating_dim]
94
- # measurements = np.asarray([det.to_xyah() for det in detections])
95
- measurements = np.asarray([det.to_xywh() for det in detections])
96
- for row, track in enumerate(tracks):
97
- gating_distance = kf.gating_distance(
98
- track.mean, track.covariance, measurements, only_position, metric="maha"
99
- )
100
- cost_matrix[row, gating_distance > gating_threshold] = np.inf
101
- cost_matrix[row] = lambda_ * cost_matrix[row] + (1 - lambda_) * gating_distance
102
- return cost_matrix
103
-
104
-
105
- def fuse_iou(cost_matrix, tracks, detections):
106
- if cost_matrix.size == 0:
107
- return cost_matrix
108
- reid_sim = 1 - cost_matrix
109
- iou_dist = iou_distance(tracks, detections)
110
- iou_sim = 1 - iou_dist
111
- fuse_sim = reid_sim * (1 + iou_sim) / 2
112
- det_scores = np.array([det.score for det in detections])
113
- det_scores = np.expand_dims(det_scores, axis=0).repeat(cost_matrix.shape[0], axis=0)
114
- # fuse_sim = fuse_sim * (1 + det_scores) / 2
115
- fuse_cost = 1 - fuse_sim
116
- return fuse_cost
117
-
118
-
119
- def fuse_score(cost_matrix, detections):
120
- if cost_matrix.size == 0:
121
- return cost_matrix
122
- iou_sim = 1 - cost_matrix
123
- det_scores = np.array([det.score for det in detections])
124
- det_scores = np.expand_dims(det_scores, axis=0).repeat(cost_matrix.shape[0], axis=0)
125
- fuse_sim = iou_sim * det_scores
126
- fuse_cost = 1 - fuse_sim
127
- return fuse_cost
@@ -1,401 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from pathlib import Path
4
- from typing import Dict, List, Tuple, Union
5
-
6
- import numpy as np
7
- import requests
8
-
9
- from supervisely import Annotation, Label
10
- from supervisely.nn.tracker.tracker import BaseDetection as Detection
11
- from supervisely.nn.tracker.tracker import BaseTrack, BaseTracker
12
- from supervisely.sly_logger import logger
13
-
14
- from . import matching
15
- from .bot_sort import (
16
- BoTSORT,
17
- STrack,
18
- TrackState,
19
- joint_stracks,
20
- remove_duplicate_stracks,
21
- sub_stracks,
22
- )
23
-
24
-
25
- class Track(BaseTrack, STrack):
26
- def __init__(self, tlwh, confidence: float, feature=None, sly_label: Label = None):
27
- self.track_id = None
28
- STrack.__init__(self, tlwh, confidence, sly_label.obj_class.name, feature)
29
- self.sly_label = sly_label
30
-
31
- def get_sly_label(self):
32
- return self.sly_label
33
-
34
- def update(self, new: Track, frame_id):
35
- STrack.update(self, new, frame_id)
36
- self.sly_label = new.get_sly_label()
37
-
38
- def re_activate(self, new: Track, frame_id, new_id=False):
39
- STrack.re_activate(self, new, frame_id, new_id)
40
- self.sly_label = new.get_sly_label()
41
-
42
-
43
- class _BoTSORT(BoTSORT):
44
- def __init__(self, args):
45
- super().__init__(args)
46
-
47
- # for type hinting
48
- self.tracked_stracks = [] # type: list[Track]
49
- self.lost_stracks = [] # type: list[Track]
50
- self.removed_stracks = [] # type: list[Track]
51
-
52
- # for pylint
53
- self.frame_id = 0
54
-
55
- def _init_track(
56
- self, dets: np.ndarray, scores: np.ndarray, labels: List[Label], features: np.ndarray
57
- ):
58
- if len(dets) == 0:
59
- return []
60
- if self.args.with_reid:
61
- return [
62
- Track(Track.tlbr_to_tlwh(tlbr), s, f, l)
63
- for (tlbr, s, f, l) in zip(dets, scores, features, labels)
64
- ]
65
- return [
66
- Track(Track.tlbr_to_tlwh(tlbr), s, None, l)
67
- for (tlbr, s, l) in zip(dets, scores, labels)
68
- ]
69
-
70
- def get_dists(self, tracks: List[Track], detections: List[Track]):
71
- # Associate with high score detection boxes
72
- ious_dists = matching.iou_distance(tracks, detections)
73
-
74
- # if not self.args.mot20:
75
- ious_dists = matching.fuse_score(ious_dists, detections)
76
-
77
- # Remove detections with different shapes
78
- for track_i, track in enumerate(tracks):
79
- for det_i, det in enumerate(detections):
80
- if track.get_sly_label().geometry.name() != det.get_sly_label().geometry.name():
81
- ious_dists[track_i, det_i] = 1.0
82
-
83
- if not self.args.with_reid:
84
- return ious_dists
85
-
86
- ious_dists_mask = ious_dists > self.args.proximity_thresh
87
- emb_dists = matching.embedding_distance(tracks, detections) / 2.0
88
- # raw_emb_dists = emb_dists.copy()
89
- emb_dists[emb_dists > self.args.appearance_thresh] = 1.0
90
- emb_dists[ious_dists_mask] = 1.0
91
- dists = np.minimum(ious_dists, emb_dists)
92
-
93
- # Popular ReID method (JDE / FairMOT)
94
- # raw_emb_dists = matching.embedding_distance(strack_pool, detections)
95
- # dists = matching.fuse_motion(self.kalman_filter, raw_emb_dists, strack_pool, detections)
96
- # emb_dists = dists
97
-
98
- # IoU making ReID
99
- # dists = matching.embedding_distance(strack_pool, detections)
100
- # dists[ious_dists_mask] = 1.0
101
- return dists
102
-
103
- def update(self, detections_: List[Detection], img) -> List[Track]:
104
- self.frame_id += 1
105
- activated_starcks = []
106
- refind_stracks = []
107
- lost_stracks = []
108
- removed_stracks = []
109
-
110
- if len(detections_):
111
- scores = np.array([det.confidence for det in detections_])
112
- bboxes = np.array([det.tlbr()[:4] for det in detections_])
113
- labels = np.array([det.sly_label for det in detections_])
114
- features = np.array([det.feature for det in detections_])
115
-
116
- # Remove bad detections
117
- lowest_inds = scores > self.args.track_low_thresh
118
- bboxes = bboxes[lowest_inds]
119
- scores = scores[lowest_inds]
120
- labels = labels[lowest_inds]
121
- features = features[lowest_inds]
122
-
123
- # Find high threshold detections
124
- remain_inds = scores > self.args.track_high_thresh
125
- dets = bboxes[remain_inds]
126
- scores_keep = scores[remain_inds]
127
- labels_keep = labels[remain_inds]
128
- features_keep = features[remain_inds]
129
- else:
130
- bboxes = []
131
- scores = []
132
- labels = []
133
- dets = []
134
- scores_keep = []
135
- labels_keep = []
136
- features_keep = []
137
-
138
- """Extract embeddings """
139
- if self.args.with_reid:
140
- features_keep = self.encoder.inference(img, dets)
141
-
142
- """Detections"""
143
- detections = self._init_track(dets, scores_keep, labels_keep, features_keep)
144
-
145
- """ Add newly detected tracklets to tracked_stracks"""
146
- unconfirmed = []
147
- tracked_stracks = [] # type: list[STrack]
148
- for track in self.tracked_stracks:
149
- if not track.is_activated:
150
- unconfirmed.append(track)
151
- else:
152
- tracked_stracks.append(track)
153
-
154
- """ Step 2: First association, with high score detection boxes"""
155
- strack_pool: List[Track] = joint_stracks(tracked_stracks, self.lost_stracks)
156
-
157
- # Predict the current location with KF
158
- STrack.multi_predict(strack_pool)
159
-
160
- # Fix camera motion
161
- warp = self.gmc.apply(img, dets)
162
- STrack.multi_gmc(strack_pool, warp)
163
- STrack.multi_gmc(unconfirmed, warp)
164
-
165
- dists = self.get_dists(strack_pool, detections)
166
-
167
- matches, u_track, u_detection = matching.linear_assignment(
168
- dists, thresh=self.args.match_thresh
169
- )
170
-
171
- for itracked, idet in matches:
172
- track: Track = strack_pool[itracked]
173
- det: Track = detections[idet]
174
- if track.state == TrackState.Tracked:
175
- track.update(detections[idet], self.frame_id)
176
- activated_starcks.append(track)
177
- else:
178
- track.re_activate(det, self.frame_id, new_id=False)
179
- refind_stracks.append(track)
180
-
181
- """ Step 3: Second association, with low score detection boxes"""
182
- if len(scores):
183
- inds_second = scores < self.args.track_high_thresh
184
- dets_second = bboxes[inds_second]
185
- scores_second = scores[inds_second]
186
- labels_second = labels[inds_second]
187
- features_second = features[inds_second]
188
- else:
189
- dets_second = []
190
- scores_second = []
191
- labels_second = []
192
- features_second = []
193
-
194
- # association the untrack to the low score detections
195
- detections_second = self._init_track(
196
- dets_second, scores_second, labels_second, features_second
197
- )
198
-
199
- r_tracked_stracks = [
200
- strack_pool[i] for i in u_track if strack_pool[i].state == TrackState.Tracked
201
- ]
202
- dists = matching.iou_distance(r_tracked_stracks, detections_second)
203
- matches, u_track, u_detection_second = matching.linear_assignment(dists, thresh=0.5)
204
- for itracked, idet in matches:
205
- track = r_tracked_stracks[itracked]
206
- det = detections_second[idet]
207
- if track.state == TrackState.Tracked:
208
- track.update(det, self.frame_id)
209
- activated_starcks.append(track)
210
- else:
211
- track.re_activate(det, self.frame_id, new_id=False)
212
- refind_stracks.append(track)
213
-
214
- for it in u_track:
215
- track = r_tracked_stracks[it]
216
- if not track.state == TrackState.Lost:
217
- track.mark_lost()
218
- lost_stracks.append(track)
219
-
220
- """Deal with unconfirmed tracks, usually tracks with only one beginning frame"""
221
- detections = [detections[i] for i in u_detection]
222
- dists = self.get_dists(unconfirmed, detections)
223
- matches, u_unconfirmed, u_detection = matching.linear_assignment(dists, thresh=0.7)
224
- for itracked, idet in matches:
225
- unconfirmed[itracked].update(detections[idet], self.frame_id)
226
- activated_starcks.append(unconfirmed[itracked])
227
- for it in u_unconfirmed:
228
- track = unconfirmed[it]
229
- track.mark_removed()
230
- removed_stracks.append(track)
231
-
232
- """ Step 4: Init new stracks"""
233
- for inew in u_detection:
234
- track = detections[inew]
235
- if track.score < self.new_track_thresh:
236
- continue
237
-
238
- track.activate(self.kalman_filter, self.frame_id)
239
- activated_starcks.append(track)
240
-
241
- """ Step 5: Update state"""
242
- for track in self.lost_stracks:
243
- if self.frame_id - track.end_frame > self.max_time_lost:
244
- track.mark_removed()
245
- removed_stracks.append(track)
246
-
247
- """ Merge """
248
- self.tracked_stracks = [t for t in self.tracked_stracks if t.state == TrackState.Tracked]
249
- self.tracked_stracks = joint_stracks(self.tracked_stracks, activated_starcks)
250
- self.tracked_stracks = joint_stracks(self.tracked_stracks, refind_stracks)
251
- self.lost_stracks = sub_stracks(self.lost_stracks, self.tracked_stracks)
252
- self.lost_stracks.extend(lost_stracks)
253
- self.lost_stracks = sub_stracks(self.lost_stracks, self.removed_stracks)
254
- self.removed_stracks.extend(removed_stracks)
255
- self.tracked_stracks, self.lost_stracks = remove_duplicate_stracks(
256
- self.tracked_stracks, self.lost_stracks
257
- )
258
-
259
- # output_stracks = [track for track in self.tracked_stracks if track.is_activated]
260
- output_stracks = [track for track in self.tracked_stracks]
261
-
262
- return output_stracks
263
-
264
-
265
- class BoTTracker(BaseTracker):
266
- def __init__(self, settings=None):
267
- if settings is None:
268
- settings = {}
269
- super().__init__(settings=settings)
270
- self.tracker = _BoTSORT(self.args)
271
-
272
- if self.args.with_reid:
273
- if not Path(self.args.fast_reid_weights).exists():
274
- logger.info("Downloading ReID weights...")
275
-
276
- with requests.get(self.args.fast_reid_weights_url, stream=True) as r:
277
- r.raise_for_status()
278
- with open(self.args.fast_reid_weights, "wb") as f:
279
- for chunk in r.iter_content(chunk_size=8192):
280
- f.write(chunk)
281
-
282
- def default_settings(self):
283
- return {
284
- "name": None,
285
- "ablation": None,
286
- # Tracking
287
- "track_high_thresh": 0.6,
288
- "track_low_thresh": 0.1,
289
- "new_track_thresh": 0.7,
290
- "track_buffer": 30,
291
- "match_thresh": 0.5,
292
- "min_box_area": 10,
293
- "fuse_score": False,
294
- # CMC
295
- "cmc_method": "sparseOptFlow",
296
- "gmc_config": None,
297
- # ReID
298
- "with_reid": False,
299
- "fast_reid_config": f"{Path(__file__).parent}/fast_reid/configs/MOT17/sbs_S50.yml",
300
- "fast_reid_weights": f"pretrained/yolo7x.pt",
301
- "fast_reid_weights_url": r"https://github.com/WongKinYiu/yolov7/releases/download/v0.1/yolov7.pt",
302
- "proximity_thresh": 0.5,
303
- "appearance_thresh": 0.25,
304
- }
305
-
306
- def track(
307
- self,
308
- source: Union[List[np.ndarray], List[str], str],
309
- frame_to_annotation: Dict[int, Annotation],
310
- frame_shape: Tuple[int, int],
311
- pbar_cb=None,
312
- ) -> Annotation:
313
- """
314
- Track objects in the video using BoTSort algorithm.
315
-
316
- :param source: List of images, paths to images or path to the video file.
317
- :type source: List[np.ndarray] | List[str] | str
318
- :param frame_to_annotation: Dictionary with frame index as key and Annotation as value.
319
- :type frame_to_annotation: Dict[int, Annotation]
320
- :param frame_shape: Size of the frame (height, width).
321
- :type frame_shape: Tuple[int, int]
322
- :param pbar_cb: Callback to update progress bar.
323
- :type pbar_cb: Callable, optional
324
-
325
- :return: Video annotation with tracked objects.
326
- :rtype: VideoAnnotation
327
-
328
- :raises ValueError: If number of images and annotations are not the same.
329
-
330
- :Usage example:
331
-
332
- .. code-block:: python
333
-
334
- import supervisely as sly
335
- from supervisely.nn.tracker import BoTTracker
336
-
337
- api = sly.Api()
338
-
339
- project_id = 12345
340
- video_id = 12345678
341
- video_path = "video.mp4"
342
-
343
- # Download video and get video info
344
- video_info = api.video.get_info_by_id(video_id)
345
- frame_shape = (video_info.frame_height, video_info.frame_width)
346
- api.video.download_path(id=video_id, path=video_path)
347
-
348
- # Run inference app to get detections
349
- task_id = 12345 # detection app task id
350
- session = sly.nn.inference.Session(api, task_id)
351
- annotations = session.inference_video_id(video_id, 0, video_info.frames_count)
352
- frame_to_annotation = {i: ann for i, ann in enumerate(annotations)}
353
-
354
- # Run tracker
355
- tracker = BoTTracker()
356
- video_ann = tracker.track(video_path, frame_to_annotation, frame_shape)
357
-
358
- # Upload result
359
- model_meta = session.get_model_meta()
360
- project_meta = sly.ProjectMeta.from_json(api.project.get_meta(project_id))
361
- project_meta = project_meta.merge(model_meta)
362
- api.project.update_meta(project_id, project_meta)
363
- api.video.annotation.append(video_id, video_ann)
364
-
365
- """
366
- if not isinstance(source, str):
367
- if len(source) != len(frame_to_annotation):
368
- raise ValueError("Number of images and annotations should be the same")
369
-
370
- tracks_data = {}
371
- logger.info("Starting BoTSort tracking...")
372
- for frame_index, img in enumerate(self.frames_generator(source)):
373
- self.update(img, frame_to_annotation[frame_index], frame_index, tracks_data=tracks_data)
374
-
375
- if pbar_cb is not None:
376
- pbar_cb()
377
-
378
- return self.get_annotation(
379
- tracks_data=tracks_data,
380
- frame_shape=frame_shape,
381
- frames_count=len(frame_to_annotation),
382
- )
383
-
384
- def update(
385
- self, img, annotation: Annotation, frame_index, tracks_data: Dict[int, List[Dict]] = None
386
- ):
387
- pred, sly_labels = self.convert_annotation(annotation)
388
-
389
- detections = [Detection(p[:4], p[4], None, label) for p, label in zip(pred, sly_labels)]
390
-
391
- self.tracker.update(detections, img)
392
-
393
- if tracks_data is None:
394
- tracks_data = {}
395
- self.update_track_data(
396
- tracks_data=tracks_data,
397
- tracks=self.tracker.tracked_stracks,
398
- frame_index=frame_index,
399
- )
400
-
401
- return tracks_data
@@ -1,6 +0,0 @@
1
- import pip
2
-
3
- # install clip
4
- pip.main(["install", "git+https://github.com/supervisely-ecosystem/depends-CLIP.git"])
5
-
6
- from .sly_tracker import DeepSortTracker
@@ -1 +0,0 @@
1
- # vim: expandtab:ts=4:sw=4
@@ -1,49 +0,0 @@
1
- # vim: expandtab:ts=4:sw=4
2
- import numpy as np
3
-
4
-
5
- class Detection(object):
6
- """
7
- This class represents a bounding box detection in a single image.
8
-
9
- Parameters
10
- ----------
11
- tlwh : array_like
12
- Bounding box in format `(x, y, w, h)`.
13
- confidence : float
14
- Detector confidence score.
15
- feature : array_like
16
- A feature vector that describes the object contained in this image.
17
-
18
- Attributes
19
- ----------
20
- tlwh : ndarray
21
- Bounding box in format `(top left x, top left y, width, height)`.
22
- confidence : ndarray
23
- Detector confidence score.
24
- feature : ndarray | NoneType
25
- A feature vector that describes the object contained in this image.
26
-
27
- """
28
-
29
- def __init__(self, tlwh, confidence, feature):
30
- self.tlwh = np.asarray(tlwh, dtype=float)
31
- self.confidence = float(confidence)
32
- self.feature = np.asarray(feature, dtype=np.float32)
33
-
34
- def to_tlbr(self):
35
- """Convert bounding box to format `(min x, min y, max x, max y)`, i.e.,
36
- `(top left, bottom right)`.
37
- """
38
- ret = self.tlwh.copy()
39
- ret[2:] += ret[:2]
40
- return ret
41
-
42
- def to_xyah(self):
43
- """Convert bounding box to format `(center x, center y, aspect ratio,
44
- height)`, where the aspect ratio is `width / height`.
45
- """
46
- ret = self.tlwh.copy()
47
- ret[:2] += ret[2:] / 2
48
- ret[2] /= ret[3]
49
- return ret
@@ -1,81 +0,0 @@
1
- # vim: expandtab:ts=4:sw=4
2
- from __future__ import absolute_import
3
- import numpy as np
4
- from . import linear_assignment
5
-
6
-
7
- def iou(bbox, candidates):
8
- """Computer intersection over union.
9
-
10
- Parameters
11
- ----------
12
- bbox : ndarray
13
- A bounding box in format `(top left x, top left y, width, height)`.
14
- candidates : ndarray
15
- A matrix of candidate bounding boxes (one per row) in the same format
16
- as `bbox`.
17
-
18
- Returns
19
- -------
20
- ndarray
21
- The intersection over union in [0, 1] between the `bbox` and each
22
- candidate. A higher score means a larger fraction of the `bbox` is
23
- occluded by the candidate.
24
-
25
- """
26
- bbox_tl, bbox_br = bbox[:2], bbox[:2] + bbox[2:]
27
- candidates_tl = candidates[:, :2]
28
- candidates_br = candidates[:, :2] + candidates[:, 2:]
29
-
30
- tl = np.c_[np.maximum(bbox_tl[0], candidates_tl[:, 0])[:, np.newaxis],
31
- np.maximum(bbox_tl[1], candidates_tl[:, 1])[:, np.newaxis]]
32
- br = np.c_[np.minimum(bbox_br[0], candidates_br[:, 0])[:, np.newaxis],
33
- np.minimum(bbox_br[1], candidates_br[:, 1])[:, np.newaxis]]
34
- wh = np.maximum(0., br - tl)
35
-
36
- area_intersection = wh.prod(axis=1)
37
- area_bbox = bbox[2:].prod()
38
- area_candidates = candidates[:, 2:].prod(axis=1)
39
- return area_intersection / (area_bbox + area_candidates - area_intersection)
40
-
41
-
42
- def iou_cost(tracks, detections, track_indices=None,
43
- detection_indices=None):
44
- """An intersection over union distance metric.
45
-
46
- Parameters
47
- ----------
48
- tracks : List[deep_sort.track.Track]
49
- A list of tracks.
50
- detections : List[deep_sort.detection.Detection]
51
- A list of detections.
52
- track_indices : Optional[List[int]]
53
- A list of indices to tracks that should be matched. Defaults to
54
- all `tracks`.
55
- detection_indices : Optional[List[int]]
56
- A list of indices to detections that should be matched. Defaults
57
- to all `detections`.
58
-
59
- Returns
60
- -------
61
- ndarray
62
- Returns a cost matrix of shape
63
- len(track_indices), len(detection_indices) where entry (i, j) is
64
- `1 - iou(tracks[track_indices[i]], detections[detection_indices[j]])`.
65
-
66
- """
67
- if track_indices is None:
68
- track_indices = np.arange(len(tracks))
69
- if detection_indices is None:
70
- detection_indices = np.arange(len(detections))
71
-
72
- cost_matrix = np.zeros((len(track_indices), len(detection_indices)))
73
- for row, track_idx in enumerate(track_indices):
74
- if tracks[track_idx].time_since_update > 1:
75
- cost_matrix[row, :] = linear_assignment.INFTY_COST
76
- continue
77
-
78
- bbox = tracks[track_idx].to_tlwh()
79
- candidates = np.asarray([detections[i].tlwh for i in detection_indices])
80
- cost_matrix[row, :] = 1. - iou(bbox, candidates)
81
- return cost_matrix