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.
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 +527 -67
  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 +44 -37
  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 +84 -56
  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.28.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.28.dist-info/METADATA +0 -373
  244. ultralytics-8.1.28.dist-info/RECORD +0 -197
  245. {ultralytics-8.1.28.dist-info → ultralytics-8.3.62.dist-info}/LICENSE +0 -0
  246. {ultralytics-8.1.28.dist-info → ultralytics-8.3.62.dist-info}/entry_points.txt +0 -0
  247. {ultralytics-8.1.28.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,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=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
- (bool): Returns True if the string is a valid URL. If 'check' is True, also returns True if the URL exists online.
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
- 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,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/coco128.zip", 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'.
200
- sf (float, optional): Safety factor, the multiplier for the required free space. Defaults to 2.0.
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(Path.cwd())) # bytes
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 = response.headers.get("content-disposition")
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
- 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}'"
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 {url}. Environment is not online.")) from e
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 {url}. Retry limit reached.")) from e
356
- 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}...")
357
365
 
358
- 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"}:
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 (".tar", ".gz"):
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='ultralytics/assets', version='latest')
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.1.0", **kwargs):
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.1.0'.
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('yolov5s.pt', repo='ultralytics/assets', release='latest')
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('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)
476
483
  ```
477
484
  """
478
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