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 shutil
5
4
  import subprocess
6
5
  import sys
@@ -8,30 +7,48 @@ from pathlib import Path
8
7
  from types import SimpleNamespace
9
8
  from typing import Dict, List, Union
10
9
 
10
+ import cv2
11
+
11
12
  from ultralytics.utils import (
12
13
  ASSETS,
13
14
  DEFAULT_CFG,
14
15
  DEFAULT_CFG_DICT,
15
16
  DEFAULT_CFG_PATH,
17
+ DEFAULT_SOL_DICT,
18
+ IS_VSCODE,
16
19
  LOGGER,
17
20
  RANK,
18
21
  ROOT,
19
22
  RUNS_DIR,
20
23
  SETTINGS,
21
- SETTINGS_YAML,
24
+ SETTINGS_FILE,
22
25
  TESTS_RUNNING,
23
26
  IterableSimpleNamespace,
24
27
  __version__,
25
28
  checks,
26
29
  colorstr,
27
30
  deprecation_warn,
31
+ vscode_msg,
28
32
  yaml_load,
29
33
  yaml_print,
30
34
  )
31
35
 
36
+ # Define valid solutions
37
+ SOLUTION_MAP = {
38
+ "count": ("ObjectCounter", "count"),
39
+ "heatmap": ("Heatmap", "generate_heatmap"),
40
+ "queue": ("QueueManager", "process_queue"),
41
+ "speed": ("SpeedEstimator", "estimate_speed"),
42
+ "workout": ("AIGym", "monitor"),
43
+ "analytics": ("Analytics", "process_data"),
44
+ "trackzone": ("TrackZone", "trackzone"),
45
+ "inference": ("Inference", "inference"),
46
+ "help": None,
47
+ }
48
+
32
49
  # Define valid tasks and modes
33
- MODES = "train", "val", "predict", "export", "track", "benchmark"
34
- TASKS = "detect", "segment", "classify", "pose", "obb"
50
+ MODES = {"train", "val", "predict", "export", "track", "benchmark"}
51
+ TASKS = {"detect", "segment", "classify", "pose", "obb"}
35
52
  TASK2DATA = {
36
53
  "detect": "coco8.yaml",
37
54
  "segment": "coco8-seg.yaml",
@@ -40,11 +57,11 @@ TASK2DATA = {
40
57
  "obb": "dota8.yaml",
41
58
  }
42
59
  TASK2MODEL = {
43
- "detect": "yolov8n.pt",
44
- "segment": "yolov8n-seg.pt",
45
- "classify": "yolov8n-cls.pt",
46
- "pose": "yolov8n-pose.pt",
47
- "obb": "yolov8n-obb.pt",
60
+ "detect": "yolo11n.pt",
61
+ "segment": "yolo11n-seg.pt",
62
+ "classify": "yolo11n-cls.pt",
63
+ "pose": "yolo11n-pose.pt",
64
+ "obb": "yolo11n-obb.pt",
48
65
  }
49
66
  TASK2METRIC = {
50
67
  "detect": "metrics/mAP50-95(B)",
@@ -53,9 +70,41 @@ TASK2METRIC = {
53
70
  "pose": "metrics/mAP50-95(P)",
54
71
  "obb": "metrics/mAP50-95(B)",
55
72
  }
56
-
73
+ MODELS = {TASK2MODEL[task] for task in TASKS}
74
+
75
+ ARGV = sys.argv or ["", ""] # sometimes sys.argv = []
76
+ SOLUTIONS_HELP_MSG = f"""
77
+ Arguments received: {str(["yolo"] + ARGV[1:])}. Ultralytics 'yolo solutions' usage overview:
78
+
79
+ yolo solutions SOLUTION ARGS
80
+
81
+ Where SOLUTION (optional) is one of {list(SOLUTION_MAP.keys())[:-1]}
82
+ ARGS (optional) are any number of custom 'arg=value' pairs like 'show_in=True' that override defaults
83
+ at https://docs.ultralytics.com/usage/cfg
84
+
85
+ 1. Call object counting solution
86
+ yolo solutions count source="path/to/video/file.mp4" region=[(20, 400), (1080, 400), (1080, 360), (20, 360)]
87
+
88
+ 2. Call heatmaps solution
89
+ yolo solutions heatmap colormap=cv2.COLORMAP_PARULA model=yolo11n.pt
90
+
91
+ 3. Call queue management solution
92
+ yolo solutions queue region=[(20, 400), (1080, 400), (1080, 360), (20, 360)] model=yolo11n.pt
93
+
94
+ 4. Call workouts monitoring solution for push-ups
95
+ yolo solutions workout model=yolo11n-pose.pt kpts=[6, 8, 10]
96
+
97
+ 5. Generate analytical graphs
98
+ yolo solutions analytics analytics_type="pie"
99
+
100
+ 6. Track objects within specific zones
101
+ yolo solutions trackzone source="path/to/video/file.mp4" region=[(150, 150), (1130, 150), (1130, 570), (150, 570)]
102
+
103
+ 7. Streamlit real-time webcam inference GUI
104
+ yolo streamlit-predict
105
+ """
57
106
  CLI_HELP_MSG = f"""
58
- Arguments received: {str(['yolo'] + sys.argv[1:])}. Ultralytics 'yolo' commands use the following syntax:
107
+ Arguments received: {str(["yolo"] + ARGV[1:])}. Ultralytics 'yolo' commands use the following syntax:
59
108
 
60
109
  yolo TASK MODE ARGS
61
110
 
@@ -65,45 +114,55 @@ CLI_HELP_MSG = f"""
65
114
  See all ARGS at https://docs.ultralytics.com/usage/cfg or with 'yolo cfg'
66
115
 
67
116
  1. Train a detection model for 10 epochs with an initial learning_rate of 0.01
68
- yolo train data=coco128.yaml model=yolov8n.pt epochs=10 lr0=0.01
117
+ yolo train data=coco8.yaml model=yolo11n.pt epochs=10 lr0=0.01
69
118
 
70
119
  2. Predict a YouTube video using a pretrained segmentation model at image size 320:
71
- yolo predict model=yolov8n-seg.pt source='https://youtu.be/LNwODJXcvt4' imgsz=320
120
+ yolo predict model=yolo11n-seg.pt source='https://youtu.be/LNwODJXcvt4' imgsz=320
72
121
 
73
122
  3. Val a pretrained detection model at batch-size 1 and image size 640:
74
- yolo val model=yolov8n.pt data=coco128.yaml batch=1 imgsz=640
123
+ yolo val model=yolo11n.pt data=coco8.yaml batch=1 imgsz=640
75
124
 
76
- 4. Export a YOLOv8n classification model to ONNX format at image size 224 by 128 (no TASK required)
77
- yolo export model=yolov8n-cls.pt format=onnx imgsz=224,128
125
+ 4. Export a YOLO11n classification model to ONNX format at image size 224 by 128 (no TASK required)
126
+ yolo export model=yolo11n-cls.pt format=onnx imgsz=224,128
78
127
 
79
- 6. Explore your datasets using semantic search and SQL with a simple GUI powered by Ultralytics Explorer API
80
- yolo explorer
128
+ 5. Ultralytics solutions usage
129
+ yolo solutions count or in {list(SOLUTION_MAP.keys())[1:-1]} source="path/to/video/file.mp4"
81
130
 
82
- 5. Run special commands:
131
+ 6. Run special commands:
83
132
  yolo help
84
133
  yolo checks
85
134
  yolo version
86
135
  yolo settings
87
136
  yolo copy-cfg
88
137
  yolo cfg
138
+ yolo solutions help
89
139
 
90
140
  Docs: https://docs.ultralytics.com
141
+ Solutions: https://docs.ultralytics.com/solutions/
91
142
  Community: https://community.ultralytics.com
92
143
  GitHub: https://github.com/ultralytics/ultralytics
93
144
  """
94
145
 
95
146
  # Define keys for arg type checks
96
- CFG_FLOAT_KEYS = "warmup_epochs", "box", "cls", "dfl", "degrees", "shear", "time"
97
- CFG_FRACTION_KEYS = (
147
+ CFG_FLOAT_KEYS = { # integer or float arguments, i.e. x=2 and x=2.0
148
+ "warmup_epochs",
149
+ "box",
150
+ "cls",
151
+ "dfl",
152
+ "degrees",
153
+ "shear",
154
+ "time",
155
+ "workspace",
156
+ "batch",
157
+ }
158
+ CFG_FRACTION_KEYS = { # fractional float arguments with 0.0<=values<=1.0
98
159
  "dropout",
99
- "iou",
100
160
  "lr0",
101
161
  "lrf",
102
162
  "momentum",
103
163
  "weight_decay",
104
164
  "warmup_momentum",
105
165
  "warmup_bias_lr",
106
- "label_smoothing",
107
166
  "hsv_h",
108
167
  "hsv_s",
109
168
  "hsv_v",
@@ -112,17 +171,17 @@ CFG_FRACTION_KEYS = (
112
171
  "perspective",
113
172
  "flipud",
114
173
  "fliplr",
174
+ "bgr",
115
175
  "mosaic",
116
176
  "mixup",
117
177
  "copy_paste",
118
178
  "conf",
119
179
  "iou",
120
180
  "fraction",
121
- ) # fraction floats 0.0 - 1.0
122
- CFG_INT_KEYS = (
181
+ }
182
+ CFG_INT_KEYS = { # integer-only arguments
123
183
  "epochs",
124
184
  "patience",
125
- "batch",
126
185
  "workers",
127
186
  "seed",
128
187
  "close_mosaic",
@@ -130,11 +189,10 @@ CFG_INT_KEYS = (
130
189
  "max_det",
131
190
  "vid_stride",
132
191
  "line_width",
133
- "workspace",
134
192
  "nbs",
135
193
  "save_period",
136
- )
137
- CFG_BOOL_KEYS = (
194
+ }
195
+ CFG_BOOL_KEYS = { # boolean-only arguments
138
196
  "save",
139
197
  "exist_ok",
140
198
  "verbose",
@@ -169,18 +227,36 @@ CFG_BOOL_KEYS = (
169
227
  "nms",
170
228
  "profile",
171
229
  "multi_scale",
172
- )
230
+ }
173
231
 
174
232
 
175
233
  def cfg2dict(cfg):
176
234
  """
177
- Convert a configuration object to a dictionary, whether it is a file path, a string, or a SimpleNamespace object.
235
+ Converts a configuration object to a dictionary.
178
236
 
179
237
  Args:
180
- cfg (str | Path | dict | SimpleNamespace): Configuration object to be converted to a dictionary.
238
+ cfg (str | Path | Dict | SimpleNamespace): Configuration object to be converted. Can be a file path,
239
+ a string, a dictionary, or a SimpleNamespace object.
181
240
 
182
241
  Returns:
183
- cfg (dict): Configuration object in dictionary format.
242
+ (Dict): Configuration object in dictionary format.
243
+
244
+ Examples:
245
+ Convert a YAML file path to a dictionary:
246
+ >>> config_dict = cfg2dict("config.yaml")
247
+
248
+ Convert a SimpleNamespace to a dictionary:
249
+ >>> from types import SimpleNamespace
250
+ >>> config_sn = SimpleNamespace(param1="value1", param2="value2")
251
+ >>> config_dict = cfg2dict(config_sn)
252
+
253
+ Pass through an already existing dictionary:
254
+ >>> config_dict = cfg2dict({"param1": "value1", "param2": "value2"})
255
+
256
+ Notes:
257
+ - If cfg is a path or string, it's loaded as YAML and converted to a dictionary.
258
+ - If cfg is a SimpleNamespace object, it's converted to a dictionary using vars().
259
+ - If cfg is already a dictionary, it's returned unchanged.
184
260
  """
185
261
  if isinstance(cfg, (str, Path)):
186
262
  cfg = yaml_load(cfg) # load dict
@@ -191,14 +267,26 @@ def cfg2dict(cfg):
191
267
 
192
268
  def get_cfg(cfg: Union[str, Path, Dict, SimpleNamespace] = DEFAULT_CFG_DICT, overrides: Dict = None):
193
269
  """
194
- Load and merge configuration data from a file or dictionary.
270
+ Load and merge configuration data from a file or dictionary, with optional overrides.
195
271
 
196
272
  Args:
197
- cfg (str | Path | Dict | SimpleNamespace): Configuration data.
198
- overrides (str | Dict | optional): Overrides in the form of a file name or a dictionary. Default is None.
273
+ cfg (str | Path | Dict | SimpleNamespace): Configuration data source. Can be a file path, dictionary, or
274
+ SimpleNamespace object.
275
+ overrides (Dict | None): Dictionary containing key-value pairs to override the base configuration.
199
276
 
200
277
  Returns:
201
- (SimpleNamespace): Training arguments namespace.
278
+ (SimpleNamespace): Namespace containing the merged configuration arguments.
279
+
280
+ Examples:
281
+ >>> from ultralytics.cfg import get_cfg
282
+ >>> config = get_cfg() # Load default configuration
283
+ >>> config_with_overrides = get_cfg("path/to/config.yaml", overrides={"epochs": 50, "batch_size": 16})
284
+
285
+ Notes:
286
+ - If both `cfg` and `overrides` are provided, the values in `overrides` will take precedence.
287
+ - Special handling ensures alignment and correctness of the configuration, such as converting numeric
288
+ `project` and `name` to strings and validating configuration keys and values.
289
+ - The function performs type and value checks on the configuration data.
202
290
  """
203
291
  cfg = cfg2dict(cfg)
204
292
 
@@ -215,42 +303,98 @@ def get_cfg(cfg: Union[str, Path, Dict, SimpleNamespace] = DEFAULT_CFG_DICT, ove
215
303
  if k in cfg and isinstance(cfg[k], (int, float)):
216
304
  cfg[k] = str(cfg[k])
217
305
  if cfg.get("name") == "model": # assign model to 'name' arg
218
- cfg["name"] = cfg.get("model", "").split(".")[0]
306
+ cfg["name"] = str(cfg.get("model", "")).split(".")[0]
219
307
  LOGGER.warning(f"WARNING ⚠️ 'name=model' automatically updated to 'name={cfg['name']}'.")
220
308
 
221
309
  # Type and Value checks
310
+ check_cfg(cfg)
311
+
312
+ # Return instance
313
+ return IterableSimpleNamespace(**cfg)
314
+
315
+
316
+ def check_cfg(cfg, hard=True):
317
+ """
318
+ Checks configuration argument types and values for the Ultralytics library.
319
+
320
+ This function validates the types and values of configuration arguments, ensuring correctness and converting
321
+ them if necessary. It checks for specific key types defined in global variables such as CFG_FLOAT_KEYS,
322
+ CFG_FRACTION_KEYS, CFG_INT_KEYS, and CFG_BOOL_KEYS.
323
+
324
+ Args:
325
+ cfg (Dict): Configuration dictionary to validate.
326
+ hard (bool): If True, raises exceptions for invalid types and values; if False, attempts to convert them.
327
+
328
+ Examples:
329
+ >>> config = {
330
+ ... "epochs": 50, # valid integer
331
+ ... "lr0": 0.01, # valid float
332
+ ... "momentum": 1.2, # invalid float (out of 0.0-1.0 range)
333
+ ... "save": "true", # invalid bool
334
+ ... }
335
+ >>> check_cfg(config, hard=False)
336
+ >>> print(config)
337
+ {'epochs': 50, 'lr0': 0.01, 'momentum': 1.2, 'save': False} # corrected 'save' key
338
+
339
+ Notes:
340
+ - The function modifies the input dictionary in-place.
341
+ - None values are ignored as they may be from optional arguments.
342
+ - Fraction keys are checked to be within the range [0.0, 1.0].
343
+ """
222
344
  for k, v in cfg.items():
223
345
  if v is not None: # None values may be from optional args
224
346
  if k in CFG_FLOAT_KEYS and not isinstance(v, (int, float)):
225
- raise TypeError(
226
- f"'{k}={v}' is of invalid type {type(v).__name__}. "
227
- f"Valid '{k}' types are int (i.e. '{k}=0') or float (i.e. '{k}=0.5')"
228
- )
229
- elif k in CFG_FRACTION_KEYS:
230
- if not isinstance(v, (int, float)):
347
+ if hard:
231
348
  raise TypeError(
232
349
  f"'{k}={v}' is of invalid type {type(v).__name__}. "
233
350
  f"Valid '{k}' types are int (i.e. '{k}=0') or float (i.e. '{k}=0.5')"
234
351
  )
352
+ cfg[k] = float(v)
353
+ elif k in CFG_FRACTION_KEYS:
354
+ if not isinstance(v, (int, float)):
355
+ if hard:
356
+ raise TypeError(
357
+ f"'{k}={v}' is of invalid type {type(v).__name__}. "
358
+ f"Valid '{k}' types are int (i.e. '{k}=0') or float (i.e. '{k}=0.5')"
359
+ )
360
+ cfg[k] = v = float(v)
235
361
  if not (0.0 <= v <= 1.0):
236
- raise ValueError(f"'{k}={v}' is an invalid value. " f"Valid '{k}' values are between 0.0 and 1.0.")
362
+ raise ValueError(f"'{k}={v}' is an invalid value. Valid '{k}' values are between 0.0 and 1.0.")
237
363
  elif k in CFG_INT_KEYS and not isinstance(v, int):
238
- raise TypeError(
239
- f"'{k}={v}' is of invalid type {type(v).__name__}. " f"'{k}' must be an int (i.e. '{k}=8')"
240
- )
364
+ if hard:
365
+ raise TypeError(
366
+ f"'{k}={v}' is of invalid type {type(v).__name__}. '{k}' must be an int (i.e. '{k}=8')"
367
+ )
368
+ cfg[k] = int(v)
241
369
  elif k in CFG_BOOL_KEYS and not isinstance(v, bool):
242
- raise TypeError(
243
- f"'{k}={v}' is of invalid type {type(v).__name__}. "
244
- f"'{k}' must be a bool (i.e. '{k}=True' or '{k}=False')"
245
- )
246
-
247
- # Return instance
248
- return IterableSimpleNamespace(**cfg)
370
+ if hard:
371
+ raise TypeError(
372
+ f"'{k}={v}' is of invalid type {type(v).__name__}. "
373
+ f"'{k}' must be a bool (i.e. '{k}=True' or '{k}=False')"
374
+ )
375
+ cfg[k] = bool(v)
249
376
 
250
377
 
251
378
  def get_save_dir(args, name=None):
252
- """Return save_dir as created from train/val/predict arguments."""
379
+ """
380
+ Returns the directory path for saving outputs, derived from arguments or default settings.
381
+
382
+ Args:
383
+ args (SimpleNamespace): Namespace object containing configurations such as 'project', 'name', 'task',
384
+ 'mode', and 'save_dir'.
385
+ name (str | None): Optional name for the output directory. If not provided, it defaults to 'args.name'
386
+ or the 'args.mode'.
253
387
 
388
+ Returns:
389
+ (Path): Directory path where outputs should be saved.
390
+
391
+ Examples:
392
+ >>> from types import SimpleNamespace
393
+ >>> args = SimpleNamespace(project="my_project", task="detect", mode="train", exist_ok=True)
394
+ >>> save_dir = get_save_dir(args)
395
+ >>> print(save_dir)
396
+ my_project/detect/train
397
+ """
254
398
  if getattr(args, "save_dir", None):
255
399
  save_dir = args.save_dir
256
400
  else:
@@ -258,14 +402,29 @@ def get_save_dir(args, name=None):
258
402
 
259
403
  project = args.project or (ROOT.parent / "tests/tmp/runs" if TESTS_RUNNING else RUNS_DIR) / args.task
260
404
  name = name or args.name or f"{args.mode}"
261
- save_dir = increment_path(Path(project) / name, exist_ok=args.exist_ok if RANK in (-1, 0) else True)
405
+ save_dir = increment_path(Path(project) / name, exist_ok=args.exist_ok if RANK in {-1, 0} else True)
262
406
 
263
407
  return Path(save_dir)
264
408
 
265
409
 
266
410
  def _handle_deprecation(custom):
267
- """Hardcoded function to handle deprecated config keys."""
411
+ """
412
+ Handles deprecated configuration keys by mapping them to current equivalents with deprecation warnings.
268
413
 
414
+ Args:
415
+ custom (Dict): Configuration dictionary potentially containing deprecated keys.
416
+
417
+ Examples:
418
+ >>> custom_config = {"boxes": True, "hide_labels": "False", "line_thickness": 2}
419
+ >>> _handle_deprecation(custom_config)
420
+ >>> print(custom_config)
421
+ {'show_boxes': True, 'show_labels': True, 'line_width': 2}
422
+
423
+ Notes:
424
+ This function modifies the input dictionary in-place, replacing deprecated keys with their current
425
+ equivalents. It also handles value conversions where necessary, such as inverting boolean values for
426
+ 'hide_labels' and 'hide_conf'.
427
+ """
269
428
  for key in custom.copy().keys():
270
429
  if key == "boxes":
271
430
  deprecation_warn(key, "show_boxes")
@@ -279,24 +438,42 @@ def _handle_deprecation(custom):
279
438
  if key == "line_thickness":
280
439
  deprecation_warn(key, "line_width")
281
440
  custom["line_width"] = custom.pop("line_thickness")
441
+ if key == "label_smoothing":
442
+ deprecation_warn(key)
443
+ custom.pop("label_smoothing")
282
444
 
283
445
  return custom
284
446
 
285
447
 
286
448
  def check_dict_alignment(base: Dict, custom: Dict, e=None):
287
449
  """
288
- This function checks for any mismatched keys between a custom configuration list and a base configuration list. If
289
- any mismatched keys are found, the function prints out similar keys from the base list and exits the program.
450
+ Checks alignment between custom and base configuration dictionaries, handling deprecated keys and providing error
451
+ messages for mismatched keys.
290
452
 
291
453
  Args:
292
- custom (dict): a dictionary of custom configuration options
293
- base (dict): a dictionary of base configuration options
294
- e (Error, optional): An optional error that is passed by the calling function.
454
+ base (Dict): The base configuration dictionary containing valid keys.
455
+ custom (Dict): The custom configuration dictionary to be checked for alignment.
456
+ e (Exception | None): Optional error instance passed by the calling function.
457
+
458
+ Raises:
459
+ SystemExit: If mismatched keys are found between the custom and base dictionaries.
460
+
461
+ Examples:
462
+ >>> base_cfg = {"epochs": 50, "lr0": 0.01, "batch_size": 16}
463
+ >>> custom_cfg = {"epoch": 100, "lr": 0.02, "batch_size": 32}
464
+ >>> try:
465
+ ... check_dict_alignment(base_cfg, custom_cfg)
466
+ ... except SystemExit:
467
+ ... print("Mismatched keys found")
468
+
469
+ Notes:
470
+ - Suggests corrections for mismatched keys based on similarity to valid keys.
471
+ - Automatically replaces deprecated keys in the custom configuration with updated equivalents.
472
+ - Prints detailed error messages for each mismatched key to help users correct their configurations.
295
473
  """
296
474
  custom = _handle_deprecation(custom)
297
475
  base_keys, custom_keys = (set(x.keys()) for x in (base, custom))
298
- mismatched = [k for k in custom_keys if k not in base_keys]
299
- if mismatched:
476
+ if mismatched := [k for k in custom_keys if k not in base_keys]:
300
477
  from difflib import get_close_matches
301
478
 
302
479
  string = ""
@@ -310,44 +487,83 @@ def check_dict_alignment(base: Dict, custom: Dict, e=None):
310
487
 
311
488
  def merge_equals_args(args: List[str]) -> List[str]:
312
489
  """
313
- Merges arguments around isolated '=' args in a list of strings. The function considers cases where the first
314
- argument ends with '=' or the second starts with '=', as well as when the middle one is an equals sign.
490
+ Merges arguments around isolated '=' in a list of strings and joins fragments with brackets.
491
+
492
+ This function handles the following cases:
493
+ 1. ['arg', '=', 'val'] becomes ['arg=val']
494
+ 2. ['arg=', 'val'] becomes ['arg=val']
495
+ 3. ['arg', '=val'] becomes ['arg=val']
496
+ 4. Joins fragments with brackets, e.g., ['imgsz=[3,', '640,', '640]'] becomes ['imgsz=[3,640,640]']
315
497
 
316
498
  Args:
317
- args (List[str]): A list of strings where each element is an argument.
499
+ args (List[str]): A list of strings where each element represents an argument or fragment.
318
500
 
319
501
  Returns:
320
- (List[str]): A list of strings where the arguments around isolated '=' are merged.
502
+ List[str]: A list of strings where the arguments around isolated '=' are merged and fragments with brackets are joined.
503
+
504
+ Examples:
505
+ >>> args = ["arg1", "=", "value", "arg2=", "value2", "arg3", "=value3", "imgsz=[3,", "640,", "640]"]
506
+ >>> merge_and_join_args(args)
507
+ ['arg1=value', 'arg2=value2', 'arg3=value3', 'imgsz=[3,640,640]']
321
508
  """
322
509
  new_args = []
323
- for i, arg in enumerate(args):
510
+ current = ""
511
+ depth = 0
512
+
513
+ i = 0
514
+ while i < len(args):
515
+ arg = args[i]
516
+
517
+ # Handle equals sign merging
324
518
  if arg == "=" and 0 < i < len(args) - 1: # merge ['arg', '=', 'val']
325
519
  new_args[-1] += f"={args[i + 1]}"
326
- del args[i + 1]
520
+ i += 2
521
+ continue
327
522
  elif arg.endswith("=") and i < len(args) - 1 and "=" not in args[i + 1]: # merge ['arg=', 'val']
328
523
  new_args.append(f"{arg}{args[i + 1]}")
329
- del args[i + 1]
524
+ i += 2
525
+ continue
330
526
  elif arg.startswith("=") and i > 0: # merge ['arg', '=val']
331
527
  new_args[-1] += arg
332
- else:
333
- new_args.append(arg)
528
+ i += 1
529
+ continue
530
+
531
+ # Handle bracket joining
532
+ depth += arg.count("[") - arg.count("]")
533
+ current += arg
534
+ if depth == 0:
535
+ new_args.append(current)
536
+ current = ""
537
+
538
+ i += 1
539
+
540
+ # Append any remaining current string
541
+ if current:
542
+ new_args.append(current)
543
+
334
544
  return new_args
335
545
 
336
546
 
337
547
  def handle_yolo_hub(args: List[str]) -> None:
338
548
  """
339
- Handle Ultralytics HUB command-line interface (CLI) commands.
549
+ Handles Ultralytics HUB command-line interface (CLI) commands for authentication.
340
550
 
341
- This function processes Ultralytics HUB CLI commands such as login and logout.
342
- It should be called when executing a script with arguments related to HUB authentication.
551
+ This function processes Ultralytics HUB CLI commands such as login and logout. It should be called when executing a
552
+ script with arguments related to HUB authentication.
343
553
 
344
554
  Args:
345
- args (List[str]): A list of command line arguments
555
+ args (List[str]): A list of command line arguments. The first argument should be either 'login'
556
+ or 'logout'. For 'login', an optional second argument can be the API key.
346
557
 
347
- Example:
558
+ Examples:
348
559
  ```bash
349
- python my_script.py hub login your_api_key
560
+ yolo login YOUR_API_KEY
350
561
  ```
562
+
563
+ Notes:
564
+ - The function imports the 'hub' module from ultralytics to perform login and logout operations.
565
+ - For the 'login' command, if no API key is provided, an empty string is passed to the login function.
566
+ - The 'logout' command does not require any additional arguments.
351
567
  """
352
568
  from ultralytics import hub
353
569
 
@@ -362,24 +578,32 @@ def handle_yolo_hub(args: List[str]) -> None:
362
578
 
363
579
  def handle_yolo_settings(args: List[str]) -> None:
364
580
  """
365
- Handle YOLO settings command-line interface (CLI) commands.
581
+ Handles YOLO settings command-line interface (CLI) commands.
366
582
 
367
- This function processes YOLO settings CLI commands such as reset.
368
- It should be called when executing a script with arguments related to YOLO settings management.
583
+ This function processes YOLO settings CLI commands such as reset and updating individual settings. It should be
584
+ called when executing a script with arguments related to YOLO settings management.
369
585
 
370
586
  Args:
371
587
  args (List[str]): A list of command line arguments for YOLO settings management.
372
588
 
373
- Example:
374
- ```bash
375
- python my_script.py yolo settings reset
376
- ```
589
+ Examples:
590
+ >>> handle_yolo_settings(["reset"]) # Reset YOLO settings
591
+ >>> handle_yolo_settings(["default_cfg_path=yolo11n.yaml"]) # Update a specific setting
592
+
593
+ Notes:
594
+ - If no arguments are provided, the function will display the current settings.
595
+ - The 'reset' command will delete the existing settings file and create new default settings.
596
+ - Other arguments are treated as key-value pairs to update specific settings.
597
+ - The function will check for alignment between the provided settings and the existing ones.
598
+ - After processing, the updated settings will be displayed.
599
+ - For more information on handling YOLO settings, visit:
600
+ https://docs.ultralytics.com/quickstart/#ultralytics-settings
377
601
  """
378
602
  url = "https://docs.ultralytics.com/quickstart/#ultralytics-settings" # help URL
379
603
  try:
380
604
  if any(args):
381
605
  if args[0] == "reset":
382
- SETTINGS_YAML.unlink() # delete the settings file
606
+ SETTINGS_FILE.unlink() # delete the settings file
383
607
  SETTINGS.reset() # create new settings
384
608
  LOGGER.info("Settings reset successfully") # inform the user that settings have been reset
385
609
  else: # save a new setting
@@ -387,21 +611,158 @@ def handle_yolo_settings(args: List[str]) -> None:
387
611
  check_dict_alignment(SETTINGS, new)
388
612
  SETTINGS.update(new)
389
613
 
390
- LOGGER.info(f"💡 Learn about settings at {url}")
391
- yaml_print(SETTINGS_YAML) # print the current settings
614
+ print(SETTINGS) # print the current settings
615
+ LOGGER.info(f"💡 Learn more about Ultralytics Settings at {url}")
392
616
  except Exception as e:
393
617
  LOGGER.warning(f"WARNING ⚠️ settings error: '{e}'. Please see {url} for help.")
394
618
 
395
619
 
396
- def handle_explorer():
397
- """Open the Ultralytics Explorer GUI."""
398
- checks.check_requirements("streamlit")
399
- LOGGER.info("💡 Loading Explorer dashboard...")
400
- subprocess.run(["streamlit", "run", ROOT / "data/explorer/gui/dash.py", "--server.maxMessageSize", "2048"])
620
+ def handle_yolo_solutions(args: List[str]) -> None:
621
+ """
622
+ Processes YOLO solutions arguments and runs the specified computer vision solutions pipeline.
623
+
624
+ Args:
625
+ args (List[str]): Command-line arguments for configuring and running the Ultralytics YOLO
626
+ solutions: https://docs.ultralytics.com/solutions/, It can include solution name, source,
627
+ and other configuration parameters.
628
+
629
+ Returns:
630
+ None: The function processes video frames and saves the output but doesn't return any value.
631
+
632
+ Examples:
633
+ Run people counting solution with default settings:
634
+ >>> handle_yolo_solutions(["count"])
635
+
636
+ Run analytics with custom configuration:
637
+ >>> handle_yolo_solutions(["analytics", "conf=0.25", "source=path/to/video/file.mp4"])
638
+
639
+ Run inference with custom configuration, requires Streamlit version 1.29.0 or higher.
640
+ >>> handle_yolo_solutions(["inference", "model=yolo11n.pt"])
641
+
642
+ Notes:
643
+ - Default configurations are merged from DEFAULT_SOL_DICT and DEFAULT_CFG_DICT
644
+ - Arguments can be provided in the format 'key=value' or as boolean flags
645
+ - Available solutions are defined in SOLUTION_MAP with their respective classes and methods
646
+ - If an invalid solution is provided, defaults to 'count' solution
647
+ - Output videos are saved in 'runs/solution/{solution_name}' directory
648
+ - For 'analytics' solution, frame numbers are tracked for generating analytical graphs
649
+ - Video processing can be interrupted by pressing 'q'
650
+ - Processes video frames sequentially and saves output in .avi format
651
+ - If no source is specified, downloads and uses a default sample video\
652
+ - The inference solution will be launched using the 'streamlit run' command.
653
+ - The Streamlit app file is located in the Ultralytics package directory.
654
+ """
655
+ full_args_dict = {**DEFAULT_SOL_DICT, **DEFAULT_CFG_DICT} # arguments dictionary
656
+ overrides = {}
657
+
658
+ # check dictionary alignment
659
+ for arg in merge_equals_args(args):
660
+ arg = arg.lstrip("-").rstrip(",")
661
+ if "=" in arg:
662
+ try:
663
+ k, v = parse_key_value_pair(arg)
664
+ overrides[k] = v
665
+ except (NameError, SyntaxError, ValueError, AssertionError) as e:
666
+ check_dict_alignment(full_args_dict, {arg: ""}, e)
667
+ elif arg in full_args_dict and isinstance(full_args_dict.get(arg), bool):
668
+ overrides[arg] = True
669
+ check_dict_alignment(full_args_dict, overrides) # dict alignment
670
+
671
+ # Get solution name
672
+ if args and args[0] in SOLUTION_MAP:
673
+ if args[0] != "help":
674
+ s_n = args.pop(0) # Extract the solution name directly
675
+ else:
676
+ LOGGER.info(SOLUTIONS_HELP_MSG)
677
+ else:
678
+ LOGGER.warning(
679
+ f"⚠️ No valid solution provided. Using default 'count'. Available: {', '.join(SOLUTION_MAP.keys())}"
680
+ )
681
+ s_n = "count" # Default solution if none provided
682
+
683
+ if args and args[0] == "help": # Add check for return if user call `yolo solutions help`
684
+ return
685
+
686
+ if s_n == "inference":
687
+ checks.check_requirements("streamlit>=1.29.0")
688
+ LOGGER.info("💡 Loading Ultralytics live inference app...")
689
+ subprocess.run(
690
+ [ # Run subprocess with Streamlit custom argument
691
+ "streamlit",
692
+ "run",
693
+ str(ROOT / "solutions/streamlit_inference.py"),
694
+ "--server.headless",
695
+ "true",
696
+ overrides.pop("model", "yolo11n.pt"),
697
+ ]
698
+ )
699
+ else:
700
+ cls, method = SOLUTION_MAP[s_n] # solution class name, method name and default source
701
+
702
+ from ultralytics import solutions # import ultralytics solutions
703
+
704
+ solution = getattr(solutions, cls)(IS_CLI=True, **overrides) # get solution class i.e ObjectCounter
705
+ process = getattr(
706
+ solution, method
707
+ ) # get specific function of class for processing i.e, count from ObjectCounter
708
+
709
+ cap = cv2.VideoCapture(solution.CFG["source"]) # read the video file
710
+
711
+ # extract width, height and fps of the video file, create save directory and initialize video writer
712
+ import os # for directory creation
713
+ from pathlib import Path
714
+
715
+ from ultralytics.utils.files import increment_path # for output directory path update
716
+
717
+ w, h, fps = (int(cap.get(x)) for x in (cv2.CAP_PROP_FRAME_WIDTH, cv2.CAP_PROP_FRAME_HEIGHT, cv2.CAP_PROP_FPS))
718
+ if s_n == "analytics": # analytical graphs follow fixed shape for output i.e w=1920, h=1080
719
+ w, h = 1920, 1080
720
+ save_dir = increment_path(Path("runs") / "solutions" / "exp", exist_ok=False)
721
+ save_dir.mkdir(parents=True, exist_ok=True) # create the output directory
722
+ vw = cv2.VideoWriter(os.path.join(save_dir, "solution.avi"), cv2.VideoWriter_fourcc(*"mp4v"), fps, (w, h))
723
+
724
+ try: # Process video frames
725
+ f_n = 0 # frame number, required for analytical graphs
726
+ while cap.isOpened():
727
+ success, frame = cap.read()
728
+ if not success:
729
+ break
730
+ frame = process(frame, f_n := f_n + 1) if s_n == "analytics" else process(frame)
731
+ vw.write(frame)
732
+ if cv2.waitKey(1) & 0xFF == ord("q"):
733
+ break
734
+ finally:
735
+ cap.release()
401
736
 
402
737
 
403
- def parse_key_value_pair(pair):
404
- """Parse one 'key=value' pair and return key and value."""
738
+ def parse_key_value_pair(pair: str = "key=value"):
739
+ """
740
+ Parses a key-value pair string into separate key and value components.
741
+
742
+ Args:
743
+ pair (str): A string containing a key-value pair in the format "key=value".
744
+
745
+ Returns:
746
+ key (str): The parsed key.
747
+ value (str): The parsed value.
748
+
749
+ Raises:
750
+ AssertionError: If the value is missing or empty.
751
+
752
+ Examples:
753
+ >>> key, value = parse_key_value_pair("model=yolo11n.pt")
754
+ >>> print(f"Key: {key}, Value: {value}")
755
+ Key: model, Value: yolo11n.pt
756
+
757
+ >>> key, value = parse_key_value_pair("epochs=100")
758
+ >>> print(f"Key: {key}, Value: {value}")
759
+ Key: epochs, Value: 100
760
+
761
+ Notes:
762
+ - The function splits the input string on the first '=' character.
763
+ - Leading and trailing whitespace is removed from both key and value.
764
+ - An assertion error is raised if the value is empty after stripping.
765
+ """
405
766
  k, v = pair.split("=", 1) # split on first '=' sign
406
767
  k, v = k.strip(), v.strip() # remove spaces
407
768
  assert v, f"missing '{k}' value"
@@ -409,7 +770,36 @@ def parse_key_value_pair(pair):
409
770
 
410
771
 
411
772
  def smart_value(v):
412
- """Convert a string to an underlying type such as int, float, bool, etc."""
773
+ """
774
+ Converts a string representation of a value to its appropriate Python type.
775
+
776
+ This function attempts to convert a given string into a Python object of the most appropriate type. It handles
777
+ conversions to None, bool, int, float, and other types that can be evaluated safely.
778
+
779
+ Args:
780
+ v (str): The string representation of the value to be converted.
781
+
782
+ Returns:
783
+ (Any): The converted value. The type can be None, bool, int, float, or the original string if no conversion
784
+ is applicable.
785
+
786
+ Examples:
787
+ >>> smart_value("42")
788
+ 42
789
+ >>> smart_value("3.14")
790
+ 3.14
791
+ >>> smart_value("True")
792
+ True
793
+ >>> smart_value("None")
794
+ None
795
+ >>> smart_value("some_string")
796
+ 'some_string'
797
+
798
+ Notes:
799
+ - The function uses a case-insensitive comparison for boolean and None values.
800
+ - For other types, it attempts to use Python's eval() function, which can be unsafe if used on untrusted input.
801
+ - If no conversion is possible, the original string is returned.
802
+ """
413
803
  v_lower = v.lower()
414
804
  if v_lower == "none":
415
805
  return None
@@ -418,27 +808,38 @@ def smart_value(v):
418
808
  elif v_lower == "false":
419
809
  return False
420
810
  else:
421
- with contextlib.suppress(Exception):
811
+ try:
422
812
  return eval(v)
423
- return v
813
+ except Exception:
814
+ return v
424
815
 
425
816
 
426
817
  def entrypoint(debug=""):
427
818
  """
428
- This function is the ultralytics package entrypoint, it's responsible for parsing the command line arguments passed
429
- to the package.
430
-
431
- This function allows for:
432
- - passing mandatory YOLO args as a list of strings
433
- - specifying the task to be performed, either 'detect', 'segment' or 'classify'
434
- - specifying the mode, either 'train', 'val', 'test', or 'predict'
435
- - running special modes like 'checks'
436
- - passing overrides to the package's configuration
437
-
438
- It uses the package's default cfg and initializes it using the passed overrides.
439
- Then it calls the CLI function with the composed cfg
819
+ Ultralytics entrypoint function for parsing and executing command-line arguments.
820
+
821
+ This function serves as the main entry point for the Ultralytics CLI, parsing command-line arguments and
822
+ executing the corresponding tasks such as training, validation, prediction, exporting models, and more.
823
+
824
+ Args:
825
+ debug (str): Space-separated string of command-line arguments for debugging purposes.
826
+
827
+ Examples:
828
+ Train a detection model for 10 epochs with an initial learning_rate of 0.01:
829
+ >>> entrypoint("train data=coco8.yaml model=yolo11n.pt epochs=10 lr0=0.01")
830
+
831
+ Predict a YouTube video using a pretrained segmentation model at image size 320:
832
+ >>> entrypoint("predict model=yolo11n-seg.pt source='https://youtu.be/LNwODJXcvt4' imgsz=320")
833
+
834
+ Validate a pretrained detection model at batch-size 1 and image size 640:
835
+ >>> entrypoint("val model=yolo11n.pt data=coco8.yaml batch=1 imgsz=640")
836
+
837
+ Notes:
838
+ - If no arguments are passed, the function will display the usage help message.
839
+ - For a list of all available commands and their arguments, see the provided help messages and the
840
+ Ultralytics documentation at https://docs.ultralytics.com.
440
841
  """
441
- args = (debug.split(" ") if debug else sys.argv)[1:]
842
+ args = (debug.split(" ") if debug else ARGV)[1:]
442
843
  if not args: # no arguments passed
443
844
  LOGGER.info(CLI_HELP_MSG)
444
845
  return
@@ -451,8 +852,9 @@ def entrypoint(debug=""):
451
852
  "cfg": lambda: yaml_print(DEFAULT_CFG_PATH),
452
853
  "hub": lambda: handle_yolo_hub(args[1:]),
453
854
  "login": lambda: handle_yolo_hub(args),
855
+ "logout": lambda: handle_yolo_hub(args),
454
856
  "copy-cfg": copy_default_cfg,
455
- "explorer": lambda: handle_explorer(),
857
+ "solutions": lambda: handle_yolo_solutions(args[1:]),
456
858
  }
457
859
  full_args_dict = {**DEFAULT_CFG_DICT, **{k: None for k in TASKS}, **{k: None for k in MODES}, **special}
458
860
 
@@ -464,10 +866,10 @@ def entrypoint(debug=""):
464
866
  overrides = {} # basic overrides, i.e. imgsz=320
465
867
  for a in merge_equals_args(args): # merge spaces around '=' sign
466
868
  if a.startswith("--"):
467
- LOGGER.warning(f"WARNING ⚠️ '{a}' does not require leading dashes '--', updating to '{a[2:]}'.")
869
+ LOGGER.warning(f"WARNING ⚠️ argument '{a}' does not require leading dashes '--', updating to '{a[2:]}'.")
468
870
  a = a[2:]
469
871
  if a.endswith(","):
470
- LOGGER.warning(f"WARNING ⚠️ '{a}' does not require trailing comma ',', updating to '{a[:-1]}'.")
872
+ LOGGER.warning(f"WARNING ⚠️ argument '{a}' does not require trailing comma ',', updating to '{a[:-1]}'.")
471
873
  a = a[:-1]
472
874
  if "=" in a:
473
875
  try:
@@ -504,23 +906,34 @@ def entrypoint(debug=""):
504
906
  mode = overrides.get("mode")
505
907
  if mode is None:
506
908
  mode = DEFAULT_CFG.mode or "predict"
507
- LOGGER.warning(f"WARNING ⚠️ 'mode' is missing. Valid modes are {MODES}. Using default 'mode={mode}'.")
909
+ LOGGER.warning(f"WARNING ⚠️ 'mode' argument is missing. Valid modes are {MODES}. Using default 'mode={mode}'.")
508
910
  elif mode not in MODES:
509
911
  raise ValueError(f"Invalid 'mode={mode}'. Valid modes are {MODES}.\n{CLI_HELP_MSG}")
510
912
 
511
913
  # Task
512
914
  task = overrides.pop("task", None)
513
915
  if task:
514
- if task not in TASKS:
515
- raise ValueError(f"Invalid 'task={task}'. Valid tasks are {TASKS}.\n{CLI_HELP_MSG}")
916
+ if task == "classify" and mode == "track":
917
+ raise ValueError(
918
+ f"❌ Classification doesn't support 'mode=track'. Valid modes for classification are"
919
+ f" {MODES - {'track'}}.\n{CLI_HELP_MSG}"
920
+ )
921
+ elif task not in TASKS:
922
+ if task == "track":
923
+ LOGGER.warning(
924
+ "WARNING ⚠️ invalid 'task=track', setting 'task=detect' and 'mode=track'. Valid tasks are {TASKS}.\n{CLI_HELP_MSG}."
925
+ )
926
+ task, mode = "detect", "track"
927
+ else:
928
+ raise ValueError(f"Invalid 'task={task}'. Valid tasks are {TASKS}.\n{CLI_HELP_MSG}")
516
929
  if "model" not in overrides:
517
930
  overrides["model"] = TASK2MODEL[task]
518
931
 
519
932
  # Model
520
933
  model = overrides.pop("model", DEFAULT_CFG.model)
521
934
  if model is None:
522
- model = "yolov8n.pt"
523
- LOGGER.warning(f"WARNING ⚠️ 'model' is missing. Using default 'model={model}'.")
935
+ model = "yolo11n.pt"
936
+ LOGGER.warning(f"WARNING ⚠️ 'model' argument is missing. Using default 'model={model}'.")
524
937
  overrides["model"] = model
525
938
  stem = Path(model).stem.lower()
526
939
  if "rtdetr" in stem: # guess architecture
@@ -531,7 +944,7 @@ def entrypoint(debug=""):
531
944
  from ultralytics import FastSAM
532
945
 
533
946
  model = FastSAM(model)
534
- elif "sam" in stem:
947
+ elif "sam_" in stem or "sam2_" in stem or "sam2.1_" in stem:
535
948
  from ultralytics import SAM
536
949
 
537
950
  model = SAM(model)
@@ -552,17 +965,19 @@ def entrypoint(debug=""):
552
965
  task = model.task
553
966
 
554
967
  # Mode
555
- if mode in ("predict", "track") and "source" not in overrides:
556
- overrides["source"] = DEFAULT_CFG.source or ASSETS
557
- LOGGER.warning(f"WARNING ⚠️ 'source' is missing. Using default 'source={overrides['source']}'.")
558
- elif mode in ("train", "val"):
968
+ if mode in {"predict", "track"} and "source" not in overrides:
969
+ overrides["source"] = (
970
+ "https://ultralytics.com/images/boats.jpg" if task == "obb" else DEFAULT_CFG.source or ASSETS
971
+ )
972
+ LOGGER.warning(f"WARNING ⚠️ 'source' argument is missing. Using default 'source={overrides['source']}'.")
973
+ elif mode in {"train", "val"}:
559
974
  if "data" not in overrides and "resume" not in overrides:
560
975
  overrides["data"] = DEFAULT_CFG.data or TASK2DATA.get(task or DEFAULT_CFG.task, DEFAULT_CFG.data)
561
- LOGGER.warning(f"WARNING ⚠️ 'data' is missing. Using default 'data={overrides['data']}'.")
976
+ LOGGER.warning(f"WARNING ⚠️ 'data' argument is missing. Using default 'data={overrides['data']}'.")
562
977
  elif mode == "export":
563
978
  if "format" not in overrides:
564
979
  overrides["format"] = DEFAULT_CFG.format or "torchscript"
565
- LOGGER.warning(f"WARNING ⚠️ 'format' is missing. Using default 'format={overrides['format']}'.")
980
+ LOGGER.warning(f"WARNING ⚠️ 'format' argument is missing. Using default 'format={overrides['format']}'.")
566
981
 
567
982
  # Run command in python
568
983
  getattr(model, mode)(**overrides) # default args from model
@@ -570,10 +985,33 @@ def entrypoint(debug=""):
570
985
  # Show help
571
986
  LOGGER.info(f"💡 Learn more at https://docs.ultralytics.com/modes/{mode}")
572
987
 
988
+ # Recommend VS Code extension
989
+ if IS_VSCODE and SETTINGS.get("vscode_msg", True):
990
+ LOGGER.info(vscode_msg())
991
+
573
992
 
574
993
  # Special modes --------------------------------------------------------------------------------------------------------
575
994
  def copy_default_cfg():
576
- """Copy and create a new default configuration file with '_copy' appended to its name."""
995
+ """
996
+ Copies the default configuration file and creates a new one with '_copy' appended to its name.
997
+
998
+ This function duplicates the existing default configuration file (DEFAULT_CFG_PATH) and saves it
999
+ with '_copy' appended to its name in the current working directory. It provides a convenient way
1000
+ to create a custom configuration file based on the default settings.
1001
+
1002
+ Examples:
1003
+ >>> copy_default_cfg()
1004
+ # Output: default.yaml copied to /path/to/current/directory/default_copy.yaml
1005
+ # Example YOLO command with this new custom cfg:
1006
+ # yolo cfg='/path/to/current/directory/default_copy.yaml' imgsz=320 batch=8
1007
+
1008
+ Notes:
1009
+ - The new configuration file is created in the current working directory.
1010
+ - After copying, the function prints a message with the new file's location and an example
1011
+ YOLO command demonstrating how to use the new configuration file.
1012
+ - This function is useful for users who want to modify the default configuration without
1013
+ altering the original file.
1014
+ """
577
1015
  new_file = Path.cwd() / DEFAULT_CFG_PATH.name.replace(".yaml", "_copy.yaml")
578
1016
  shutil.copy2(DEFAULT_CFG_PATH, new_file)
579
1017
  LOGGER.info(
@@ -583,5 +1021,5 @@ def copy_default_cfg():
583
1021
 
584
1022
 
585
1023
  if __name__ == "__main__":
586
- # Example: entrypoint(debug='yolo predict model=yolov8n.pt')
1024
+ # Example: entrypoint(debug='yolo predict model=yolo11n.pt')
587
1025
  entrypoint(debug="")