ultralytics 8.0.237__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/cfg/datasets/DOTAv1.5.yaml +1 -1
- ultralytics/cfg/datasets/DOTAv1.yaml +1 -1
- ultralytics/cfg/datasets/dota8.yaml +34 -0
- 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 +5 -0
- ultralytics/data/explorer/explorer.py +170 -97
- ultralytics/data/explorer/gui/__init__.py +1 -0
- ultralytics/data/explorer/gui/dash.py +146 -76
- ultralytics/data/explorer/utils.py +87 -25
- 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 +63 -40
- 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 -12
- ultralytics/models/yolo/obb/train.py +3 -3
- ultralytics/models/yolo/obb/val.py +80 -58
- 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 +67 -59
- 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 +22 -15
- ultralytics/solutions/heatmap.py +76 -54
- 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 -151
- 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 +39 -29
- 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.237.dist-info → ultralytics-8.0.239.dist-info}/METADATA +5 -4
- ultralytics-8.0.239.dist-info/RECORD +188 -0
- ultralytics-8.0.237.dist-info/RECORD +0 -187
- {ultralytics-8.0.237.dist-info → ultralytics-8.0.239.dist-info}/LICENSE +0 -0
- {ultralytics-8.0.237.dist-info → ultralytics-8.0.239.dist-info}/WHEEL +0 -0
- {ultralytics-8.0.237.dist-info → ultralytics-8.0.239.dist-info}/entry_points.txt +0 -0
- {ultralytics-8.0.237.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,18 +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`.")
|
|
332
|
+
return
|
|
333
|
+
if self.obb is not None:
|
|
334
|
+
LOGGER.warning("WARNING ⚠️ OBB task do not support `save_crop`.")
|
|
325
335
|
return
|
|
326
336
|
for d in self.boxes:
|
|
327
|
-
save_one_box(
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
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
|
+
)
|
|
331
343
|
|
|
332
344
|
def tojson(self, normalize=False):
|
|
333
345
|
"""Convert the object to JSON format."""
|
|
334
346
|
if self.probs is not None:
|
|
335
|
-
LOGGER.warning(
|
|
347
|
+
LOGGER.warning("Warning: Classify task do not support `tojson` yet.")
|
|
336
348
|
return
|
|
337
349
|
|
|
338
350
|
import json
|
|
@@ -342,19 +354,19 @@ class Results(SimpleClass):
|
|
|
342
354
|
data = self.boxes.data.cpu().tolist()
|
|
343
355
|
h, w = self.orig_shape if normalize else (1, 1)
|
|
344
356
|
for i, row in enumerate(data): # xyxy, track_id if tracking, conf, class_id
|
|
345
|
-
box = {
|
|
357
|
+
box = {"x1": row[0] / w, "y1": row[1] / h, "x2": row[2] / w, "y2": row[3] / h}
|
|
346
358
|
conf = row[-2]
|
|
347
359
|
class_id = int(row[-1])
|
|
348
360
|
name = self.names[class_id]
|
|
349
|
-
result = {
|
|
361
|
+
result = {"name": name, "class": class_id, "confidence": conf, "box": box}
|
|
350
362
|
if self.boxes.is_track:
|
|
351
|
-
result[
|
|
363
|
+
result["track_id"] = int(row[-3]) # track ID
|
|
352
364
|
if self.masks:
|
|
353
365
|
x, y = self.masks.xy[i][:, 0], self.masks.xy[i][:, 1] # numpy array
|
|
354
|
-
result[
|
|
366
|
+
result["segments"] = {"x": (x / w).tolist(), "y": (y / h).tolist()}
|
|
355
367
|
if self.keypoints is not None:
|
|
356
368
|
x, y, visible = self.keypoints[i].data[0].cpu().unbind(dim=1) # torch Tensor
|
|
357
|
-
result[
|
|
369
|
+
result["keypoints"] = {"x": (x / w).tolist(), "y": (y / h).tolist(), "visible": visible.tolist()}
|
|
358
370
|
results.append(result)
|
|
359
371
|
|
|
360
372
|
# Convert detections to JSON
|
|
@@ -393,7 +405,7 @@ class Boxes(BaseTensor):
|
|
|
393
405
|
if boxes.ndim == 1:
|
|
394
406
|
boxes = boxes[None, :]
|
|
395
407
|
n = boxes.shape[-1]
|
|
396
|
-
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
|
|
397
409
|
super().__init__(boxes, orig_shape)
|
|
398
410
|
self.is_track = n == 7
|
|
399
411
|
self.orig_shape = orig_shape
|
|
@@ -470,7 +482,8 @@ class Masks(BaseTensor):
|
|
|
470
482
|
"""Return normalized segments."""
|
|
471
483
|
return [
|
|
472
484
|
ops.scale_coords(self.data.shape[1:], x, self.orig_shape, normalize=True)
|
|
473
|
-
for x in ops.masks2segments(self.data)
|
|
485
|
+
for x in ops.masks2segments(self.data)
|
|
486
|
+
]
|
|
474
487
|
|
|
475
488
|
@property
|
|
476
489
|
@lru_cache(maxsize=1)
|
|
@@ -478,7 +491,8 @@ class Masks(BaseTensor):
|
|
|
478
491
|
"""Return segments in pixel coordinates."""
|
|
479
492
|
return [
|
|
480
493
|
ops.scale_coords(self.data.shape[1:], x, self.orig_shape, normalize=False)
|
|
481
|
-
for x in ops.masks2segments(self.data)
|
|
494
|
+
for x in ops.masks2segments(self.data)
|
|
495
|
+
]
|
|
482
496
|
|
|
483
497
|
|
|
484
498
|
class Keypoints(BaseTensor):
|
|
@@ -606,7 +620,7 @@ class OBB(BaseTensor):
|
|
|
606
620
|
if boxes.ndim == 1:
|
|
607
621
|
boxes = boxes[None, :]
|
|
608
622
|
n = boxes.shape[-1]
|
|
609
|
-
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
|
|
610
624
|
super().__init__(boxes, orig_shape)
|
|
611
625
|
self.is_track = n == 8
|
|
612
626
|
self.orig_shape = orig_shape
|
|
@@ -637,6 +651,15 @@ class OBB(BaseTensor):
|
|
|
637
651
|
"""Return the boxes in xyxyxyxy format, (N, 4, 2)."""
|
|
638
652
|
return ops.xywhr2xyxyxyxy(self.xywhr)
|
|
639
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
|
+
|
|
640
663
|
@property
|
|
641
664
|
@lru_cache(maxsize=2)
|
|
642
665
|
def xyxy(self):
|