ultralytics 8.2.76__py3-none-any.whl → 8.2.77__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.
Potentially problematic release.
This version of ultralytics might be problematic. Click here for more details.
- ultralytics/__init__.py +1 -1
- ultralytics/cfg/__init__.py +1 -1
- ultralytics/engine/results.py +19 -5
- ultralytics/engine/trainer.py +3 -1
- ultralytics/models/yolo/detect/train.py +1 -1
- ultralytics/trackers/basetrack.py +31 -12
- ultralytics/trackers/bot_sort.py +58 -24
- ultralytics/trackers/byte_tracker.py +75 -42
- ultralytics/trackers/track.py +17 -2
- ultralytics/trackers/utils/gmc.py +52 -38
- ultralytics/trackers/utils/kalman_filter.py +162 -31
- ultralytics/trackers/utils/matching.py +38 -14
- ultralytics/utils/__init__.py +1 -1
- ultralytics/utils/files.py +69 -34
- ultralytics/utils/plotting.py +11 -3
- {ultralytics-8.2.76.dist-info → ultralytics-8.2.77.dist-info}/METADATA +2 -2
- {ultralytics-8.2.76.dist-info → ultralytics-8.2.77.dist-info}/RECORD +21 -21
- {ultralytics-8.2.76.dist-info → ultralytics-8.2.77.dist-info}/WHEEL +1 -1
- {ultralytics-8.2.76.dist-info → ultralytics-8.2.77.dist-info}/LICENSE +0 -0
- {ultralytics-8.2.76.dist-info → ultralytics-8.2.77.dist-info}/entry_points.txt +0 -0
- {ultralytics-8.2.76.dist-info → ultralytics-8.2.77.dist-info}/top_level.txt +0 -0
|
@@ -6,17 +6,49 @@ import scipy.linalg
|
|
|
6
6
|
|
|
7
7
|
class KalmanFilterXYAH:
|
|
8
8
|
"""
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
ratio a, height h, and their
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
A KalmanFilterXYAH class for tracking bounding boxes in image space using a Kalman filter.
|
|
10
|
+
|
|
11
|
+
Implements a simple Kalman filter for tracking bounding boxes in image space. The 8-dimensional state space
|
|
12
|
+
(x, y, a, h, vx, vy, va, vh) contains the bounding box center position (x, y), aspect ratio a, height h, and their
|
|
13
|
+
respective velocities. Object motion follows a constant velocity model, and bounding box location (x, y, a, h) is
|
|
14
|
+
taken as a direct observation of the state space (linear observation model).
|
|
15
|
+
|
|
16
|
+
Attributes:
|
|
17
|
+
_motion_mat (np.ndarray): The motion matrix for the Kalman filter.
|
|
18
|
+
_update_mat (np.ndarray): The update matrix for the Kalman filter.
|
|
19
|
+
_std_weight_position (float): Standard deviation weight for position.
|
|
20
|
+
_std_weight_velocity (float): Standard deviation weight for velocity.
|
|
21
|
+
|
|
22
|
+
Methods:
|
|
23
|
+
initiate: Creates a track from an unassociated measurement.
|
|
24
|
+
predict: Runs the Kalman filter prediction step.
|
|
25
|
+
project: Projects the state distribution to measurement space.
|
|
26
|
+
multi_predict: Runs the Kalman filter prediction step (vectorized version).
|
|
27
|
+
update: Runs the Kalman filter correction step.
|
|
28
|
+
gating_distance: Computes the gating distance between state distribution and measurements.
|
|
29
|
+
|
|
30
|
+
Examples:
|
|
31
|
+
Initialize the Kalman filter and create a track from a measurement
|
|
32
|
+
>>> kf = KalmanFilterXYAH()
|
|
33
|
+
>>> measurement = np.array([100, 200, 1.5, 50])
|
|
34
|
+
>>> mean, covariance = kf.initiate(measurement)
|
|
35
|
+
>>> print(mean)
|
|
36
|
+
>>> print(covariance)
|
|
16
37
|
"""
|
|
17
38
|
|
|
18
39
|
def __init__(self):
|
|
19
|
-
"""
|
|
40
|
+
"""
|
|
41
|
+
Initialize Kalman filter model matrices with motion and observation uncertainty weights.
|
|
42
|
+
|
|
43
|
+
The Kalman filter is initialized with an 8-dimensional state space (x, y, a, h, vx, vy, va, vh), where (x, y)
|
|
44
|
+
represents the bounding box center position, 'a' is the aspect ratio, 'h' is the height, and their respective
|
|
45
|
+
velocities are (vx, vy, va, vh). The filter uses a constant velocity model for object motion and a linear
|
|
46
|
+
observation model for bounding box location.
|
|
47
|
+
|
|
48
|
+
Examples:
|
|
49
|
+
Initialize a Kalman filter for tracking:
|
|
50
|
+
>>> kf = KalmanFilterXYAH()
|
|
51
|
+
"""
|
|
20
52
|
ndim, dt = 4, 1.0
|
|
21
53
|
|
|
22
54
|
# Create Kalman filter model matrices
|
|
@@ -32,15 +64,20 @@ class KalmanFilterXYAH:
|
|
|
32
64
|
|
|
33
65
|
def initiate(self, measurement: np.ndarray) -> tuple:
|
|
34
66
|
"""
|
|
35
|
-
Create track from unassociated measurement.
|
|
67
|
+
Create a track from an unassociated measurement.
|
|
36
68
|
|
|
37
69
|
Args:
|
|
38
70
|
measurement (ndarray): Bounding box coordinates (x, y, a, h) with center position (x, y), aspect ratio a,
|
|
39
71
|
and height h.
|
|
40
72
|
|
|
41
73
|
Returns:
|
|
42
|
-
(tuple[ndarray, ndarray]): Returns the mean vector (8
|
|
74
|
+
(tuple[ndarray, ndarray]): Returns the mean vector (8-dimensional) and covariance matrix (8x8 dimensional)
|
|
43
75
|
of the new track. Unobserved velocities are initialized to 0 mean.
|
|
76
|
+
|
|
77
|
+
Examples:
|
|
78
|
+
>>> kf = KalmanFilterXYAH()
|
|
79
|
+
>>> measurement = np.array([100, 50, 1.5, 200])
|
|
80
|
+
>>> mean, covariance = kf.initiate(measurement)
|
|
44
81
|
"""
|
|
45
82
|
mean_pos = measurement
|
|
46
83
|
mean_vel = np.zeros_like(mean_pos)
|
|
@@ -64,12 +101,18 @@ class KalmanFilterXYAH:
|
|
|
64
101
|
Run Kalman filter prediction step.
|
|
65
102
|
|
|
66
103
|
Args:
|
|
67
|
-
mean (ndarray): The 8
|
|
68
|
-
covariance (ndarray): The 8x8
|
|
104
|
+
mean (ndarray): The 8-dimensional mean vector of the object state at the previous time step.
|
|
105
|
+
covariance (ndarray): The 8x8-dimensional covariance matrix of the object state at the previous time step.
|
|
69
106
|
|
|
70
107
|
Returns:
|
|
71
108
|
(tuple[ndarray, ndarray]): Returns the mean vector and covariance matrix of the predicted state. Unobserved
|
|
72
109
|
velocities are initialized to 0 mean.
|
|
110
|
+
|
|
111
|
+
Examples:
|
|
112
|
+
>>> kf = KalmanFilterXYAH()
|
|
113
|
+
>>> mean = np.array([0, 0, 1, 1, 0, 0, 0, 0])
|
|
114
|
+
>>> covariance = np.eye(8)
|
|
115
|
+
>>> predicted_mean, predicted_covariance = kf.predict(mean, covariance)
|
|
73
116
|
"""
|
|
74
117
|
std_pos = [
|
|
75
118
|
self._std_weight_position * mean[3],
|
|
@@ -100,6 +143,12 @@ class KalmanFilterXYAH:
|
|
|
100
143
|
|
|
101
144
|
Returns:
|
|
102
145
|
(tuple[ndarray, ndarray]): Returns the projected mean and covariance matrix of the given state estimate.
|
|
146
|
+
|
|
147
|
+
Examples:
|
|
148
|
+
>>> kf = KalmanFilterXYAH()
|
|
149
|
+
>>> mean = np.array([0, 0, 1, 1, 0, 0, 0, 0])
|
|
150
|
+
>>> covariance = np.eye(8)
|
|
151
|
+
>>> projected_mean, projected_covariance = kf.project(mean, covariance)
|
|
103
152
|
"""
|
|
104
153
|
std = [
|
|
105
154
|
self._std_weight_position * mean[3],
|
|
@@ -115,15 +164,21 @@ class KalmanFilterXYAH:
|
|
|
115
164
|
|
|
116
165
|
def multi_predict(self, mean: np.ndarray, covariance: np.ndarray) -> tuple:
|
|
117
166
|
"""
|
|
118
|
-
Run Kalman filter prediction step (Vectorized version).
|
|
167
|
+
Run Kalman filter prediction step for multiple object states (Vectorized version).
|
|
119
168
|
|
|
120
169
|
Args:
|
|
121
170
|
mean (ndarray): The Nx8 dimensional mean matrix of the object states at the previous time step.
|
|
122
171
|
covariance (ndarray): The Nx8x8 covariance matrix of the object states at the previous time step.
|
|
123
172
|
|
|
124
173
|
Returns:
|
|
125
|
-
(tuple[ndarray, ndarray]): Returns the mean
|
|
126
|
-
|
|
174
|
+
(tuple[ndarray, ndarray]): Returns the mean matrix and covariance matrix of the predicted states.
|
|
175
|
+
The mean matrix has shape (N, 8) and the covariance matrix has shape (N, 8, 8). Unobserved velocities
|
|
176
|
+
are initialized to 0 mean.
|
|
177
|
+
|
|
178
|
+
Examples:
|
|
179
|
+
>>> mean = np.random.rand(10, 8) # 10 object states
|
|
180
|
+
>>> covariance = np.random.rand(10, 8, 8) # Covariance matrices for 10 object states
|
|
181
|
+
>>> predicted_mean, predicted_covariance = kalman_filter.multi_predict(mean, covariance)
|
|
127
182
|
"""
|
|
128
183
|
std_pos = [
|
|
129
184
|
self._std_weight_position * mean[:, 3],
|
|
@@ -160,6 +215,13 @@ class KalmanFilterXYAH:
|
|
|
160
215
|
|
|
161
216
|
Returns:
|
|
162
217
|
(tuple[ndarray, ndarray]): Returns the measurement-corrected state distribution.
|
|
218
|
+
|
|
219
|
+
Examples:
|
|
220
|
+
>>> kf = KalmanFilterXYAH()
|
|
221
|
+
>>> mean = np.array([0, 0, 1, 1, 0, 0, 0, 0])
|
|
222
|
+
>>> covariance = np.eye(8)
|
|
223
|
+
>>> measurement = np.array([1, 1, 1, 1])
|
|
224
|
+
>>> new_mean, new_covariance = kf.update(mean, covariance, measurement)
|
|
163
225
|
"""
|
|
164
226
|
projected_mean, projected_cov = self.project(mean, covariance)
|
|
165
227
|
|
|
@@ -182,23 +244,31 @@ class KalmanFilterXYAH:
|
|
|
182
244
|
metric: str = "maha",
|
|
183
245
|
) -> np.ndarray:
|
|
184
246
|
"""
|
|
185
|
-
Compute gating distance between state distribution and measurements.
|
|
186
|
-
|
|
187
|
-
|
|
247
|
+
Compute gating distance between state distribution and measurements.
|
|
248
|
+
|
|
249
|
+
A suitable distance threshold can be obtained from `chi2inv95`. If `only_position` is False, the chi-square
|
|
250
|
+
distribution has 4 degrees of freedom, otherwise 2.
|
|
188
251
|
|
|
189
252
|
Args:
|
|
190
253
|
mean (ndarray): Mean vector over the state distribution (8 dimensional).
|
|
191
254
|
covariance (ndarray): Covariance of the state distribution (8x8 dimensional).
|
|
192
|
-
measurements (ndarray): An
|
|
193
|
-
|
|
194
|
-
only_position (bool
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
squared Euclidean distance and 'maha' for the squared Mahalanobis distance. Defaults to 'maha'.
|
|
255
|
+
measurements (ndarray): An (N, 4) matrix of N measurements, each in format (x, y, a, h) where (x, y) is the
|
|
256
|
+
bounding box center position, a the aspect ratio, and h the height.
|
|
257
|
+
only_position (bool): If True, distance computation is done with respect to box center position only.
|
|
258
|
+
metric (str): The metric to use for calculating the distance. Options are 'gaussian' for the squared
|
|
259
|
+
Euclidean distance and 'maha' for the squared Mahalanobis distance.
|
|
198
260
|
|
|
199
261
|
Returns:
|
|
200
262
|
(np.ndarray): Returns an array of length N, where the i-th element contains the squared distance between
|
|
201
263
|
(mean, covariance) and `measurements[i]`.
|
|
264
|
+
|
|
265
|
+
Examples:
|
|
266
|
+
Compute gating distance using Mahalanobis metric:
|
|
267
|
+
>>> kf = KalmanFilterXYAH()
|
|
268
|
+
>>> mean = np.array([0, 0, 1, 1, 0, 0, 0, 0])
|
|
269
|
+
>>> covariance = np.eye(8)
|
|
270
|
+
>>> measurements = np.array([[1, 1, 1, 1], [2, 2, 1, 1]])
|
|
271
|
+
>>> distances = kf.gating_distance(mean, covariance, measurements, only_position=False, metric='maha')
|
|
202
272
|
"""
|
|
203
273
|
mean, covariance = self.project(mean, covariance)
|
|
204
274
|
if only_position:
|
|
@@ -218,13 +288,33 @@ class KalmanFilterXYAH:
|
|
|
218
288
|
|
|
219
289
|
class KalmanFilterXYWH(KalmanFilterXYAH):
|
|
220
290
|
"""
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
The 8-dimensional state space (x, y, w, h, vx, vy, vw, vh) contains the bounding box center position (x, y), width
|
|
224
|
-
w, height h, and their respective velocities.
|
|
291
|
+
A KalmanFilterXYWH class for tracking bounding boxes in image space using a Kalman filter.
|
|
225
292
|
|
|
226
|
-
|
|
293
|
+
Implements a Kalman filter for tracking bounding boxes with state space (x, y, w, h, vx, vy, vw, vh), where
|
|
294
|
+
(x, y) is the center position, w is the width, h is the height, and vx, vy, vw, vh are their respective velocities.
|
|
295
|
+
The object motion follows a constant velocity model, and the bounding box location (x, y, w, h) is taken as a direct
|
|
227
296
|
observation of the state space (linear observation model).
|
|
297
|
+
|
|
298
|
+
Attributes:
|
|
299
|
+
_motion_mat (np.ndarray): The motion matrix for the Kalman filter.
|
|
300
|
+
_update_mat (np.ndarray): The update matrix for the Kalman filter.
|
|
301
|
+
_std_weight_position (float): Standard deviation weight for position.
|
|
302
|
+
_std_weight_velocity (float): Standard deviation weight for velocity.
|
|
303
|
+
|
|
304
|
+
Methods:
|
|
305
|
+
initiate: Creates a track from an unassociated measurement.
|
|
306
|
+
predict: Runs the Kalman filter prediction step.
|
|
307
|
+
project: Projects the state distribution to measurement space.
|
|
308
|
+
multi_predict: Runs the Kalman filter prediction step in a vectorized manner.
|
|
309
|
+
update: Runs the Kalman filter correction step.
|
|
310
|
+
|
|
311
|
+
Examples:
|
|
312
|
+
Create a Kalman filter and initialize a track
|
|
313
|
+
>>> kf = KalmanFilterXYWH()
|
|
314
|
+
>>> measurement = np.array([100, 50, 20, 40])
|
|
315
|
+
>>> mean, covariance = kf.initiate(measurement)
|
|
316
|
+
>>> print(mean)
|
|
317
|
+
>>> print(covariance)
|
|
228
318
|
"""
|
|
229
319
|
|
|
230
320
|
def initiate(self, measurement: np.ndarray) -> tuple:
|
|
@@ -237,6 +327,22 @@ class KalmanFilterXYWH(KalmanFilterXYAH):
|
|
|
237
327
|
Returns:
|
|
238
328
|
(tuple[ndarray, ndarray]): Returns the mean vector (8 dimensional) and covariance matrix (8x8 dimensional)
|
|
239
329
|
of the new track. Unobserved velocities are initialized to 0 mean.
|
|
330
|
+
|
|
331
|
+
Examples:
|
|
332
|
+
>>> kf = KalmanFilterXYWH()
|
|
333
|
+
>>> measurement = np.array([100, 50, 20, 40])
|
|
334
|
+
>>> mean, covariance = kf.initiate(measurement)
|
|
335
|
+
>>> print(mean)
|
|
336
|
+
[100. 50. 20. 40. 0. 0. 0. 0.]
|
|
337
|
+
>>> print(covariance)
|
|
338
|
+
[[ 4. 0. 0. 0. 0. 0. 0. 0.]
|
|
339
|
+
[ 0. 4. 0. 0. 0. 0. 0. 0.]
|
|
340
|
+
[ 0. 0. 4. 0. 0. 0. 0. 0.]
|
|
341
|
+
[ 0. 0. 0. 4. 0. 0. 0. 0.]
|
|
342
|
+
[ 0. 0. 0. 0. 0.25 0. 0. 0.]
|
|
343
|
+
[ 0. 0. 0. 0. 0. 0.25 0. 0.]
|
|
344
|
+
[ 0. 0. 0. 0. 0. 0. 0.25 0.]
|
|
345
|
+
[ 0. 0. 0. 0. 0. 0. 0. 0.25]]
|
|
240
346
|
"""
|
|
241
347
|
mean_pos = measurement
|
|
242
348
|
mean_vel = np.zeros_like(mean_pos)
|
|
@@ -260,12 +366,18 @@ class KalmanFilterXYWH(KalmanFilterXYAH):
|
|
|
260
366
|
Run Kalman filter prediction step.
|
|
261
367
|
|
|
262
368
|
Args:
|
|
263
|
-
mean (ndarray): The 8
|
|
264
|
-
covariance (ndarray): The 8x8
|
|
369
|
+
mean (ndarray): The 8-dimensional mean vector of the object state at the previous time step.
|
|
370
|
+
covariance (ndarray): The 8x8-dimensional covariance matrix of the object state at the previous time step.
|
|
265
371
|
|
|
266
372
|
Returns:
|
|
267
373
|
(tuple[ndarray, ndarray]): Returns the mean vector and covariance matrix of the predicted state. Unobserved
|
|
268
374
|
velocities are initialized to 0 mean.
|
|
375
|
+
|
|
376
|
+
Examples:
|
|
377
|
+
>>> kf = KalmanFilterXYWH()
|
|
378
|
+
>>> mean = np.array([0, 0, 1, 1, 0, 0, 0, 0])
|
|
379
|
+
>>> covariance = np.eye(8)
|
|
380
|
+
>>> predicted_mean, predicted_covariance = kf.predict(mean, covariance)
|
|
269
381
|
"""
|
|
270
382
|
std_pos = [
|
|
271
383
|
self._std_weight_position * mean[2],
|
|
@@ -296,6 +408,12 @@ class KalmanFilterXYWH(KalmanFilterXYAH):
|
|
|
296
408
|
|
|
297
409
|
Returns:
|
|
298
410
|
(tuple[ndarray, ndarray]): Returns the projected mean and covariance matrix of the given state estimate.
|
|
411
|
+
|
|
412
|
+
Examples:
|
|
413
|
+
>>> kf = KalmanFilterXYWH()
|
|
414
|
+
>>> mean = np.array([0, 0, 1, 1, 0, 0, 0, 0])
|
|
415
|
+
>>> covariance = np.eye(8)
|
|
416
|
+
>>> projected_mean, projected_cov = kf.project(mean, covariance)
|
|
299
417
|
"""
|
|
300
418
|
std = [
|
|
301
419
|
self._std_weight_position * mean[2],
|
|
@@ -320,6 +438,12 @@ class KalmanFilterXYWH(KalmanFilterXYAH):
|
|
|
320
438
|
Returns:
|
|
321
439
|
(tuple[ndarray, ndarray]): Returns the mean vector and covariance matrix of the predicted state. Unobserved
|
|
322
440
|
velocities are initialized to 0 mean.
|
|
441
|
+
|
|
442
|
+
Examples:
|
|
443
|
+
>>> mean = np.random.rand(5, 8) # 5 objects with 8-dimensional state vectors
|
|
444
|
+
>>> covariance = np.random.rand(5, 8, 8) # 5 objects with 8x8 covariance matrices
|
|
445
|
+
>>> kf = KalmanFilterXYWH()
|
|
446
|
+
>>> predicted_mean, predicted_covariance = kf.multi_predict(mean, covariance)
|
|
323
447
|
"""
|
|
324
448
|
std_pos = [
|
|
325
449
|
self._std_weight_position * mean[:, 2],
|
|
@@ -356,5 +480,12 @@ class KalmanFilterXYWH(KalmanFilterXYAH):
|
|
|
356
480
|
|
|
357
481
|
Returns:
|
|
358
482
|
(tuple[ndarray, ndarray]): Returns the measurement-corrected state distribution.
|
|
483
|
+
|
|
484
|
+
Examples:
|
|
485
|
+
>>> kf = KalmanFilterXYWH()
|
|
486
|
+
>>> mean = np.array([0, 0, 1, 1, 0, 0, 0, 0])
|
|
487
|
+
>>> covariance = np.eye(8)
|
|
488
|
+
>>> measurement = np.array([0.5, 0.5, 1.2, 1.2])
|
|
489
|
+
>>> new_mean, new_covariance = kf.update(mean, covariance, measurement)
|
|
359
490
|
"""
|
|
360
491
|
return super().update(mean, covariance, measurement)
|
|
@@ -19,18 +19,23 @@ except (ImportError, AssertionError, AttributeError):
|
|
|
19
19
|
|
|
20
20
|
def linear_assignment(cost_matrix: np.ndarray, thresh: float, use_lap: bool = True) -> tuple:
|
|
21
21
|
"""
|
|
22
|
-
Perform linear assignment using scipy or lap.lapjv.
|
|
22
|
+
Perform linear assignment using either the scipy or lap.lapjv method.
|
|
23
23
|
|
|
24
24
|
Args:
|
|
25
|
-
cost_matrix (np.ndarray): The matrix containing cost values for assignments.
|
|
25
|
+
cost_matrix (np.ndarray): The matrix containing cost values for assignments, with shape (N, M).
|
|
26
26
|
thresh (float): Threshold for considering an assignment valid.
|
|
27
|
-
use_lap (bool
|
|
27
|
+
use_lap (bool): Use lap.lapjv for the assignment. If False, scipy.optimize.linear_sum_assignment is used.
|
|
28
28
|
|
|
29
29
|
Returns:
|
|
30
|
-
|
|
31
|
-
- matched indices
|
|
32
|
-
- unmatched indices from
|
|
33
|
-
- unmatched indices from
|
|
30
|
+
(tuple): A tuple containing:
|
|
31
|
+
- matched_indices (np.ndarray): Array of matched indices of shape (K, 2), where K is the number of matches.
|
|
32
|
+
- unmatched_a (np.ndarray): Array of unmatched indices from the first set, with shape (L,).
|
|
33
|
+
- unmatched_b (np.ndarray): Array of unmatched indices from the second set, with shape (M,).
|
|
34
|
+
|
|
35
|
+
Examples:
|
|
36
|
+
>>> cost_matrix = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
|
|
37
|
+
>>> thresh = 5.0
|
|
38
|
+
>>> matched_indices, unmatched_a, unmatched_b = linear_assignment(cost_matrix, thresh, use_lap=True)
|
|
34
39
|
"""
|
|
35
40
|
|
|
36
41
|
if cost_matrix.size == 0:
|
|
@@ -68,6 +73,12 @@ def iou_distance(atracks: list, btracks: list) -> np.ndarray:
|
|
|
68
73
|
|
|
69
74
|
Returns:
|
|
70
75
|
(np.ndarray): Cost matrix computed based on IoU.
|
|
76
|
+
|
|
77
|
+
Examples:
|
|
78
|
+
Compute IoU distance between two sets of tracks
|
|
79
|
+
>>> atracks = [np.array([0, 0, 10, 10]), np.array([20, 20, 30, 30])]
|
|
80
|
+
>>> btracks = [np.array([5, 5, 15, 15]), np.array([25, 25, 35, 35])]
|
|
81
|
+
>>> cost_matrix = iou_distance(atracks, btracks)
|
|
71
82
|
"""
|
|
72
83
|
|
|
73
84
|
if atracks and isinstance(atracks[0], np.ndarray) or btracks and isinstance(btracks[0], np.ndarray):
|
|
@@ -98,12 +109,19 @@ def embedding_distance(tracks: list, detections: list, metric: str = "cosine") -
|
|
|
98
109
|
Compute distance between tracks and detections based on embeddings.
|
|
99
110
|
|
|
100
111
|
Args:
|
|
101
|
-
tracks (list[STrack]): List of tracks.
|
|
102
|
-
detections (list[BaseTrack]): List of detections.
|
|
103
|
-
metric (str
|
|
112
|
+
tracks (list[STrack]): List of tracks, where each track contains embedding features.
|
|
113
|
+
detections (list[BaseTrack]): List of detections, where each detection contains embedding features.
|
|
114
|
+
metric (str): Metric for distance computation. Supported metrics include 'cosine', 'euclidean', etc.
|
|
104
115
|
|
|
105
116
|
Returns:
|
|
106
|
-
(np.ndarray): Cost matrix computed based on embeddings
|
|
117
|
+
(np.ndarray): Cost matrix computed based on embeddings with shape (N, M), where N is the number of tracks
|
|
118
|
+
and M is the number of detections.
|
|
119
|
+
|
|
120
|
+
Examples:
|
|
121
|
+
Compute the embedding distance between tracks and detections using cosine metric
|
|
122
|
+
>>> tracks = [STrack(...), STrack(...)] # List of track objects with embedding features
|
|
123
|
+
>>> detections = [BaseTrack(...), BaseTrack(...)] # List of detection objects with embedding features
|
|
124
|
+
>>> cost_matrix = embedding_distance(tracks, detections, metric='cosine')
|
|
107
125
|
"""
|
|
108
126
|
|
|
109
127
|
cost_matrix = np.zeros((len(tracks), len(detections)), dtype=np.float32)
|
|
@@ -122,11 +140,17 @@ def fuse_score(cost_matrix: np.ndarray, detections: list) -> np.ndarray:
|
|
|
122
140
|
Fuses cost matrix with detection scores to produce a single similarity matrix.
|
|
123
141
|
|
|
124
142
|
Args:
|
|
125
|
-
cost_matrix (np.ndarray): The matrix containing cost values for assignments.
|
|
126
|
-
detections (list[BaseTrack]): List of detections
|
|
143
|
+
cost_matrix (np.ndarray): The matrix containing cost values for assignments, with shape (N, M).
|
|
144
|
+
detections (list[BaseTrack]): List of detections, each containing a score attribute.
|
|
127
145
|
|
|
128
146
|
Returns:
|
|
129
|
-
(np.ndarray): Fused similarity matrix.
|
|
147
|
+
(np.ndarray): Fused similarity matrix with shape (N, M).
|
|
148
|
+
|
|
149
|
+
Examples:
|
|
150
|
+
Fuse a cost matrix with detection scores
|
|
151
|
+
>>> cost_matrix = np.random.rand(5, 10) # 5 tracks and 10 detections
|
|
152
|
+
>>> detections = [BaseTrack(score=np.random.rand()) for _ in range(10)]
|
|
153
|
+
>>> fused_matrix = fuse_score(cost_matrix, detections)
|
|
130
154
|
"""
|
|
131
155
|
|
|
132
156
|
if cost_matrix.size == 0:
|
ultralytics/utils/__init__.py
CHANGED
|
@@ -47,7 +47,7 @@ PYTHON_VERSION = platform.python_version()
|
|
|
47
47
|
TORCH_VERSION = torch.__version__
|
|
48
48
|
TORCHVISION_VERSION = importlib.metadata.version("torchvision") # faster than importing torchvision
|
|
49
49
|
HELP_MSG = """
|
|
50
|
-
|
|
50
|
+
Examples for running Ultralytics:
|
|
51
51
|
|
|
52
52
|
1. Install the ultralytics package:
|
|
53
53
|
|
ultralytics/utils/files.py
CHANGED
|
@@ -11,19 +11,44 @@ from pathlib import Path
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class WorkingDirectory(contextlib.ContextDecorator):
|
|
14
|
-
"""
|
|
14
|
+
"""
|
|
15
|
+
A context manager and decorator for temporarily changing the working directory.
|
|
16
|
+
|
|
17
|
+
This class allows for the temporary change of the working directory using a context manager or decorator.
|
|
18
|
+
It ensures that the original working directory is restored after the context or decorated function completes.
|
|
19
|
+
|
|
20
|
+
Attributes:
|
|
21
|
+
dir (Path): The new directory to switch to.
|
|
22
|
+
cwd (Path): The original current working directory before the switch.
|
|
23
|
+
|
|
24
|
+
Methods:
|
|
25
|
+
__enter__: Changes the current directory to the specified directory.
|
|
26
|
+
__exit__: Restores the original working directory on context exit.
|
|
27
|
+
|
|
28
|
+
Examples:
|
|
29
|
+
Using as a context manager:
|
|
30
|
+
>>> with WorkingDirectory('/path/to/new/dir'):
|
|
31
|
+
>>> # Perform operations in the new directory
|
|
32
|
+
>>> pass
|
|
33
|
+
|
|
34
|
+
Using as a decorator:
|
|
35
|
+
>>> @WorkingDirectory('/path/to/new/dir')
|
|
36
|
+
>>> def some_function():
|
|
37
|
+
>>> # Perform operations in the new directory
|
|
38
|
+
>>> pass
|
|
39
|
+
"""
|
|
15
40
|
|
|
16
41
|
def __init__(self, new_dir):
|
|
17
|
-
"""Sets the working directory to 'new_dir' upon instantiation."""
|
|
42
|
+
"""Sets the working directory to 'new_dir' upon instantiation for use with context managers or decorators."""
|
|
18
43
|
self.dir = new_dir # new dir
|
|
19
44
|
self.cwd = Path.cwd().resolve() # current dir
|
|
20
45
|
|
|
21
46
|
def __enter__(self):
|
|
22
|
-
"""Changes the current directory to the specified directory."""
|
|
47
|
+
"""Changes the current working directory to the specified directory upon entering the context."""
|
|
23
48
|
os.chdir(self.dir)
|
|
24
49
|
|
|
25
50
|
def __exit__(self, exc_type, exc_val, exc_tb): # noqa
|
|
26
|
-
"""
|
|
51
|
+
"""Restores the original working directory when exiting the context."""
|
|
27
52
|
os.chdir(self.cwd)
|
|
28
53
|
|
|
29
54
|
|
|
@@ -35,18 +60,16 @@ def spaces_in_path(path):
|
|
|
35
60
|
file/directory back to its original location.
|
|
36
61
|
|
|
37
62
|
Args:
|
|
38
|
-
path (str | Path): The original path.
|
|
63
|
+
path (str | Path): The original path that may contain spaces.
|
|
39
64
|
|
|
40
65
|
Yields:
|
|
41
66
|
(Path): Temporary path with spaces replaced by underscores if spaces were present, otherwise the original path.
|
|
42
67
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
# Your code here
|
|
49
|
-
```
|
|
68
|
+
Examples:
|
|
69
|
+
Use the context manager to handle paths with spaces:
|
|
70
|
+
>>> from ultralytics.utils.files import spaces_in_path
|
|
71
|
+
>>> with spaces_in_path('/path/with spaces') as new_path:
|
|
72
|
+
>>> # Your code here
|
|
50
73
|
"""
|
|
51
74
|
|
|
52
75
|
# If path has spaces, replace them with underscores
|
|
@@ -84,21 +107,35 @@ def spaces_in_path(path):
|
|
|
84
107
|
|
|
85
108
|
def increment_path(path, exist_ok=False, sep="", mkdir=False):
|
|
86
109
|
"""
|
|
87
|
-
Increments a file or directory path, i.e
|
|
110
|
+
Increments a file or directory path, i.e., runs/exp --> runs/exp{sep}2, runs/exp{sep}3, ... etc.
|
|
88
111
|
|
|
89
|
-
If the path exists and exist_ok is not
|
|
112
|
+
If the path exists and `exist_ok` is not True, the path will be incremented by appending a number and `sep` to
|
|
90
113
|
the end of the path. If the path is a file, the file extension will be preserved. If the path is a directory, the
|
|
91
|
-
number will be appended directly to the end of the path. If mkdir is set to True, the path will be created as a
|
|
114
|
+
number will be appended directly to the end of the path. If `mkdir` is set to True, the path will be created as a
|
|
92
115
|
directory if it does not already exist.
|
|
93
116
|
|
|
94
117
|
Args:
|
|
95
|
-
path (str
|
|
96
|
-
exist_ok (bool
|
|
97
|
-
sep (str
|
|
98
|
-
mkdir (bool
|
|
118
|
+
path (str | pathlib.Path): Path to increment.
|
|
119
|
+
exist_ok (bool): If True, the path will not be incremented and returned as-is.
|
|
120
|
+
sep (str): Separator to use between the path and the incrementation number.
|
|
121
|
+
mkdir (bool): Create a directory if it does not exist.
|
|
99
122
|
|
|
100
123
|
Returns:
|
|
101
124
|
(pathlib.Path): Incremented path.
|
|
125
|
+
|
|
126
|
+
Examples:
|
|
127
|
+
Increment a directory path:
|
|
128
|
+
>>> from pathlib import Path
|
|
129
|
+
>>> path = Path("runs/exp")
|
|
130
|
+
>>> new_path = increment_path(path)
|
|
131
|
+
>>> print(new_path)
|
|
132
|
+
runs/exp2
|
|
133
|
+
|
|
134
|
+
Increment a file path:
|
|
135
|
+
>>> path = Path("runs/exp/results.txt")
|
|
136
|
+
>>> new_path = increment_path(path)
|
|
137
|
+
>>> print(new_path)
|
|
138
|
+
runs/exp/results2.txt
|
|
102
139
|
"""
|
|
103
140
|
path = Path(path) # os-agnostic
|
|
104
141
|
if path.exists() and not exist_ok:
|
|
@@ -118,19 +155,19 @@ def increment_path(path, exist_ok=False, sep="", mkdir=False):
|
|
|
118
155
|
|
|
119
156
|
|
|
120
157
|
def file_age(path=__file__):
|
|
121
|
-
"""Return days since last file
|
|
158
|
+
"""Return days since the last modification of the specified file."""
|
|
122
159
|
dt = datetime.now() - datetime.fromtimestamp(Path(path).stat().st_mtime) # delta
|
|
123
160
|
return dt.days # + dt.seconds / 86400 # fractional days
|
|
124
161
|
|
|
125
162
|
|
|
126
163
|
def file_date(path=__file__):
|
|
127
|
-
"""
|
|
164
|
+
"""Returns the file modification date in 'YYYY-M-D' format."""
|
|
128
165
|
t = datetime.fromtimestamp(Path(path).stat().st_mtime)
|
|
129
166
|
return f"{t.year}-{t.month}-{t.day}"
|
|
130
167
|
|
|
131
168
|
|
|
132
169
|
def file_size(path):
|
|
133
|
-
"""
|
|
170
|
+
"""Returns the size of a file or directory in megabytes (MB)."""
|
|
134
171
|
if isinstance(path, (str, Path)):
|
|
135
172
|
mb = 1 << 20 # bytes to MiB (1024 ** 2)
|
|
136
173
|
path = Path(path)
|
|
@@ -142,7 +179,7 @@ def file_size(path):
|
|
|
142
179
|
|
|
143
180
|
|
|
144
181
|
def get_latest_run(search_dir="."):
|
|
145
|
-
"""
|
|
182
|
+
"""Returns the path to the most recent 'last.pt' file in the specified directory for resuming training."""
|
|
146
183
|
last_list = glob.glob(f"{search_dir}/**/last*.pt", recursive=True)
|
|
147
184
|
return max(last_list, key=os.path.getctime) if last_list else ""
|
|
148
185
|
|
|
@@ -152,17 +189,15 @@ def update_models(model_names=("yolov8n.pt",), source_dir=Path("."), update_name
|
|
|
152
189
|
Updates and re-saves specified YOLO models in an 'updated_models' subdirectory.
|
|
153
190
|
|
|
154
191
|
Args:
|
|
155
|
-
model_names (
|
|
156
|
-
source_dir (Path
|
|
157
|
-
update_names (bool
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
from ultralytics.utils.files import update_models
|
|
162
|
-
|
|
163
|
-
model_names =
|
|
164
|
-
update_models(model_names)
|
|
165
|
-
```
|
|
192
|
+
model_names (Tuple[str, ...]): Model filenames to update.
|
|
193
|
+
source_dir (Path): Directory containing models and target subdirectory.
|
|
194
|
+
update_names (bool): Update model names from a data YAML.
|
|
195
|
+
|
|
196
|
+
Examples:
|
|
197
|
+
Update specified YOLO models and save them in 'updated_models' subdirectory:
|
|
198
|
+
>>> from ultralytics.utils.files import update_models
|
|
199
|
+
>>> model_names = ("yolov8n.pt", "yolov8s.pt")
|
|
200
|
+
>>> update_models(model_names, source_dir=Path("/models"), update_names=True)
|
|
166
201
|
"""
|
|
167
202
|
from ultralytics import YOLO
|
|
168
203
|
from ultralytics.nn.autobackend import default_class_names
|
ultralytics/utils/plotting.py
CHANGED
|
@@ -369,7 +369,7 @@ class Annotator:
|
|
|
369
369
|
# Convert im back to PIL and update draw
|
|
370
370
|
self.fromarray(self.im)
|
|
371
371
|
|
|
372
|
-
def kpts(self, kpts, shape=(640, 640), radius=5, kpt_line=True, conf_thres=0.25):
|
|
372
|
+
def kpts(self, kpts, shape=(640, 640), radius=5, kpt_line=True, conf_thres=0.25, kpt_color=None):
|
|
373
373
|
"""
|
|
374
374
|
Plot keypoints on the image.
|
|
375
375
|
|
|
@@ -379,6 +379,7 @@ class Annotator:
|
|
|
379
379
|
radius (int, optional): Radius of the drawn keypoints. Default is 5.
|
|
380
380
|
kpt_line (bool, optional): If True, the function will draw lines connecting keypoints
|
|
381
381
|
for human pose. Default is True.
|
|
382
|
+
kpt_color (tuple, optional): The color of the keypoints (B, G, R).
|
|
382
383
|
|
|
383
384
|
Note:
|
|
384
385
|
`kpt_line=True` currently only supports human pose plotting.
|
|
@@ -391,7 +392,7 @@ class Annotator:
|
|
|
391
392
|
is_pose = nkpt == 17 and ndim in {2, 3}
|
|
392
393
|
kpt_line &= is_pose # `kpt_line=True` for now only supports human pose plotting
|
|
393
394
|
for i, k in enumerate(kpts):
|
|
394
|
-
color_k =
|
|
395
|
+
color_k = kpt_color or (self.kpt_color[i].tolist() if is_pose else colors(i))
|
|
395
396
|
x_coord, y_coord = k[0], k[1]
|
|
396
397
|
if x_coord % shape[1] != 0 and y_coord % shape[0] != 0:
|
|
397
398
|
if len(k) == 3:
|
|
@@ -414,7 +415,14 @@ class Annotator:
|
|
|
414
415
|
continue
|
|
415
416
|
if pos2[0] % shape[1] == 0 or pos2[1] % shape[0] == 0 or pos2[0] < 0 or pos2[1] < 0:
|
|
416
417
|
continue
|
|
417
|
-
cv2.line(
|
|
418
|
+
cv2.line(
|
|
419
|
+
self.im,
|
|
420
|
+
pos1,
|
|
421
|
+
pos2,
|
|
422
|
+
kpt_color or self.limb_color[i].tolist(),
|
|
423
|
+
thickness=2,
|
|
424
|
+
lineType=cv2.LINE_AA,
|
|
425
|
+
)
|
|
418
426
|
if self.pil:
|
|
419
427
|
# Convert im back to PIL and update draw
|
|
420
428
|
self.fromarray(self.im)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ultralytics
|
|
3
|
-
Version: 8.2.
|
|
3
|
+
Version: 8.2.77
|
|
4
4
|
Summary: Ultralytics YOLOv8 for SOTA object detection, multi-object tracking, instance segmentation, pose estimation and image classification.
|
|
5
5
|
Author: Glenn Jocher, Ayush Chaurasia, Jing Qiu
|
|
6
6
|
Maintainer: Glenn Jocher, Ayush Chaurasia, Jing Qiu
|
|
@@ -349,7 +349,7 @@ We love your input! YOLOv5 and YOLOv8 would not be possible without help from ou
|
|
|
349
349
|
|
|
350
350
|
<!-- SVG image from https://opencollective.com/ultralytics/contributors.svg?width=990 -->
|
|
351
351
|
|
|
352
|
-
<a href="https://github.com/ultralytics/
|
|
352
|
+
<a href="https://github.com/ultralytics/ultralytics/graphs/contributors">
|
|
353
353
|
<img width="100%" src="https://github.com/ultralytics/assets/raw/main/im/image-contributors.png" alt="Ultralytics open-source contributors"></a>
|
|
354
354
|
|
|
355
355
|
## <div align="center">License</div>
|