ultralytics 8.1.29__py3-none-any.whl → 8.3.63__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tests/__init__.py +22 -0
- tests/conftest.py +83 -0
- tests/test_cli.py +122 -0
- tests/test_cuda.py +155 -0
- tests/test_engine.py +131 -0
- tests/test_exports.py +216 -0
- tests/test_integrations.py +150 -0
- tests/test_python.py +615 -0
- tests/test_solutions.py +94 -0
- ultralytics/__init__.py +11 -8
- ultralytics/cfg/__init__.py +569 -131
- ultralytics/cfg/datasets/Argoverse.yaml +2 -1
- ultralytics/cfg/datasets/DOTAv1.5.yaml +3 -2
- ultralytics/cfg/datasets/DOTAv1.yaml +3 -2
- ultralytics/cfg/datasets/GlobalWheat2020.yaml +3 -2
- ultralytics/cfg/datasets/ImageNet.yaml +2 -1
- ultralytics/cfg/datasets/Objects365.yaml +5 -4
- ultralytics/cfg/datasets/SKU-110K.yaml +2 -1
- ultralytics/cfg/datasets/VOC.yaml +3 -2
- ultralytics/cfg/datasets/VisDrone.yaml +6 -5
- ultralytics/cfg/datasets/african-wildlife.yaml +25 -0
- ultralytics/cfg/datasets/brain-tumor.yaml +23 -0
- ultralytics/cfg/datasets/carparts-seg.yaml +3 -2
- ultralytics/cfg/datasets/coco-pose.yaml +7 -6
- ultralytics/cfg/datasets/coco.yaml +3 -2
- ultralytics/cfg/datasets/coco128-seg.yaml +4 -3
- ultralytics/cfg/datasets/coco128.yaml +4 -3
- ultralytics/cfg/datasets/coco8-pose.yaml +3 -2
- ultralytics/cfg/datasets/coco8-seg.yaml +3 -2
- ultralytics/cfg/datasets/coco8.yaml +3 -2
- ultralytics/cfg/datasets/crack-seg.yaml +3 -2
- ultralytics/cfg/datasets/dog-pose.yaml +24 -0
- ultralytics/cfg/datasets/dota8.yaml +3 -2
- ultralytics/cfg/datasets/hand-keypoints.yaml +26 -0
- ultralytics/cfg/datasets/lvis.yaml +1236 -0
- ultralytics/cfg/datasets/medical-pills.yaml +22 -0
- ultralytics/cfg/datasets/open-images-v7.yaml +2 -1
- ultralytics/cfg/datasets/package-seg.yaml +5 -4
- ultralytics/cfg/datasets/signature.yaml +21 -0
- ultralytics/cfg/datasets/tiger-pose.yaml +3 -2
- ultralytics/cfg/datasets/xView.yaml +2 -1
- ultralytics/cfg/default.yaml +14 -11
- ultralytics/cfg/models/11/yolo11-cls-resnet18.yaml +24 -0
- ultralytics/cfg/models/11/yolo11-cls.yaml +33 -0
- ultralytics/cfg/models/11/yolo11-obb.yaml +50 -0
- ultralytics/cfg/models/11/yolo11-pose.yaml +51 -0
- ultralytics/cfg/models/11/yolo11-seg.yaml +50 -0
- ultralytics/cfg/models/11/yolo11.yaml +50 -0
- ultralytics/cfg/models/rt-detr/rtdetr-l.yaml +5 -2
- ultralytics/cfg/models/rt-detr/rtdetr-resnet101.yaml +5 -2
- ultralytics/cfg/models/rt-detr/rtdetr-resnet50.yaml +5 -2
- ultralytics/cfg/models/rt-detr/rtdetr-x.yaml +5 -2
- ultralytics/cfg/models/v10/yolov10b.yaml +45 -0
- ultralytics/cfg/models/v10/yolov10l.yaml +45 -0
- ultralytics/cfg/models/v10/yolov10m.yaml +45 -0
- ultralytics/cfg/models/v10/yolov10n.yaml +45 -0
- ultralytics/cfg/models/v10/yolov10s.yaml +45 -0
- ultralytics/cfg/models/v10/yolov10x.yaml +45 -0
- ultralytics/cfg/models/v3/yolov3-spp.yaml +5 -2
- ultralytics/cfg/models/v3/yolov3-tiny.yaml +5 -2
- ultralytics/cfg/models/v3/yolov3.yaml +5 -2
- ultralytics/cfg/models/v5/yolov5-p6.yaml +5 -2
- ultralytics/cfg/models/v5/yolov5.yaml +5 -2
- ultralytics/cfg/models/v6/yolov6.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-cls-resnet101.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-cls-resnet50.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-cls.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-ghost-p2.yaml +6 -2
- ultralytics/cfg/models/v8/yolov8-ghost-p6.yaml +6 -2
- ultralytics/cfg/models/v8/yolov8-ghost.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-obb.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-p2.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-p6.yaml +10 -7
- ultralytics/cfg/models/v8/yolov8-pose-p6.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-pose.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-rtdetr.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-seg-p6.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-seg.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-world.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8-worldv2.yaml +5 -2
- ultralytics/cfg/models/v8/yolov8.yaml +5 -2
- ultralytics/cfg/models/v9/yolov9c-seg.yaml +41 -0
- ultralytics/cfg/models/v9/yolov9c.yaml +30 -25
- ultralytics/cfg/models/v9/yolov9e-seg.yaml +64 -0
- ultralytics/cfg/models/v9/yolov9e.yaml +46 -42
- ultralytics/cfg/models/v9/yolov9m.yaml +41 -0
- ultralytics/cfg/models/v9/yolov9s.yaml +41 -0
- ultralytics/cfg/models/v9/yolov9t.yaml +41 -0
- ultralytics/cfg/solutions/default.yaml +24 -0
- ultralytics/cfg/trackers/botsort.yaml +8 -5
- ultralytics/cfg/trackers/bytetrack.yaml +8 -5
- ultralytics/data/__init__.py +14 -3
- ultralytics/data/annotator.py +37 -15
- ultralytics/data/augment.py +1783 -289
- ultralytics/data/base.py +62 -27
- ultralytics/data/build.py +37 -8
- ultralytics/data/converter.py +196 -36
- ultralytics/data/dataset.py +233 -94
- ultralytics/data/loaders.py +199 -96
- ultralytics/data/split_dota.py +39 -29
- ultralytics/data/utils.py +111 -41
- ultralytics/engine/__init__.py +1 -1
- ultralytics/engine/exporter.py +579 -244
- ultralytics/engine/model.py +604 -252
- ultralytics/engine/predictor.py +22 -11
- ultralytics/engine/results.py +1228 -218
- ultralytics/engine/trainer.py +191 -129
- ultralytics/engine/tuner.py +18 -18
- ultralytics/engine/validator.py +18 -15
- ultralytics/hub/__init__.py +31 -13
- ultralytics/hub/auth.py +11 -7
- ultralytics/hub/google/__init__.py +159 -0
- ultralytics/hub/session.py +128 -94
- ultralytics/hub/utils.py +20 -21
- ultralytics/models/__init__.py +4 -2
- ultralytics/models/fastsam/__init__.py +2 -3
- ultralytics/models/fastsam/model.py +26 -4
- ultralytics/models/fastsam/predict.py +127 -63
- ultralytics/models/fastsam/utils.py +1 -44
- ultralytics/models/fastsam/val.py +1 -1
- ultralytics/models/nas/__init__.py +1 -1
- ultralytics/models/nas/model.py +21 -10
- ultralytics/models/nas/predict.py +3 -6
- ultralytics/models/nas/val.py +4 -4
- ultralytics/models/rtdetr/__init__.py +1 -1
- ultralytics/models/rtdetr/model.py +1 -1
- ultralytics/models/rtdetr/predict.py +6 -8
- ultralytics/models/rtdetr/train.py +6 -2
- ultralytics/models/rtdetr/val.py +3 -3
- ultralytics/models/sam/__init__.py +3 -3
- ultralytics/models/sam/amg.py +29 -23
- ultralytics/models/sam/build.py +211 -13
- ultralytics/models/sam/model.py +91 -30
- ultralytics/models/sam/modules/__init__.py +1 -1
- ultralytics/models/sam/modules/blocks.py +1129 -0
- ultralytics/models/sam/modules/decoders.py +381 -53
- ultralytics/models/sam/modules/encoders.py +515 -324
- ultralytics/models/sam/modules/memory_attention.py +237 -0
- ultralytics/models/sam/modules/sam.py +969 -21
- ultralytics/models/sam/modules/tiny_encoder.py +425 -154
- ultralytics/models/sam/modules/transformer.py +159 -60
- ultralytics/models/sam/modules/utils.py +293 -0
- ultralytics/models/sam/predict.py +1263 -132
- ultralytics/models/utils/__init__.py +1 -1
- ultralytics/models/utils/loss.py +36 -24
- ultralytics/models/utils/ops.py +3 -7
- ultralytics/models/yolo/__init__.py +3 -3
- ultralytics/models/yolo/classify/__init__.py +1 -1
- ultralytics/models/yolo/classify/predict.py +7 -8
- ultralytics/models/yolo/classify/train.py +17 -22
- ultralytics/models/yolo/classify/val.py +8 -4
- ultralytics/models/yolo/detect/__init__.py +1 -1
- ultralytics/models/yolo/detect/predict.py +3 -5
- ultralytics/models/yolo/detect/train.py +11 -4
- ultralytics/models/yolo/detect/val.py +90 -52
- ultralytics/models/yolo/model.py +14 -9
- ultralytics/models/yolo/obb/__init__.py +1 -1
- ultralytics/models/yolo/obb/predict.py +2 -2
- ultralytics/models/yolo/obb/train.py +5 -3
- ultralytics/models/yolo/obb/val.py +41 -23
- ultralytics/models/yolo/pose/__init__.py +1 -1
- ultralytics/models/yolo/pose/predict.py +3 -5
- ultralytics/models/yolo/pose/train.py +2 -2
- ultralytics/models/yolo/pose/val.py +51 -17
- ultralytics/models/yolo/segment/__init__.py +1 -1
- ultralytics/models/yolo/segment/predict.py +3 -5
- ultralytics/models/yolo/segment/train.py +2 -2
- ultralytics/models/yolo/segment/val.py +60 -19
- ultralytics/models/yolo/world/__init__.py +5 -0
- ultralytics/models/yolo/world/train.py +92 -0
- ultralytics/models/yolo/world/train_world.py +109 -0
- ultralytics/nn/__init__.py +1 -1
- ultralytics/nn/autobackend.py +228 -93
- ultralytics/nn/modules/__init__.py +39 -14
- ultralytics/nn/modules/activation.py +21 -0
- ultralytics/nn/modules/block.py +526 -66
- ultralytics/nn/modules/conv.py +24 -7
- ultralytics/nn/modules/head.py +177 -34
- ultralytics/nn/modules/transformer.py +6 -5
- ultralytics/nn/modules/utils.py +1 -2
- ultralytics/nn/tasks.py +226 -82
- ultralytics/solutions/__init__.py +30 -1
- ultralytics/solutions/ai_gym.py +96 -143
- ultralytics/solutions/analytics.py +247 -0
- ultralytics/solutions/distance_calculation.py +78 -135
- ultralytics/solutions/heatmap.py +93 -247
- ultralytics/solutions/object_counter.py +184 -259
- ultralytics/solutions/parking_management.py +246 -0
- ultralytics/solutions/queue_management.py +112 -0
- ultralytics/solutions/region_counter.py +116 -0
- ultralytics/solutions/security_alarm.py +144 -0
- ultralytics/solutions/solutions.py +178 -0
- ultralytics/solutions/speed_estimation.py +86 -174
- ultralytics/solutions/streamlit_inference.py +190 -0
- ultralytics/solutions/trackzone.py +68 -0
- ultralytics/trackers/__init__.py +1 -1
- ultralytics/trackers/basetrack.py +32 -13
- ultralytics/trackers/bot_sort.py +61 -28
- ultralytics/trackers/byte_tracker.py +83 -51
- ultralytics/trackers/track.py +21 -6
- ultralytics/trackers/utils/__init__.py +1 -1
- ultralytics/trackers/utils/gmc.py +62 -48
- ultralytics/trackers/utils/kalman_filter.py +166 -35
- ultralytics/trackers/utils/matching.py +40 -21
- ultralytics/utils/__init__.py +511 -239
- ultralytics/utils/autobatch.py +40 -22
- ultralytics/utils/benchmarks.py +266 -85
- ultralytics/utils/callbacks/__init__.py +1 -1
- ultralytics/utils/callbacks/base.py +1 -3
- ultralytics/utils/callbacks/clearml.py +7 -6
- ultralytics/utils/callbacks/comet.py +39 -17
- ultralytics/utils/callbacks/dvc.py +1 -1
- ultralytics/utils/callbacks/hub.py +16 -16
- ultralytics/utils/callbacks/mlflow.py +28 -24
- ultralytics/utils/callbacks/neptune.py +6 -2
- ultralytics/utils/callbacks/raytune.py +3 -4
- ultralytics/utils/callbacks/tensorboard.py +18 -18
- ultralytics/utils/callbacks/wb.py +27 -20
- ultralytics/utils/checks.py +172 -100
- ultralytics/utils/dist.py +2 -1
- ultralytics/utils/downloads.py +40 -34
- ultralytics/utils/errors.py +1 -1
- ultralytics/utils/files.py +72 -38
- ultralytics/utils/instance.py +41 -19
- ultralytics/utils/loss.py +83 -55
- ultralytics/utils/metrics.py +61 -56
- ultralytics/utils/ops.py +94 -89
- ultralytics/utils/patches.py +30 -14
- ultralytics/utils/plotting.py +600 -269
- ultralytics/utils/tal.py +67 -26
- ultralytics/utils/torch_utils.py +305 -112
- ultralytics/utils/triton.py +2 -1
- ultralytics/utils/tuner.py +21 -12
- ultralytics-8.3.63.dist-info/METADATA +370 -0
- ultralytics-8.3.63.dist-info/RECORD +241 -0
- {ultralytics-8.1.29.dist-info → ultralytics-8.3.63.dist-info}/WHEEL +1 -1
- ultralytics/data/explorer/__init__.py +0 -5
- ultralytics/data/explorer/explorer.py +0 -472
- ultralytics/data/explorer/gui/__init__.py +0 -1
- ultralytics/data/explorer/gui/dash.py +0 -268
- ultralytics/data/explorer/utils.py +0 -166
- ultralytics/models/fastsam/prompt.py +0 -357
- ultralytics-8.1.29.dist-info/METADATA +0 -373
- ultralytics-8.1.29.dist-info/RECORD +0 -197
- {ultralytics-8.1.29.dist-info → ultralytics-8.3.63.dist-info}/LICENSE +0 -0
- {ultralytics-8.1.29.dist-info → ultralytics-8.3.63.dist-info}/entry_points.txt +0 -0
- {ultralytics-8.1.29.dist-info → ultralytics-8.3.63.dist-info}/top_level.txt +0 -0
ultralytics/cfg/__init__.py
CHANGED
@@ -1,6 +1,5 @@
|
|
1
|
-
# Ultralytics
|
1
|
+
# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
|
2
2
|
|
3
|
-
import contextlib
|
4
3
|
import 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
|
-
|
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": "
|
44
|
-
"segment": "
|
45
|
-
"classify": "
|
46
|
-
"pose": "
|
47
|
-
"obb": "
|
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([
|
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=
|
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=
|
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=
|
123
|
+
yolo val model=yolo11n.pt data=coco8.yaml batch=1 imgsz=640
|
75
124
|
|
76
|
-
4. Export a
|
77
|
-
yolo export model=
|
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
|
-
|
80
|
-
yolo
|
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
|
-
|
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 =
|
97
|
-
|
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
|
-
|
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
|
-
|
235
|
+
Converts a configuration object to a dictionary.
|
178
236
|
|
179
237
|
Args:
|
180
|
-
cfg (str | Path |
|
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
|
-
|
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
|
-
|
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):
|
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
|
-
|
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.
|
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
|
-
|
239
|
-
|
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
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
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
|
-
"""
|
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
|
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
|
-
"""
|
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
|
-
|
289
|
-
|
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
|
-
|
293
|
-
|
294
|
-
e (
|
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
|
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 '='
|
314
|
-
|
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
|
499
|
+
args (List[str]): A list of strings where each element represents an argument or fragment.
|
318
500
|
|
319
501
|
Returns:
|
320
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
524
|
+
i += 2
|
525
|
+
continue
|
330
526
|
elif arg.startswith("=") and i > 0: # merge ['arg', '=val']
|
331
527
|
new_args[-1] += arg
|
332
|
-
|
333
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
558
|
+
Examples:
|
348
559
|
```bash
|
349
|
-
|
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
|
-
|
581
|
+
Handles YOLO settings command-line interface (CLI) commands.
|
366
582
|
|
367
|
-
This function processes YOLO settings CLI commands such as reset.
|
368
|
-
|
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
|
-
|
374
|
-
|
375
|
-
|
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
|
-
|
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
|
-
|
391
|
-
|
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
|
397
|
-
"""
|
398
|
-
|
399
|
-
|
400
|
-
|
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
|
-
"""
|
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
|
-
"""
|
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
|
-
|
811
|
+
try:
|
422
812
|
return eval(v)
|
423
|
-
|
813
|
+
except Exception:
|
814
|
+
return v
|
424
815
|
|
425
816
|
|
426
817
|
def entrypoint(debug=""):
|
427
818
|
"""
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
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
|
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
|
-
"
|
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
|
515
|
-
raise ValueError(
|
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 = "
|
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 "
|
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
|
556
|
-
overrides["source"] =
|
557
|
-
|
558
|
-
|
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
|
-
"""
|
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=
|
1024
|
+
# Example: entrypoint(debug='yolo predict model=yolo11n.pt')
|
587
1025
|
entrypoint(debug="")
|