ultralytics 8.3.85__py3-none-any.whl → 8.3.87__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 (42) hide show
  1. tests/test_solutions.py +21 -2
  2. ultralytics/__init__.py +1 -1
  3. ultralytics/cfg/__init__.py +17 -25
  4. ultralytics/cfg/datasets/Argoverse.yaml +15 -13
  5. ultralytics/cfg/datasets/GlobalWheat2020.yaml +24 -10
  6. ultralytics/cfg/datasets/ImageNet.yaml +1 -1
  7. ultralytics/cfg/datasets/Objects365.yaml +21 -21
  8. ultralytics/cfg/datasets/SKU-110K.yaml +11 -11
  9. ultralytics/cfg/datasets/VOC.yaml +34 -28
  10. ultralytics/cfg/datasets/VisDrone.yaml +19 -15
  11. ultralytics/cfg/datasets/coco-pose.yaml +11 -8
  12. ultralytics/cfg/datasets/coco.yaml +11 -8
  13. ultralytics/cfg/datasets/lvis.yaml +12 -8
  14. ultralytics/cfg/datasets/open-images-v7.yaml +25 -20
  15. ultralytics/cfg/datasets/xView.yaml +28 -26
  16. ultralytics/cfg/models/11/yolo11-cls-resnet18.yaml +1 -1
  17. ultralytics/cfg/models/11/yolo11-cls.yaml +6 -6
  18. ultralytics/data/annotator.py +1 -1
  19. ultralytics/data/base.py +1 -1
  20. ultralytics/data/converter.py +6 -6
  21. ultralytics/data/loaders.py +1 -1
  22. ultralytics/data/split_dota.py +2 -2
  23. ultralytics/data/utils.py +4 -4
  24. ultralytics/engine/exporter.py +3 -3
  25. ultralytics/engine/results.py +77 -42
  26. ultralytics/engine/trainer.py +12 -6
  27. ultralytics/engine/tuner.py +4 -3
  28. ultralytics/engine/validator.py +1 -1
  29. ultralytics/models/yolo/obb/val.py +2 -2
  30. ultralytics/nn/autobackend.py +3 -2
  31. ultralytics/nn/tasks.py +1 -1
  32. ultralytics/solutions/parking_management.py +19 -4
  33. ultralytics/utils/__init__.py +3 -4
  34. ultralytics/utils/benchmarks.py +5 -5
  35. ultralytics/utils/callbacks/comet.py +37 -5
  36. ultralytics/utils/loss.py +1 -1
  37. {ultralytics-8.3.85.dist-info → ultralytics-8.3.87.dist-info}/METADATA +8 -8
  38. {ultralytics-8.3.85.dist-info → ultralytics-8.3.87.dist-info}/RECORD +42 -42
  39. {ultralytics-8.3.85.dist-info → ultralytics-8.3.87.dist-info}/WHEEL +1 -1
  40. {ultralytics-8.3.85.dist-info → ultralytics-8.3.87.dist-info}/LICENSE +0 -0
  41. {ultralytics-8.3.85.dist-info → ultralytics-8.3.87.dist-info}/entry_points.txt +0 -0
  42. {ultralytics-8.3.85.dist-info → ultralytics-8.3.87.dist-info}/top_level.txt +0 -0
@@ -620,30 +620,33 @@ names:
620
620
 
621
621
  # Download script/URL (optional) ---------------------------------------------------------------------------------------
622
622
  download: |
623
- from ultralytics.utils import LOGGER, SETTINGS, Path, is_ubuntu, get_ubuntu_version
623
+ import warnings
624
+
625
+ from ultralytics.utils import LOGGER, SETTINGS, Path, get_ubuntu_version, is_ubuntu
624
626
  from ultralytics.utils.checks import check_requirements, check_version
625
627
 
626
- check_requirements('fiftyone')
627
- if is_ubuntu() and check_version(get_ubuntu_version(), '>=22.04'):
628
+ check_requirements("fiftyone")
629
+ if is_ubuntu() and check_version(get_ubuntu_version(), ">=22.04"):
628
630
  # Ubuntu>=22.04 patch https://github.com/voxel51/fiftyone/issues/2961#issuecomment-1666519347
629
- check_requirements('fiftyone-db-ubuntu2204')
630
-
631
+ check_requirements("fiftyone-db-ubuntu2204")
632
+
631
633
  import fiftyone as fo
632
634
  import fiftyone.zoo as foz
633
- import warnings
634
635
 
635
- name = 'open-images-v7'
636
+ name = "open-images-v7"
636
637
  fo.config.dataset_zoo_dir = Path(SETTINGS["datasets_dir"]) / "fiftyone" / name
637
638
  fraction = 1.0 # fraction of full dataset to use
638
- LOGGER.warning('WARNING ⚠️ Open Images V7 dataset requires at least **561 GB of free space. Starting download...')
639
- for split in 'train', 'validation': # 1743042 train, 41620 val images
640
- train = split == 'train'
639
+ LOGGER.warning("WARNING ⚠️ Open Images V7 dataset requires at least **561 GB of free space. Starting download...")
640
+ for split in "train", "validation": # 1743042 train, 41620 val images
641
+ train = split == "train"
641
642
 
642
643
  # Load Open Images dataset
643
- dataset = foz.load_zoo_dataset(name,
644
- split=split,
645
- label_types=['detections'],
646
- max_samples=round((1743042 if train else 41620) * fraction))
644
+ dataset = foz.load_zoo_dataset(
645
+ name,
646
+ split=split,
647
+ label_types=["detections"],
648
+ max_samples=round((1743042 if train else 41620) * fraction),
649
+ )
647
650
 
648
651
  # Define classes
649
652
  if train:
@@ -653,9 +656,11 @@ download: |
653
656
  # Export to YOLO format
654
657
  with warnings.catch_warnings():
655
658
  warnings.filterwarnings("ignore", category=UserWarning, module="fiftyone.utils.yolo")
656
- dataset.export(export_dir=str(Path(SETTINGS['datasets_dir']) / name),
657
- dataset_type=fo.types.YOLOv5Dataset,
658
- label_field='ground_truth',
659
- split='val' if split == 'validation' else split,
660
- classes=classes,
661
- overwrite=train)
659
+ dataset.export(
660
+ export_dir=str(Path(SETTINGS["datasets_dir"]) / name),
661
+ dataset_type=fo.types.YOLOv5Dataset,
662
+ label_field="ground_truth",
663
+ split="val" if split == "validation" else split,
664
+ classes=classes,
665
+ overwrite=train,
666
+ )
@@ -91,16 +91,16 @@ download: |
91
91
  from ultralytics.utils.ops import xyxy2xywhn
92
92
 
93
93
 
94
- def convert_labels(fname=Path('xView/xView_train.geojson')):
95
- # Convert xView geoJSON labels to YOLO format
94
+ def convert_labels(fname=Path("xView/xView_train.geojson")):
95
+ """Converts xView geoJSON labels to YOLO format, mapping classes to indices 0-59 and saving as text files."""
96
96
  path = fname.parent
97
- with open(fname) as f:
98
- print(f'Loading {fname}...')
97
+ with open(fname, encoding="utf-8") as f:
98
+ print(f"Loading {fname}...")
99
99
  data = json.load(f)
100
100
 
101
101
  # Make dirs
102
- labels = Path(path / 'labels' / 'train')
103
- os.system(f'rm -rf {labels}')
102
+ labels = Path(path / "labels" / "train")
103
+ os.system(f"rm -rf {labels}")
104
104
  labels.mkdir(parents=True, exist_ok=True)
105
105
 
106
106
  # xView classes 11-94 to 0-59
@@ -110,44 +110,46 @@ download: |
110
110
  47, 48, 49, -1, 50, 51, -1, 52, -1, -1, -1, 53, 54, -1, 55, -1, -1, 56, -1, 57, -1, 58, 59]
111
111
 
112
112
  shapes = {}
113
- for feature in tqdm(data['features'], desc=f'Converting {fname}'):
114
- p = feature['properties']
115
- if p['bounds_imcoords']:
116
- id = p['image_id']
117
- file = path / 'train_images' / id
113
+ for feature in tqdm(data["features"], desc=f"Converting {fname}"):
114
+ p = feature["properties"]
115
+ if p["bounds_imcoords"]:
116
+ id = p["image_id"]
117
+ file = path / "train_images" / id
118
118
  if file.exists(): # 1395.tif missing
119
119
  try:
120
- box = np.array([int(num) for num in p['bounds_imcoords'].split(",")])
121
- assert box.shape[0] == 4, f'incorrect box shape {box.shape[0]}'
122
- cls = p['type_id']
120
+ box = np.array([int(num) for num in p["bounds_imcoords"].split(",")])
121
+ assert box.shape[0] == 4, f"incorrect box shape {box.shape[0]}"
122
+ cls = p["type_id"]
123
123
  cls = xview_class2index[int(cls)] # xView class to 0-60
124
- assert 59 >= cls >= 0, f'incorrect class index {cls}'
124
+ assert 59 >= cls >= 0, f"incorrect class index {cls}"
125
125
 
126
126
  # Write YOLO label
127
127
  if id not in shapes:
128
128
  shapes[id] = Image.open(file).size
129
129
  box = xyxy2xywhn(box[None].astype(np.float), w=shapes[id][0], h=shapes[id][1], clip=True)
130
- with open((labels / id).with_suffix('.txt'), 'a') as f:
130
+ with open((labels / id).with_suffix(".txt"), "a", encoding="utf-8") as f:
131
131
  f.write(f"{cls} {' '.join(f'{x:.6f}' for x in box[0])}\n") # write label.txt
132
132
  except Exception as e:
133
- print(f'WARNING: skipping one label for {file}: {e}')
133
+ print(f"WARNING: skipping one label for {file}: {e}")
134
134
 
135
135
 
136
136
  # Download manually from https://challenge.xviewdataset.org
137
- dir = Path(yaml['path']) # dataset root dir
138
- # urls = ['https://d307kc0mrhucc3.cloudfront.net/train_labels.zip', # train labels
139
- # 'https://d307kc0mrhucc3.cloudfront.net/train_images.zip', # 15G, 847 train images
140
- # 'https://d307kc0mrhucc3.cloudfront.net/val_images.zip'] # 5G, 282 val images (no labels)
137
+ dir = Path(yaml["path"]) # dataset root dir
138
+ # urls = [
139
+ # "https://d307kc0mrhucc3.cloudfront.net/train_labels.zip", # train labels
140
+ # "https://d307kc0mrhucc3.cloudfront.net/train_images.zip", # 15G, 847 train images
141
+ # "https://d307kc0mrhucc3.cloudfront.net/val_images.zip", # 5G, 282 val images (no labels)
142
+ # ]
141
143
  # download(urls, dir=dir)
142
144
 
143
145
  # Convert labels
144
- convert_labels(dir / 'xView_train.geojson')
146
+ convert_labels(dir / "xView_train.geojson")
145
147
 
146
148
  # Move images
147
- images = Path(dir / 'images')
149
+ images = Path(dir / "images")
148
150
  images.mkdir(parents=True, exist_ok=True)
149
- Path(dir / 'train_images').rename(dir / 'images' / 'train')
150
- Path(dir / 'val_images').rename(dir / 'images' / 'val')
151
+ Path(dir / "train_images").rename(dir / "images" / "train")
152
+ Path(dir / "val_images").rename(dir / "images" / "val")
151
153
 
152
154
  # Split
153
- autosplit(dir / 'images' / 'train')
155
+ autosplit(dir / "images" / "train")
@@ -5,7 +5,7 @@
5
5
  # Task docs: https://docs.ultralytics.com/tasks/classify
6
6
 
7
7
  # Parameters
8
- nc: 10 # number of classes
8
+ nc: 1000 # number of classes
9
9
 
10
10
  # ResNet18 backbone
11
11
  backbone:
@@ -5,14 +5,14 @@
5
5
  # Task docs: https://docs.ultralytics.com/tasks/classify
6
6
 
7
7
  # Parameters
8
- nc: 80 # number of classes
8
+ nc: 1000 # number of classes
9
9
  scales: # model compound scaling constants, i.e. 'model=yolo11n-cls.yaml' will call yolo11-cls.yaml with scale 'n'
10
10
  # [depth, width, max_channels]
11
- n: [0.50, 0.25, 1024] # summary: 151 layers, 1633584 parameters, 1633584 gradients, 3.3 GFLOPs
12
- s: [0.50, 0.50, 1024] # summary: 151 layers, 5545488 parameters, 5545488 gradients, 12.2 GFLOPs
13
- m: [0.50, 1.00, 512] # summary: 187 layers, 10455696 parameters, 10455696 gradients, 39.7 GFLOPs
14
- l: [1.00, 1.00, 512] # summary: 309 layers, 12937104 parameters, 12937104 gradients, 49.9 GFLOPs
15
- x: [1.00, 1.50, 512] # summary: 309 layers, 28458544 parameters, 28458544 gradients, 111.1 GFLOPs
11
+ n: [0.50, 0.25, 1024] # summary: 151 layers, 1633584 parameters, 1633584 gradients, 0.5 GFLOPs
12
+ s: [0.50, 0.50, 1024] # summary: 151 layers, 5545488 parameters, 5545488 gradients, 1.6 GFLOPs
13
+ m: [0.50, 1.00, 512] # summary: 187 layers, 10455696 parameters, 10455696 gradients, 5.0 GFLOPs
14
+ l: [1.00, 1.00, 512] # summary: 309 layers, 12937104 parameters, 12937104 gradients, 6.2 GFLOPs
15
+ x: [1.00, 1.50, 512] # summary: 309 layers, 28458544 parameters, 28458544 gradients, 13.7 GFLOPs
16
16
 
17
17
  # YOLO11n backbone
18
18
  backbone:
@@ -63,7 +63,7 @@ def auto_annotate(
63
63
  sam_results = sam_model(result.orig_img, bboxes=boxes, verbose=False, save=False, device=device)
64
64
  segments = sam_results[0].masks.xyn # noqa
65
65
 
66
- with open(f"{Path(output_dir) / Path(result.path).stem}.txt", "w") as f:
66
+ with open(f"{Path(output_dir) / Path(result.path).stem}.txt", "w", encoding="utf-8") as f:
67
67
  for i, s in enumerate(segments):
68
68
  if s.any():
69
69
  segment = map(str, s.reshape(-1).tolist())
ultralytics/data/base.py CHANGED
@@ -113,7 +113,7 @@ class BaseDataset(Dataset):
113
113
  f += glob.glob(str(p / "**" / "*.*"), recursive=True)
114
114
  # F = list(p.rglob('*.*')) # pathlib
115
115
  elif p.is_file(): # file
116
- with open(p) as t:
116
+ with open(p, encoding="utf-8") as t:
117
117
  t = t.read().strip().splitlines()
118
118
  parent = str(p.parent) + os.sep
119
119
  f += [x.replace("./", parent) if x.startswith("./") else x for x in t] # local to global path
@@ -323,7 +323,7 @@ def convert_coco(
323
323
  )
324
324
 
325
325
  # Write
326
- with open((fn / f).with_suffix(".txt"), "a") as file:
326
+ with open((fn / f).with_suffix(".txt"), "a", encoding="utf-8") as file:
327
327
  for i in range(len(bboxes)):
328
328
  if use_keypoints:
329
329
  line = (*(keypoints[i]),) # cls, box, keypoints
@@ -334,7 +334,8 @@ def convert_coco(
334
334
  file.write(("%g " * len(line)).rstrip() % line + "\n")
335
335
 
336
336
  if lvis:
337
- with open((Path(save_dir) / json_file.name.replace("lvis_v1_", "").replace(".json", ".txt")), "a") as f:
337
+ filename = Path(save_dir) / json_file.name.replace("lvis_v1_", "").replace(".json", ".txt")
338
+ with open(filename, "a", encoding="utf-8") as f:
338
339
  f.writelines(f"{line}\n" for line in image_txt)
339
340
 
340
341
  LOGGER.info(f"{'LVIS' if lvis else 'COCO'} data converted successfully.\nResults saved to {save_dir.resolve()}")
@@ -411,7 +412,7 @@ def convert_segment_masks_to_yolo_seg(masks_dir, output_dir, classes):
411
412
  yolo_format_data.append(yolo_format)
412
413
  # Save Ultralytics YOLO format data to file
413
414
  output_path = Path(output_dir) / f"{mask_path.stem}.txt"
414
- with open(output_path, "w") as file:
415
+ with open(output_path, "w", encoding="utf-8") as file:
415
416
  for item in yolo_format_data:
416
417
  line = " ".join(map(str, item))
417
418
  file.write(line + "\n")
@@ -605,7 +606,6 @@ def yolo_bbox2segment(im_dir, save_dir=None, sam_model="sam_b.pt", device=None):
605
606
  """
606
607
  from ultralytics import SAM
607
608
  from ultralytics.data import YOLODataset
608
- from ultralytics.utils import LOGGER
609
609
  from ultralytics.utils.ops import xywh2xyxy
610
610
 
611
611
  # NOTE: add placeholder to pass class index check
@@ -639,7 +639,7 @@ def yolo_bbox2segment(im_dir, save_dir=None, sam_model="sam_b.pt", device=None):
639
639
  continue
640
640
  line = (int(cls[i]), *s.reshape(-1))
641
641
  texts.append(("%g " * len(line)).rstrip() % line)
642
- with open(txt_file, "a") as f:
642
+ with open(txt_file, "a", encoding="utf-8") as f:
643
643
  f.writelines(text + "\n" for text in texts)
644
644
  LOGGER.info(f"Generated segment labels saved in {save_dir}")
645
645
 
@@ -689,7 +689,7 @@ def create_synthetic_coco_dataset():
689
689
  # Read image filenames from label list file
690
690
  label_list_file = dir / f"{subset}.txt"
691
691
  if label_list_file.exists():
692
- with open(label_list_file) as f:
692
+ with open(label_list_file, encoding="utf-8") as f:
693
693
  image_files = [dir / line.strip() for line in f]
694
694
 
695
695
  # Submit all tasks
@@ -106,7 +106,7 @@ class LoadStreams:
106
106
  self.caps = [None] * n # video capture objects
107
107
  self.imgs = [[] for _ in range(n)] # images
108
108
  self.shape = [[] for _ in range(n)] # image shapes
109
- self.sources = [ops.clean_str(x) for x in sources] # clean source names for later
109
+ self.sources = [ops.clean_str(x).replace(os.sep, "_") for x in sources] # clean source names for later
110
110
  for i, s in enumerate(sources): # index, source
111
111
  # Start thread to read frames from video stream
112
112
  st = f"{i + 1}/{n}: {s}... "
@@ -87,7 +87,7 @@ def load_yolo_dota(data_root, split="train"):
87
87
  annos = []
88
88
  for im_file, lb_file in zip(im_files, lb_files):
89
89
  w, h = exif_size(Image.open(im_file))
90
- with open(lb_file) as f:
90
+ with open(lb_file, encoding="utf-8") as f:
91
91
  lb = [x.split() for x in f.read().strip().splitlines() if len(x)]
92
92
  lb = np.array(lb, dtype=np.float32)
93
93
  annos.append(dict(ori_size=(h, w), label=lb, filepath=im_file))
@@ -191,7 +191,7 @@ def crop_and_save(anno, windows, window_objs, im_dir, lb_dir, allow_background_i
191
191
  label[:, 1::2] /= pw
192
192
  label[:, 2::2] /= ph
193
193
 
194
- with open(Path(lb_dir) / f"{new_name}.txt", "w") as f:
194
+ with open(Path(lb_dir) / f"{new_name}.txt", "w", encoding="utf-8") as f:
195
195
  for lb in label:
196
196
  formatted_coords = [f"{coord:.6g}" for coord in lb[1:]]
197
197
  f.write(f"{int(lb[0])} {' '.join(formatted_coords)}\n")
ultralytics/data/utils.py CHANGED
@@ -117,7 +117,7 @@ def verify_image_label(args):
117
117
  # Verify labels
118
118
  if os.path.isfile(lb_file):
119
119
  nf = 1 # label found
120
- with open(lb_file) as f:
120
+ with open(lb_file, encoding="utf-8") as f:
121
121
  lb = [x.split() for x in f.read().strip().splitlines() if len(x)]
122
122
  if any(len(x) > 6 for x in lb) and (not keypoint): # is segment
123
123
  classes = np.array([x[0] for x in lb], dtype=np.float32)
@@ -195,7 +195,7 @@ def visualize_image_annotations(image_path, txt_path, label_map):
195
195
  img = np.array(Image.open(image_path))
196
196
  img_height, img_width = img.shape[:2]
197
197
  annotations = []
198
- with open(txt_path) as file:
198
+ with open(txt_path, encoding="utf-8") as file:
199
199
  for line in file:
200
200
  class_id, x_center, y_center, width, height = map(float, line.split())
201
201
  x = (x_center - width / 2) * img_width
@@ -605,7 +605,7 @@ class HUBDatasetStats:
605
605
  self.hub_dir.mkdir(parents=True, exist_ok=True) # makes dataset-hub/
606
606
  stats_path = self.hub_dir / "stats.json"
607
607
  LOGGER.info(f"Saving {stats_path.resolve()}...")
608
- with open(stats_path, "w") as f:
608
+ with open(stats_path, "w", encoding="utf-8") as f:
609
609
  json.dump(self.stats, f) # save stats.json
610
610
  if verbose:
611
611
  LOGGER.info(json.dumps(self.stats, indent=2, sort_keys=False))
@@ -694,7 +694,7 @@ def autosplit(path=DATASETS_DIR / "coco8/images", weights=(0.9, 0.1, 0.0), annot
694
694
  LOGGER.info(f"Autosplitting images from {path}" + ", using *.txt labeled images only" * annotated_only)
695
695
  for i, img in TQDM(zip(indices, files), total=n):
696
696
  if not annotated_only or Path(img2label_paths([str(img)])[0]).exists(): # check label
697
- with open(path.parent / txt[i], "a") as f:
697
+ with open(path.parent / txt[i], "a", encoding="utf-8") as f:
698
698
  f.write(f"./{img.relative_to(path.parent).as_posix()}" + "\n") # add image to txt file
699
699
 
700
700
 
@@ -590,7 +590,7 @@ class Exporter:
590
590
  @try_export
591
591
  def export_openvino(self, prefix=colorstr("OpenVINO:")):
592
592
  """YOLO OpenVINO export."""
593
- check_requirements("openvino>=2024.0.0,<2025.0.0")
593
+ check_requirements("openvino>=2024.0.0,!=2025.0.0")
594
594
  import openvino as ov
595
595
 
596
596
  LOGGER.info(f"\n{prefix} starting export with openvino {ov.__version__}...")
@@ -1342,7 +1342,7 @@ class Exporter:
1342
1342
  )
1343
1343
 
1344
1344
  # Needed for imx models.
1345
- with open(f / "labels.txt", "w") as file:
1345
+ with open(f / "labels.txt", "w", encoding="utf-8") as file:
1346
1346
  file.writelines([f"{name}\n" for _, name in self.model.names.items()])
1347
1347
 
1348
1348
  return f, None
@@ -1368,7 +1368,7 @@ class Exporter:
1368
1368
 
1369
1369
  # Label file
1370
1370
  tmp_file = Path(file).parent / "temp_meta.txt"
1371
- with open(tmp_file, "w") as f:
1371
+ with open(tmp_file, "w", encoding="utf-8") as f:
1372
1372
  f.write(str(self.metadata))
1373
1373
 
1374
1374
  label_file = schema.AssociatedFileT()
@@ -188,43 +188,50 @@ class Results(SimpleClass):
188
188
  """
189
189
  A class for storing and manipulating inference results.
190
190
 
191
- This class encapsulates the functionality for handling detection, segmentation, pose estimation,
192
- and classification results from YOLO models.
191
+ This class provides methods for accessing, manipulating, and visualizing inference results from various
192
+ Ultralytics models, including detection, segmentation, classification, and pose estimation.
193
193
 
194
194
  Attributes:
195
- orig_img (numpy.ndarray): Original image as a numpy array.
195
+ orig_img (numpy.ndarray): The original image as a numpy array.
196
196
  orig_shape (Tuple[int, int]): Original image shape in (height, width) format.
197
- boxes (Boxes | None): Object containing detection bounding boxes.
198
- masks (Masks | None): Object containing detection masks.
199
- probs (Probs | None): Object containing class probabilities for classification tasks.
200
- keypoints (Keypoints | None): Object containing detected keypoints for each object.
201
- obb (OBB | None): Object containing oriented bounding boxes.
202
- speed (Dict[str, float | None]): Dictionary of preprocess, inference, and postprocess speeds.
203
- names (Dict[int, str]): Dictionary mapping class IDs to class names.
204
- path (str): Path to the image file.
205
- _keys (Tuple[str, ...]): Tuple of attribute names for internal use.
197
+ boxes (Boxes | None): Detected bounding boxes.
198
+ masks (Masks | None): Segmentation masks.
199
+ probs (Probs | None): Classification probabilities.
200
+ keypoints (Keypoints | None): Detected keypoints.
201
+ obb (OBB | None): Oriented bounding boxes.
202
+ speed (Dict): Dictionary containing inference speed information.
203
+ names (Dict): Dictionary mapping class indices to class names.
204
+ path (str): Path to the input image file.
205
+ save_dir (str | None): Directory to save results.
206
206
 
207
207
  Methods:
208
- update: Updates object attributes with new detection results.
209
- cpu: Returns a copy of the Results object with all tensors on CPU memory.
210
- numpy: Returns a copy of the Results object with all tensors as numpy arrays.
211
- cuda: Returns a copy of the Results object with all tensors on GPU memory.
212
- to: Returns a copy of the Results object with tensors on a specified device and dtype.
213
- new: Returns a new Results object with the same image, path, and names.
214
- plot: Plots detection results on an input image, returning an annotated image.
215
- show: Shows annotated results on screen.
216
- save: Saves annotated results to file.
217
- verbose: Returns a log string for each task, detailing detections and classifications.
208
+ update: Updates the Results object with new detection data.
209
+ cpu: Returns a copy of the Results object with all tensors moved to CPU memory.
210
+ numpy: Converts all tensors in the Results object to numpy arrays.
211
+ cuda: Moves all tensors in the Results object to GPU memory.
212
+ to: Moves all tensors to the specified device and dtype.
213
+ new: Creates a new Results object with the same image, path, names, and speed attributes.
214
+ plot: Plots detection results on an input RGB image.
215
+ show: Displays the image with annotated inference results.
216
+ save: Saves annotated inference results image to file.
217
+ verbose: Returns a log string for each task in the results.
218
218
  save_txt: Saves detection results to a text file.
219
- save_crop: Saves cropped detection images.
220
- tojson: Converts detection results to JSON format.
219
+ save_crop: Saves cropped detection images to specified directory.
220
+ summary: Converts inference results to a summarized dictionary.
221
+ to_df: Converts detection results to a Pandas Dataframe.
222
+ to_json: Converts detection results to JSON format.
223
+ to_csv: Converts detection results to a CSV format.
224
+ to_xml: Converts detection results to XML format.
225
+ to_html: Converts detection results to HTML format.
226
+ to_sql: Converts detection results to an SQL-compatible format.
221
227
 
222
228
  Examples:
223
229
  >>> results = model("path/to/image.jpg")
230
+ >>> result = results[0] # Get the first result
231
+ >>> boxes = result.boxes # Get the boxes for the first result
232
+ >>> masks = result.masks # Get the masks for the first result
224
233
  >>> for result in results:
225
- ... print(result.boxes) # Print detection boxes
226
- ... result.show() # Display the annotated image
227
- ... result.save(filename="result.jpg") # Save annotated image
234
+ >>> result.plot() # Plot detection results
228
235
  """
229
236
 
230
237
  def __init__(
@@ -717,7 +724,7 @@ class Results(SimpleClass):
717
724
 
718
725
  if texts:
719
726
  Path(txt_file).parent.mkdir(parents=True, exist_ok=True) # make directory
720
- with open(txt_file, "a") as f:
727
+ with open(txt_file, "a", encoding="utf-8") as f:
721
728
  f.writelines(text + "\n" for text in texts)
722
729
 
723
730
  def save_crop(self, save_dir, file_name=Path("im.jpg")):
@@ -766,8 +773,8 @@ class Results(SimpleClass):
766
773
  optionally mask segments and keypoints.
767
774
 
768
775
  Args:
769
- normalize (bool): Whether to normalize bounding box coordinates by image dimensions. Defaults to False.
770
- decimals (int): Number of decimal places to round the output values to. Defaults to 5.
776
+ normalize (bool): Whether to normalize bounding box coordinates by image dimensions.
777
+ decimals (int): Number of decimal places to round the output values to.
771
778
 
772
779
  Returns:
773
780
  (List[Dict]): A list of dictionaries, each containing summarized information for a single
@@ -832,8 +839,8 @@ class Results(SimpleClass):
832
839
 
833
840
  Args:
834
841
  normalize (bool): Whether to normalize the bounding box coordinates by the image dimensions.
835
- If True, coordinates will be returned as float values between 0 and 1. Defaults to False.
836
- decimals (int): Number of decimal places to round the output values to. Defaults to 5.
842
+ If True, coordinates will be returned as float values between 0 and 1.
843
+ decimals (int): Number of decimal places to round the output values to.
837
844
 
838
845
  Returns:
839
846
  (DataFrame): A Pandas Dataframe containing all the information in results in an organized way.
@@ -858,8 +865,8 @@ class Results(SimpleClass):
858
865
 
859
866
  Args:
860
867
  normalize (bool): Whether to normalize the bounding box coordinates by the image dimensions.
861
- If True, coordinates will be returned as float values between 0 and 1. Defaults to False.
862
- decimals (int): Number of decimal places to round the output values to. Defaults to 5.
868
+ If True, coordinates will be returned as float values between 0 and 1.
869
+ decimals (int): Number of decimal places to round the output values to.
863
870
  *args (Any): Variable length argument list to be passed to pandas.DataFrame.to_csv().
864
871
  **kwargs (Any): Arbitrary keyword arguments to be passed to pandas.DataFrame.to_csv().
865
872
 
@@ -885,8 +892,8 @@ class Results(SimpleClass):
885
892
 
886
893
  Args:
887
894
  normalize (bool): Whether to normalize the bounding box coordinates by the image dimensions.
888
- If True, coordinates will be returned as float values between 0 and 1. Defaults to False.
889
- decimals (int): Number of decimal places to round the output values to. Defaults to 5.
895
+ If True, coordinates will be returned as float values between 0 and 1.
896
+ decimals (int): Number of decimal places to round the output values to.
890
897
  *args (Any): Variable length argument list to be passed to pandas.DataFrame.to_xml().
891
898
  **kwargs (Any): Arbitrary keyword arguments to be passed to pandas.DataFrame.to_xml().
892
899
 
@@ -903,6 +910,34 @@ class Results(SimpleClass):
903
910
  df = self.to_df(normalize=normalize, decimals=decimals)
904
911
  return '<?xml version="1.0" encoding="utf-8"?>\n<root></root>' if df.empty else df.to_xml(*args, **kwargs)
905
912
 
913
+ def to_html(self, normalize=False, decimals=5, index=False, *args, **kwargs):
914
+ """
915
+ Converts detection results to HTML format.
916
+
917
+ This method serializes the detection results into an HTML format. It includes information
918
+ about detected objects such as bounding boxes, class names, confidence scores, and optionally
919
+ segmentation masks and keypoints.
920
+
921
+ Args:
922
+ normalize (bool): Whether to normalize the bounding box coordinates by the image dimensions.
923
+ If True, coordinates will be returned as float values between 0 and 1.
924
+ decimals (int): Number of decimal places to round the output values to.
925
+ index (bool): Whether to include the DataFrame index in the HTML output.
926
+ *args (Any): Variable length argument list to be passed to pandas.DataFrame.to_html().
927
+ **kwargs (Any): Arbitrary keyword arguments to be passed to pandas.DataFrame.to_html().
928
+
929
+ Returns:
930
+ (str): An HTML string containing all the information in results in an organized way.
931
+
932
+ Examples:
933
+ >>> results = model("path/to/image.jpg")
934
+ >>> for result in results:
935
+ >>> html_result = result.to_html()
936
+ >>> print(html_result)
937
+ """
938
+ df = self.to_df(normalize=normalize, decimals=decimals)
939
+ return "<table></table>" if df.empty else df.to_html(index=index, *args, **kwargs)
940
+
906
941
  def tojson(self, normalize=False, decimals=5):
907
942
  """Deprecated version of to_json()."""
908
943
  LOGGER.warning("WARNING ⚠️ 'result.tojson()' is deprecated, replace with 'result.to_json()'.")
@@ -918,8 +953,8 @@ class Results(SimpleClass):
918
953
 
919
954
  Args:
920
955
  normalize (bool): Whether to normalize the bounding box coordinates by the image dimensions.
921
- If True, coordinates will be returned as float values between 0 and 1. Defaults to False.
922
- decimals (int): Number of decimal places to round the output values to. Defaults to 5.
956
+ If True, coordinates will be returned as float values between 0 and 1.
957
+ decimals (int): Number of decimal places to round the output values to.
923
958
 
924
959
  Returns:
925
960
  (str): A JSON string containing the serialized detection results.
@@ -951,11 +986,11 @@ class Results(SimpleClass):
951
986
  and optionally segmentation masks, keypoints or oriented bounding boxes.
952
987
 
953
988
  Args:
954
- table_name (str): Name of the SQL table where the data will be inserted. Defaults to "detection_results".
989
+ table_name (str): Name of the SQL table where the data will be inserted.
955
990
  normalize (bool): Whether to normalize the bounding box coordinates by the image dimensions.
956
- If True, coordinates will be returned as float values between 0 and 1. Defaults to False.
957
- decimals (int): Number of decimal places to round the bounding boxes values to. Defaults to 5.
958
- db_path (str): Path to the SQLite database file. Defaults to "results.db".
991
+ If True, coordinates will be returned as float values between 0 and 1.
992
+ decimals (int): Number of decimal places to round the bounding boxes values to.
993
+ db_path (str): Path to the SQLite database file.
959
994
 
960
995
  Examples:
961
996
  >>> results = model("path/to/image.jpg")
@@ -452,7 +452,8 @@ class BaseTrainer:
452
452
  self.scheduler.last_epoch = self.epoch # do not move
453
453
  self.stop |= epoch >= self.epochs # stop if exceeded epochs
454
454
  self.run_callbacks("on_fit_epoch_end")
455
- self._clear_memory()
455
+ if self._get_memory(fraction=True) > 0.9:
456
+ self._clear_memory() # clear if memory utilization > 90%
456
457
 
457
458
  # Early Stopping
458
459
  if RANK != -1: # if DDP training
@@ -485,15 +486,20 @@ class BaseTrainer:
485
486
  max_num_obj=max_num_obj,
486
487
  ) # returns batch size
487
488
 
488
- def _get_memory(self):
489
- """Get accelerator memory utilization in GB."""
489
+ def _get_memory(self, fraction=False):
490
+ """Get accelerator memory utilization in GB or fraction."""
491
+ memory, total = 0, 0
490
492
  if self.device.type == "mps":
491
493
  memory = torch.mps.driver_allocated_memory()
494
+ if fraction:
495
+ total = torch.mps.get_mem_info()[0]
492
496
  elif self.device.type == "cpu":
493
- memory = 0
497
+ pass
494
498
  else:
495
499
  memory = torch.cuda.memory_reserved()
496
- return memory / (2**30)
500
+ if fraction:
501
+ total = torch.cuda.get_device_properties(self.device).total_memory
502
+ return ((memory / total) if total > 0 else 0) if fraction else (memory / 2**30)
497
503
 
498
504
  def _clear_memory(self):
499
505
  """Clear accelerator memory on different platforms."""
@@ -666,7 +672,7 @@ class BaseTrainer:
666
672
  n = len(metrics) + 2 # number of cols
667
673
  s = "" if self.csv.exists() else (("%s," * n % tuple(["epoch", "time"] + keys)).rstrip(",") + "\n") # header
668
674
  t = time.time() - self.train_time_start
669
- with open(self.csv, "a") as f:
675
+ with open(self.csv, "a", encoding="utf-8") as f:
670
676
  f.write(s + ("%.6g," * n % tuple([self.epoch + 1, t] + vals)).rstrip(",") + "\n")
671
677
 
672
678
  def plot_metrics(self):
@@ -191,8 +191,9 @@ class Tuner:
191
191
  weights_dir = save_dir / "weights"
192
192
  try:
193
193
  # Train YOLO model with mutated hyperparameters (run in subprocess to avoid dataloader hang)
194
- cmd = ["yolo", "train", *(f"{k}={v}" for k, v in train_args.items())]
195
- return_code = subprocess.run(" ".join(cmd), check=True, shell=True).returncode
194
+ launch = [__import__("sys").executable, "-m", "ultralytics.cfg.__init__"] # workaround yolo not found
195
+ cmd = [*launch, "train", *(f"{k}={v}" for k, v in train_args.items())]
196
+ return_code = subprocess.run(cmd, check=True).returncode
196
197
  ckpt_file = weights_dir / ("best.pt" if (weights_dir / "best.pt").exists() else "last.pt")
197
198
  metrics = torch.load(ckpt_file)["train_metrics"]
198
199
  assert return_code == 0, "training failed"
@@ -204,7 +205,7 @@ class Tuner:
204
205
  fitness = metrics.get("fitness", 0.0)
205
206
  log_row = [round(fitness, 5)] + [mutated_hyp[k] for k in self.space.keys()]
206
207
  headers = "" if self.tune_csv.exists() else (",".join(["fitness"] + list(self.space.keys())) + "\n")
207
- with open(self.tune_csv, "a") as f:
208
+ with open(self.tune_csv, "a", encoding="utf-8") as f:
208
209
  f.write(headers + ",".join(map(str, log_row)) + "\n")
209
210
 
210
211
  # Get best results
@@ -213,7 +213,7 @@ class BaseValidator:
213
213
  )
214
214
  )
215
215
  if self.args.save_json and self.jdict:
216
- with open(str(self.save_dir / "predictions.json"), "w") as f:
216
+ with open(str(self.save_dir / "predictions.json"), "w", encoding="utf-8") as f:
217
217
  LOGGER.info(f"Saving {f.name}...")
218
218
  json.dump(self.jdict, f) # flatten and save
219
219
  stats = self.eval_json(stats) # update stats