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.
- tests/test_solutions.py +21 -2
- ultralytics/__init__.py +1 -1
- ultralytics/cfg/__init__.py +17 -25
- ultralytics/cfg/datasets/Argoverse.yaml +15 -13
- ultralytics/cfg/datasets/GlobalWheat2020.yaml +24 -10
- ultralytics/cfg/datasets/ImageNet.yaml +1 -1
- ultralytics/cfg/datasets/Objects365.yaml +21 -21
- ultralytics/cfg/datasets/SKU-110K.yaml +11 -11
- ultralytics/cfg/datasets/VOC.yaml +34 -28
- ultralytics/cfg/datasets/VisDrone.yaml +19 -15
- ultralytics/cfg/datasets/coco-pose.yaml +11 -8
- ultralytics/cfg/datasets/coco.yaml +11 -8
- ultralytics/cfg/datasets/lvis.yaml +12 -8
- ultralytics/cfg/datasets/open-images-v7.yaml +25 -20
- ultralytics/cfg/datasets/xView.yaml +28 -26
- ultralytics/cfg/models/11/yolo11-cls-resnet18.yaml +1 -1
- ultralytics/cfg/models/11/yolo11-cls.yaml +6 -6
- ultralytics/data/annotator.py +1 -1
- ultralytics/data/base.py +1 -1
- ultralytics/data/converter.py +6 -6
- ultralytics/data/loaders.py +1 -1
- ultralytics/data/split_dota.py +2 -2
- ultralytics/data/utils.py +4 -4
- ultralytics/engine/exporter.py +3 -3
- ultralytics/engine/results.py +77 -42
- ultralytics/engine/trainer.py +12 -6
- ultralytics/engine/tuner.py +4 -3
- ultralytics/engine/validator.py +1 -1
- ultralytics/models/yolo/obb/val.py +2 -2
- ultralytics/nn/autobackend.py +3 -2
- ultralytics/nn/tasks.py +1 -1
- ultralytics/solutions/parking_management.py +19 -4
- ultralytics/utils/__init__.py +3 -4
- ultralytics/utils/benchmarks.py +5 -5
- ultralytics/utils/callbacks/comet.py +37 -5
- ultralytics/utils/loss.py +1 -1
- {ultralytics-8.3.85.dist-info → ultralytics-8.3.87.dist-info}/METADATA +8 -8
- {ultralytics-8.3.85.dist-info → ultralytics-8.3.87.dist-info}/RECORD +42 -42
- {ultralytics-8.3.85.dist-info → ultralytics-8.3.87.dist-info}/WHEEL +1 -1
- {ultralytics-8.3.85.dist-info → ultralytics-8.3.87.dist-info}/LICENSE +0 -0
- {ultralytics-8.3.85.dist-info → ultralytics-8.3.87.dist-info}/entry_points.txt +0 -0
- {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
|
-
|
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(
|
627
|
-
if is_ubuntu() and check_version(get_ubuntu_version(),
|
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(
|
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 =
|
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(
|
639
|
-
for split in
|
640
|
-
train = split ==
|
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(
|
644
|
-
|
645
|
-
|
646
|
-
|
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(
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
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(
|
95
|
-
|
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
|
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 /
|
103
|
-
os.system(f
|
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[
|
114
|
-
p = feature[
|
115
|
-
if p[
|
116
|
-
id = p[
|
117
|
-
file = path /
|
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[
|
121
|
-
assert box.shape[0] == 4, f
|
122
|
-
cls = p[
|
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
|
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(
|
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
|
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[
|
138
|
-
# urls = [
|
139
|
-
#
|
140
|
-
#
|
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 /
|
146
|
+
convert_labels(dir / "xView_train.geojson")
|
145
147
|
|
146
148
|
# Move images
|
147
|
-
images = Path(dir /
|
149
|
+
images = Path(dir / "images")
|
148
150
|
images.mkdir(parents=True, exist_ok=True)
|
149
|
-
Path(dir /
|
150
|
-
Path(dir /
|
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 /
|
155
|
+
autosplit(dir / "images" / "train")
|
@@ -5,14 +5,14 @@
|
|
5
5
|
# Task docs: https://docs.ultralytics.com/tasks/classify
|
6
6
|
|
7
7
|
# Parameters
|
8
|
-
nc:
|
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,
|
12
|
-
s: [0.50, 0.50, 1024] # summary: 151 layers, 5545488 parameters, 5545488 gradients,
|
13
|
-
m: [0.50, 1.00, 512] # summary: 187 layers, 10455696 parameters, 10455696 gradients,
|
14
|
-
l: [1.00, 1.00, 512] # summary: 309 layers, 12937104 parameters, 12937104 gradients,
|
15
|
-
x: [1.00, 1.50, 512] # summary: 309 layers, 28458544 parameters, 28458544 gradients,
|
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:
|
ultralytics/data/annotator.py
CHANGED
@@ -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
|
ultralytics/data/converter.py
CHANGED
@@ -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
|
-
|
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
|
ultralytics/data/loaders.py
CHANGED
@@ -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}... "
|
ultralytics/data/split_dota.py
CHANGED
@@ -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
|
|
ultralytics/engine/exporter.py
CHANGED
@@ -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
|
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()
|
ultralytics/engine/results.py
CHANGED
@@ -188,43 +188,50 @@ class Results(SimpleClass):
|
|
188
188
|
"""
|
189
189
|
A class for storing and manipulating inference results.
|
190
190
|
|
191
|
-
This class
|
192
|
-
|
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):
|
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):
|
198
|
-
masks (Masks | None):
|
199
|
-
probs (Probs | None):
|
200
|
-
keypoints (Keypoints | None):
|
201
|
-
obb (OBB | None):
|
202
|
-
speed (Dict
|
203
|
-
names (Dict
|
204
|
-
path (str): Path to the image file.
|
205
|
-
|
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
|
209
|
-
cpu: Returns a copy of the Results object with all tensors
|
210
|
-
numpy:
|
211
|
-
cuda:
|
212
|
-
to:
|
213
|
-
new:
|
214
|
-
plot: Plots detection results on an input
|
215
|
-
show:
|
216
|
-
save: Saves annotated results to file.
|
217
|
-
verbose: Returns a log string for each task
|
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
|
-
|
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
|
-
|
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.
|
770
|
-
decimals (int): Number of decimal places to round the output values to.
|
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.
|
836
|
-
decimals (int): Number of decimal places to round the output values to.
|
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.
|
862
|
-
decimals (int): Number of decimal places to round the output values to.
|
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.
|
889
|
-
decimals (int): Number of decimal places to round the output values to.
|
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.
|
922
|
-
decimals (int): Number of decimal places to round the output values to.
|
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.
|
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.
|
957
|
-
decimals (int): Number of decimal places to round the bounding boxes values to.
|
958
|
-
db_path (str): Path to the SQLite database file.
|
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")
|
ultralytics/engine/trainer.py
CHANGED
@@ -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.
|
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
|
-
|
497
|
+
pass
|
494
498
|
else:
|
495
499
|
memory = torch.cuda.memory_reserved()
|
496
|
-
|
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):
|
ultralytics/engine/tuner.py
CHANGED
@@ -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
|
-
|
195
|
-
|
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
|
ultralytics/engine/validator.py
CHANGED
@@ -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
|