ultralytics 8.1.28__py3-none-any.whl → 8.3.62__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/__init__.py +22 -0
- tests/conftest.py +83 -0
- tests/test_cli.py +122 -0
- tests/test_cuda.py +155 -0
- tests/test_engine.py +131 -0
- tests/test_exports.py +216 -0
- tests/test_integrations.py +150 -0
- tests/test_python.py +615 -0
- tests/test_solutions.py +94 -0
- ultralytics/__init__.py +11 -8
- ultralytics/cfg/__init__.py +569 -131
- ultralytics/cfg/datasets/Argoverse.yaml +2 -1
- ultralytics/cfg/datasets/DOTAv1.5.yaml +3 -2
- ultralytics/cfg/datasets/DOTAv1.yaml +3 -2
- ultralytics/cfg/datasets/GlobalWheat2020.yaml +3 -2
- ultralytics/cfg/datasets/ImageNet.yaml +2 -1
- ultralytics/cfg/datasets/Objects365.yaml +5 -4
- ultralytics/cfg/datasets/SKU-110K.yaml +2 -1
- ultralytics/cfg/datasets/VOC.yaml +3 -2
- ultralytics/cfg/datasets/VisDrone.yaml +6 -5
- ultralytics/cfg/datasets/african-wildlife.yaml +25 -0
- ultralytics/cfg/datasets/brain-tumor.yaml +23 -0
- ultralytics/cfg/datasets/carparts-seg.yaml +3 -2
- ultralytics/cfg/datasets/coco-pose.yaml +7 -6
- ultralytics/cfg/datasets/coco.yaml +3 -2
- ultralytics/cfg/datasets/coco128-seg.yaml +4 -3
- ultralytics/cfg/datasets/coco128.yaml +4 -3
- ultralytics/cfg/datasets/coco8-pose.yaml +3 -2
- ultralytics/cfg/datasets/coco8-seg.yaml +3 -2
- ultralytics/cfg/datasets/coco8.yaml +3 -2
- ultralytics/cfg/datasets/crack-seg.yaml +3 -2
- ultralytics/cfg/datasets/dog-pose.yaml +24 -0
- ultralytics/cfg/datasets/dota8.yaml +3 -2
- ultralytics/cfg/datasets/hand-keypoints.yaml +26 -0
- ultralytics/cfg/datasets/lvis.yaml +1236 -0
- ultralytics/cfg/datasets/medical-pills.yaml +22 -0
- ultralytics/cfg/datasets/open-images-v7.yaml +2 -1
- ultralytics/cfg/datasets/package-seg.yaml +5 -4
- ultralytics/cfg/datasets/signature.yaml +21 -0
- ultralytics/cfg/datasets/tiger-pose.yaml +3 -2
- ultralytics/cfg/datasets/xView.yaml +2 -1
- ultralytics/cfg/default.yaml +14 -11
- ultralytics/cfg/models/11/yolo11-cls-resnet18.yaml +24 -0
- ultralytics/cfg/models/11/yolo11-cls.yaml +33 -0
- ultralytics/cfg/models/11/yolo11-obb.yaml +50 -0
- ultralytics/cfg/models/11/yolo11-pose.yaml +51 -0
- ultralytics/cfg/models/11/yolo11-seg.yaml +50 -0
- ultralytics/cfg/models/11/yolo11.yaml +50 -0
- ultralytics/cfg/models/rt-detr/rtdetr-l.yaml +5 -2
- ultralytics/cfg/models/rt-detr/rtdetr-resnet101.yaml +5 -2
- ultralytics/cfg/models/rt-detr/rtdetr-resnet50.yaml +5 -2
- ultralytics/cfg/models/rt-detr/rtdetr-x.yaml +5 -2
- ultralytics/cfg/models/v10/yolov10b.yaml +45 -0
- ultralytics/cfg/models/v10/yolov10l.yaml +45 -0
- ultralytics/cfg/models/v10/yolov10m.yaml +45 -0
- ultralytics/cfg/models/v10/yolov10n.yaml +45 -0
- ultralytics/cfg/models/v10/yolov10s.yaml +45 -0
- ultralytics/cfg/models/v10/yolov10x.yaml +45 -0
- ultralytics/cfg/models/v3/yolov3-spp.yaml +5 -2
- ultralytics/cfg/models/v3/yolov3-tiny.yaml +5 -2
- ultralytics/cfg/models/v3/yolov3.yaml +5 -2
- ultralytics/cfg/models/v5/yolov5-p6.yaml +5 -2
- ultralytics/cfg/models/v5/yolov5.yaml +5 -2
- ultralytics/cfg/models/v6/yolov6.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-cls-resnet101.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-cls-resnet50.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-cls.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-ghost-p2.yaml +6 -2
- ultralytics/cfg/models/v8/yolov8-ghost-p6.yaml +6 -2
- ultralytics/cfg/models/v8/yolov8-ghost.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-obb.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-p2.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-p6.yaml +10 -7
- ultralytics/cfg/models/v8/yolov8-pose-p6.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-pose.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-rtdetr.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-seg-p6.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-seg.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-world.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-worldv2.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8.yaml +5 -2
- ultralytics/cfg/models/v9/yolov9c-seg.yaml +41 -0
- ultralytics/cfg/models/v9/yolov9c.yaml +30 -25
- ultralytics/cfg/models/v9/yolov9e-seg.yaml +64 -0
- ultralytics/cfg/models/v9/yolov9e.yaml +46 -42
- ultralytics/cfg/models/v9/yolov9m.yaml +41 -0
- ultralytics/cfg/models/v9/yolov9s.yaml +41 -0
- ultralytics/cfg/models/v9/yolov9t.yaml +41 -0
- ultralytics/cfg/solutions/default.yaml +24 -0
- ultralytics/cfg/trackers/botsort.yaml +8 -5
- ultralytics/cfg/trackers/bytetrack.yaml +8 -5
- ultralytics/data/__init__.py +14 -3
- ultralytics/data/annotator.py +37 -15
- ultralytics/data/augment.py +1783 -289
- ultralytics/data/base.py +62 -27
- ultralytics/data/build.py +36 -8
- ultralytics/data/converter.py +196 -36
- ultralytics/data/dataset.py +233 -94
- ultralytics/data/loaders.py +199 -96
- ultralytics/data/split_dota.py +39 -29
- ultralytics/data/utils.py +110 -40
- ultralytics/engine/__init__.py +1 -1
- ultralytics/engine/exporter.py +569 -242
- ultralytics/engine/model.py +604 -252
- ultralytics/engine/predictor.py +22 -11
- ultralytics/engine/results.py +1228 -218
- ultralytics/engine/trainer.py +190 -129
- ultralytics/engine/tuner.py +18 -18
- ultralytics/engine/validator.py +18 -15
- ultralytics/hub/__init__.py +31 -13
- ultralytics/hub/auth.py +11 -7
- ultralytics/hub/google/__init__.py +159 -0
- ultralytics/hub/session.py +128 -94
- ultralytics/hub/utils.py +20 -21
- ultralytics/models/__init__.py +4 -2
- ultralytics/models/fastsam/__init__.py +2 -3
- ultralytics/models/fastsam/model.py +26 -4
- ultralytics/models/fastsam/predict.py +127 -63
- ultralytics/models/fastsam/utils.py +1 -44
- ultralytics/models/fastsam/val.py +1 -1
- ultralytics/models/nas/__init__.py +1 -1
- ultralytics/models/nas/model.py +21 -10
- ultralytics/models/nas/predict.py +3 -6
- ultralytics/models/nas/val.py +4 -4
- ultralytics/models/rtdetr/__init__.py +1 -1
- ultralytics/models/rtdetr/model.py +1 -1
- ultralytics/models/rtdetr/predict.py +6 -8
- ultralytics/models/rtdetr/train.py +6 -2
- ultralytics/models/rtdetr/val.py +3 -3
- ultralytics/models/sam/__init__.py +3 -3
- ultralytics/models/sam/amg.py +29 -23
- ultralytics/models/sam/build.py +211 -13
- ultralytics/models/sam/model.py +91 -30
- ultralytics/models/sam/modules/__init__.py +1 -1
- ultralytics/models/sam/modules/blocks.py +1129 -0
- ultralytics/models/sam/modules/decoders.py +381 -53
- ultralytics/models/sam/modules/encoders.py +515 -324
- ultralytics/models/sam/modules/memory_attention.py +237 -0
- ultralytics/models/sam/modules/sam.py +969 -21
- ultralytics/models/sam/modules/tiny_encoder.py +425 -154
- ultralytics/models/sam/modules/transformer.py +159 -60
- ultralytics/models/sam/modules/utils.py +293 -0
- ultralytics/models/sam/predict.py +1263 -132
- ultralytics/models/utils/__init__.py +1 -1
- ultralytics/models/utils/loss.py +36 -24
- ultralytics/models/utils/ops.py +3 -7
- ultralytics/models/yolo/__init__.py +3 -3
- ultralytics/models/yolo/classify/__init__.py +1 -1
- ultralytics/models/yolo/classify/predict.py +7 -8
- ultralytics/models/yolo/classify/train.py +17 -22
- ultralytics/models/yolo/classify/val.py +8 -4
- ultralytics/models/yolo/detect/__init__.py +1 -1
- ultralytics/models/yolo/detect/predict.py +3 -5
- ultralytics/models/yolo/detect/train.py +11 -4
- ultralytics/models/yolo/detect/val.py +90 -52
- ultralytics/models/yolo/model.py +14 -9
- ultralytics/models/yolo/obb/__init__.py +1 -1
- ultralytics/models/yolo/obb/predict.py +2 -2
- ultralytics/models/yolo/obb/train.py +5 -3
- ultralytics/models/yolo/obb/val.py +41 -23
- ultralytics/models/yolo/pose/__init__.py +1 -1
- ultralytics/models/yolo/pose/predict.py +3 -5
- ultralytics/models/yolo/pose/train.py +2 -2
- ultralytics/models/yolo/pose/val.py +51 -17
- ultralytics/models/yolo/segment/__init__.py +1 -1
- ultralytics/models/yolo/segment/predict.py +3 -5
- ultralytics/models/yolo/segment/train.py +2 -2
- ultralytics/models/yolo/segment/val.py +60 -19
- ultralytics/models/yolo/world/__init__.py +5 -0
- ultralytics/models/yolo/world/train.py +92 -0
- ultralytics/models/yolo/world/train_world.py +109 -0
- ultralytics/nn/__init__.py +1 -1
- ultralytics/nn/autobackend.py +228 -93
- ultralytics/nn/modules/__init__.py +39 -14
- ultralytics/nn/modules/activation.py +21 -0
- ultralytics/nn/modules/block.py +527 -67
- ultralytics/nn/modules/conv.py +24 -7
- ultralytics/nn/modules/head.py +177 -34
- ultralytics/nn/modules/transformer.py +6 -5
- ultralytics/nn/modules/utils.py +1 -2
- ultralytics/nn/tasks.py +225 -77
- ultralytics/solutions/__init__.py +30 -1
- ultralytics/solutions/ai_gym.py +96 -143
- ultralytics/solutions/analytics.py +247 -0
- ultralytics/solutions/distance_calculation.py +78 -135
- ultralytics/solutions/heatmap.py +93 -247
- ultralytics/solutions/object_counter.py +184 -259
- ultralytics/solutions/parking_management.py +246 -0
- ultralytics/solutions/queue_management.py +112 -0
- ultralytics/solutions/region_counter.py +116 -0
- ultralytics/solutions/security_alarm.py +144 -0
- ultralytics/solutions/solutions.py +178 -0
- ultralytics/solutions/speed_estimation.py +86 -174
- ultralytics/solutions/streamlit_inference.py +190 -0
- ultralytics/solutions/trackzone.py +68 -0
- ultralytics/trackers/__init__.py +1 -1
- ultralytics/trackers/basetrack.py +32 -13
- ultralytics/trackers/bot_sort.py +61 -28
- ultralytics/trackers/byte_tracker.py +83 -51
- ultralytics/trackers/track.py +21 -6
- ultralytics/trackers/utils/__init__.py +1 -1
- ultralytics/trackers/utils/gmc.py +62 -48
- ultralytics/trackers/utils/kalman_filter.py +166 -35
- ultralytics/trackers/utils/matching.py +40 -21
- ultralytics/utils/__init__.py +511 -239
- ultralytics/utils/autobatch.py +40 -22
- ultralytics/utils/benchmarks.py +266 -85
- ultralytics/utils/callbacks/__init__.py +1 -1
- ultralytics/utils/callbacks/base.py +1 -3
- ultralytics/utils/callbacks/clearml.py +7 -6
- ultralytics/utils/callbacks/comet.py +39 -17
- ultralytics/utils/callbacks/dvc.py +1 -1
- ultralytics/utils/callbacks/hub.py +16 -16
- ultralytics/utils/callbacks/mlflow.py +28 -24
- ultralytics/utils/callbacks/neptune.py +6 -2
- ultralytics/utils/callbacks/raytune.py +3 -4
- ultralytics/utils/callbacks/tensorboard.py +18 -18
- ultralytics/utils/callbacks/wb.py +27 -20
- ultralytics/utils/checks.py +160 -100
- ultralytics/utils/dist.py +2 -1
- ultralytics/utils/downloads.py +44 -37
- ultralytics/utils/errors.py +1 -1
- ultralytics/utils/files.py +72 -38
- ultralytics/utils/instance.py +41 -19
- ultralytics/utils/loss.py +84 -56
- ultralytics/utils/metrics.py +61 -56
- ultralytics/utils/ops.py +94 -89
- ultralytics/utils/patches.py +30 -14
- ultralytics/utils/plotting.py +600 -269
- ultralytics/utils/tal.py +67 -26
- ultralytics/utils/torch_utils.py +302 -102
- ultralytics/utils/triton.py +2 -1
- ultralytics/utils/tuner.py +21 -12
- ultralytics-8.3.62.dist-info/METADATA +370 -0
- ultralytics-8.3.62.dist-info/RECORD +241 -0
- {ultralytics-8.1.28.dist-info → ultralytics-8.3.62.dist-info}/WHEEL +1 -1
- ultralytics/data/explorer/__init__.py +0 -5
- ultralytics/data/explorer/explorer.py +0 -472
- ultralytics/data/explorer/gui/__init__.py +0 -1
- ultralytics/data/explorer/gui/dash.py +0 -268
- ultralytics/data/explorer/utils.py +0 -166
- ultralytics/models/fastsam/prompt.py +0 -357
- ultralytics-8.1.28.dist-info/METADATA +0 -373
- ultralytics-8.1.28.dist-info/RECORD +0 -197
- {ultralytics-8.1.28.dist-info → ultralytics-8.3.62.dist-info}/LICENSE +0 -0
- {ultralytics-8.1.28.dist-info → ultralytics-8.3.62.dist-info}/entry_points.txt +0 -0
- {ultralytics-8.1.28.dist-info → ultralytics-8.3.62.dist-info}/top_level.txt +0 -0
ultralytics/utils/downloads.py
CHANGED
@@ -1,6 +1,5 @@
|
|
1
|
-
# Ultralytics
|
1
|
+
# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
|
2
2
|
|
3
|
-
import contextlib
|
4
3
|
import re
|
5
4
|
import shutil
|
6
5
|
import subprocess
|
@@ -17,12 +16,14 @@ from ultralytics.utils import LOGGER, TQDM, checks, clean_url, emojis, is_online
|
|
17
16
|
# Define Ultralytics GitHub assets maintained at https://github.com/ultralytics/assets
|
18
17
|
GITHUB_ASSETS_REPO = "ultralytics/assets"
|
19
18
|
GITHUB_ASSETS_NAMES = (
|
20
|
-
[f"yolov8{k}{suffix}.pt" for k in "nsmlx" for suffix in ("", "-cls", "-seg", "-pose", "-obb")]
|
19
|
+
[f"yolov8{k}{suffix}.pt" for k in "nsmlx" for suffix in ("", "-cls", "-seg", "-pose", "-obb", "-oiv7")]
|
20
|
+
+ [f"yolo11{k}{suffix}.pt" for k in "nsmlx" for suffix in ("", "-cls", "-seg", "-pose", "-obb")]
|
21
21
|
+ [f"yolov5{k}{resolution}u.pt" for k in "nsmlx" for resolution in ("", "6")]
|
22
22
|
+ [f"yolov3{k}u.pt" for k in ("", "-spp", "-tiny")]
|
23
23
|
+ [f"yolov8{k}-world.pt" for k in "smlx"]
|
24
24
|
+ [f"yolov8{k}-worldv2.pt" for k in "smlx"]
|
25
|
-
+ [f"yolov9{k}.pt" for k in "
|
25
|
+
+ [f"yolov9{k}.pt" for k in "tsmce"]
|
26
|
+
+ [f"yolov10{k}.pt" for k in "nsmblx"]
|
26
27
|
+ [f"yolo_nas_{k}.pt" for k in "sml"]
|
27
28
|
+ [f"sam_{k}.pt" for k in "bl"]
|
28
29
|
+ [f"FastSAM-{k}.pt" for k in "sx"]
|
@@ -33,17 +34,17 @@ GITHUB_ASSETS_NAMES = (
|
|
33
34
|
GITHUB_ASSETS_STEMS = [Path(k).stem for k in GITHUB_ASSETS_NAMES]
|
34
35
|
|
35
36
|
|
36
|
-
def is_url(url, check=
|
37
|
+
def is_url(url, check=False):
|
37
38
|
"""
|
38
39
|
Validates if the given string is a URL and optionally checks if the URL exists online.
|
39
40
|
|
40
41
|
Args:
|
41
42
|
url (str): The string to be validated as a URL.
|
42
43
|
check (bool, optional): If True, performs an additional check to see if the URL exists online.
|
43
|
-
Defaults to
|
44
|
+
Defaults to False.
|
44
45
|
|
45
46
|
Returns:
|
46
|
-
(bool): Returns True
|
47
|
+
(bool): Returns True for a valid URL. If 'check' is True, also returns True if the URL exists online.
|
47
48
|
Returns False otherwise.
|
48
49
|
|
49
50
|
Example:
|
@@ -51,7 +52,7 @@ def is_url(url, check=True):
|
|
51
52
|
valid = is_url("https://www.example.com")
|
52
53
|
```
|
53
54
|
"""
|
54
|
-
|
55
|
+
try:
|
55
56
|
url = str(url)
|
56
57
|
result = parse.urlparse(url)
|
57
58
|
assert all([result.scheme, result.netloc]) # check if is url
|
@@ -59,7 +60,8 @@ def is_url(url, check=True):
|
|
59
60
|
with request.urlopen(url) as response:
|
60
61
|
return response.getcode() == 200 # check if exists online
|
61
62
|
return True
|
62
|
-
|
63
|
+
except Exception:
|
64
|
+
return False
|
63
65
|
|
64
66
|
|
65
67
|
def delete_dsstore(path, files_to_delete=(".DS_Store", "__MACOSX")):
|
@@ -74,7 +76,7 @@ def delete_dsstore(path, files_to_delete=(".DS_Store", "__MACOSX")):
|
|
74
76
|
```python
|
75
77
|
from ultralytics.utils.downloads import delete_dsstore
|
76
78
|
|
77
|
-
delete_dsstore(
|
79
|
+
delete_dsstore("path/to/dir")
|
78
80
|
```
|
79
81
|
|
80
82
|
Note:
|
@@ -106,7 +108,7 @@ def zip_directory(directory, compress=True, exclude=(".DS_Store", "__MACOSX"), p
|
|
106
108
|
```python
|
107
109
|
from ultralytics.utils.downloads import zip_directory
|
108
110
|
|
109
|
-
file = zip_directory(
|
111
|
+
file = zip_directory("path/to/dir")
|
110
112
|
```
|
111
113
|
"""
|
112
114
|
from zipfile import ZIP_DEFLATED, ZIP_STORED, ZipFile
|
@@ -136,7 +138,7 @@ def unzip_file(file, path=None, exclude=(".DS_Store", "__MACOSX"), exist_ok=Fals
|
|
136
138
|
If a path is not provided, the function will use the parent directory of the zipfile as the default path.
|
137
139
|
|
138
140
|
Args:
|
139
|
-
file (str): The path to the zipfile to be extracted.
|
141
|
+
file (str | Path): The path to the zipfile to be extracted.
|
140
142
|
path (str, optional): The path to extract the zipfile to. Defaults to None.
|
141
143
|
exclude (tuple, optional): A tuple of filename strings to be excluded. Defaults to ('.DS_Store', '__MACOSX').
|
142
144
|
exist_ok (bool, optional): Whether to overwrite existing contents if they exist. Defaults to False.
|
@@ -152,7 +154,7 @@ def unzip_file(file, path=None, exclude=(".DS_Store", "__MACOSX"), exist_ok=Fals
|
|
152
154
|
```python
|
153
155
|
from ultralytics.utils.downloads import unzip_file
|
154
156
|
|
155
|
-
dir = unzip_file(
|
157
|
+
dir = unzip_file("path/to/file.zip")
|
156
158
|
```
|
157
159
|
"""
|
158
160
|
from zipfile import BadZipFile, ZipFile, is_zipfile
|
@@ -167,13 +169,15 @@ def unzip_file(file, path=None, exclude=(".DS_Store", "__MACOSX"), exist_ok=Fals
|
|
167
169
|
files = [f for f in zipObj.namelist() if all(x not in f for x in exclude)]
|
168
170
|
top_level_dirs = {Path(f).parts[0] for f in files}
|
169
171
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
else:
|
172
|
+
# Decide to unzip directly or unzip into a directory
|
173
|
+
unzip_as_dir = len(top_level_dirs) == 1 # (len(files) > 1 and not files[0].endswith("/"))
|
174
|
+
if unzip_as_dir:
|
174
175
|
# Zip has 1 top-level directory
|
175
176
|
extract_path = path # i.e. ../datasets
|
176
|
-
path = Path(path) / list(top_level_dirs)[0] # i.e. ../datasets/
|
177
|
+
path = Path(path) / list(top_level_dirs)[0] # i.e. extract coco8/ dir to ../datasets/
|
178
|
+
else:
|
179
|
+
# Zip has multiple files at top level
|
180
|
+
path = extract_path = Path(path) / Path(file).stem # i.e. extract multiple files to ../datasets/coco8/
|
177
181
|
|
178
182
|
# Check if destination directory already exists and contains files
|
179
183
|
if path.exists() and any(path.iterdir()) and not exist_ok:
|
@@ -191,13 +195,14 @@ def unzip_file(file, path=None, exclude=(".DS_Store", "__MACOSX"), exist_ok=Fals
|
|
191
195
|
return path # return unzip dir
|
192
196
|
|
193
197
|
|
194
|
-
def check_disk_space(url="https://ultralytics.com/assets/
|
198
|
+
def check_disk_space(url="https://ultralytics.com/assets/coco8.zip", path=Path.cwd(), sf=1.5, hard=True):
|
195
199
|
"""
|
196
200
|
Check if there is sufficient disk space to download and store a file.
|
197
201
|
|
198
202
|
Args:
|
199
|
-
url (str, optional): The URL to the file. Defaults to 'https://ultralytics.com/assets/
|
200
|
-
|
203
|
+
url (str, optional): The URL to the file. Defaults to 'https://ultralytics.com/assets/coco8.zip'.
|
204
|
+
path (str | Path, optional): The path or drive to check the available free space on.
|
205
|
+
sf (float, optional): Safety factor, the multiplier for the required free space. Defaults to 1.5.
|
201
206
|
hard (bool, optional): Whether to throw an error or not on insufficient disk space. Defaults to True.
|
202
207
|
|
203
208
|
Returns:
|
@@ -212,7 +217,7 @@ def check_disk_space(url="https://ultralytics.com/assets/coco128.zip", sf=1.5, h
|
|
212
217
|
# Check file size
|
213
218
|
gib = 1 << 30 # bytes per GiB
|
214
219
|
data = int(r.headers.get("Content-Length", 0)) / gib # file size (GB)
|
215
|
-
total, used, free = (x / gib for x in shutil.disk_usage(
|
220
|
+
total, used, free = (x / gib for x in shutil.disk_usage(path)) # bytes
|
216
221
|
|
217
222
|
if data * sf < free:
|
218
223
|
return True # sufficient space
|
@@ -264,8 +269,7 @@ def get_google_drive_file_info(link):
|
|
264
269
|
for k, v in response.cookies.items():
|
265
270
|
if k.startswith("download_warning"):
|
266
271
|
drive_url += f"&confirm={v}" # v is token
|
267
|
-
cd
|
268
|
-
if cd:
|
272
|
+
if cd := response.headers.get("content-disposition"):
|
269
273
|
filename = re.findall('filename="(.+)"', cd)[0]
|
270
274
|
return drive_url, filename
|
271
275
|
|
@@ -316,10 +320,14 @@ def safe_download(
|
|
316
320
|
if "://" not in str(url) and Path(url).is_file(): # URL exists ('://' check required in Windows Python<3.10)
|
317
321
|
f = Path(url) # filename
|
318
322
|
elif not f.is_file(): # URL and file do not exist
|
319
|
-
|
323
|
+
uri = (url if gdrive else clean_url(url)).replace( # cleaned and aliased url
|
324
|
+
"https://github.com/ultralytics/assets/releases/download/v0.0.0/",
|
325
|
+
"https://ultralytics.com/assets/", # assets alias
|
326
|
+
)
|
327
|
+
desc = f"Downloading {uri} to '{f}'"
|
320
328
|
LOGGER.info(f"{desc}...")
|
321
329
|
f.parent.mkdir(parents=True, exist_ok=True) # make directory if missing
|
322
|
-
check_disk_space(url)
|
330
|
+
check_disk_space(url, path=f.parent)
|
323
331
|
for i in range(retry + 1):
|
324
332
|
try:
|
325
333
|
if curl or i > 0: # curl download with retry, continue
|
@@ -350,18 +358,18 @@ def safe_download(
|
|
350
358
|
f.unlink() # remove partial downloads
|
351
359
|
except Exception as e:
|
352
360
|
if i == 0 and not is_online():
|
353
|
-
raise ConnectionError(emojis(f"❌ Download failure for {
|
361
|
+
raise ConnectionError(emojis(f"❌ Download failure for {uri}. Environment is not online.")) from e
|
354
362
|
elif i >= retry:
|
355
|
-
raise ConnectionError(emojis(f"❌ Download failure for {
|
356
|
-
LOGGER.warning(f"⚠️ Download failure, retrying {i + 1}/{retry} {
|
363
|
+
raise ConnectionError(emojis(f"❌ Download failure for {uri}. Retry limit reached.")) from e
|
364
|
+
LOGGER.warning(f"⚠️ Download failure, retrying {i + 1}/{retry} {uri}...")
|
357
365
|
|
358
|
-
if unzip and f.exists() and f.suffix in
|
366
|
+
if unzip and f.exists() and f.suffix in {"", ".zip", ".tar", ".gz"}:
|
359
367
|
from zipfile import is_zipfile
|
360
368
|
|
361
369
|
unzip_dir = (dir or f.parent).resolve() # unzip to dir if provided else unzip in place
|
362
370
|
if is_zipfile(f):
|
363
371
|
unzip_dir = unzip_file(file=f, path=unzip_dir, exist_ok=exist_ok, progress=progress) # unzip
|
364
|
-
elif f.suffix in
|
372
|
+
elif f.suffix in {".tar", ".gz"}:
|
365
373
|
LOGGER.info(f"Unzipping {f} to {unzip_dir}...")
|
366
374
|
subprocess.run(["tar", "xf" if f.suffix == ".tar" else "xfz", f, "--directory", unzip_dir], check=True)
|
367
375
|
if delete:
|
@@ -384,10 +392,9 @@ def get_github_assets(repo="ultralytics/assets", version="latest", retry=False):
|
|
384
392
|
|
385
393
|
Example:
|
386
394
|
```python
|
387
|
-
tag, assets = get_github_assets(repo=
|
395
|
+
tag, assets = get_github_assets(repo="ultralytics/assets", version="latest")
|
388
396
|
```
|
389
397
|
"""
|
390
|
-
|
391
398
|
if version != "latest":
|
392
399
|
version = f"tags/{version}" # i.e. tags/v6.2
|
393
400
|
url = f"https://api.github.com/repos/{repo}/releases/{version}"
|
@@ -401,7 +408,7 @@ def get_github_assets(repo="ultralytics/assets", version="latest", retry=False):
|
|
401
408
|
return data["tag_name"], [x["name"] for x in data["assets"]] # tag, assets i.e. ['yolov8n.pt', 'yolov8s.pt', ...]
|
402
409
|
|
403
410
|
|
404
|
-
def attempt_download_asset(file, repo="ultralytics/assets", release="v8.
|
411
|
+
def attempt_download_asset(file, repo="ultralytics/assets", release="v8.3.0", **kwargs):
|
405
412
|
"""
|
406
413
|
Attempt to download a file from GitHub release assets if it is not found locally. The function checks for the file
|
407
414
|
locally first, then tries to download it from the specified GitHub repository release.
|
@@ -409,7 +416,7 @@ def attempt_download_asset(file, repo="ultralytics/assets", release="v8.1.0", **
|
|
409
416
|
Args:
|
410
417
|
file (str | Path): The filename or file path to be downloaded.
|
411
418
|
repo (str, optional): The GitHub repository in the format 'owner/repo'. Defaults to 'ultralytics/assets'.
|
412
|
-
release (str, optional): The specific release version to be downloaded. Defaults to 'v8.
|
419
|
+
release (str, optional): The specific release version to be downloaded. Defaults to 'v8.3.0'.
|
413
420
|
**kwargs (any): Additional keyword arguments for the download process.
|
414
421
|
|
415
422
|
Returns:
|
@@ -417,7 +424,7 @@ def attempt_download_asset(file, repo="ultralytics/assets", release="v8.1.0", **
|
|
417
424
|
|
418
425
|
Example:
|
419
426
|
```python
|
420
|
-
file_path = attempt_download_asset(
|
427
|
+
file_path = attempt_download_asset("yolo11n.pt", repo="ultralytics/assets", release="latest")
|
421
428
|
```
|
422
429
|
"""
|
423
430
|
from ultralytics.utils import SETTINGS # scoped for circular import
|
@@ -472,7 +479,7 @@ def download(url, dir=Path.cwd(), unzip=True, delete=False, curl=False, threads=
|
|
472
479
|
|
473
480
|
Example:
|
474
481
|
```python
|
475
|
-
download(
|
482
|
+
download("https://ultralytics.com/assets/example.zip", dir="path/to/dir", unzip=True)
|
476
483
|
```
|
477
484
|
"""
|
478
485
|
dir = Path(dir)
|
ultralytics/utils/errors.py
CHANGED
ultralytics/utils/files.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Ultralytics
|
1
|
+
# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
|
2
2
|
|
3
3
|
import contextlib
|
4
4
|
import glob
|
@@ -11,19 +11,44 @@ from pathlib import Path
|
|
11
11
|
|
12
12
|
|
13
13
|
class WorkingDirectory(contextlib.ContextDecorator):
|
14
|
-
"""
|
14
|
+
"""
|
15
|
+
A context manager and decorator for temporarily changing the working directory.
|
16
|
+
|
17
|
+
This class allows for the temporary change of the working directory using a context manager or decorator.
|
18
|
+
It ensures that the original working directory is restored after the context or decorated function completes.
|
19
|
+
|
20
|
+
Attributes:
|
21
|
+
dir (Path): The new directory to switch to.
|
22
|
+
cwd (Path): The original current working directory before the switch.
|
23
|
+
|
24
|
+
Methods:
|
25
|
+
__enter__: Changes the current directory to the specified directory.
|
26
|
+
__exit__: Restores the original working directory on context exit.
|
27
|
+
|
28
|
+
Examples:
|
29
|
+
Using as a context manager:
|
30
|
+
>>> with WorkingDirectory('/path/to/new/dir'):
|
31
|
+
>>> # Perform operations in the new directory
|
32
|
+
>>> pass
|
33
|
+
|
34
|
+
Using as a decorator:
|
35
|
+
>>> @WorkingDirectory('/path/to/new/dir')
|
36
|
+
>>> def some_function():
|
37
|
+
>>> # Perform operations in the new directory
|
38
|
+
>>> pass
|
39
|
+
"""
|
15
40
|
|
16
41
|
def __init__(self, new_dir):
|
17
|
-
"""Sets the working directory to 'new_dir' upon instantiation."""
|
42
|
+
"""Sets the working directory to 'new_dir' upon instantiation for use with context managers or decorators."""
|
18
43
|
self.dir = new_dir # new dir
|
19
44
|
self.cwd = Path.cwd().resolve() # current dir
|
20
45
|
|
21
46
|
def __enter__(self):
|
22
|
-
"""Changes the current directory to the specified directory."""
|
47
|
+
"""Changes the current working directory to the specified directory upon entering the context."""
|
23
48
|
os.chdir(self.dir)
|
24
49
|
|
25
50
|
def __exit__(self, exc_type, exc_val, exc_tb): # noqa
|
26
|
-
"""
|
51
|
+
"""Restores the original working directory when exiting the context."""
|
27
52
|
os.chdir(self.cwd)
|
28
53
|
|
29
54
|
|
@@ -35,20 +60,17 @@ def spaces_in_path(path):
|
|
35
60
|
file/directory back to its original location.
|
36
61
|
|
37
62
|
Args:
|
38
|
-
path (str | Path): The original path.
|
63
|
+
path (str | Path): The original path that may contain spaces.
|
39
64
|
|
40
65
|
Yields:
|
41
66
|
(Path): Temporary path with spaces replaced by underscores if spaces were present, otherwise the original path.
|
42
67
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
# Your code here
|
49
|
-
```
|
68
|
+
Examples:
|
69
|
+
Use the context manager to handle paths with spaces:
|
70
|
+
>>> from ultralytics.utils.files import spaces_in_path
|
71
|
+
>>> with spaces_in_path('/path/with spaces') as new_path:
|
72
|
+
>>> # Your code here
|
50
73
|
"""
|
51
|
-
|
52
74
|
# If path has spaces, replace them with underscores
|
53
75
|
if " " in str(path):
|
54
76
|
string = isinstance(path, str) # input type
|
@@ -84,21 +106,35 @@ def spaces_in_path(path):
|
|
84
106
|
|
85
107
|
def increment_path(path, exist_ok=False, sep="", mkdir=False):
|
86
108
|
"""
|
87
|
-
Increments a file or directory path, i.e
|
109
|
+
Increments a file or directory path, i.e., runs/exp --> runs/exp{sep}2, runs/exp{sep}3, ... etc.
|
88
110
|
|
89
|
-
If the path exists and exist_ok is not
|
111
|
+
If the path exists and `exist_ok` is not True, the path will be incremented by appending a number and `sep` to
|
90
112
|
the end of the path. If the path is a file, the file extension will be preserved. If the path is a directory, the
|
91
|
-
number will be appended directly to the end of the path. If mkdir is set to True, the path will be created as a
|
113
|
+
number will be appended directly to the end of the path. If `mkdir` is set to True, the path will be created as a
|
92
114
|
directory if it does not already exist.
|
93
115
|
|
94
116
|
Args:
|
95
|
-
path (str
|
96
|
-
exist_ok (bool
|
97
|
-
sep (str
|
98
|
-
mkdir (bool
|
117
|
+
path (str | pathlib.Path): Path to increment.
|
118
|
+
exist_ok (bool): If True, the path will not be incremented and returned as-is.
|
119
|
+
sep (str): Separator to use between the path and the incrementation number.
|
120
|
+
mkdir (bool): Create a directory if it does not exist.
|
99
121
|
|
100
122
|
Returns:
|
101
123
|
(pathlib.Path): Incremented path.
|
124
|
+
|
125
|
+
Examples:
|
126
|
+
Increment a directory path:
|
127
|
+
>>> from pathlib import Path
|
128
|
+
>>> path = Path("runs/exp")
|
129
|
+
>>> new_path = increment_path(path)
|
130
|
+
>>> print(new_path)
|
131
|
+
runs/exp2
|
132
|
+
|
133
|
+
Increment a file path:
|
134
|
+
>>> path = Path("runs/exp/results.txt")
|
135
|
+
>>> new_path = increment_path(path)
|
136
|
+
>>> print(new_path)
|
137
|
+
runs/exp/results2.txt
|
102
138
|
"""
|
103
139
|
path = Path(path) # os-agnostic
|
104
140
|
if path.exists() and not exist_ok:
|
@@ -118,19 +154,19 @@ def increment_path(path, exist_ok=False, sep="", mkdir=False):
|
|
118
154
|
|
119
155
|
|
120
156
|
def file_age(path=__file__):
|
121
|
-
"""Return days since last file
|
157
|
+
"""Return days since the last modification of the specified file."""
|
122
158
|
dt = datetime.now() - datetime.fromtimestamp(Path(path).stat().st_mtime) # delta
|
123
159
|
return dt.days # + dt.seconds / 86400 # fractional days
|
124
160
|
|
125
161
|
|
126
162
|
def file_date(path=__file__):
|
127
|
-
"""
|
163
|
+
"""Returns the file modification date in 'YYYY-M-D' format."""
|
128
164
|
t = datetime.fromtimestamp(Path(path).stat().st_mtime)
|
129
165
|
return f"{t.year}-{t.month}-{t.day}"
|
130
166
|
|
131
167
|
|
132
168
|
def file_size(path):
|
133
|
-
"""
|
169
|
+
"""Returns the size of a file or directory in megabytes (MB)."""
|
134
170
|
if isinstance(path, (str, Path)):
|
135
171
|
mb = 1 << 20 # bytes to MiB (1024 ** 2)
|
136
172
|
path = Path(path)
|
@@ -142,27 +178,25 @@ def file_size(path):
|
|
142
178
|
|
143
179
|
|
144
180
|
def get_latest_run(search_dir="."):
|
145
|
-
"""
|
181
|
+
"""Returns the path to the most recent 'last.pt' file in the specified directory for resuming training."""
|
146
182
|
last_list = glob.glob(f"{search_dir}/**/last*.pt", recursive=True)
|
147
183
|
return max(last_list, key=os.path.getctime) if last_list else ""
|
148
184
|
|
149
185
|
|
150
|
-
def update_models(model_names=("
|
186
|
+
def update_models(model_names=("yolo11n.pt",), source_dir=Path("."), update_names=False):
|
151
187
|
"""
|
152
188
|
Updates and re-saves specified YOLO models in an 'updated_models' subdirectory.
|
153
189
|
|
154
190
|
Args:
|
155
|
-
model_names (
|
156
|
-
source_dir (Path
|
157
|
-
update_names (bool
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
from ultralytics.utils.files import update_models
|
162
|
-
|
163
|
-
model_names =
|
164
|
-
update_models(model_names)
|
165
|
-
```
|
191
|
+
model_names (Tuple[str, ...]): Model filenames to update.
|
192
|
+
source_dir (Path): Directory containing models and target subdirectory.
|
193
|
+
update_names (bool): Update model names from a data YAML.
|
194
|
+
|
195
|
+
Examples:
|
196
|
+
Update specified YOLO models and save them in 'updated_models' subdirectory:
|
197
|
+
>>> from ultralytics.utils.files import update_models
|
198
|
+
>>> model_names = ("yolo11n.pt", "yolov8s.pt")
|
199
|
+
>>> update_models(model_names, source_dir=Path("/models"), update_names=True)
|
166
200
|
"""
|
167
201
|
from ultralytics import YOLO
|
168
202
|
from ultralytics.nn.autobackend import default_class_names
|
@@ -185,4 +219,4 @@ def update_models(model_names=("yolov8n.pt",), source_dir=Path("."), update_name
|
|
185
219
|
|
186
220
|
# Save model using model.save()
|
187
221
|
print(f"Re-saving {model_name} model to {save_path}")
|
188
|
-
model.save(save_path
|
222
|
+
model.save(save_path)
|
ultralytics/utils/instance.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Ultralytics
|
1
|
+
# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
|
2
2
|
|
3
3
|
from collections import abc
|
4
4
|
from itertools import repeat
|
@@ -7,7 +7,7 @@ from typing import List
|
|
7
7
|
|
8
8
|
import numpy as np
|
9
9
|
|
10
|
-
from .ops import ltwh2xywh, ltwh2xyxy, xywh2ltwh, xywh2xyxy, xyxy2ltwh, xyxy2xywh
|
10
|
+
from .ops import ltwh2xywh, ltwh2xyxy, resample_segments, xywh2ltwh, xywh2xyxy, xyxy2ltwh, xyxy2xywh
|
11
11
|
|
12
12
|
|
13
13
|
def _ntuple(n):
|
@@ -28,7 +28,7 @@ to_4tuple = _ntuple(4)
|
|
28
28
|
# `ltwh` means left top and width, height(COCO format)
|
29
29
|
_formats = ["xyxy", "xywh", "ltwh"]
|
30
30
|
|
31
|
-
__all__ = ("Bboxes",) # tuple or list
|
31
|
+
__all__ = ("Bboxes", "Instances") # tuple or list
|
32
32
|
|
33
33
|
|
34
34
|
class Bboxes:
|
@@ -72,8 +72,11 @@ class Bboxes:
|
|
72
72
|
|
73
73
|
def areas(self):
|
74
74
|
"""Return box areas."""
|
75
|
-
|
76
|
-
|
75
|
+
return (
|
76
|
+
(self.bboxes[:, 2] - self.bboxes[:, 0]) * (self.bboxes[:, 3] - self.bboxes[:, 1]) # format xyxy
|
77
|
+
if self.format == "xyxy"
|
78
|
+
else self.bboxes[:, 3] * self.bboxes[:, 2] # format xywh or ltwh
|
79
|
+
)
|
77
80
|
|
78
81
|
# def denormalize(self, w, h):
|
79
82
|
# if not self.normalized:
|
@@ -93,8 +96,11 @@ class Bboxes:
|
|
93
96
|
|
94
97
|
def mul(self, scale):
|
95
98
|
"""
|
99
|
+
Multiply bounding box coordinates by scale factor(s).
|
100
|
+
|
96
101
|
Args:
|
97
|
-
scale (
|
102
|
+
scale (int | tuple | list): Scale factor(s) for four coordinates.
|
103
|
+
If int, the same scale is applied to all coordinates.
|
98
104
|
"""
|
99
105
|
if isinstance(scale, Number):
|
100
106
|
scale = to_4tuple(scale)
|
@@ -107,8 +113,11 @@ class Bboxes:
|
|
107
113
|
|
108
114
|
def add(self, offset):
|
109
115
|
"""
|
116
|
+
Add offset to bounding box coordinates.
|
117
|
+
|
110
118
|
Args:
|
111
|
-
offset (
|
119
|
+
offset (int | tuple | list): Offset(s) for four coordinates.
|
120
|
+
If int, the same offset is applied to all coordinates.
|
112
121
|
"""
|
113
122
|
if isinstance(offset, Number):
|
114
123
|
offset = to_4tuple(offset)
|
@@ -167,7 +176,7 @@ class Bboxes:
|
|
167
176
|
length as the number of bounding boxes.
|
168
177
|
"""
|
169
178
|
if isinstance(index, int):
|
170
|
-
return Bboxes(self.bboxes[index].
|
179
|
+
return Bboxes(self.bboxes[index].reshape(1, -1))
|
171
180
|
b = self.bboxes[index]
|
172
181
|
assert b.ndim == 2, f"Indexing on Bboxes with {index} failed to return a matrix!"
|
173
182
|
return Bboxes(b)
|
@@ -196,7 +205,7 @@ class Instances:
|
|
196
205
|
instances = Instances(
|
197
206
|
bboxes=np.array([[10, 10, 30, 30], [20, 20, 40, 40]]),
|
198
207
|
segments=[np.array([[5, 5], [10, 10]]), np.array([[15, 15], [20, 20]])],
|
199
|
-
keypoints=np.array([[[5, 5, 1], [10, 10, 1]], [[15, 15, 1], [20, 20, 1]]])
|
208
|
+
keypoints=np.array([[[5, 5, 1], [10, 10, 1]], [[15, 15, 1], [20, 20, 1]]]),
|
200
209
|
)
|
201
210
|
```
|
202
211
|
|
@@ -207,10 +216,14 @@ class Instances:
|
|
207
216
|
|
208
217
|
def __init__(self, bboxes, segments=None, keypoints=None, bbox_format="xywh", normalized=True) -> None:
|
209
218
|
"""
|
219
|
+
Initialize the object with bounding boxes, segments, and keypoints.
|
220
|
+
|
210
221
|
Args:
|
211
|
-
bboxes (ndarray):
|
212
|
-
segments (list | ndarray):
|
213
|
-
keypoints (ndarray):
|
222
|
+
bboxes (np.ndarray): Bounding boxes, shape [N, 4].
|
223
|
+
segments (list | np.ndarray, optional): Segmentation masks. Defaults to None.
|
224
|
+
keypoints (np.ndarray, optional): Keypoints, shape [N, 17, 3] and format (x, y, visible). Defaults to None.
|
225
|
+
bbox_format (str, optional): Format of bboxes. Defaults to "xywh".
|
226
|
+
normalized (bool, optional): Whether the coordinates are normalized. Defaults to True.
|
214
227
|
"""
|
215
228
|
self._bboxes = Bboxes(bboxes=bboxes, format=bbox_format)
|
216
229
|
self.keypoints = keypoints
|
@@ -227,7 +240,7 @@ class Instances:
|
|
227
240
|
return self._bboxes.areas()
|
228
241
|
|
229
242
|
def scale(self, scale_w, scale_h, bbox_only=False):
|
230
|
-
"""
|
243
|
+
"""Similar to denormalize func but without normalized sign."""
|
231
244
|
self._bboxes.mul(scale=(scale_w, scale_h, scale_w, scale_h))
|
232
245
|
if bbox_only:
|
233
246
|
return
|
@@ -340,11 +353,7 @@ class Instances:
|
|
340
353
|
self.keypoints[..., 1] = self.keypoints[..., 1].clip(0, h)
|
341
354
|
|
342
355
|
def remove_zero_area_boxes(self):
|
343
|
-
"""
|
344
|
-
Remove zero-area boxes, i.e. after clipping some boxes may have zero width or height.
|
345
|
-
|
346
|
-
This removes them.
|
347
|
-
"""
|
356
|
+
"""Remove zero-area boxes, i.e. after clipping some boxes may have zero width or height."""
|
348
357
|
good = self.bbox_areas > 0
|
349
358
|
if not all(good):
|
350
359
|
self._bboxes = self._bboxes[good]
|
@@ -397,7 +406,20 @@ class Instances:
|
|
397
406
|
normalized = instances_list[0].normalized
|
398
407
|
|
399
408
|
cat_boxes = np.concatenate([ins.bboxes for ins in instances_list], axis=axis)
|
400
|
-
|
409
|
+
seg_len = [b.segments.shape[1] for b in instances_list]
|
410
|
+
if len(set(seg_len)) > 1: # resample segments if there's different length
|
411
|
+
max_len = max(seg_len)
|
412
|
+
cat_segments = np.concatenate(
|
413
|
+
[
|
414
|
+
resample_segments(list(b.segments), max_len)
|
415
|
+
if len(b.segments)
|
416
|
+
else np.zeros((0, max_len, 2), dtype=np.float32) # re-generating empty segments
|
417
|
+
for b in instances_list
|
418
|
+
],
|
419
|
+
axis=axis,
|
420
|
+
)
|
421
|
+
else:
|
422
|
+
cat_segments = np.concatenate([b.segments for b in instances_list], axis=axis)
|
401
423
|
cat_keypoints = np.concatenate([b.keypoints for b in instances_list], axis=axis) if use_keypoint else None
|
402
424
|
return cls(cat_boxes, cat_segments, cat_keypoints, bbox_format, normalized)
|
403
425
|
|