ultralytics 8.1.29__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.
Files changed (247) hide show
  1. tests/__init__.py +22 -0
  2. tests/conftest.py +83 -0
  3. tests/test_cli.py +122 -0
  4. tests/test_cuda.py +155 -0
  5. tests/test_engine.py +131 -0
  6. tests/test_exports.py +216 -0
  7. tests/test_integrations.py +150 -0
  8. tests/test_python.py +615 -0
  9. tests/test_solutions.py +94 -0
  10. ultralytics/__init__.py +11 -8
  11. ultralytics/cfg/__init__.py +569 -131
  12. ultralytics/cfg/datasets/Argoverse.yaml +2 -1
  13. ultralytics/cfg/datasets/DOTAv1.5.yaml +3 -2
  14. ultralytics/cfg/datasets/DOTAv1.yaml +3 -2
  15. ultralytics/cfg/datasets/GlobalWheat2020.yaml +3 -2
  16. ultralytics/cfg/datasets/ImageNet.yaml +2 -1
  17. ultralytics/cfg/datasets/Objects365.yaml +5 -4
  18. ultralytics/cfg/datasets/SKU-110K.yaml +2 -1
  19. ultralytics/cfg/datasets/VOC.yaml +3 -2
  20. ultralytics/cfg/datasets/VisDrone.yaml +6 -5
  21. ultralytics/cfg/datasets/african-wildlife.yaml +25 -0
  22. ultralytics/cfg/datasets/brain-tumor.yaml +23 -0
  23. ultralytics/cfg/datasets/carparts-seg.yaml +3 -2
  24. ultralytics/cfg/datasets/coco-pose.yaml +7 -6
  25. ultralytics/cfg/datasets/coco.yaml +3 -2
  26. ultralytics/cfg/datasets/coco128-seg.yaml +4 -3
  27. ultralytics/cfg/datasets/coco128.yaml +4 -3
  28. ultralytics/cfg/datasets/coco8-pose.yaml +3 -2
  29. ultralytics/cfg/datasets/coco8-seg.yaml +3 -2
  30. ultralytics/cfg/datasets/coco8.yaml +3 -2
  31. ultralytics/cfg/datasets/crack-seg.yaml +3 -2
  32. ultralytics/cfg/datasets/dog-pose.yaml +24 -0
  33. ultralytics/cfg/datasets/dota8.yaml +3 -2
  34. ultralytics/cfg/datasets/hand-keypoints.yaml +26 -0
  35. ultralytics/cfg/datasets/lvis.yaml +1236 -0
  36. ultralytics/cfg/datasets/medical-pills.yaml +22 -0
  37. ultralytics/cfg/datasets/open-images-v7.yaml +2 -1
  38. ultralytics/cfg/datasets/package-seg.yaml +5 -4
  39. ultralytics/cfg/datasets/signature.yaml +21 -0
  40. ultralytics/cfg/datasets/tiger-pose.yaml +3 -2
  41. ultralytics/cfg/datasets/xView.yaml +2 -1
  42. ultralytics/cfg/default.yaml +14 -11
  43. ultralytics/cfg/models/11/yolo11-cls-resnet18.yaml +24 -0
  44. ultralytics/cfg/models/11/yolo11-cls.yaml +33 -0
  45. ultralytics/cfg/models/11/yolo11-obb.yaml +50 -0
  46. ultralytics/cfg/models/11/yolo11-pose.yaml +51 -0
  47. ultralytics/cfg/models/11/yolo11-seg.yaml +50 -0
  48. ultralytics/cfg/models/11/yolo11.yaml +50 -0
  49. ultralytics/cfg/models/rt-detr/rtdetr-l.yaml +5 -2
  50. ultralytics/cfg/models/rt-detr/rtdetr-resnet101.yaml +5 -2
  51. ultralytics/cfg/models/rt-detr/rtdetr-resnet50.yaml +5 -2
  52. ultralytics/cfg/models/rt-detr/rtdetr-x.yaml +5 -2
  53. ultralytics/cfg/models/v10/yolov10b.yaml +45 -0
  54. ultralytics/cfg/models/v10/yolov10l.yaml +45 -0
  55. ultralytics/cfg/models/v10/yolov10m.yaml +45 -0
  56. ultralytics/cfg/models/v10/yolov10n.yaml +45 -0
  57. ultralytics/cfg/models/v10/yolov10s.yaml +45 -0
  58. ultralytics/cfg/models/v10/yolov10x.yaml +45 -0
  59. ultralytics/cfg/models/v3/yolov3-spp.yaml +5 -2
  60. ultralytics/cfg/models/v3/yolov3-tiny.yaml +5 -2
  61. ultralytics/cfg/models/v3/yolov3.yaml +5 -2
  62. ultralytics/cfg/models/v5/yolov5-p6.yaml +5 -2
  63. ultralytics/cfg/models/v5/yolov5.yaml +5 -2
  64. ultralytics/cfg/models/v6/yolov6.yaml +5 -2
  65. ultralytics/cfg/models/v8/yolov8-cls-resnet101.yaml +5 -2
  66. ultralytics/cfg/models/v8/yolov8-cls-resnet50.yaml +5 -2
  67. ultralytics/cfg/models/v8/yolov8-cls.yaml +5 -2
  68. ultralytics/cfg/models/v8/yolov8-ghost-p2.yaml +6 -2
  69. ultralytics/cfg/models/v8/yolov8-ghost-p6.yaml +6 -2
  70. ultralytics/cfg/models/v8/yolov8-ghost.yaml +5 -2
  71. ultralytics/cfg/models/v8/yolov8-obb.yaml +5 -2
  72. ultralytics/cfg/models/v8/yolov8-p2.yaml +5 -2
  73. ultralytics/cfg/models/v8/yolov8-p6.yaml +10 -7
  74. ultralytics/cfg/models/v8/yolov8-pose-p6.yaml +5 -2
  75. ultralytics/cfg/models/v8/yolov8-pose.yaml +5 -2
  76. ultralytics/cfg/models/v8/yolov8-rtdetr.yaml +5 -2
  77. ultralytics/cfg/models/v8/yolov8-seg-p6.yaml +5 -2
  78. ultralytics/cfg/models/v8/yolov8-seg.yaml +5 -2
  79. ultralytics/cfg/models/v8/yolov8-world.yaml +5 -2
  80. ultralytics/cfg/models/v8/yolov8-worldv2.yaml +5 -2
  81. ultralytics/cfg/models/v8/yolov8.yaml +5 -2
  82. ultralytics/cfg/models/v9/yolov9c-seg.yaml +41 -0
  83. ultralytics/cfg/models/v9/yolov9c.yaml +30 -25
  84. ultralytics/cfg/models/v9/yolov9e-seg.yaml +64 -0
  85. ultralytics/cfg/models/v9/yolov9e.yaml +46 -42
  86. ultralytics/cfg/models/v9/yolov9m.yaml +41 -0
  87. ultralytics/cfg/models/v9/yolov9s.yaml +41 -0
  88. ultralytics/cfg/models/v9/yolov9t.yaml +41 -0
  89. ultralytics/cfg/solutions/default.yaml +24 -0
  90. ultralytics/cfg/trackers/botsort.yaml +8 -5
  91. ultralytics/cfg/trackers/bytetrack.yaml +8 -5
  92. ultralytics/data/__init__.py +14 -3
  93. ultralytics/data/annotator.py +37 -15
  94. ultralytics/data/augment.py +1783 -289
  95. ultralytics/data/base.py +62 -27
  96. ultralytics/data/build.py +36 -8
  97. ultralytics/data/converter.py +196 -36
  98. ultralytics/data/dataset.py +233 -94
  99. ultralytics/data/loaders.py +199 -96
  100. ultralytics/data/split_dota.py +39 -29
  101. ultralytics/data/utils.py +110 -40
  102. ultralytics/engine/__init__.py +1 -1
  103. ultralytics/engine/exporter.py +569 -242
  104. ultralytics/engine/model.py +604 -252
  105. ultralytics/engine/predictor.py +22 -11
  106. ultralytics/engine/results.py +1228 -218
  107. ultralytics/engine/trainer.py +190 -129
  108. ultralytics/engine/tuner.py +18 -18
  109. ultralytics/engine/validator.py +18 -15
  110. ultralytics/hub/__init__.py +31 -13
  111. ultralytics/hub/auth.py +11 -7
  112. ultralytics/hub/google/__init__.py +159 -0
  113. ultralytics/hub/session.py +128 -94
  114. ultralytics/hub/utils.py +20 -21
  115. ultralytics/models/__init__.py +4 -2
  116. ultralytics/models/fastsam/__init__.py +2 -3
  117. ultralytics/models/fastsam/model.py +26 -4
  118. ultralytics/models/fastsam/predict.py +127 -63
  119. ultralytics/models/fastsam/utils.py +1 -44
  120. ultralytics/models/fastsam/val.py +1 -1
  121. ultralytics/models/nas/__init__.py +1 -1
  122. ultralytics/models/nas/model.py +21 -10
  123. ultralytics/models/nas/predict.py +3 -6
  124. ultralytics/models/nas/val.py +4 -4
  125. ultralytics/models/rtdetr/__init__.py +1 -1
  126. ultralytics/models/rtdetr/model.py +1 -1
  127. ultralytics/models/rtdetr/predict.py +6 -8
  128. ultralytics/models/rtdetr/train.py +6 -2
  129. ultralytics/models/rtdetr/val.py +3 -3
  130. ultralytics/models/sam/__init__.py +3 -3
  131. ultralytics/models/sam/amg.py +29 -23
  132. ultralytics/models/sam/build.py +211 -13
  133. ultralytics/models/sam/model.py +91 -30
  134. ultralytics/models/sam/modules/__init__.py +1 -1
  135. ultralytics/models/sam/modules/blocks.py +1129 -0
  136. ultralytics/models/sam/modules/decoders.py +381 -53
  137. ultralytics/models/sam/modules/encoders.py +515 -324
  138. ultralytics/models/sam/modules/memory_attention.py +237 -0
  139. ultralytics/models/sam/modules/sam.py +969 -21
  140. ultralytics/models/sam/modules/tiny_encoder.py +425 -154
  141. ultralytics/models/sam/modules/transformer.py +159 -60
  142. ultralytics/models/sam/modules/utils.py +293 -0
  143. ultralytics/models/sam/predict.py +1263 -132
  144. ultralytics/models/utils/__init__.py +1 -1
  145. ultralytics/models/utils/loss.py +36 -24
  146. ultralytics/models/utils/ops.py +3 -7
  147. ultralytics/models/yolo/__init__.py +3 -3
  148. ultralytics/models/yolo/classify/__init__.py +1 -1
  149. ultralytics/models/yolo/classify/predict.py +7 -8
  150. ultralytics/models/yolo/classify/train.py +17 -22
  151. ultralytics/models/yolo/classify/val.py +8 -4
  152. ultralytics/models/yolo/detect/__init__.py +1 -1
  153. ultralytics/models/yolo/detect/predict.py +3 -5
  154. ultralytics/models/yolo/detect/train.py +11 -4
  155. ultralytics/models/yolo/detect/val.py +90 -52
  156. ultralytics/models/yolo/model.py +14 -9
  157. ultralytics/models/yolo/obb/__init__.py +1 -1
  158. ultralytics/models/yolo/obb/predict.py +2 -2
  159. ultralytics/models/yolo/obb/train.py +5 -3
  160. ultralytics/models/yolo/obb/val.py +41 -23
  161. ultralytics/models/yolo/pose/__init__.py +1 -1
  162. ultralytics/models/yolo/pose/predict.py +3 -5
  163. ultralytics/models/yolo/pose/train.py +2 -2
  164. ultralytics/models/yolo/pose/val.py +51 -17
  165. ultralytics/models/yolo/segment/__init__.py +1 -1
  166. ultralytics/models/yolo/segment/predict.py +3 -5
  167. ultralytics/models/yolo/segment/train.py +2 -2
  168. ultralytics/models/yolo/segment/val.py +60 -19
  169. ultralytics/models/yolo/world/__init__.py +5 -0
  170. ultralytics/models/yolo/world/train.py +92 -0
  171. ultralytics/models/yolo/world/train_world.py +109 -0
  172. ultralytics/nn/__init__.py +1 -1
  173. ultralytics/nn/autobackend.py +228 -93
  174. ultralytics/nn/modules/__init__.py +39 -14
  175. ultralytics/nn/modules/activation.py +21 -0
  176. ultralytics/nn/modules/block.py +526 -66
  177. ultralytics/nn/modules/conv.py +24 -7
  178. ultralytics/nn/modules/head.py +177 -34
  179. ultralytics/nn/modules/transformer.py +6 -5
  180. ultralytics/nn/modules/utils.py +1 -2
  181. ultralytics/nn/tasks.py +225 -77
  182. ultralytics/solutions/__init__.py +30 -1
  183. ultralytics/solutions/ai_gym.py +96 -143
  184. ultralytics/solutions/analytics.py +247 -0
  185. ultralytics/solutions/distance_calculation.py +78 -135
  186. ultralytics/solutions/heatmap.py +93 -247
  187. ultralytics/solutions/object_counter.py +184 -259
  188. ultralytics/solutions/parking_management.py +246 -0
  189. ultralytics/solutions/queue_management.py +112 -0
  190. ultralytics/solutions/region_counter.py +116 -0
  191. ultralytics/solutions/security_alarm.py +144 -0
  192. ultralytics/solutions/solutions.py +178 -0
  193. ultralytics/solutions/speed_estimation.py +86 -174
  194. ultralytics/solutions/streamlit_inference.py +190 -0
  195. ultralytics/solutions/trackzone.py +68 -0
  196. ultralytics/trackers/__init__.py +1 -1
  197. ultralytics/trackers/basetrack.py +32 -13
  198. ultralytics/trackers/bot_sort.py +61 -28
  199. ultralytics/trackers/byte_tracker.py +83 -51
  200. ultralytics/trackers/track.py +21 -6
  201. ultralytics/trackers/utils/__init__.py +1 -1
  202. ultralytics/trackers/utils/gmc.py +62 -48
  203. ultralytics/trackers/utils/kalman_filter.py +166 -35
  204. ultralytics/trackers/utils/matching.py +40 -21
  205. ultralytics/utils/__init__.py +511 -239
  206. ultralytics/utils/autobatch.py +40 -22
  207. ultralytics/utils/benchmarks.py +266 -85
  208. ultralytics/utils/callbacks/__init__.py +1 -1
  209. ultralytics/utils/callbacks/base.py +1 -3
  210. ultralytics/utils/callbacks/clearml.py +7 -6
  211. ultralytics/utils/callbacks/comet.py +39 -17
  212. ultralytics/utils/callbacks/dvc.py +1 -1
  213. ultralytics/utils/callbacks/hub.py +16 -16
  214. ultralytics/utils/callbacks/mlflow.py +28 -24
  215. ultralytics/utils/callbacks/neptune.py +6 -2
  216. ultralytics/utils/callbacks/raytune.py +3 -4
  217. ultralytics/utils/callbacks/tensorboard.py +18 -18
  218. ultralytics/utils/callbacks/wb.py +27 -20
  219. ultralytics/utils/checks.py +160 -100
  220. ultralytics/utils/dist.py +2 -1
  221. ultralytics/utils/downloads.py +40 -34
  222. ultralytics/utils/errors.py +1 -1
  223. ultralytics/utils/files.py +72 -38
  224. ultralytics/utils/instance.py +41 -19
  225. ultralytics/utils/loss.py +83 -55
  226. ultralytics/utils/metrics.py +61 -56
  227. ultralytics/utils/ops.py +94 -89
  228. ultralytics/utils/patches.py +30 -14
  229. ultralytics/utils/plotting.py +600 -269
  230. ultralytics/utils/tal.py +67 -26
  231. ultralytics/utils/torch_utils.py +302 -102
  232. ultralytics/utils/triton.py +2 -1
  233. ultralytics/utils/tuner.py +21 -12
  234. ultralytics-8.3.62.dist-info/METADATA +370 -0
  235. ultralytics-8.3.62.dist-info/RECORD +241 -0
  236. {ultralytics-8.1.29.dist-info → ultralytics-8.3.62.dist-info}/WHEEL +1 -1
  237. ultralytics/data/explorer/__init__.py +0 -5
  238. ultralytics/data/explorer/explorer.py +0 -472
  239. ultralytics/data/explorer/gui/__init__.py +0 -1
  240. ultralytics/data/explorer/gui/dash.py +0 -268
  241. ultralytics/data/explorer/utils.py +0 -166
  242. ultralytics/models/fastsam/prompt.py +0 -357
  243. ultralytics-8.1.29.dist-info/METADATA +0 -373
  244. ultralytics-8.1.29.dist-info/RECORD +0 -197
  245. {ultralytics-8.1.29.dist-info → ultralytics-8.3.62.dist-info}/LICENSE +0 -0
  246. {ultralytics-8.1.29.dist-info → ultralytics-8.3.62.dist-info}/entry_points.txt +0 -0
  247. {ultralytics-8.1.29.dist-info → ultralytics-8.3.62.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,5 @@
1
- # Ultralytics YOLO 🚀, AGPL-3.0 license
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 "ce"]
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,14 +34,14 @@ 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=True):
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 True.
44
+ Defaults to False.
44
45
 
45
46
  Returns:
46
47
  (bool): Returns True for a valid URL. If 'check' is True, also returns True if the URL exists online.
@@ -51,7 +52,7 @@ def is_url(url, check=True):
51
52
  valid = is_url("https://www.example.com")
52
53
  ```
53
54
  """
54
- with contextlib.suppress(Exception):
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
- return False
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('path/to/dir')
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('path/to/dir')
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('path/to/file.zip')
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
- if len(top_level_dirs) > 1 or (len(files) > 1 and not files[0].endswith("/")):
171
- # Zip has multiple files at top level
172
- path = extract_path = Path(path) / Path(file).stem # i.e. ../datasets/coco8
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/coco8
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,14 +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/coco128.zip", path=Path.cwd(), sf=1.5, hard=True):
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/coco128.zip'.
203
+ url (str, optional): The URL to the file. Defaults to 'https://ultralytics.com/assets/coco8.zip'.
200
204
  path (str | Path, optional): The path or drive to check the available free space on.
201
- sf (float, optional): Safety factor, the multiplier for the required free space. Defaults to 2.0.
205
+ sf (float, optional): Safety factor, the multiplier for the required free space. Defaults to 1.5.
202
206
  hard (bool, optional): Whether to throw an error or not on insufficient disk space. Defaults to True.
203
207
 
204
208
  Returns:
@@ -265,8 +269,7 @@ def get_google_drive_file_info(link):
265
269
  for k, v in response.cookies.items():
266
270
  if k.startswith("download_warning"):
267
271
  drive_url += f"&confirm={v}" # v is token
268
- cd = response.headers.get("content-disposition")
269
- if cd:
272
+ if cd := response.headers.get("content-disposition"):
270
273
  filename = re.findall('filename="(.+)"', cd)[0]
271
274
  return drive_url, filename
272
275
 
@@ -317,7 +320,11 @@ def safe_download(
317
320
  if "://" not in str(url) and Path(url).is_file(): # URL exists ('://' check required in Windows Python<3.10)
318
321
  f = Path(url) # filename
319
322
  elif not f.is_file(): # URL and file do not exist
320
- desc = f"Downloading {url if gdrive else clean_url(url)} to '{f}'"
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}'"
321
328
  LOGGER.info(f"{desc}...")
322
329
  f.parent.mkdir(parents=True, exist_ok=True) # make directory if missing
323
330
  check_disk_space(url, path=f.parent)
@@ -351,18 +358,18 @@ def safe_download(
351
358
  f.unlink() # remove partial downloads
352
359
  except Exception as e:
353
360
  if i == 0 and not is_online():
354
- raise ConnectionError(emojis(f"❌ Download failure for {url}. Environment is not online.")) from e
361
+ raise ConnectionError(emojis(f"❌ Download failure for {uri}. Environment is not online.")) from e
355
362
  elif i >= retry:
356
- raise ConnectionError(emojis(f"❌ Download failure for {url}. Retry limit reached.")) from e
357
- LOGGER.warning(f"⚠️ Download failure, retrying {i + 1}/{retry} {url}...")
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}...")
358
365
 
359
- if unzip and f.exists() and f.suffix in ("", ".zip", ".tar", ".gz"):
366
+ if unzip and f.exists() and f.suffix in {"", ".zip", ".tar", ".gz"}:
360
367
  from zipfile import is_zipfile
361
368
 
362
369
  unzip_dir = (dir or f.parent).resolve() # unzip to dir if provided else unzip in place
363
370
  if is_zipfile(f):
364
371
  unzip_dir = unzip_file(file=f, path=unzip_dir, exist_ok=exist_ok, progress=progress) # unzip
365
- elif f.suffix in (".tar", ".gz"):
372
+ elif f.suffix in {".tar", ".gz"}:
366
373
  LOGGER.info(f"Unzipping {f} to {unzip_dir}...")
367
374
  subprocess.run(["tar", "xf" if f.suffix == ".tar" else "xfz", f, "--directory", unzip_dir], check=True)
368
375
  if delete:
@@ -385,10 +392,9 @@ def get_github_assets(repo="ultralytics/assets", version="latest", retry=False):
385
392
 
386
393
  Example:
387
394
  ```python
388
- tag, assets = get_github_assets(repo='ultralytics/assets', version='latest')
395
+ tag, assets = get_github_assets(repo="ultralytics/assets", version="latest")
389
396
  ```
390
397
  """
391
-
392
398
  if version != "latest":
393
399
  version = f"tags/{version}" # i.e. tags/v6.2
394
400
  url = f"https://api.github.com/repos/{repo}/releases/{version}"
@@ -402,7 +408,7 @@ def get_github_assets(repo="ultralytics/assets", version="latest", retry=False):
402
408
  return data["tag_name"], [x["name"] for x in data["assets"]] # tag, assets i.e. ['yolov8n.pt', 'yolov8s.pt', ...]
403
409
 
404
410
 
405
- def attempt_download_asset(file, repo="ultralytics/assets", release="v8.1.0", **kwargs):
411
+ def attempt_download_asset(file, repo="ultralytics/assets", release="v8.3.0", **kwargs):
406
412
  """
407
413
  Attempt to download a file from GitHub release assets if it is not found locally. The function checks for the file
408
414
  locally first, then tries to download it from the specified GitHub repository release.
@@ -410,7 +416,7 @@ def attempt_download_asset(file, repo="ultralytics/assets", release="v8.1.0", **
410
416
  Args:
411
417
  file (str | Path): The filename or file path to be downloaded.
412
418
  repo (str, optional): The GitHub repository in the format 'owner/repo'. Defaults to 'ultralytics/assets'.
413
- release (str, optional): The specific release version to be downloaded. Defaults to 'v8.1.0'.
419
+ release (str, optional): The specific release version to be downloaded. Defaults to 'v8.3.0'.
414
420
  **kwargs (any): Additional keyword arguments for the download process.
415
421
 
416
422
  Returns:
@@ -418,7 +424,7 @@ def attempt_download_asset(file, repo="ultralytics/assets", release="v8.1.0", **
418
424
 
419
425
  Example:
420
426
  ```python
421
- file_path = attempt_download_asset('yolov5s.pt', repo='ultralytics/assets', release='latest')
427
+ file_path = attempt_download_asset("yolo11n.pt", repo="ultralytics/assets", release="latest")
422
428
  ```
423
429
  """
424
430
  from ultralytics.utils import SETTINGS # scoped for circular import
@@ -473,7 +479,7 @@ def download(url, dir=Path.cwd(), unzip=True, delete=False, curl=False, threads=
473
479
 
474
480
  Example:
475
481
  ```python
476
- download('https://ultralytics.com/assets/example.zip', dir='path/to/dir', unzip=True)
482
+ download("https://ultralytics.com/assets/example.zip", dir="path/to/dir", unzip=True)
477
483
  ```
478
484
  """
479
485
  dir = Path(dir)
@@ -1,4 +1,4 @@
1
- # Ultralytics YOLO 🚀, AGPL-3.0 license
1
+ # Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
2
2
 
3
3
  from ultralytics.utils import emojis
4
4
 
@@ -1,4 +1,4 @@
1
- # Ultralytics YOLO 🚀, AGPL-3.0 license
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
- """Usage: @WorkingDirectory(dir) decorator or 'with WorkingDirectory(dir):' context manager."""
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
- """Restore the current working directory on context exit."""
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
- Example:
44
- ```python
45
- with ultralytics.utils.files import spaces_in_path
46
-
47
- with spaces_in_path('/path/with spaces') as new_path:
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. runs/exp --> runs/exp{sep}2, runs/exp{sep}3, ... etc.
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 set to True, the path will be incremented by appending a number and sep to
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, pathlib.Path): Path to increment.
96
- exist_ok (bool, optional): If True, the path will not be incremented and returned as-is. Defaults to False.
97
- sep (str, optional): Separator to use between the path and the incrementation number. Defaults to ''.
98
- mkdir (bool, optional): Create a directory if it does not exist. Defaults to False.
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 update."""
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
- """Return human-readable file modification date, i.e. '2021-3-26'."""
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
- """Return file/dir size (MB)."""
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
- """Return path to most recent 'last.pt' in /runs (i.e. to --resume from)."""
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=("yolov8n.pt",), source_dir=Path("."), update_names=False):
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 (tuple, optional): Model filenames to update, defaults to ("yolov8n.pt").
156
- source_dir (Path, optional): Directory containing models and target subdirectory, defaults to current directory.
157
- update_names (bool, optional): Update model names from a data YAML.
158
-
159
- Example:
160
- ```python
161
- from ultralytics.utils.files import update_models
162
-
163
- model_names = (f"rtdetr-{size}.pt" for size in "lx")
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, use_dill=False)
222
+ model.save(save_path)
@@ -1,4 +1,4 @@
1
- # Ultralytics YOLO 🚀, AGPL-3.0 license
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
- self.convert("xyxy")
76
- return (self.bboxes[:, 2] - self.bboxes[:, 0]) * (self.bboxes[:, 3] - self.bboxes[:, 1])
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 (tuple | list | int): the scale for four coords.
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 (tuple | list | int): the offset for four coords.
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].view(1, -1))
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): bboxes with shape [N, 4].
212
- segments (list | ndarray): segments.
213
- keypoints (ndarray): keypoints(x, y, visible) with shape [N, 17, 3].
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
- """This might be similar with denormalize func but without normalized sign."""
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
- cat_segments = np.concatenate([b.segments for b in instances_list], axis=axis)
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