dgenerate-ultralytics-headless 8.3.137__py3-none-any.whl → 8.3.224__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.
- {dgenerate_ultralytics_headless-8.3.137.dist-info → dgenerate_ultralytics_headless-8.3.224.dist-info}/METADATA +41 -34
- dgenerate_ultralytics_headless-8.3.224.dist-info/RECORD +285 -0
- {dgenerate_ultralytics_headless-8.3.137.dist-info → dgenerate_ultralytics_headless-8.3.224.dist-info}/WHEEL +1 -1
- tests/__init__.py +7 -6
- tests/conftest.py +15 -39
- tests/test_cli.py +17 -17
- tests/test_cuda.py +17 -8
- tests/test_engine.py +36 -10
- tests/test_exports.py +98 -37
- tests/test_integrations.py +12 -15
- tests/test_python.py +126 -82
- tests/test_solutions.py +319 -135
- ultralytics/__init__.py +27 -9
- ultralytics/cfg/__init__.py +83 -87
- ultralytics/cfg/datasets/Argoverse.yaml +4 -4
- ultralytics/cfg/datasets/DOTAv1.5.yaml +2 -2
- ultralytics/cfg/datasets/DOTAv1.yaml +2 -2
- ultralytics/cfg/datasets/GlobalWheat2020.yaml +2 -2
- ultralytics/cfg/datasets/HomeObjects-3K.yaml +4 -5
- ultralytics/cfg/datasets/ImageNet.yaml +3 -3
- ultralytics/cfg/datasets/Objects365.yaml +24 -20
- ultralytics/cfg/datasets/SKU-110K.yaml +9 -9
- ultralytics/cfg/datasets/VOC.yaml +10 -13
- ultralytics/cfg/datasets/VisDrone.yaml +43 -33
- ultralytics/cfg/datasets/african-wildlife.yaml +5 -5
- ultralytics/cfg/datasets/brain-tumor.yaml +4 -5
- ultralytics/cfg/datasets/carparts-seg.yaml +5 -5
- ultralytics/cfg/datasets/coco-pose.yaml +26 -4
- ultralytics/cfg/datasets/coco.yaml +4 -4
- ultralytics/cfg/datasets/coco128-seg.yaml +2 -2
- ultralytics/cfg/datasets/coco128.yaml +2 -2
- ultralytics/cfg/datasets/coco8-grayscale.yaml +103 -0
- ultralytics/cfg/datasets/coco8-multispectral.yaml +2 -2
- ultralytics/cfg/datasets/coco8-pose.yaml +23 -2
- ultralytics/cfg/datasets/coco8-seg.yaml +2 -2
- ultralytics/cfg/datasets/coco8.yaml +2 -2
- ultralytics/cfg/datasets/construction-ppe.yaml +32 -0
- ultralytics/cfg/datasets/crack-seg.yaml +5 -5
- ultralytics/cfg/datasets/dog-pose.yaml +32 -4
- ultralytics/cfg/datasets/dota8-multispectral.yaml +2 -2
- ultralytics/cfg/datasets/dota8.yaml +2 -2
- ultralytics/cfg/datasets/hand-keypoints.yaml +29 -4
- ultralytics/cfg/datasets/lvis.yaml +9 -9
- ultralytics/cfg/datasets/medical-pills.yaml +4 -5
- ultralytics/cfg/datasets/open-images-v7.yaml +7 -10
- ultralytics/cfg/datasets/package-seg.yaml +5 -5
- ultralytics/cfg/datasets/signature.yaml +4 -4
- ultralytics/cfg/datasets/tiger-pose.yaml +20 -4
- ultralytics/cfg/datasets/xView.yaml +5 -5
- ultralytics/cfg/default.yaml +96 -93
- ultralytics/cfg/trackers/botsort.yaml +16 -17
- ultralytics/cfg/trackers/bytetrack.yaml +9 -11
- ultralytics/data/__init__.py +4 -4
- ultralytics/data/annotator.py +12 -12
- ultralytics/data/augment.py +531 -564
- ultralytics/data/base.py +76 -81
- ultralytics/data/build.py +206 -42
- ultralytics/data/converter.py +179 -78
- ultralytics/data/dataset.py +121 -121
- ultralytics/data/loaders.py +114 -91
- ultralytics/data/split.py +28 -15
- ultralytics/data/split_dota.py +67 -48
- ultralytics/data/utils.py +110 -89
- ultralytics/engine/exporter.py +422 -460
- ultralytics/engine/model.py +224 -252
- ultralytics/engine/predictor.py +94 -89
- ultralytics/engine/results.py +345 -595
- ultralytics/engine/trainer.py +231 -134
- ultralytics/engine/tuner.py +279 -73
- ultralytics/engine/validator.py +53 -46
- ultralytics/hub/__init__.py +26 -28
- ultralytics/hub/auth.py +30 -16
- ultralytics/hub/google/__init__.py +34 -36
- ultralytics/hub/session.py +53 -77
- ultralytics/hub/utils.py +23 -109
- ultralytics/models/__init__.py +1 -1
- ultralytics/models/fastsam/__init__.py +1 -1
- ultralytics/models/fastsam/model.py +36 -18
- ultralytics/models/fastsam/predict.py +33 -44
- ultralytics/models/fastsam/utils.py +4 -5
- ultralytics/models/fastsam/val.py +12 -14
- ultralytics/models/nas/__init__.py +1 -1
- ultralytics/models/nas/model.py +16 -20
- ultralytics/models/nas/predict.py +12 -14
- ultralytics/models/nas/val.py +4 -5
- ultralytics/models/rtdetr/__init__.py +1 -1
- ultralytics/models/rtdetr/model.py +9 -9
- ultralytics/models/rtdetr/predict.py +22 -17
- ultralytics/models/rtdetr/train.py +20 -16
- ultralytics/models/rtdetr/val.py +79 -59
- ultralytics/models/sam/__init__.py +8 -2
- ultralytics/models/sam/amg.py +53 -38
- ultralytics/models/sam/build.py +29 -31
- ultralytics/models/sam/model.py +33 -38
- ultralytics/models/sam/modules/blocks.py +159 -182
- ultralytics/models/sam/modules/decoders.py +38 -47
- ultralytics/models/sam/modules/encoders.py +114 -133
- ultralytics/models/sam/modules/memory_attention.py +38 -31
- ultralytics/models/sam/modules/sam.py +114 -93
- ultralytics/models/sam/modules/tiny_encoder.py +268 -291
- ultralytics/models/sam/modules/transformer.py +59 -66
- ultralytics/models/sam/modules/utils.py +55 -72
- ultralytics/models/sam/predict.py +745 -341
- ultralytics/models/utils/loss.py +118 -107
- ultralytics/models/utils/ops.py +118 -71
- ultralytics/models/yolo/__init__.py +1 -1
- ultralytics/models/yolo/classify/predict.py +28 -26
- ultralytics/models/yolo/classify/train.py +50 -81
- ultralytics/models/yolo/classify/val.py +68 -61
- ultralytics/models/yolo/detect/predict.py +12 -15
- ultralytics/models/yolo/detect/train.py +56 -46
- ultralytics/models/yolo/detect/val.py +279 -223
- ultralytics/models/yolo/model.py +167 -86
- ultralytics/models/yolo/obb/predict.py +7 -11
- ultralytics/models/yolo/obb/train.py +23 -25
- ultralytics/models/yolo/obb/val.py +107 -99
- ultralytics/models/yolo/pose/__init__.py +1 -1
- ultralytics/models/yolo/pose/predict.py +12 -14
- ultralytics/models/yolo/pose/train.py +31 -69
- ultralytics/models/yolo/pose/val.py +119 -254
- ultralytics/models/yolo/segment/predict.py +21 -25
- ultralytics/models/yolo/segment/train.py +12 -66
- ultralytics/models/yolo/segment/val.py +126 -305
- ultralytics/models/yolo/world/train.py +53 -45
- ultralytics/models/yolo/world/train_world.py +51 -32
- ultralytics/models/yolo/yoloe/__init__.py +7 -7
- ultralytics/models/yolo/yoloe/predict.py +30 -37
- ultralytics/models/yolo/yoloe/train.py +89 -71
- ultralytics/models/yolo/yoloe/train_seg.py +15 -17
- ultralytics/models/yolo/yoloe/val.py +56 -41
- ultralytics/nn/__init__.py +9 -11
- ultralytics/nn/autobackend.py +179 -107
- ultralytics/nn/modules/__init__.py +67 -67
- ultralytics/nn/modules/activation.py +8 -7
- ultralytics/nn/modules/block.py +302 -323
- ultralytics/nn/modules/conv.py +61 -104
- ultralytics/nn/modules/head.py +488 -186
- ultralytics/nn/modules/transformer.py +183 -123
- ultralytics/nn/modules/utils.py +15 -20
- ultralytics/nn/tasks.py +327 -203
- ultralytics/nn/text_model.py +81 -65
- ultralytics/py.typed +1 -0
- ultralytics/solutions/__init__.py +12 -12
- ultralytics/solutions/ai_gym.py +19 -27
- ultralytics/solutions/analytics.py +36 -26
- ultralytics/solutions/config.py +29 -28
- ultralytics/solutions/distance_calculation.py +23 -24
- ultralytics/solutions/heatmap.py +17 -19
- ultralytics/solutions/instance_segmentation.py +21 -19
- ultralytics/solutions/object_blurrer.py +16 -17
- ultralytics/solutions/object_counter.py +48 -53
- ultralytics/solutions/object_cropper.py +22 -16
- ultralytics/solutions/parking_management.py +61 -58
- ultralytics/solutions/queue_management.py +19 -19
- ultralytics/solutions/region_counter.py +63 -50
- ultralytics/solutions/security_alarm.py +22 -25
- ultralytics/solutions/similarity_search.py +107 -60
- ultralytics/solutions/solutions.py +343 -262
- ultralytics/solutions/speed_estimation.py +35 -31
- ultralytics/solutions/streamlit_inference.py +104 -40
- ultralytics/solutions/templates/similarity-search.html +31 -24
- ultralytics/solutions/trackzone.py +24 -24
- ultralytics/solutions/vision_eye.py +11 -12
- ultralytics/trackers/__init__.py +1 -1
- ultralytics/trackers/basetrack.py +18 -27
- ultralytics/trackers/bot_sort.py +48 -39
- ultralytics/trackers/byte_tracker.py +94 -94
- ultralytics/trackers/track.py +7 -16
- ultralytics/trackers/utils/gmc.py +37 -69
- ultralytics/trackers/utils/kalman_filter.py +68 -76
- ultralytics/trackers/utils/matching.py +13 -17
- ultralytics/utils/__init__.py +251 -275
- ultralytics/utils/autobatch.py +19 -7
- ultralytics/utils/autodevice.py +68 -38
- ultralytics/utils/benchmarks.py +169 -130
- ultralytics/utils/callbacks/base.py +12 -13
- ultralytics/utils/callbacks/clearml.py +14 -15
- ultralytics/utils/callbacks/comet.py +139 -66
- ultralytics/utils/callbacks/dvc.py +19 -27
- ultralytics/utils/callbacks/hub.py +8 -6
- ultralytics/utils/callbacks/mlflow.py +6 -10
- ultralytics/utils/callbacks/neptune.py +11 -19
- ultralytics/utils/callbacks/platform.py +73 -0
- ultralytics/utils/callbacks/raytune.py +3 -4
- ultralytics/utils/callbacks/tensorboard.py +9 -12
- ultralytics/utils/callbacks/wb.py +33 -30
- ultralytics/utils/checks.py +163 -114
- ultralytics/utils/cpu.py +89 -0
- ultralytics/utils/dist.py +24 -20
- ultralytics/utils/downloads.py +176 -146
- ultralytics/utils/errors.py +11 -13
- ultralytics/utils/events.py +113 -0
- ultralytics/utils/export/__init__.py +7 -0
- ultralytics/utils/{export.py → export/engine.py} +81 -63
- ultralytics/utils/export/imx.py +294 -0
- ultralytics/utils/export/tensorflow.py +217 -0
- ultralytics/utils/files.py +33 -36
- ultralytics/utils/git.py +137 -0
- ultralytics/utils/instance.py +105 -120
- ultralytics/utils/logger.py +404 -0
- ultralytics/utils/loss.py +99 -61
- ultralytics/utils/metrics.py +649 -478
- ultralytics/utils/nms.py +337 -0
- ultralytics/utils/ops.py +263 -451
- ultralytics/utils/patches.py +70 -31
- ultralytics/utils/plotting.py +253 -223
- ultralytics/utils/tal.py +48 -61
- ultralytics/utils/torch_utils.py +244 -251
- ultralytics/utils/tqdm.py +438 -0
- ultralytics/utils/triton.py +22 -23
- ultralytics/utils/tuner.py +11 -10
- dgenerate_ultralytics_headless-8.3.137.dist-info/RECORD +0 -272
- {dgenerate_ultralytics_headless-8.3.137.dist-info → dgenerate_ultralytics_headless-8.3.224.dist-info}/entry_points.txt +0 -0
- {dgenerate_ultralytics_headless-8.3.137.dist-info → dgenerate_ultralytics_headless-8.3.224.dist-info}/licenses/LICENSE +0 -0
- {dgenerate_ultralytics_headless-8.3.137.dist-info → dgenerate_ultralytics_headless-8.3.224.dist-info}/top_level.txt +0 -0
ultralytics/utils/__init__.py
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
|
|
2
2
|
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
3
5
|
import contextlib
|
|
4
6
|
import importlib.metadata
|
|
5
7
|
import inspect
|
|
@@ -8,24 +10,24 @@ import logging
|
|
|
8
10
|
import os
|
|
9
11
|
import platform
|
|
10
12
|
import re
|
|
11
|
-
import
|
|
13
|
+
import socket
|
|
12
14
|
import sys
|
|
13
15
|
import threading
|
|
14
16
|
import time
|
|
15
|
-
import
|
|
17
|
+
from functools import lru_cache
|
|
16
18
|
from pathlib import Path
|
|
17
19
|
from threading import Lock
|
|
18
20
|
from types import SimpleNamespace
|
|
19
|
-
from typing import Union
|
|
20
21
|
from urllib.parse import unquote
|
|
21
22
|
|
|
22
23
|
import cv2
|
|
23
24
|
import numpy as np
|
|
24
25
|
import torch
|
|
25
|
-
import tqdm
|
|
26
26
|
|
|
27
27
|
from ultralytics import __version__
|
|
28
|
-
from ultralytics.utils.
|
|
28
|
+
from ultralytics.utils.git import GitRepo
|
|
29
|
+
from ultralytics.utils.patches import imread, imshow, imwrite, torch_save # for patches
|
|
30
|
+
from ultralytics.utils.tqdm import TQDM # noqa
|
|
29
31
|
|
|
30
32
|
# PyTorch Multi-GPU DDP Constants
|
|
31
33
|
RANK = int(os.getenv("RANK", -1))
|
|
@@ -41,13 +43,13 @@ DEFAULT_CFG_PATH = ROOT / "cfg/default.yaml"
|
|
|
41
43
|
NUM_THREADS = min(8, max(1, os.cpu_count() - 1)) # number of YOLO multiprocessing threads
|
|
42
44
|
AUTOINSTALL = str(os.getenv("YOLO_AUTOINSTALL", True)).lower() == "true" # global auto-install mode
|
|
43
45
|
VERBOSE = str(os.getenv("YOLO_VERBOSE", True)).lower() == "true" # global verbose mode
|
|
44
|
-
TQDM_BAR_FORMAT = "{l_bar}{bar:10}{r_bar}" if VERBOSE else None # tqdm bar format
|
|
45
46
|
LOGGING_NAME = "ultralytics"
|
|
46
47
|
MACOS, LINUX, WINDOWS = (platform.system() == x for x in ["Darwin", "Linux", "Windows"]) # environment booleans
|
|
47
48
|
MACOS_VERSION = platform.mac_ver()[0] if MACOS else None
|
|
49
|
+
NOT_MACOS14 = not (MACOS and MACOS_VERSION.startswith("14."))
|
|
48
50
|
ARM64 = platform.machine() in {"arm64", "aarch64"} # ARM64 booleans
|
|
49
51
|
PYTHON_VERSION = platform.python_version()
|
|
50
|
-
TORCH_VERSION = torch.__version__
|
|
52
|
+
TORCH_VERSION = str(torch.__version__) # Normalize torch.__version__ (PyTorch>1.9 returns TorchVersion objects)
|
|
51
53
|
TORCHVISION_VERSION = importlib.metadata.version("torchvision") # faster than importing torchvision
|
|
52
54
|
IS_VSCODE = os.environ.get("TERM_PROGRAM", False) == "vscode"
|
|
53
55
|
RKNN_CHIPS = frozenset(
|
|
@@ -130,74 +132,100 @@ os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3" # suppress verbose TF compiler warning
|
|
|
130
132
|
os.environ["TORCH_CPP_LOG_LEVEL"] = "ERROR" # suppress "NNPACK.cpp could not initialize NNPACK" warnings
|
|
131
133
|
os.environ["KINETO_LOG_LEVEL"] = "5" # suppress verbose PyTorch profiler output when computing FLOPs
|
|
132
134
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
135
|
+
# Precompiled type tuples for faster isinstance() checks
|
|
136
|
+
FLOAT_OR_INT = (float, int)
|
|
137
|
+
STR_OR_PATH = (str, Path)
|
|
136
138
|
|
|
137
|
-
class TQDM(rich.tqdm if TQDM_RICH else tqdm.tqdm):
|
|
138
|
-
"""
|
|
139
|
-
A custom TQDM progress bar class that extends the original tqdm functionality.
|
|
140
139
|
|
|
141
|
-
|
|
142
|
-
|
|
140
|
+
class DataExportMixin:
|
|
141
|
+
"""Mixin class for exporting validation metrics or prediction results in various formats.
|
|
143
142
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
bar_format (str): The format string for the progress bar. Uses the global TQDM_BAR_FORMAT if not
|
|
148
|
-
explicitly set.
|
|
143
|
+
This class provides utilities to export performance metrics (e.g., mAP, precision, recall) or prediction results
|
|
144
|
+
from classification, object detection, segmentation, or pose estimation tasks into various formats: Polars
|
|
145
|
+
DataFrame, CSV and JSON.
|
|
149
146
|
|
|
150
147
|
Methods:
|
|
151
|
-
|
|
148
|
+
to_df: Convert summary to a Polars DataFrame.
|
|
149
|
+
to_csv: Export results as a CSV string.
|
|
150
|
+
to_json: Export results as a JSON string.
|
|
151
|
+
tojson: Deprecated alias for `to_json()`.
|
|
152
152
|
|
|
153
153
|
Examples:
|
|
154
|
-
>>>
|
|
155
|
-
>>>
|
|
156
|
-
|
|
157
|
-
|
|
154
|
+
>>> model = YOLO("yolo11n.pt")
|
|
155
|
+
>>> results = model("image.jpg")
|
|
156
|
+
>>> df = results.to_df()
|
|
157
|
+
>>> print(df)
|
|
158
|
+
>>> csv_data = results.to_csv()
|
|
158
159
|
"""
|
|
159
160
|
|
|
160
|
-
def
|
|
161
|
+
def to_df(self, normalize=False, decimals=5):
|
|
162
|
+
"""Create a polars DataFrame from the prediction results summary or validation metrics.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
normalize (bool, optional): Normalize numerical values for easier comparison.
|
|
166
|
+
decimals (int, optional): Decimal places to round floats.
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
(DataFrame): DataFrame containing the summary data.
|
|
161
170
|
"""
|
|
162
|
-
|
|
171
|
+
import polars as pl # scope for faster 'import ultralytics'
|
|
172
|
+
|
|
173
|
+
return pl.DataFrame(self.summary(normalize=normalize, decimals=decimals))
|
|
163
174
|
|
|
164
|
-
|
|
175
|
+
def to_csv(self, normalize=False, decimals=5):
|
|
176
|
+
"""Export results or metrics to CSV string format.
|
|
165
177
|
|
|
166
178
|
Args:
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
- The default bar format is set to TQDM_BAR_FORMAT unless overridden in kwargs.
|
|
173
|
-
|
|
174
|
-
Examples:
|
|
175
|
-
>>> from ultralytics.utils import TQDM
|
|
176
|
-
>>> for i in TQDM(range(100)):
|
|
177
|
-
... # Your code here
|
|
178
|
-
... pass
|
|
179
|
+
normalize (bool, optional): Normalize numeric values.
|
|
180
|
+
decimals (int, optional): Decimal precision.
|
|
181
|
+
|
|
182
|
+
Returns:
|
|
183
|
+
(str): CSV content as string.
|
|
179
184
|
"""
|
|
180
|
-
|
|
181
|
-
kwargs["disable"] = not VERBOSE or kwargs.get("disable", False)
|
|
182
|
-
kwargs.setdefault("bar_format", TQDM_BAR_FORMAT) # override default value if passed
|
|
183
|
-
super().__init__(*args, **kwargs)
|
|
185
|
+
import polars as pl
|
|
184
186
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
187
|
+
df = self.to_df(normalize=normalize, decimals=decimals)
|
|
188
|
+
|
|
189
|
+
try:
|
|
190
|
+
return df.write_csv()
|
|
191
|
+
except Exception:
|
|
192
|
+
# Minimal string conversion for any remaining complex types
|
|
193
|
+
def _to_str_simple(v):
|
|
194
|
+
if v is None:
|
|
195
|
+
return ""
|
|
196
|
+
elif isinstance(v, (dict, list, tuple, set)):
|
|
197
|
+
return repr(v)
|
|
198
|
+
else:
|
|
199
|
+
return str(v)
|
|
200
|
+
|
|
201
|
+
df_str = df.select(
|
|
202
|
+
[pl.col(c).map_elements(_to_str_simple, return_dtype=pl.String).alias(c) for c in df.columns]
|
|
203
|
+
)
|
|
204
|
+
return df_str.write_csv()
|
|
205
|
+
|
|
206
|
+
def to_json(self, normalize=False, decimals=5):
|
|
207
|
+
"""Export results to JSON format.
|
|
208
|
+
|
|
209
|
+
Args:
|
|
210
|
+
normalize (bool, optional): Normalize numeric values.
|
|
211
|
+
decimals (int, optional): Decimal precision.
|
|
212
|
+
|
|
213
|
+
Returns:
|
|
214
|
+
(str): JSON-formatted string of the results.
|
|
215
|
+
"""
|
|
216
|
+
return self.to_df(normalize=normalize, decimals=decimals).write_json()
|
|
188
217
|
|
|
189
218
|
|
|
190
219
|
class SimpleClass:
|
|
191
|
-
"""
|
|
192
|
-
A simple base class for creating objects with string representations of their attributes.
|
|
220
|
+
"""A simple base class for creating objects with string representations of their attributes.
|
|
193
221
|
|
|
194
|
-
This class provides a foundation for creating objects that can be easily printed or represented as strings,
|
|
195
|
-
|
|
222
|
+
This class provides a foundation for creating objects that can be easily printed or represented as strings, showing
|
|
223
|
+
all their non-callable attributes. It's useful for debugging and introspection of object states.
|
|
196
224
|
|
|
197
225
|
Methods:
|
|
198
|
-
__str__:
|
|
199
|
-
__repr__:
|
|
200
|
-
__getattr__:
|
|
226
|
+
__str__: Return a human-readable string representation of the object.
|
|
227
|
+
__repr__: Return a machine-readable string representation of the object.
|
|
228
|
+
__getattr__: Provide a custom attribute access error message with helpful information.
|
|
201
229
|
|
|
202
230
|
Examples:
|
|
203
231
|
>>> class MyClass(SimpleClass):
|
|
@@ -227,7 +255,7 @@ class SimpleClass:
|
|
|
227
255
|
# Display only the module and class name for subclasses
|
|
228
256
|
s = f"{a}: {v.__module__}.{v.__class__.__name__} object"
|
|
229
257
|
else:
|
|
230
|
-
s = f"{a}: {
|
|
258
|
+
s = f"{a}: {v!r}"
|
|
231
259
|
attr.append(s)
|
|
232
260
|
return f"{self.__module__}.{self.__class__.__name__} object with attributes:\n\n" + "\n".join(attr)
|
|
233
261
|
|
|
@@ -236,24 +264,23 @@ class SimpleClass:
|
|
|
236
264
|
return self.__str__()
|
|
237
265
|
|
|
238
266
|
def __getattr__(self, attr):
|
|
239
|
-
"""
|
|
267
|
+
"""Provide a custom attribute access error message with helpful information."""
|
|
240
268
|
name = self.__class__.__name__
|
|
241
269
|
raise AttributeError(f"'{name}' object has no attribute '{attr}'. See valid attributes below.\n{self.__doc__}")
|
|
242
270
|
|
|
243
271
|
|
|
244
272
|
class IterableSimpleNamespace(SimpleNamespace):
|
|
245
|
-
"""
|
|
246
|
-
An iterable SimpleNamespace class that provides enhanced functionality for attribute access and iteration.
|
|
273
|
+
"""An iterable SimpleNamespace class that provides enhanced functionality for attribute access and iteration.
|
|
247
274
|
|
|
248
|
-
This class extends the SimpleNamespace class with additional methods for iteration, string representation,
|
|
249
|
-
|
|
250
|
-
|
|
275
|
+
This class extends the SimpleNamespace class with additional methods for iteration, string representation, and
|
|
276
|
+
attribute access. It is designed to be used as a convenient container for storing and accessing configuration
|
|
277
|
+
parameters.
|
|
251
278
|
|
|
252
279
|
Methods:
|
|
253
|
-
__iter__:
|
|
254
|
-
__str__:
|
|
255
|
-
__getattr__:
|
|
256
|
-
get:
|
|
280
|
+
__iter__: Return an iterator of key-value pairs from the namespace's attributes.
|
|
281
|
+
__str__: Return a human-readable string representation of the object.
|
|
282
|
+
__getattr__: Provide a custom attribute access error message with helpful information.
|
|
283
|
+
get: Retrieve the value of a specified key, or a default value if the key doesn't exist.
|
|
257
284
|
|
|
258
285
|
Examples:
|
|
259
286
|
>>> cfg = IterableSimpleNamespace(a=1, b=2, c=3)
|
|
@@ -285,7 +312,7 @@ class IterableSimpleNamespace(SimpleNamespace):
|
|
|
285
312
|
return "\n".join(f"{k}={v}" for k, v in vars(self).items())
|
|
286
313
|
|
|
287
314
|
def __getattr__(self, attr):
|
|
288
|
-
"""
|
|
315
|
+
"""Provide a custom attribute access error message with helpful information."""
|
|
289
316
|
name = self.__class__.__name__
|
|
290
317
|
raise AttributeError(
|
|
291
318
|
f"""
|
|
@@ -302,12 +329,11 @@ class IterableSimpleNamespace(SimpleNamespace):
|
|
|
302
329
|
|
|
303
330
|
|
|
304
331
|
def plt_settings(rcparams=None, backend="Agg"):
|
|
305
|
-
"""
|
|
306
|
-
Decorator to temporarily set rc parameters and the backend for a plotting function.
|
|
332
|
+
"""Decorator to temporarily set rc parameters and the backend for a plotting function.
|
|
307
333
|
|
|
308
334
|
Args:
|
|
309
335
|
rcparams (dict, optional): Dictionary of rc parameters to set.
|
|
310
|
-
backend (str, optional): Name of the backend to use.
|
|
336
|
+
backend (str, optional): Name of the backend to use.
|
|
311
337
|
|
|
312
338
|
Returns:
|
|
313
339
|
(Callable): Decorated function with temporarily set rc parameters and backend.
|
|
@@ -331,7 +357,7 @@ def plt_settings(rcparams=None, backend="Agg"):
|
|
|
331
357
|
"""Decorator to apply temporary rc parameters and backend to a function."""
|
|
332
358
|
|
|
333
359
|
def wrapper(*args, **kwargs):
|
|
334
|
-
"""
|
|
360
|
+
"""Set rc parameters and backend, call the original function, and restore the settings."""
|
|
335
361
|
import matplotlib.pyplot as plt # scope for faster 'import ultralytics'
|
|
336
362
|
|
|
337
363
|
original_backend = plt.get_backend()
|
|
@@ -356,16 +382,15 @@ def plt_settings(rcparams=None, backend="Agg"):
|
|
|
356
382
|
|
|
357
383
|
|
|
358
384
|
def set_logging(name="LOGGING_NAME", verbose=True):
|
|
359
|
-
"""
|
|
360
|
-
Sets up logging with UTF-8 encoding and configurable verbosity.
|
|
385
|
+
"""Set up logging with UTF-8 encoding and configurable verbosity.
|
|
361
386
|
|
|
362
|
-
This function configures logging for the Ultralytics library, setting the appropriate logging level and
|
|
363
|
-
|
|
364
|
-
|
|
387
|
+
This function configures logging for the Ultralytics library, setting the appropriate logging level and formatter
|
|
388
|
+
based on the verbosity flag and the current process rank. It handles special cases for Windows environments where
|
|
389
|
+
UTF-8 encoding might not be the default.
|
|
365
390
|
|
|
366
391
|
Args:
|
|
367
|
-
name (str): Name of the logger.
|
|
368
|
-
verbose (bool): Flag to set logging level to INFO if True, ERROR otherwise.
|
|
392
|
+
name (str): Name of the logger.
|
|
393
|
+
verbose (bool): Flag to set logging level to INFO if True, ERROR otherwise.
|
|
369
394
|
|
|
370
395
|
Returns:
|
|
371
396
|
(logging.Logger): Configured logger object.
|
|
@@ -388,10 +413,10 @@ def set_logging(name="LOGGING_NAME", verbose=True):
|
|
|
388
413
|
"""Format log records with prefixes based on level."""
|
|
389
414
|
# Apply prefixes based on log level
|
|
390
415
|
if record.levelno == logging.WARNING:
|
|
391
|
-
prefix = "WARNING
|
|
416
|
+
prefix = "WARNING" if WINDOWS else "WARNING ⚠️"
|
|
392
417
|
record.msg = f"{prefix} {record.msg}"
|
|
393
418
|
elif record.levelno == logging.ERROR:
|
|
394
|
-
prefix = "ERROR
|
|
419
|
+
prefix = "ERROR" if WINDOWS else "ERROR ❌"
|
|
395
420
|
record.msg = f"{prefix} {record.msg}"
|
|
396
421
|
|
|
397
422
|
# Handle emojis in message based on platform
|
|
@@ -402,7 +427,7 @@ def set_logging(name="LOGGING_NAME", verbose=True):
|
|
|
402
427
|
|
|
403
428
|
# Handle Windows UTF-8 encoding issues
|
|
404
429
|
if WINDOWS and hasattr(sys.stdout, "encoding") and sys.stdout.encoding != "utf-8":
|
|
405
|
-
|
|
430
|
+
with contextlib.suppress(Exception):
|
|
406
431
|
# Attempt to reconfigure stdout to use UTF-8 encoding if possible
|
|
407
432
|
if hasattr(sys.stdout, "reconfigure"):
|
|
408
433
|
sys.stdout.reconfigure(encoding="utf-8")
|
|
@@ -411,8 +436,6 @@ def set_logging(name="LOGGING_NAME", verbose=True):
|
|
|
411
436
|
import io
|
|
412
437
|
|
|
413
438
|
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
|
|
414
|
-
except Exception:
|
|
415
|
-
pass
|
|
416
439
|
|
|
417
440
|
# Create and configure the StreamHandler with the appropriate formatter and level
|
|
418
441
|
stream_handler = logging.StreamHandler(sys.stdout)
|
|
@@ -429,8 +452,7 @@ def set_logging(name="LOGGING_NAME", verbose=True):
|
|
|
429
452
|
|
|
430
453
|
# Set logger
|
|
431
454
|
LOGGER = set_logging(LOGGING_NAME, verbose=VERBOSE) # define globally (used in train.py, val.py, predict.py, etc.)
|
|
432
|
-
|
|
433
|
-
logging.getLogger(logger).setLevel(logging.CRITICAL + 1)
|
|
455
|
+
logging.getLogger("sentry_sdk").setLevel(logging.CRITICAL + 1)
|
|
434
456
|
|
|
435
457
|
|
|
436
458
|
def emojis(string=""):
|
|
@@ -439,8 +461,7 @@ def emojis(string=""):
|
|
|
439
461
|
|
|
440
462
|
|
|
441
463
|
class ThreadingLocked:
|
|
442
|
-
"""
|
|
443
|
-
A decorator class for ensuring thread-safe execution of a function or method.
|
|
464
|
+
"""A decorator class for ensuring thread-safe execution of a function or method.
|
|
444
465
|
|
|
445
466
|
This class can be used as a decorator to make sure that if the decorated function is called from multiple threads,
|
|
446
467
|
only one thread at a time will be able to execute the function.
|
|
@@ -465,7 +486,7 @@ class ThreadingLocked:
|
|
|
465
486
|
|
|
466
487
|
@wraps(f)
|
|
467
488
|
def decorated(*args, **kwargs):
|
|
468
|
-
"""
|
|
489
|
+
"""Apply thread-safety to the decorated function or method."""
|
|
469
490
|
with self.lock:
|
|
470
491
|
return f(*args, **kwargs)
|
|
471
492
|
|
|
@@ -473,8 +494,7 @@ class ThreadingLocked:
|
|
|
473
494
|
|
|
474
495
|
|
|
475
496
|
class YAML:
|
|
476
|
-
"""
|
|
477
|
-
YAML utility class for efficient file operations with automatic C-implementation detection.
|
|
497
|
+
"""YAML utility class for efficient file operations with automatic C-implementation detection.
|
|
478
498
|
|
|
479
499
|
This class provides optimized YAML loading and saving operations using PyYAML's fastest available implementation
|
|
480
500
|
(C-based when possible). It implements a singleton pattern with lazy initialization, allowing direct class method
|
|
@@ -524,8 +544,7 @@ class YAML:
|
|
|
524
544
|
|
|
525
545
|
@classmethod
|
|
526
546
|
def save(cls, file="data.yaml", data=None, header=""):
|
|
527
|
-
"""
|
|
528
|
-
Save Python object as YAML file.
|
|
547
|
+
"""Save Python object as YAML file.
|
|
529
548
|
|
|
530
549
|
Args:
|
|
531
550
|
file (str | Path): Path to save YAML file.
|
|
@@ -554,8 +573,7 @@ class YAML:
|
|
|
554
573
|
|
|
555
574
|
@classmethod
|
|
556
575
|
def load(cls, file="data.yaml", append_filename=False):
|
|
557
|
-
"""
|
|
558
|
-
Load YAML file to Python object with robust error handling.
|
|
576
|
+
"""Load YAML file to Python object with robust error handling.
|
|
559
577
|
|
|
560
578
|
Args:
|
|
561
579
|
file (str | Path): Path to YAML file.
|
|
@@ -589,8 +607,7 @@ class YAML:
|
|
|
589
607
|
|
|
590
608
|
@classmethod
|
|
591
609
|
def print(cls, yaml_file):
|
|
592
|
-
"""
|
|
593
|
-
Pretty print YAML file or object to console.
|
|
610
|
+
"""Pretty print YAML file or object to console.
|
|
594
611
|
|
|
595
612
|
Args:
|
|
596
613
|
yaml_file (str | Path | dict): Path to YAML file or dict to print.
|
|
@@ -613,8 +630,7 @@ DEFAULT_CFG = IterableSimpleNamespace(**DEFAULT_CFG_DICT)
|
|
|
613
630
|
|
|
614
631
|
|
|
615
632
|
def read_device_model() -> str:
|
|
616
|
-
"""
|
|
617
|
-
Reads the device model information from the system and caches it for quick access.
|
|
633
|
+
"""Read the device model information from the system and cache it for quick access.
|
|
618
634
|
|
|
619
635
|
Returns:
|
|
620
636
|
(str): Kernel release information.
|
|
@@ -623,8 +639,7 @@ def read_device_model() -> str:
|
|
|
623
639
|
|
|
624
640
|
|
|
625
641
|
def is_ubuntu() -> bool:
|
|
626
|
-
"""
|
|
627
|
-
Check if the OS is Ubuntu.
|
|
642
|
+
"""Check if the OS is Ubuntu.
|
|
628
643
|
|
|
629
644
|
Returns:
|
|
630
645
|
(bool): True if OS is Ubuntu, False otherwise.
|
|
@@ -637,8 +652,7 @@ def is_ubuntu() -> bool:
|
|
|
637
652
|
|
|
638
653
|
|
|
639
654
|
def is_colab():
|
|
640
|
-
"""
|
|
641
|
-
Check if the current script is running inside a Google Colab notebook.
|
|
655
|
+
"""Check if the current script is running inside a Google Colab notebook.
|
|
642
656
|
|
|
643
657
|
Returns:
|
|
644
658
|
(bool): True if running inside a Colab notebook, False otherwise.
|
|
@@ -647,8 +661,7 @@ def is_colab():
|
|
|
647
661
|
|
|
648
662
|
|
|
649
663
|
def is_kaggle():
|
|
650
|
-
"""
|
|
651
|
-
Check if the current script is running inside a Kaggle kernel.
|
|
664
|
+
"""Check if the current script is running inside a Kaggle kernel.
|
|
652
665
|
|
|
653
666
|
Returns:
|
|
654
667
|
(bool): True if running inside a Kaggle kernel, False otherwise.
|
|
@@ -657,13 +670,12 @@ def is_kaggle():
|
|
|
657
670
|
|
|
658
671
|
|
|
659
672
|
def is_jupyter():
|
|
660
|
-
"""
|
|
661
|
-
Check if the current script is running inside a Jupyter Notebook.
|
|
673
|
+
"""Check if the current script is running inside a Jupyter Notebook.
|
|
662
674
|
|
|
663
675
|
Returns:
|
|
664
676
|
(bool): True if running inside a Jupyter Notebook, False otherwise.
|
|
665
677
|
|
|
666
|
-
|
|
678
|
+
Notes:
|
|
667
679
|
- Only works on Colab and Kaggle, other environments like Jupyterlab and Paperspace are not reliably detectable.
|
|
668
680
|
- "get_ipython" in globals() method suffers false positives when IPython package installed manually.
|
|
669
681
|
"""
|
|
@@ -671,8 +683,7 @@ def is_jupyter():
|
|
|
671
683
|
|
|
672
684
|
|
|
673
685
|
def is_runpod():
|
|
674
|
-
"""
|
|
675
|
-
Check if the current script is running inside a RunPod container.
|
|
686
|
+
"""Check if the current script is running inside a RunPod container.
|
|
676
687
|
|
|
677
688
|
Returns:
|
|
678
689
|
(bool): True if running in RunPod, False otherwise.
|
|
@@ -681,22 +692,19 @@ def is_runpod():
|
|
|
681
692
|
|
|
682
693
|
|
|
683
694
|
def is_docker() -> bool:
|
|
684
|
-
"""
|
|
685
|
-
Determine if the script is running inside a Docker container.
|
|
695
|
+
"""Determine if the script is running inside a Docker container.
|
|
686
696
|
|
|
687
697
|
Returns:
|
|
688
698
|
(bool): True if the script is running inside a Docker container, False otherwise.
|
|
689
699
|
"""
|
|
690
700
|
try:
|
|
691
|
-
|
|
692
|
-
return "docker" in f.read()
|
|
701
|
+
return os.path.exists("/.dockerenv")
|
|
693
702
|
except Exception:
|
|
694
703
|
return False
|
|
695
704
|
|
|
696
705
|
|
|
697
706
|
def is_raspberrypi() -> bool:
|
|
698
|
-
"""
|
|
699
|
-
Determines if the Python environment is running on a Raspberry Pi.
|
|
707
|
+
"""Determine if the Python environment is running on a Raspberry Pi.
|
|
700
708
|
|
|
701
709
|
Returns:
|
|
702
710
|
(bool): True if running on a Raspberry Pi, False otherwise.
|
|
@@ -704,37 +712,47 @@ def is_raspberrypi() -> bool:
|
|
|
704
712
|
return "rpi" in DEVICE_MODEL
|
|
705
713
|
|
|
706
714
|
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
715
|
+
@lru_cache(maxsize=3)
|
|
716
|
+
def is_jetson(jetpack=None) -> bool:
|
|
717
|
+
"""Determine if the Python environment is running on an NVIDIA Jetson device.
|
|
718
|
+
|
|
719
|
+
Args:
|
|
720
|
+
jetpack (int | None): If specified, check for specific JetPack version (4, 5, 6).
|
|
710
721
|
|
|
711
722
|
Returns:
|
|
712
723
|
(bool): True if running on an NVIDIA Jetson device, False otherwise.
|
|
713
724
|
"""
|
|
714
|
-
|
|
725
|
+
if jetson := ("tegra" in DEVICE_MODEL):
|
|
726
|
+
if jetpack:
|
|
727
|
+
try:
|
|
728
|
+
content = open("/etc/nv_tegra_release").read()
|
|
729
|
+
version_map = {4: "R32", 5: "R35", 6: "R36"} # JetPack to L4T major version mapping
|
|
730
|
+
return jetpack in version_map and version_map[jetpack] in content
|
|
731
|
+
except Exception:
|
|
732
|
+
return False
|
|
733
|
+
return jetson
|
|
715
734
|
|
|
716
735
|
|
|
717
736
|
def is_online() -> bool:
|
|
718
|
-
"""
|
|
719
|
-
Check internet connectivity by attempting to connect to a known online host.
|
|
737
|
+
"""Fast online check using DNS (v4/v6) resolution (Cloudflare + Google).
|
|
720
738
|
|
|
721
739
|
Returns:
|
|
722
740
|
(bool): True if connection is successful, False otherwise.
|
|
723
741
|
"""
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
import socket
|
|
742
|
+
if str(os.getenv("YOLO_OFFLINE", "")).lower() == "true":
|
|
743
|
+
return False
|
|
727
744
|
|
|
728
|
-
|
|
729
|
-
|
|
745
|
+
for host in ("one.one.one.one", "dns.google"):
|
|
746
|
+
try:
|
|
747
|
+
socket.getaddrinfo(host, 0, socket.AF_UNSPEC, 0, 0, socket.AI_ADDRCONFIG)
|
|
730
748
|
return True
|
|
731
|
-
|
|
732
|
-
|
|
749
|
+
except OSError:
|
|
750
|
+
continue
|
|
751
|
+
return False
|
|
733
752
|
|
|
734
753
|
|
|
735
754
|
def is_pip_package(filepath: str = __name__) -> bool:
|
|
736
|
-
"""
|
|
737
|
-
Determines if the file at the given filepath is part of a pip package.
|
|
755
|
+
"""Determine if the file at the given filepath is part of a pip package.
|
|
738
756
|
|
|
739
757
|
Args:
|
|
740
758
|
filepath (str): The filepath to check.
|
|
@@ -751,22 +769,20 @@ def is_pip_package(filepath: str = __name__) -> bool:
|
|
|
751
769
|
return spec is not None and spec.origin is not None
|
|
752
770
|
|
|
753
771
|
|
|
754
|
-
def is_dir_writeable(dir_path:
|
|
755
|
-
"""
|
|
756
|
-
Check if a directory is writeable.
|
|
772
|
+
def is_dir_writeable(dir_path: str | Path) -> bool:
|
|
773
|
+
"""Check if a directory is writable.
|
|
757
774
|
|
|
758
775
|
Args:
|
|
759
776
|
dir_path (str | Path): The path to the directory.
|
|
760
777
|
|
|
761
778
|
Returns:
|
|
762
|
-
(bool): True if the directory is
|
|
779
|
+
(bool): True if the directory is writable, False otherwise.
|
|
763
780
|
"""
|
|
764
781
|
return os.access(str(dir_path), os.W_OK)
|
|
765
782
|
|
|
766
783
|
|
|
767
784
|
def is_pytest_running():
|
|
768
|
-
"""
|
|
769
|
-
Determines whether pytest is currently running or not.
|
|
785
|
+
"""Determine whether pytest is currently running or not.
|
|
770
786
|
|
|
771
787
|
Returns:
|
|
772
788
|
(bool): True if pytest is running, False otherwise.
|
|
@@ -775,8 +791,7 @@ def is_pytest_running():
|
|
|
775
791
|
|
|
776
792
|
|
|
777
793
|
def is_github_action_running() -> bool:
|
|
778
|
-
"""
|
|
779
|
-
Determine if the current environment is a GitHub Actions runner.
|
|
794
|
+
"""Determine if the current environment is a GitHub Actions runner.
|
|
780
795
|
|
|
781
796
|
Returns:
|
|
782
797
|
(bool): True if the current environment is a GitHub Actions runner, False otherwise.
|
|
@@ -784,61 +799,8 @@ def is_github_action_running() -> bool:
|
|
|
784
799
|
return "GITHUB_ACTIONS" in os.environ and "GITHUB_WORKFLOW" in os.environ and "RUNNER_OS" in os.environ
|
|
785
800
|
|
|
786
801
|
|
|
787
|
-
def get_git_dir():
|
|
788
|
-
"""
|
|
789
|
-
Determines whether the current file is part of a git repository and if so, returns the repository root directory.
|
|
790
|
-
|
|
791
|
-
Returns:
|
|
792
|
-
(Path | None): Git root directory if found or None if not found.
|
|
793
|
-
"""
|
|
794
|
-
for d in Path(__file__).parents:
|
|
795
|
-
if (d / ".git").is_dir():
|
|
796
|
-
return d
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
def is_git_dir():
|
|
800
|
-
"""
|
|
801
|
-
Determines whether the current file is part of a git repository.
|
|
802
|
-
|
|
803
|
-
Returns:
|
|
804
|
-
(bool): True if current file is part of a git repository.
|
|
805
|
-
"""
|
|
806
|
-
return GIT_DIR is not None
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
def get_git_origin_url():
|
|
810
|
-
"""
|
|
811
|
-
Retrieves the origin URL of a git repository.
|
|
812
|
-
|
|
813
|
-
Returns:
|
|
814
|
-
(str | None): The origin URL of the git repository or None if not git directory.
|
|
815
|
-
"""
|
|
816
|
-
if IS_GIT_DIR:
|
|
817
|
-
try:
|
|
818
|
-
origin = subprocess.check_output(["git", "config", "--get", "remote.origin.url"])
|
|
819
|
-
return origin.decode().strip()
|
|
820
|
-
except subprocess.CalledProcessError:
|
|
821
|
-
return None
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
def get_git_branch():
|
|
825
|
-
"""
|
|
826
|
-
Returns the current git branch name. If not in a git repository, returns None.
|
|
827
|
-
|
|
828
|
-
Returns:
|
|
829
|
-
(str | None): The current git branch name or None if not a git directory.
|
|
830
|
-
"""
|
|
831
|
-
if IS_GIT_DIR:
|
|
832
|
-
try:
|
|
833
|
-
origin = subprocess.check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"])
|
|
834
|
-
return origin.decode().strip()
|
|
835
|
-
except subprocess.CalledProcessError:
|
|
836
|
-
return None
|
|
837
|
-
|
|
838
|
-
|
|
839
802
|
def get_default_args(func):
|
|
840
|
-
"""
|
|
841
|
-
Returns a dictionary of default arguments for a function.
|
|
803
|
+
"""Return a dictionary of default arguments for a function.
|
|
842
804
|
|
|
843
805
|
Args:
|
|
844
806
|
func (callable): The function to inspect.
|
|
@@ -851,8 +813,7 @@ def get_default_args(func):
|
|
|
851
813
|
|
|
852
814
|
|
|
853
815
|
def get_ubuntu_version():
|
|
854
|
-
"""
|
|
855
|
-
Retrieve the Ubuntu version if the OS is Ubuntu.
|
|
816
|
+
"""Retrieve the Ubuntu version if the OS is Ubuntu.
|
|
856
817
|
|
|
857
818
|
Returns:
|
|
858
819
|
(str): Ubuntu version or None if not an Ubuntu OS.
|
|
@@ -866,8 +827,7 @@ def get_ubuntu_version():
|
|
|
866
827
|
|
|
867
828
|
|
|
868
829
|
def get_user_config_dir(sub_dir="Ultralytics"):
|
|
869
|
-
"""
|
|
870
|
-
Return the appropriate config directory based on the environment operating system.
|
|
830
|
+
"""Return a writable config dir, preferring YOLO_CONFIG_DIR and being OS-aware.
|
|
871
831
|
|
|
872
832
|
Args:
|
|
873
833
|
sub_dir (str): The name of the subdirectory to create.
|
|
@@ -875,27 +835,38 @@ def get_user_config_dir(sub_dir="Ultralytics"):
|
|
|
875
835
|
Returns:
|
|
876
836
|
(Path): The path to the user config directory.
|
|
877
837
|
"""
|
|
878
|
-
if
|
|
879
|
-
|
|
880
|
-
elif MACOS: # macOS
|
|
881
|
-
path = Path.home() / "Library" / "Application Support" / sub_dir
|
|
838
|
+
if env_dir := os.getenv("YOLO_CONFIG_DIR"):
|
|
839
|
+
p = Path(env_dir).expanduser() / sub_dir
|
|
882
840
|
elif LINUX:
|
|
883
|
-
|
|
841
|
+
p = Path(os.getenv("XDG_CONFIG_HOME", Path.home() / ".config")) / sub_dir
|
|
842
|
+
elif WINDOWS:
|
|
843
|
+
p = Path.home() / "AppData" / "Roaming" / sub_dir
|
|
844
|
+
elif MACOS:
|
|
845
|
+
p = Path.home() / "Library" / "Application Support" / sub_dir
|
|
884
846
|
else:
|
|
885
847
|
raise ValueError(f"Unsupported operating system: {platform.system()}")
|
|
886
848
|
|
|
887
|
-
#
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
849
|
+
if p.exists(): # already created → trust it
|
|
850
|
+
return p
|
|
851
|
+
if is_dir_writeable(p.parent): # create if possible
|
|
852
|
+
p.mkdir(parents=True, exist_ok=True)
|
|
853
|
+
return p
|
|
854
|
+
|
|
855
|
+
# Fallbacks for Docker, GCP/AWS functions where only /tmp is writable
|
|
856
|
+
for alt in [Path("/tmp") / sub_dir, Path.cwd() / sub_dir]:
|
|
857
|
+
if alt.exists():
|
|
858
|
+
return alt
|
|
859
|
+
if is_dir_writeable(alt.parent):
|
|
860
|
+
alt.mkdir(parents=True, exist_ok=True)
|
|
861
|
+
LOGGER.warning(
|
|
862
|
+
f"user config directory '{p}' is not writable, using '{alt}'. Set YOLO_CONFIG_DIR to override."
|
|
863
|
+
)
|
|
864
|
+
return alt
|
|
897
865
|
|
|
898
|
-
|
|
866
|
+
# Last fallback → CWD
|
|
867
|
+
p = Path.cwd() / sub_dir
|
|
868
|
+
p.mkdir(parents=True, exist_ok=True)
|
|
869
|
+
return p
|
|
899
870
|
|
|
900
871
|
|
|
901
872
|
# Define constants (required below)
|
|
@@ -908,16 +879,13 @@ IS_JETSON = is_jetson()
|
|
|
908
879
|
IS_JUPYTER = is_jupyter()
|
|
909
880
|
IS_PIP_PACKAGE = is_pip_package()
|
|
910
881
|
IS_RASPBERRYPI = is_raspberrypi()
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
USER_CONFIG_DIR = Path(os.getenv("YOLO_CONFIG_DIR") or get_user_config_dir()) # Ultralytics settings dir
|
|
882
|
+
GIT = GitRepo()
|
|
883
|
+
USER_CONFIG_DIR = get_user_config_dir() # Ultralytics settings dir
|
|
914
884
|
SETTINGS_FILE = USER_CONFIG_DIR / "settings.json"
|
|
915
885
|
|
|
916
886
|
|
|
917
887
|
def colorstr(*input):
|
|
918
|
-
r"""
|
|
919
|
-
Colors a string based on the provided color and style arguments. Utilizes ANSI escape codes.
|
|
920
|
-
See https://en.wikipedia.org/wiki/ANSI_escape_code for more details.
|
|
888
|
+
r"""Color a string based on the provided color and style arguments using ANSI escape codes.
|
|
921
889
|
|
|
922
890
|
This function can be called in two ways:
|
|
923
891
|
- colorstr('color', 'style', 'your string')
|
|
@@ -926,14 +894,8 @@ def colorstr(*input):
|
|
|
926
894
|
In the second form, 'blue' and 'bold' will be applied by default.
|
|
927
895
|
|
|
928
896
|
Args:
|
|
929
|
-
*input (str | Path): A sequence of strings where the first n-1 strings are color and style arguments,
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
Supported Colors and Styles:
|
|
933
|
-
Basic Colors: 'black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'
|
|
934
|
-
Bright Colors: 'bright_black', 'bright_red', 'bright_green', 'bright_yellow',
|
|
935
|
-
'bright_blue', 'bright_magenta', 'bright_cyan', 'bright_white'
|
|
936
|
-
Misc: 'end', 'bold', 'underline'
|
|
897
|
+
*input (str | Path): A sequence of strings where the first n-1 strings are color and style arguments, and the
|
|
898
|
+
last string is the one to be colored.
|
|
937
899
|
|
|
938
900
|
Returns:
|
|
939
901
|
(str): The input string wrapped with ANSI escape codes for the specified color and style.
|
|
@@ -941,6 +903,16 @@ def colorstr(*input):
|
|
|
941
903
|
Examples:
|
|
942
904
|
>>> colorstr("blue", "bold", "hello world")
|
|
943
905
|
>>> "\033[34m\033[1mhello world\033[0m"
|
|
906
|
+
|
|
907
|
+
Notes:
|
|
908
|
+
Supported Colors and Styles:
|
|
909
|
+
- Basic Colors: 'black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'
|
|
910
|
+
- Bright Colors: 'bright_black', 'bright_red', 'bright_green', 'bright_yellow',
|
|
911
|
+
'bright_blue', 'bright_magenta', 'bright_cyan', 'bright_white'
|
|
912
|
+
- Misc: 'end', 'bold', 'underline'
|
|
913
|
+
|
|
914
|
+
References:
|
|
915
|
+
https://en.wikipedia.org/wiki/ANSI_escape_code
|
|
944
916
|
"""
|
|
945
917
|
*args, string = input if len(input) > 1 else ("blue", "bold", input[0]) # color arguments, string
|
|
946
918
|
colors = {
|
|
@@ -968,8 +940,7 @@ def colorstr(*input):
|
|
|
968
940
|
|
|
969
941
|
|
|
970
942
|
def remove_colorstr(input_string):
|
|
971
|
-
"""
|
|
972
|
-
Removes ANSI escape codes from a string, effectively un-coloring it.
|
|
943
|
+
"""Remove ANSI escape codes from a string, effectively un-coloring it.
|
|
973
944
|
|
|
974
945
|
Args:
|
|
975
946
|
input_string (str): The string to remove color and style from.
|
|
@@ -986,8 +957,14 @@ def remove_colorstr(input_string):
|
|
|
986
957
|
|
|
987
958
|
|
|
988
959
|
class TryExcept(contextlib.ContextDecorator):
|
|
989
|
-
"""
|
|
990
|
-
|
|
960
|
+
"""Ultralytics TryExcept class for handling exceptions gracefully.
|
|
961
|
+
|
|
962
|
+
This class can be used as a decorator or context manager to catch exceptions and optionally print warning messages.
|
|
963
|
+
It allows code to continue execution even when exceptions occur, which is useful for non-critical operations.
|
|
964
|
+
|
|
965
|
+
Attributes:
|
|
966
|
+
msg (str): Optional message to display when an exception occurs.
|
|
967
|
+
verbose (bool): Whether to print the exception message.
|
|
991
968
|
|
|
992
969
|
Examples:
|
|
993
970
|
As a decorator:
|
|
@@ -1008,22 +985,26 @@ class TryExcept(contextlib.ContextDecorator):
|
|
|
1008
985
|
self.verbose = verbose
|
|
1009
986
|
|
|
1010
987
|
def __enter__(self):
|
|
1011
|
-
"""
|
|
988
|
+
"""Execute when entering TryExcept context, initialize instance."""
|
|
1012
989
|
pass
|
|
1013
990
|
|
|
1014
991
|
def __exit__(self, exc_type, value, traceback):
|
|
1015
|
-
"""
|
|
992
|
+
"""Define behavior when exiting a 'with' block, print error message if necessary."""
|
|
1016
993
|
if self.verbose and value:
|
|
1017
994
|
LOGGER.warning(f"{self.msg}{': ' if self.msg else ''}{value}")
|
|
1018
995
|
return True
|
|
1019
996
|
|
|
1020
997
|
|
|
1021
998
|
class Retry(contextlib.ContextDecorator):
|
|
1022
|
-
"""
|
|
1023
|
-
Retry class for function execution with exponential backoff.
|
|
999
|
+
"""Retry class for function execution with exponential backoff.
|
|
1024
1000
|
|
|
1025
|
-
|
|
1026
|
-
exponentially increasing delay between retries.
|
|
1001
|
+
This decorator can be used to retry a function on exceptions, up to a specified number of times with an
|
|
1002
|
+
exponentially increasing delay between retries. It's useful for handling transient failures in network operations or
|
|
1003
|
+
other unreliable processes.
|
|
1004
|
+
|
|
1005
|
+
Attributes:
|
|
1006
|
+
times (int): Maximum number of retry attempts.
|
|
1007
|
+
delay (int): Initial delay between retries in seconds.
|
|
1027
1008
|
|
|
1028
1009
|
Examples:
|
|
1029
1010
|
Example usage as a decorator:
|
|
@@ -1043,7 +1024,7 @@ class Retry(contextlib.ContextDecorator):
|
|
|
1043
1024
|
"""Decorator implementation for Retry with exponential backoff."""
|
|
1044
1025
|
|
|
1045
1026
|
def wrapped_func(*args, **kwargs):
|
|
1046
|
-
"""
|
|
1027
|
+
"""Apply retries to the decorated function or method."""
|
|
1047
1028
|
self._attempts = 0
|
|
1048
1029
|
while self._attempts < self.times:
|
|
1049
1030
|
try:
|
|
@@ -1059,12 +1040,11 @@ class Retry(contextlib.ContextDecorator):
|
|
|
1059
1040
|
|
|
1060
1041
|
|
|
1061
1042
|
def threaded(func):
|
|
1062
|
-
"""
|
|
1063
|
-
Multi-threads a target function by default and returns the thread or function result.
|
|
1043
|
+
"""Multi-thread a target function by default and return the thread or function result.
|
|
1064
1044
|
|
|
1065
|
-
This decorator provides flexible execution of the target function, either in a separate thread or synchronously.
|
|
1066
|
-
|
|
1067
|
-
|
|
1045
|
+
This decorator provides flexible execution of the target function, either in a separate thread or synchronously. By
|
|
1046
|
+
default, the function runs in a thread, but this can be controlled via the 'threaded=False' keyword argument which
|
|
1047
|
+
is removed from kwargs before calling the function.
|
|
1068
1048
|
|
|
1069
1049
|
Args:
|
|
1070
1050
|
func (callable): The function to be potentially executed in a separate thread.
|
|
@@ -1082,7 +1062,7 @@ def threaded(func):
|
|
|
1082
1062
|
"""
|
|
1083
1063
|
|
|
1084
1064
|
def wrapper(*args, **kwargs):
|
|
1085
|
-
"""Multi-
|
|
1065
|
+
"""Multi-thread a given function based on 'threaded' kwarg and return the thread or function result."""
|
|
1086
1066
|
if kwargs.pop("threaded", True): # run in thread
|
|
1087
1067
|
thread = threading.Thread(target=func, args=args, kwargs=kwargs, daemon=True)
|
|
1088
1068
|
thread.start()
|
|
@@ -1094,8 +1074,7 @@ def threaded(func):
|
|
|
1094
1074
|
|
|
1095
1075
|
|
|
1096
1076
|
def set_sentry():
|
|
1097
|
-
"""
|
|
1098
|
-
Initialize the Sentry SDK for error tracking and reporting.
|
|
1077
|
+
"""Initialize the Sentry SDK for error tracking and reporting.
|
|
1099
1078
|
|
|
1100
1079
|
Only used if sentry_sdk package is installed and sync=True in settings. Run 'yolo settings' to see and update
|
|
1101
1080
|
settings.
|
|
@@ -1117,25 +1096,24 @@ def set_sentry():
|
|
|
1117
1096
|
or TESTS_RUNNING
|
|
1118
1097
|
or not ONLINE
|
|
1119
1098
|
or not IS_PIP_PACKAGE
|
|
1120
|
-
or
|
|
1099
|
+
or GIT.is_repo
|
|
1121
1100
|
):
|
|
1122
1101
|
return
|
|
1123
1102
|
# If sentry_sdk package is not installed then return and do not use Sentry
|
|
1124
1103
|
try:
|
|
1125
|
-
import sentry_sdk
|
|
1104
|
+
import sentry_sdk
|
|
1126
1105
|
except ImportError:
|
|
1127
1106
|
return
|
|
1128
1107
|
|
|
1129
1108
|
def before_send(event, hint):
|
|
1130
|
-
"""
|
|
1131
|
-
Modify the event before sending it to Sentry based on specific exception types and messages.
|
|
1109
|
+
"""Modify the event before sending it to Sentry based on specific exception types and messages.
|
|
1132
1110
|
|
|
1133
1111
|
Args:
|
|
1134
1112
|
event (dict): The event dictionary containing information about the error.
|
|
1135
1113
|
hint (dict): A dictionary containing additional information about the error.
|
|
1136
1114
|
|
|
1137
1115
|
Returns:
|
|
1138
|
-
dict: The modified event or None if the event should not be sent to Sentry.
|
|
1116
|
+
(dict | None): The modified event or None if the event should not be sent to Sentry.
|
|
1139
1117
|
"""
|
|
1140
1118
|
if "exc_info" in hint:
|
|
1141
1119
|
exc_type, exc_value, _ = hint["exc_info"]
|
|
@@ -1145,7 +1123,7 @@ def set_sentry():
|
|
|
1145
1123
|
event["tags"] = {
|
|
1146
1124
|
"sys_argv": ARGV[0],
|
|
1147
1125
|
"sys_argv_name": Path(ARGV[0]).name,
|
|
1148
|
-
"install": "git" if
|
|
1126
|
+
"install": "git" if GIT.is_repo else "pip" if IS_PIP_PACKAGE else "other",
|
|
1149
1127
|
"os": ENVIRONMENT,
|
|
1150
1128
|
}
|
|
1151
1129
|
return event
|
|
@@ -1164,23 +1142,22 @@ def set_sentry():
|
|
|
1164
1142
|
|
|
1165
1143
|
|
|
1166
1144
|
class JSONDict(dict):
|
|
1167
|
-
"""
|
|
1168
|
-
A dictionary-like class that provides JSON persistence for its contents.
|
|
1145
|
+
"""A dictionary-like class that provides JSON persistence for its contents.
|
|
1169
1146
|
|
|
1170
1147
|
This class extends the built-in dictionary to automatically save its contents to a JSON file whenever they are
|
|
1171
|
-
modified. It ensures thread-safe operations using a lock.
|
|
1148
|
+
modified. It ensures thread-safe operations using a lock and handles JSON serialization of Path objects.
|
|
1172
1149
|
|
|
1173
1150
|
Attributes:
|
|
1174
1151
|
file_path (Path): The path to the JSON file used for persistence.
|
|
1175
1152
|
lock (threading.Lock): A lock object to ensure thread-safe operations.
|
|
1176
1153
|
|
|
1177
1154
|
Methods:
|
|
1178
|
-
_load:
|
|
1179
|
-
_save:
|
|
1180
|
-
__setitem__:
|
|
1181
|
-
__delitem__:
|
|
1182
|
-
update:
|
|
1183
|
-
clear:
|
|
1155
|
+
_load: Load the data from the JSON file into the dictionary.
|
|
1156
|
+
_save: Save the current state of the dictionary to the JSON file.
|
|
1157
|
+
__setitem__: Store a key-value pair and persist it to disk.
|
|
1158
|
+
__delitem__: Remove an item and update the persistent storage.
|
|
1159
|
+
update: Update the dictionary and persist changes.
|
|
1160
|
+
clear: Clear all entries and update the persistent storage.
|
|
1184
1161
|
|
|
1185
1162
|
Examples:
|
|
1186
1163
|
>>> json_dict = JSONDict("data.json")
|
|
@@ -1192,7 +1169,7 @@ class JSONDict(dict):
|
|
|
1192
1169
|
>>> json_dict.clear()
|
|
1193
1170
|
"""
|
|
1194
1171
|
|
|
1195
|
-
def __init__(self, file_path:
|
|
1172
|
+
def __init__(self, file_path: str | Path = "data.json"):
|
|
1196
1173
|
"""Initialize a JSONDict object with a specified file path for JSON persistence."""
|
|
1197
1174
|
super().__init__()
|
|
1198
1175
|
self.file_path = Path(file_path)
|
|
@@ -1257,11 +1234,11 @@ class JSONDict(dict):
|
|
|
1257
1234
|
|
|
1258
1235
|
|
|
1259
1236
|
class SettingsManager(JSONDict):
|
|
1260
|
-
"""
|
|
1261
|
-
SettingsManager class for managing and persisting Ultralytics settings.
|
|
1237
|
+
"""SettingsManager class for managing and persisting Ultralytics settings.
|
|
1262
1238
|
|
|
1263
1239
|
This class extends JSONDict to provide JSON persistence for settings, ensuring thread-safe operations and default
|
|
1264
|
-
values. It validates settings on initialization and provides methods to update or reset settings.
|
|
1240
|
+
values. It validates settings on initialization and provides methods to update or reset settings. The settings
|
|
1241
|
+
include directories for datasets, weights, and runs, as well as various integration flags.
|
|
1265
1242
|
|
|
1266
1243
|
Attributes:
|
|
1267
1244
|
file (Path): The path to the JSON file used for persistence.
|
|
@@ -1270,9 +1247,9 @@ class SettingsManager(JSONDict):
|
|
|
1270
1247
|
help_msg (str): A help message for users on how to view and update settings.
|
|
1271
1248
|
|
|
1272
1249
|
Methods:
|
|
1273
|
-
_validate_settings:
|
|
1274
|
-
update:
|
|
1275
|
-
reset:
|
|
1250
|
+
_validate_settings: Validate the current settings and reset if necessary.
|
|
1251
|
+
update: Update settings, validating keys and types.
|
|
1252
|
+
reset: Reset the settings to default and save them.
|
|
1276
1253
|
|
|
1277
1254
|
Examples:
|
|
1278
1255
|
Initialize and update settings:
|
|
@@ -1283,14 +1260,14 @@ class SettingsManager(JSONDict):
|
|
|
1283
1260
|
"""
|
|
1284
1261
|
|
|
1285
1262
|
def __init__(self, file=SETTINGS_FILE, version="0.0.6"):
|
|
1286
|
-
"""
|
|
1263
|
+
"""Initialize the SettingsManager with default settings and load user settings."""
|
|
1287
1264
|
import hashlib
|
|
1288
1265
|
import uuid
|
|
1289
1266
|
|
|
1290
1267
|
from ultralytics.utils.torch_utils import torch_distributed_zero_first
|
|
1291
1268
|
|
|
1292
|
-
root =
|
|
1293
|
-
datasets_root = (root.parent if
|
|
1269
|
+
root = GIT.root or Path()
|
|
1270
|
+
datasets_root = (root.parent if GIT.root and is_dir_writeable(root.parent) else root).resolve()
|
|
1294
1271
|
|
|
1295
1272
|
self.file = Path(file)
|
|
1296
1273
|
self.version = version
|
|
@@ -1352,11 +1329,11 @@ class SettingsManager(JSONDict):
|
|
|
1352
1329
|
)
|
|
1353
1330
|
|
|
1354
1331
|
def __setitem__(self, key, value):
|
|
1355
|
-
"""
|
|
1332
|
+
"""Update one key: value pair."""
|
|
1356
1333
|
self.update({key: value})
|
|
1357
1334
|
|
|
1358
1335
|
def update(self, *args, **kwargs):
|
|
1359
|
-
"""
|
|
1336
|
+
"""Update settings, validating keys and types."""
|
|
1360
1337
|
for arg in args:
|
|
1361
1338
|
if isinstance(arg, dict):
|
|
1362
1339
|
kwargs.update(arg)
|
|
@@ -1371,14 +1348,14 @@ class SettingsManager(JSONDict):
|
|
|
1371
1348
|
super().update(*args, **kwargs)
|
|
1372
1349
|
|
|
1373
1350
|
def reset(self):
|
|
1374
|
-
"""
|
|
1351
|
+
"""Reset the settings to default and save them."""
|
|
1375
1352
|
self.clear()
|
|
1376
1353
|
self.update(self.defaults)
|
|
1377
1354
|
|
|
1378
1355
|
|
|
1379
1356
|
def deprecation_warn(arg, new_arg=None):
|
|
1380
1357
|
"""Issue a deprecation warning when a deprecated argument is used, suggesting an updated argument."""
|
|
1381
|
-
msg = f"'{arg}' is deprecated and will be removed in
|
|
1358
|
+
msg = f"'{arg}' is deprecated and will be removed in the future."
|
|
1382
1359
|
if new_arg is not None:
|
|
1383
1360
|
msg += f" Use '{new_arg}' instead."
|
|
1384
1361
|
LOGGER.warning(msg)
|
|
@@ -1387,7 +1364,7 @@ def deprecation_warn(arg, new_arg=None):
|
|
|
1387
1364
|
def clean_url(url):
|
|
1388
1365
|
"""Strip auth from URL, i.e. https://url.com/file.txt?auth -> https://url.com/file.txt."""
|
|
1389
1366
|
url = Path(url).as_posix().replace(":/", "://") # Pathlib turns :// -> :/, as_posix() for Windows
|
|
1390
|
-
return unquote(url).split("?")[0] # '%2F' to '/', split https://url.com/file.txt?auth
|
|
1367
|
+
return unquote(url).split("?", 1)[0] # '%2F' to '/', split https://url.com/file.txt?auth
|
|
1391
1368
|
|
|
1392
1369
|
|
|
1393
1370
|
def url2file(url):
|
|
@@ -1428,7 +1405,6 @@ TESTS_RUNNING = is_pytest_running() or is_github_action_running()
|
|
|
1428
1405
|
set_sentry()
|
|
1429
1406
|
|
|
1430
1407
|
# Apply monkey patches
|
|
1431
|
-
torch.load = torch_load
|
|
1432
1408
|
torch.save = torch_save
|
|
1433
1409
|
if WINDOWS:
|
|
1434
1410
|
# Apply cv2 patches for non-ASCII and non-UTF characters in image paths
|