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.

Files changed (134) hide show
  1. ultralytics/__init__.py +2 -2
  2. ultralytics/cfg/__init__.py +241 -138
  3. ultralytics/data/__init__.py +9 -2
  4. ultralytics/data/annotator.py +4 -4
  5. ultralytics/data/augment.py +186 -169
  6. ultralytics/data/base.py +54 -48
  7. ultralytics/data/build.py +34 -23
  8. ultralytics/data/converter.py +242 -70
  9. ultralytics/data/dataset.py +117 -95
  10. ultralytics/data/explorer/__init__.py +3 -1
  11. ultralytics/data/explorer/explorer.py +120 -100
  12. ultralytics/data/explorer/gui/__init__.py +1 -0
  13. ultralytics/data/explorer/gui/dash.py +123 -89
  14. ultralytics/data/explorer/utils.py +37 -39
  15. ultralytics/data/loaders.py +75 -62
  16. ultralytics/data/split_dota.py +44 -36
  17. ultralytics/data/utils.py +160 -142
  18. ultralytics/engine/exporter.py +348 -292
  19. ultralytics/engine/model.py +102 -66
  20. ultralytics/engine/predictor.py +74 -55
  21. ultralytics/engine/results.py +61 -41
  22. ultralytics/engine/trainer.py +192 -144
  23. ultralytics/engine/tuner.py +66 -59
  24. ultralytics/engine/validator.py +31 -26
  25. ultralytics/hub/__init__.py +54 -31
  26. ultralytics/hub/auth.py +28 -25
  27. ultralytics/hub/session.py +282 -133
  28. ultralytics/hub/utils.py +64 -42
  29. ultralytics/models/__init__.py +1 -1
  30. ultralytics/models/fastsam/__init__.py +1 -1
  31. ultralytics/models/fastsam/model.py +6 -6
  32. ultralytics/models/fastsam/predict.py +3 -2
  33. ultralytics/models/fastsam/prompt.py +55 -48
  34. ultralytics/models/fastsam/val.py +1 -1
  35. ultralytics/models/nas/__init__.py +1 -1
  36. ultralytics/models/nas/model.py +9 -8
  37. ultralytics/models/nas/predict.py +8 -6
  38. ultralytics/models/nas/val.py +11 -9
  39. ultralytics/models/rtdetr/__init__.py +1 -1
  40. ultralytics/models/rtdetr/model.py +11 -9
  41. ultralytics/models/rtdetr/train.py +18 -16
  42. ultralytics/models/rtdetr/val.py +25 -19
  43. ultralytics/models/sam/__init__.py +1 -1
  44. ultralytics/models/sam/amg.py +13 -14
  45. ultralytics/models/sam/build.py +44 -42
  46. ultralytics/models/sam/model.py +6 -6
  47. ultralytics/models/sam/modules/decoders.py +6 -4
  48. ultralytics/models/sam/modules/encoders.py +37 -35
  49. ultralytics/models/sam/modules/sam.py +5 -4
  50. ultralytics/models/sam/modules/tiny_encoder.py +95 -73
  51. ultralytics/models/sam/modules/transformer.py +3 -2
  52. ultralytics/models/sam/predict.py +39 -27
  53. ultralytics/models/utils/loss.py +99 -95
  54. ultralytics/models/utils/ops.py +34 -31
  55. ultralytics/models/yolo/__init__.py +1 -1
  56. ultralytics/models/yolo/classify/__init__.py +1 -1
  57. ultralytics/models/yolo/classify/predict.py +8 -6
  58. ultralytics/models/yolo/classify/train.py +37 -31
  59. ultralytics/models/yolo/classify/val.py +26 -24
  60. ultralytics/models/yolo/detect/__init__.py +1 -1
  61. ultralytics/models/yolo/detect/predict.py +8 -6
  62. ultralytics/models/yolo/detect/train.py +47 -37
  63. ultralytics/models/yolo/detect/val.py +100 -82
  64. ultralytics/models/yolo/model.py +31 -25
  65. ultralytics/models/yolo/obb/__init__.py +1 -1
  66. ultralytics/models/yolo/obb/predict.py +13 -11
  67. ultralytics/models/yolo/obb/train.py +3 -3
  68. ultralytics/models/yolo/obb/val.py +70 -59
  69. ultralytics/models/yolo/pose/__init__.py +1 -1
  70. ultralytics/models/yolo/pose/predict.py +17 -12
  71. ultralytics/models/yolo/pose/train.py +28 -25
  72. ultralytics/models/yolo/pose/val.py +91 -64
  73. ultralytics/models/yolo/segment/__init__.py +1 -1
  74. ultralytics/models/yolo/segment/predict.py +10 -8
  75. ultralytics/models/yolo/segment/train.py +16 -15
  76. ultralytics/models/yolo/segment/val.py +90 -68
  77. ultralytics/nn/__init__.py +26 -6
  78. ultralytics/nn/autobackend.py +144 -112
  79. ultralytics/nn/modules/__init__.py +96 -13
  80. ultralytics/nn/modules/block.py +28 -7
  81. ultralytics/nn/modules/conv.py +41 -23
  82. ultralytics/nn/modules/head.py +60 -52
  83. ultralytics/nn/modules/transformer.py +49 -32
  84. ultralytics/nn/modules/utils.py +20 -15
  85. ultralytics/nn/tasks.py +215 -141
  86. ultralytics/solutions/ai_gym.py +59 -47
  87. ultralytics/solutions/distance_calculation.py +17 -14
  88. ultralytics/solutions/heatmap.py +57 -55
  89. ultralytics/solutions/object_counter.py +46 -39
  90. ultralytics/solutions/speed_estimation.py +13 -16
  91. ultralytics/trackers/__init__.py +1 -1
  92. ultralytics/trackers/basetrack.py +1 -0
  93. ultralytics/trackers/bot_sort.py +2 -1
  94. ultralytics/trackers/byte_tracker.py +10 -7
  95. ultralytics/trackers/track.py +7 -7
  96. ultralytics/trackers/utils/gmc.py +25 -25
  97. ultralytics/trackers/utils/kalman_filter.py +85 -42
  98. ultralytics/trackers/utils/matching.py +8 -7
  99. ultralytics/utils/__init__.py +173 -152
  100. ultralytics/utils/autobatch.py +10 -10
  101. ultralytics/utils/benchmarks.py +76 -86
  102. ultralytics/utils/callbacks/__init__.py +1 -1
  103. ultralytics/utils/callbacks/base.py +29 -29
  104. ultralytics/utils/callbacks/clearml.py +51 -43
  105. ultralytics/utils/callbacks/comet.py +81 -66
  106. ultralytics/utils/callbacks/dvc.py +33 -26
  107. ultralytics/utils/callbacks/hub.py +44 -26
  108. ultralytics/utils/callbacks/mlflow.py +31 -24
  109. ultralytics/utils/callbacks/neptune.py +35 -25
  110. ultralytics/utils/callbacks/raytune.py +9 -4
  111. ultralytics/utils/callbacks/tensorboard.py +16 -11
  112. ultralytics/utils/callbacks/wb.py +39 -33
  113. ultralytics/utils/checks.py +189 -141
  114. ultralytics/utils/dist.py +15 -12
  115. ultralytics/utils/downloads.py +112 -96
  116. ultralytics/utils/errors.py +1 -1
  117. ultralytics/utils/files.py +11 -11
  118. ultralytics/utils/instance.py +22 -22
  119. ultralytics/utils/loss.py +117 -67
  120. ultralytics/utils/metrics.py +224 -158
  121. ultralytics/utils/ops.py +38 -28
  122. ultralytics/utils/patches.py +3 -3
  123. ultralytics/utils/plotting.py +217 -120
  124. ultralytics/utils/tal.py +19 -13
  125. ultralytics/utils/torch_utils.py +138 -109
  126. ultralytics/utils/triton.py +12 -10
  127. ultralytics/utils/tuner.py +49 -47
  128. {ultralytics-8.0.238.dist-info → ultralytics-8.0.239.dist-info}/METADATA +2 -1
  129. ultralytics-8.0.239.dist-info/RECORD +188 -0
  130. ultralytics-8.0.238.dist-info/RECORD +0 -188
  131. {ultralytics-8.0.238.dist-info → ultralytics-8.0.239.dist-info}/LICENSE +0 -0
  132. {ultralytics-8.0.238.dist-info → ultralytics-8.0.239.dist-info}/WHEEL +0 -0
  133. {ultralytics-8.0.238.dist-info → ultralytics-8.0.239.dist-info}/entry_points.txt +0 -0
  134. {ultralytics-8.0.238.dist-info → ultralytics-8.0.239.dist-info}/top_level.txt +0 -0
@@ -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 = {'preprocess': None, 'inference': None, 'postprocess': None} # milliseconds per image
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 = 'boxes', 'masks', 'probs', 'keypoints', 'obb'
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('__getitem__', idx)
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('cpu')
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('numpy')
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('cuda')
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('to', *args, **kwargs)
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='Arial.ttf',
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 = torch.as_tensor(img, dtype=torch.float16, device=pred_masks.data.device).permute(
239
- 2, 0, 1).flip(0).contiguous() / 255
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 = ('' if id is None else f'id:{id} ') + names[c]
248
- label = (f'{name} {conf:.2f}' if conf else name) if labels else None
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 = ',\n'.join(f'{names[j] if names else j} {pred_probs.data[j]:.2f}' for j in pred_probs.top5)
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'{log_string}(no detections), '
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
- boxes = self.boxes
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'{probs.data[j]:.2f} {self.names[j]}') for j in probs.top5]
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, ) * save_conf + (() if id is None else (id, ))
308
- texts.append(('%g ' * len(line)).rstrip() % line)
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, 'a') as f:
313
- f.writelines(text + '\n' for text in texts)
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('im.jpg')):
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('WARNING ⚠️ Classify task do not support `save_crop`.')
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('WARNING ⚠️ OBB task do not support `save_crop`.')
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(d.xyxy,
331
- self.orig_img.copy(),
332
- file=Path(save_dir) / self.names[int(d.cls)] / f'{Path(file_name)}.jpg',
333
- BGR=True)
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('Warning: Classify task do not support `tojson` yet.')
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 = {'x1': row[0] / w, 'y1': row[1] / h, 'x2': row[2] / w, 'y2': row[3] / h}
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 = {'name': name, 'class': class_id, 'confidence': conf, 'box': box}
361
+ result = {"name": name, "class": class_id, "confidence": conf, "box": box}
353
362
  if self.boxes.is_track:
354
- result['track_id'] = int(row[-3]) # track ID
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['segments'] = {'x': (x / w).tolist(), 'y': (y / h).tolist()}
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['keypoints'] = {'x': (x / w).tolist(), 'y': (y / h).tolist(), 'visible': visible.tolist()}
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'expected 6 or 7 values but got {n}' # xyxy, track_id, conf, cls
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'expected 7 or 8 values but got {n}' # xywh, rotation, track_id, conf, cls
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):