ultralytics 8.0.238__py3-none-any.whl → 8.0.239__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 +2 -2
- ultralytics/cfg/__init__.py +241 -138
- ultralytics/data/__init__.py +9 -2
- ultralytics/data/annotator.py +4 -4
- ultralytics/data/augment.py +186 -169
- ultralytics/data/base.py +54 -48
- ultralytics/data/build.py +34 -23
- ultralytics/data/converter.py +242 -70
- ultralytics/data/dataset.py +117 -95
- ultralytics/data/explorer/__init__.py +3 -1
- ultralytics/data/explorer/explorer.py +120 -100
- ultralytics/data/explorer/gui/__init__.py +1 -0
- ultralytics/data/explorer/gui/dash.py +123 -89
- ultralytics/data/explorer/utils.py +37 -39
- ultralytics/data/loaders.py +75 -62
- ultralytics/data/split_dota.py +44 -36
- ultralytics/data/utils.py +160 -142
- ultralytics/engine/exporter.py +348 -292
- ultralytics/engine/model.py +102 -66
- ultralytics/engine/predictor.py +74 -55
- ultralytics/engine/results.py +61 -41
- ultralytics/engine/trainer.py +192 -144
- ultralytics/engine/tuner.py +66 -59
- ultralytics/engine/validator.py +31 -26
- ultralytics/hub/__init__.py +54 -31
- ultralytics/hub/auth.py +28 -25
- ultralytics/hub/session.py +282 -133
- ultralytics/hub/utils.py +64 -42
- ultralytics/models/__init__.py +1 -1
- ultralytics/models/fastsam/__init__.py +1 -1
- ultralytics/models/fastsam/model.py +6 -6
- ultralytics/models/fastsam/predict.py +3 -2
- ultralytics/models/fastsam/prompt.py +55 -48
- ultralytics/models/fastsam/val.py +1 -1
- ultralytics/models/nas/__init__.py +1 -1
- ultralytics/models/nas/model.py +9 -8
- ultralytics/models/nas/predict.py +8 -6
- ultralytics/models/nas/val.py +11 -9
- ultralytics/models/rtdetr/__init__.py +1 -1
- ultralytics/models/rtdetr/model.py +11 -9
- ultralytics/models/rtdetr/train.py +18 -16
- ultralytics/models/rtdetr/val.py +25 -19
- ultralytics/models/sam/__init__.py +1 -1
- ultralytics/models/sam/amg.py +13 -14
- ultralytics/models/sam/build.py +44 -42
- ultralytics/models/sam/model.py +6 -6
- ultralytics/models/sam/modules/decoders.py +6 -4
- ultralytics/models/sam/modules/encoders.py +37 -35
- ultralytics/models/sam/modules/sam.py +5 -4
- ultralytics/models/sam/modules/tiny_encoder.py +95 -73
- ultralytics/models/sam/modules/transformer.py +3 -2
- ultralytics/models/sam/predict.py +39 -27
- ultralytics/models/utils/loss.py +99 -95
- ultralytics/models/utils/ops.py +34 -31
- ultralytics/models/yolo/__init__.py +1 -1
- ultralytics/models/yolo/classify/__init__.py +1 -1
- ultralytics/models/yolo/classify/predict.py +8 -6
- ultralytics/models/yolo/classify/train.py +37 -31
- ultralytics/models/yolo/classify/val.py +26 -24
- ultralytics/models/yolo/detect/__init__.py +1 -1
- ultralytics/models/yolo/detect/predict.py +8 -6
- ultralytics/models/yolo/detect/train.py +47 -37
- ultralytics/models/yolo/detect/val.py +100 -82
- ultralytics/models/yolo/model.py +31 -25
- ultralytics/models/yolo/obb/__init__.py +1 -1
- ultralytics/models/yolo/obb/predict.py +13 -11
- ultralytics/models/yolo/obb/train.py +3 -3
- ultralytics/models/yolo/obb/val.py +70 -59
- ultralytics/models/yolo/pose/__init__.py +1 -1
- ultralytics/models/yolo/pose/predict.py +17 -12
- ultralytics/models/yolo/pose/train.py +28 -25
- ultralytics/models/yolo/pose/val.py +91 -64
- ultralytics/models/yolo/segment/__init__.py +1 -1
- ultralytics/models/yolo/segment/predict.py +10 -8
- ultralytics/models/yolo/segment/train.py +16 -15
- ultralytics/models/yolo/segment/val.py +90 -68
- ultralytics/nn/__init__.py +26 -6
- ultralytics/nn/autobackend.py +144 -112
- ultralytics/nn/modules/__init__.py +96 -13
- ultralytics/nn/modules/block.py +28 -7
- ultralytics/nn/modules/conv.py +41 -23
- ultralytics/nn/modules/head.py +60 -52
- ultralytics/nn/modules/transformer.py +49 -32
- ultralytics/nn/modules/utils.py +20 -15
- ultralytics/nn/tasks.py +215 -141
- ultralytics/solutions/ai_gym.py +59 -47
- ultralytics/solutions/distance_calculation.py +17 -14
- ultralytics/solutions/heatmap.py +57 -55
- ultralytics/solutions/object_counter.py +46 -39
- ultralytics/solutions/speed_estimation.py +13 -16
- ultralytics/trackers/__init__.py +1 -1
- ultralytics/trackers/basetrack.py +1 -0
- ultralytics/trackers/bot_sort.py +2 -1
- ultralytics/trackers/byte_tracker.py +10 -7
- ultralytics/trackers/track.py +7 -7
- ultralytics/trackers/utils/gmc.py +25 -25
- ultralytics/trackers/utils/kalman_filter.py +85 -42
- ultralytics/trackers/utils/matching.py +8 -7
- ultralytics/utils/__init__.py +173 -152
- ultralytics/utils/autobatch.py +10 -10
- ultralytics/utils/benchmarks.py +76 -86
- ultralytics/utils/callbacks/__init__.py +1 -1
- ultralytics/utils/callbacks/base.py +29 -29
- ultralytics/utils/callbacks/clearml.py +51 -43
- ultralytics/utils/callbacks/comet.py +81 -66
- ultralytics/utils/callbacks/dvc.py +33 -26
- ultralytics/utils/callbacks/hub.py +44 -26
- ultralytics/utils/callbacks/mlflow.py +31 -24
- ultralytics/utils/callbacks/neptune.py +35 -25
- ultralytics/utils/callbacks/raytune.py +9 -4
- ultralytics/utils/callbacks/tensorboard.py +16 -11
- ultralytics/utils/callbacks/wb.py +39 -33
- ultralytics/utils/checks.py +189 -141
- ultralytics/utils/dist.py +15 -12
- ultralytics/utils/downloads.py +112 -96
- ultralytics/utils/errors.py +1 -1
- ultralytics/utils/files.py +11 -11
- ultralytics/utils/instance.py +22 -22
- ultralytics/utils/loss.py +117 -67
- ultralytics/utils/metrics.py +224 -158
- ultralytics/utils/ops.py +38 -28
- ultralytics/utils/patches.py +3 -3
- ultralytics/utils/plotting.py +217 -120
- ultralytics/utils/tal.py +19 -13
- ultralytics/utils/torch_utils.py +138 -109
- ultralytics/utils/triton.py +12 -10
- ultralytics/utils/tuner.py +49 -47
- {ultralytics-8.0.238.dist-info → ultralytics-8.0.239.dist-info}/METADATA +2 -1
- ultralytics-8.0.239.dist-info/RECORD +188 -0
- ultralytics-8.0.238.dist-info/RECORD +0 -188
- {ultralytics-8.0.238.dist-info → ultralytics-8.0.239.dist-info}/LICENSE +0 -0
- {ultralytics-8.0.238.dist-info → ultralytics-8.0.239.dist-info}/WHEEL +0 -0
- {ultralytics-8.0.238.dist-info → ultralytics-8.0.239.dist-info}/entry_points.txt +0 -0
- {ultralytics-8.0.238.dist-info → ultralytics-8.0.239.dist-info}/top_level.txt +0 -0
ultralytics/engine/results.py
CHANGED
|
@@ -98,15 +98,15 @@ class Results(SimpleClass):
|
|
|
98
98
|
self.probs = Probs(probs) if probs is not None else None
|
|
99
99
|
self.keypoints = Keypoints(keypoints, self.orig_shape) if keypoints is not None else None
|
|
100
100
|
self.obb = OBB(obb, self.orig_shape) if obb is not None else None
|
|
101
|
-
self.speed = {
|
|
101
|
+
self.speed = {"preprocess": None, "inference": None, "postprocess": None} # milliseconds per image
|
|
102
102
|
self.names = names
|
|
103
103
|
self.path = path
|
|
104
104
|
self.save_dir = None
|
|
105
|
-
self._keys =
|
|
105
|
+
self._keys = "boxes", "masks", "probs", "keypoints", "obb"
|
|
106
106
|
|
|
107
107
|
def __getitem__(self, idx):
|
|
108
108
|
"""Return a Results object for the specified index."""
|
|
109
|
-
return self._apply(
|
|
109
|
+
return self._apply("__getitem__", idx)
|
|
110
110
|
|
|
111
111
|
def __len__(self):
|
|
112
112
|
"""Return the number of detections in the Results object."""
|
|
@@ -146,19 +146,19 @@ class Results(SimpleClass):
|
|
|
146
146
|
|
|
147
147
|
def cpu(self):
|
|
148
148
|
"""Return a copy of the Results object with all tensors on CPU memory."""
|
|
149
|
-
return self._apply(
|
|
149
|
+
return self._apply("cpu")
|
|
150
150
|
|
|
151
151
|
def numpy(self):
|
|
152
152
|
"""Return a copy of the Results object with all tensors as numpy arrays."""
|
|
153
|
-
return self._apply(
|
|
153
|
+
return self._apply("numpy")
|
|
154
154
|
|
|
155
155
|
def cuda(self):
|
|
156
156
|
"""Return a copy of the Results object with all tensors on GPU memory."""
|
|
157
|
-
return self._apply(
|
|
157
|
+
return self._apply("cuda")
|
|
158
158
|
|
|
159
159
|
def to(self, *args, **kwargs):
|
|
160
160
|
"""Return a copy of the Results object with tensors on the specified device and dtype."""
|
|
161
|
-
return self._apply(
|
|
161
|
+
return self._apply("to", *args, **kwargs)
|
|
162
162
|
|
|
163
163
|
def new(self):
|
|
164
164
|
"""Return a new Results object with the same image, path, and names."""
|
|
@@ -169,7 +169,7 @@ class Results(SimpleClass):
|
|
|
169
169
|
conf=True,
|
|
170
170
|
line_width=None,
|
|
171
171
|
font_size=None,
|
|
172
|
-
font=
|
|
172
|
+
font="Arial.ttf",
|
|
173
173
|
pil=False,
|
|
174
174
|
img=None,
|
|
175
175
|
im_gpu=None,
|
|
@@ -229,14 +229,20 @@ class Results(SimpleClass):
|
|
|
229
229
|
font_size,
|
|
230
230
|
font,
|
|
231
231
|
pil or (pred_probs is not None and show_probs), # Classify tasks default to pil=True
|
|
232
|
-
example=names
|
|
232
|
+
example=names,
|
|
233
|
+
)
|
|
233
234
|
|
|
234
235
|
# Plot Segment results
|
|
235
236
|
if pred_masks and show_masks:
|
|
236
237
|
if im_gpu is None:
|
|
237
238
|
img = LetterBox(pred_masks.shape[1:])(image=annotator.result())
|
|
238
|
-
im_gpu =
|
|
239
|
-
|
|
239
|
+
im_gpu = (
|
|
240
|
+
torch.as_tensor(img, dtype=torch.float16, device=pred_masks.data.device)
|
|
241
|
+
.permute(2, 0, 1)
|
|
242
|
+
.flip(0)
|
|
243
|
+
.contiguous()
|
|
244
|
+
/ 255
|
|
245
|
+
)
|
|
240
246
|
idx = pred_boxes.cls if pred_boxes else range(len(pred_masks))
|
|
241
247
|
annotator.masks(pred_masks.data, colors=[colors(x, True) for x in idx], im_gpu=im_gpu)
|
|
242
248
|
|
|
@@ -244,14 +250,14 @@ class Results(SimpleClass):
|
|
|
244
250
|
if pred_boxes is not None and show_boxes:
|
|
245
251
|
for d in reversed(pred_boxes):
|
|
246
252
|
c, conf, id = int(d.cls), float(d.conf) if conf else None, None if d.id is None else int(d.id.item())
|
|
247
|
-
name = (
|
|
248
|
-
label = (f
|
|
253
|
+
name = ("" if id is None else f"id:{id} ") + names[c]
|
|
254
|
+
label = (f"{name} {conf:.2f}" if conf else name) if labels else None
|
|
249
255
|
box = d.xyxyxyxy.reshape(-1, 4, 2).squeeze() if is_obb else d.xyxy.squeeze()
|
|
250
256
|
annotator.box_label(box, label, color=colors(c, True), rotated=is_obb)
|
|
251
257
|
|
|
252
258
|
# Plot Classify results
|
|
253
259
|
if pred_probs is not None and show_probs:
|
|
254
|
-
text =
|
|
260
|
+
text = ",\n".join(f"{names[j] if names else j} {pred_probs.data[j]:.2f}" for j in pred_probs.top5)
|
|
255
261
|
x = round(self.orig_shape[0] * 0.03)
|
|
256
262
|
annotator.text([x, x], text, txt_color=(255, 255, 255)) # TODO: allow setting colors
|
|
257
263
|
|
|
@@ -264,11 +270,11 @@ class Results(SimpleClass):
|
|
|
264
270
|
|
|
265
271
|
def verbose(self):
|
|
266
272
|
"""Return log string for each task."""
|
|
267
|
-
log_string =
|
|
273
|
+
log_string = ""
|
|
268
274
|
probs = self.probs
|
|
269
275
|
boxes = self.boxes
|
|
270
276
|
if len(self) == 0:
|
|
271
|
-
return log_string if probs is not None else f
|
|
277
|
+
return log_string if probs is not None else f"{log_string}(no detections), "
|
|
272
278
|
if probs is not None:
|
|
273
279
|
log_string += f"{', '.join(f'{self.names[j]} {probs.data[j]:.2f}' for j in probs.top5)}, "
|
|
274
280
|
if boxes:
|
|
@@ -285,34 +291,35 @@ class Results(SimpleClass):
|
|
|
285
291
|
txt_file (str): txt file path.
|
|
286
292
|
save_conf (bool): save confidence score or not.
|
|
287
293
|
"""
|
|
288
|
-
|
|
294
|
+
is_obb = self.obb is not None
|
|
295
|
+
boxes = self.obb if is_obb else self.boxes
|
|
289
296
|
masks = self.masks
|
|
290
297
|
probs = self.probs
|
|
291
298
|
kpts = self.keypoints
|
|
292
299
|
texts = []
|
|
293
300
|
if probs is not None:
|
|
294
301
|
# Classify
|
|
295
|
-
[texts.append(f
|
|
302
|
+
[texts.append(f"{probs.data[j]:.2f} {self.names[j]}") for j in probs.top5]
|
|
296
303
|
elif boxes:
|
|
297
304
|
# Detect/segment/pose
|
|
298
305
|
for j, d in enumerate(boxes):
|
|
299
306
|
c, conf, id = int(d.cls), float(d.conf), None if d.id is None else int(d.id.item())
|
|
300
|
-
line = (c, *d.xywhn.view(-1))
|
|
307
|
+
line = (c, *(d.xyxyxyxyn.view(-1) if is_obb else d.xywhn.view(-1)))
|
|
301
308
|
if masks:
|
|
302
309
|
seg = masks[j].xyn[0].copy().reshape(-1) # reversed mask.xyn, (n,2) to (n*2)
|
|
303
310
|
line = (c, *seg)
|
|
304
311
|
if kpts is not None:
|
|
305
312
|
kpt = torch.cat((kpts[j].xyn, kpts[j].conf[..., None]), 2) if kpts[j].has_visible else kpts[j].xyn
|
|
306
|
-
line += (*kpt.reshape(-1).tolist(),
|
|
307
|
-
line += (conf,
|
|
308
|
-
texts.append((
|
|
313
|
+
line += (*kpt.reshape(-1).tolist(),)
|
|
314
|
+
line += (conf,) * save_conf + (() if id is None else (id,))
|
|
315
|
+
texts.append(("%g " * len(line)).rstrip() % line)
|
|
309
316
|
|
|
310
317
|
if texts:
|
|
311
318
|
Path(txt_file).parent.mkdir(parents=True, exist_ok=True) # make directory
|
|
312
|
-
with open(txt_file,
|
|
313
|
-
f.writelines(text +
|
|
319
|
+
with open(txt_file, "a") as f:
|
|
320
|
+
f.writelines(text + "\n" for text in texts)
|
|
314
321
|
|
|
315
|
-
def save_crop(self, save_dir, file_name=Path(
|
|
322
|
+
def save_crop(self, save_dir, file_name=Path("im.jpg")):
|
|
316
323
|
"""
|
|
317
324
|
Save cropped predictions to `save_dir/cls/file_name.jpg`.
|
|
318
325
|
|
|
@@ -321,21 +328,23 @@ class Results(SimpleClass):
|
|
|
321
328
|
file_name (str | pathlib.Path): File name.
|
|
322
329
|
"""
|
|
323
330
|
if self.probs is not None:
|
|
324
|
-
LOGGER.warning(
|
|
331
|
+
LOGGER.warning("WARNING ⚠️ Classify task do not support `save_crop`.")
|
|
325
332
|
return
|
|
326
333
|
if self.obb is not None:
|
|
327
|
-
LOGGER.warning(
|
|
334
|
+
LOGGER.warning("WARNING ⚠️ OBB task do not support `save_crop`.")
|
|
328
335
|
return
|
|
329
336
|
for d in self.boxes:
|
|
330
|
-
save_one_box(
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
337
|
+
save_one_box(
|
|
338
|
+
d.xyxy,
|
|
339
|
+
self.orig_img.copy(),
|
|
340
|
+
file=Path(save_dir) / self.names[int(d.cls)] / f"{Path(file_name)}.jpg",
|
|
341
|
+
BGR=True,
|
|
342
|
+
)
|
|
334
343
|
|
|
335
344
|
def tojson(self, normalize=False):
|
|
336
345
|
"""Convert the object to JSON format."""
|
|
337
346
|
if self.probs is not None:
|
|
338
|
-
LOGGER.warning(
|
|
347
|
+
LOGGER.warning("Warning: Classify task do not support `tojson` yet.")
|
|
339
348
|
return
|
|
340
349
|
|
|
341
350
|
import json
|
|
@@ -345,19 +354,19 @@ class Results(SimpleClass):
|
|
|
345
354
|
data = self.boxes.data.cpu().tolist()
|
|
346
355
|
h, w = self.orig_shape if normalize else (1, 1)
|
|
347
356
|
for i, row in enumerate(data): # xyxy, track_id if tracking, conf, class_id
|
|
348
|
-
box = {
|
|
357
|
+
box = {"x1": row[0] / w, "y1": row[1] / h, "x2": row[2] / w, "y2": row[3] / h}
|
|
349
358
|
conf = row[-2]
|
|
350
359
|
class_id = int(row[-1])
|
|
351
360
|
name = self.names[class_id]
|
|
352
|
-
result = {
|
|
361
|
+
result = {"name": name, "class": class_id, "confidence": conf, "box": box}
|
|
353
362
|
if self.boxes.is_track:
|
|
354
|
-
result[
|
|
363
|
+
result["track_id"] = int(row[-3]) # track ID
|
|
355
364
|
if self.masks:
|
|
356
365
|
x, y = self.masks.xy[i][:, 0], self.masks.xy[i][:, 1] # numpy array
|
|
357
|
-
result[
|
|
366
|
+
result["segments"] = {"x": (x / w).tolist(), "y": (y / h).tolist()}
|
|
358
367
|
if self.keypoints is not None:
|
|
359
368
|
x, y, visible = self.keypoints[i].data[0].cpu().unbind(dim=1) # torch Tensor
|
|
360
|
-
result[
|
|
369
|
+
result["keypoints"] = {"x": (x / w).tolist(), "y": (y / h).tolist(), "visible": visible.tolist()}
|
|
361
370
|
results.append(result)
|
|
362
371
|
|
|
363
372
|
# Convert detections to JSON
|
|
@@ -396,7 +405,7 @@ class Boxes(BaseTensor):
|
|
|
396
405
|
if boxes.ndim == 1:
|
|
397
406
|
boxes = boxes[None, :]
|
|
398
407
|
n = boxes.shape[-1]
|
|
399
|
-
assert n in (6, 7), f
|
|
408
|
+
assert n in (6, 7), f"expected 6 or 7 values but got {n}" # xyxy, track_id, conf, cls
|
|
400
409
|
super().__init__(boxes, orig_shape)
|
|
401
410
|
self.is_track = n == 7
|
|
402
411
|
self.orig_shape = orig_shape
|
|
@@ -473,7 +482,8 @@ class Masks(BaseTensor):
|
|
|
473
482
|
"""Return normalized segments."""
|
|
474
483
|
return [
|
|
475
484
|
ops.scale_coords(self.data.shape[1:], x, self.orig_shape, normalize=True)
|
|
476
|
-
for x in ops.masks2segments(self.data)
|
|
485
|
+
for x in ops.masks2segments(self.data)
|
|
486
|
+
]
|
|
477
487
|
|
|
478
488
|
@property
|
|
479
489
|
@lru_cache(maxsize=1)
|
|
@@ -481,7 +491,8 @@ class Masks(BaseTensor):
|
|
|
481
491
|
"""Return segments in pixel coordinates."""
|
|
482
492
|
return [
|
|
483
493
|
ops.scale_coords(self.data.shape[1:], x, self.orig_shape, normalize=False)
|
|
484
|
-
for x in ops.masks2segments(self.data)
|
|
494
|
+
for x in ops.masks2segments(self.data)
|
|
495
|
+
]
|
|
485
496
|
|
|
486
497
|
|
|
487
498
|
class Keypoints(BaseTensor):
|
|
@@ -609,7 +620,7 @@ class OBB(BaseTensor):
|
|
|
609
620
|
if boxes.ndim == 1:
|
|
610
621
|
boxes = boxes[None, :]
|
|
611
622
|
n = boxes.shape[-1]
|
|
612
|
-
assert n in (7, 8), f
|
|
623
|
+
assert n in (7, 8), f"expected 7 or 8 values but got {n}" # xywh, rotation, track_id, conf, cls
|
|
613
624
|
super().__init__(boxes, orig_shape)
|
|
614
625
|
self.is_track = n == 8
|
|
615
626
|
self.orig_shape = orig_shape
|
|
@@ -640,6 +651,15 @@ class OBB(BaseTensor):
|
|
|
640
651
|
"""Return the boxes in xyxyxyxy format, (N, 4, 2)."""
|
|
641
652
|
return ops.xywhr2xyxyxyxy(self.xywhr)
|
|
642
653
|
|
|
654
|
+
@property
|
|
655
|
+
@lru_cache(maxsize=2)
|
|
656
|
+
def xyxyxyxyn(self):
|
|
657
|
+
"""Return the boxes in xyxyxyxy format, (N, 4, 2)."""
|
|
658
|
+
xyxyxyxyn = self.xyxyxyxy.clone() if isinstance(self.xyxyxyxy, torch.Tensor) else np.copy(self.xyxyxyxy)
|
|
659
|
+
xyxyxyxyn[..., 0] /= self.orig_shape[1]
|
|
660
|
+
xyxyxyxyn[..., 1] /= self.orig_shape[1]
|
|
661
|
+
return xyxyxyxyn
|
|
662
|
+
|
|
643
663
|
@property
|
|
644
664
|
@lru_cache(maxsize=2)
|
|
645
665
|
def xyxy(self):
|