ultralytics 8.0.238__py3-none-any.whl → 8.0.239__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.

Potentially problematic release.


This version of ultralytics might be problematic. Click here for more details.

Files changed (134) hide show
  1. ultralytics/__init__.py +2 -2
  2. ultralytics/cfg/__init__.py +241 -138
  3. ultralytics/data/__init__.py +9 -2
  4. ultralytics/data/annotator.py +4 -4
  5. ultralytics/data/augment.py +186 -169
  6. ultralytics/data/base.py +54 -48
  7. ultralytics/data/build.py +34 -23
  8. ultralytics/data/converter.py +242 -70
  9. ultralytics/data/dataset.py +117 -95
  10. ultralytics/data/explorer/__init__.py +3 -1
  11. ultralytics/data/explorer/explorer.py +120 -100
  12. ultralytics/data/explorer/gui/__init__.py +1 -0
  13. ultralytics/data/explorer/gui/dash.py +123 -89
  14. ultralytics/data/explorer/utils.py +37 -39
  15. ultralytics/data/loaders.py +75 -62
  16. ultralytics/data/split_dota.py +44 -36
  17. ultralytics/data/utils.py +160 -142
  18. ultralytics/engine/exporter.py +348 -292
  19. ultralytics/engine/model.py +102 -66
  20. ultralytics/engine/predictor.py +74 -55
  21. ultralytics/engine/results.py +61 -41
  22. ultralytics/engine/trainer.py +192 -144
  23. ultralytics/engine/tuner.py +66 -59
  24. ultralytics/engine/validator.py +31 -26
  25. ultralytics/hub/__init__.py +54 -31
  26. ultralytics/hub/auth.py +28 -25
  27. ultralytics/hub/session.py +282 -133
  28. ultralytics/hub/utils.py +64 -42
  29. ultralytics/models/__init__.py +1 -1
  30. ultralytics/models/fastsam/__init__.py +1 -1
  31. ultralytics/models/fastsam/model.py +6 -6
  32. ultralytics/models/fastsam/predict.py +3 -2
  33. ultralytics/models/fastsam/prompt.py +55 -48
  34. ultralytics/models/fastsam/val.py +1 -1
  35. ultralytics/models/nas/__init__.py +1 -1
  36. ultralytics/models/nas/model.py +9 -8
  37. ultralytics/models/nas/predict.py +8 -6
  38. ultralytics/models/nas/val.py +11 -9
  39. ultralytics/models/rtdetr/__init__.py +1 -1
  40. ultralytics/models/rtdetr/model.py +11 -9
  41. ultralytics/models/rtdetr/train.py +18 -16
  42. ultralytics/models/rtdetr/val.py +25 -19
  43. ultralytics/models/sam/__init__.py +1 -1
  44. ultralytics/models/sam/amg.py +13 -14
  45. ultralytics/models/sam/build.py +44 -42
  46. ultralytics/models/sam/model.py +6 -6
  47. ultralytics/models/sam/modules/decoders.py +6 -4
  48. ultralytics/models/sam/modules/encoders.py +37 -35
  49. ultralytics/models/sam/modules/sam.py +5 -4
  50. ultralytics/models/sam/modules/tiny_encoder.py +95 -73
  51. ultralytics/models/sam/modules/transformer.py +3 -2
  52. ultralytics/models/sam/predict.py +39 -27
  53. ultralytics/models/utils/loss.py +99 -95
  54. ultralytics/models/utils/ops.py +34 -31
  55. ultralytics/models/yolo/__init__.py +1 -1
  56. ultralytics/models/yolo/classify/__init__.py +1 -1
  57. ultralytics/models/yolo/classify/predict.py +8 -6
  58. ultralytics/models/yolo/classify/train.py +37 -31
  59. ultralytics/models/yolo/classify/val.py +26 -24
  60. ultralytics/models/yolo/detect/__init__.py +1 -1
  61. ultralytics/models/yolo/detect/predict.py +8 -6
  62. ultralytics/models/yolo/detect/train.py +47 -37
  63. ultralytics/models/yolo/detect/val.py +100 -82
  64. ultralytics/models/yolo/model.py +31 -25
  65. ultralytics/models/yolo/obb/__init__.py +1 -1
  66. ultralytics/models/yolo/obb/predict.py +13 -11
  67. ultralytics/models/yolo/obb/train.py +3 -3
  68. ultralytics/models/yolo/obb/val.py +70 -59
  69. ultralytics/models/yolo/pose/__init__.py +1 -1
  70. ultralytics/models/yolo/pose/predict.py +17 -12
  71. ultralytics/models/yolo/pose/train.py +28 -25
  72. ultralytics/models/yolo/pose/val.py +91 -64
  73. ultralytics/models/yolo/segment/__init__.py +1 -1
  74. ultralytics/models/yolo/segment/predict.py +10 -8
  75. ultralytics/models/yolo/segment/train.py +16 -15
  76. ultralytics/models/yolo/segment/val.py +90 -68
  77. ultralytics/nn/__init__.py +26 -6
  78. ultralytics/nn/autobackend.py +144 -112
  79. ultralytics/nn/modules/__init__.py +96 -13
  80. ultralytics/nn/modules/block.py +28 -7
  81. ultralytics/nn/modules/conv.py +41 -23
  82. ultralytics/nn/modules/head.py +60 -52
  83. ultralytics/nn/modules/transformer.py +49 -32
  84. ultralytics/nn/modules/utils.py +20 -15
  85. ultralytics/nn/tasks.py +215 -141
  86. ultralytics/solutions/ai_gym.py +59 -47
  87. ultralytics/solutions/distance_calculation.py +17 -14
  88. ultralytics/solutions/heatmap.py +57 -55
  89. ultralytics/solutions/object_counter.py +46 -39
  90. ultralytics/solutions/speed_estimation.py +13 -16
  91. ultralytics/trackers/__init__.py +1 -1
  92. ultralytics/trackers/basetrack.py +1 -0
  93. ultralytics/trackers/bot_sort.py +2 -1
  94. ultralytics/trackers/byte_tracker.py +10 -7
  95. ultralytics/trackers/track.py +7 -7
  96. ultralytics/trackers/utils/gmc.py +25 -25
  97. ultralytics/trackers/utils/kalman_filter.py +85 -42
  98. ultralytics/trackers/utils/matching.py +8 -7
  99. ultralytics/utils/__init__.py +173 -152
  100. ultralytics/utils/autobatch.py +10 -10
  101. ultralytics/utils/benchmarks.py +76 -86
  102. ultralytics/utils/callbacks/__init__.py +1 -1
  103. ultralytics/utils/callbacks/base.py +29 -29
  104. ultralytics/utils/callbacks/clearml.py +51 -43
  105. ultralytics/utils/callbacks/comet.py +81 -66
  106. ultralytics/utils/callbacks/dvc.py +33 -26
  107. ultralytics/utils/callbacks/hub.py +44 -26
  108. ultralytics/utils/callbacks/mlflow.py +31 -24
  109. ultralytics/utils/callbacks/neptune.py +35 -25
  110. ultralytics/utils/callbacks/raytune.py +9 -4
  111. ultralytics/utils/callbacks/tensorboard.py +16 -11
  112. ultralytics/utils/callbacks/wb.py +39 -33
  113. ultralytics/utils/checks.py +189 -141
  114. ultralytics/utils/dist.py +15 -12
  115. ultralytics/utils/downloads.py +112 -96
  116. ultralytics/utils/errors.py +1 -1
  117. ultralytics/utils/files.py +11 -11
  118. ultralytics/utils/instance.py +22 -22
  119. ultralytics/utils/loss.py +117 -67
  120. ultralytics/utils/metrics.py +224 -158
  121. ultralytics/utils/ops.py +38 -28
  122. ultralytics/utils/patches.py +3 -3
  123. ultralytics/utils/plotting.py +217 -120
  124. ultralytics/utils/tal.py +19 -13
  125. ultralytics/utils/torch_utils.py +138 -109
  126. ultralytics/utils/triton.py +12 -10
  127. ultralytics/utils/tuner.py +49 -47
  128. {ultralytics-8.0.238.dist-info → ultralytics-8.0.239.dist-info}/METADATA +2 -1
  129. ultralytics-8.0.239.dist-info/RECORD +188 -0
  130. ultralytics-8.0.238.dist-info/RECORD +0 -188
  131. {ultralytics-8.0.238.dist-info → ultralytics-8.0.239.dist-info}/LICENSE +0 -0
  132. {ultralytics-8.0.238.dist-info → ultralytics-8.0.239.dist-info}/WHEEL +0 -0
  133. {ultralytics-8.0.238.dist-info → ultralytics-8.0.239.dist-info}/entry_points.txt +0 -0
  134. {ultralytics-8.0.238.dist-info → ultralytics-8.0.239.dist-info}/top_level.txt +0 -0
@@ -25,23 +25,22 @@ from tqdm import tqdm as tqdm_original
25
25
  from ultralytics import __version__
26
26
 
27
27
  # PyTorch Multi-GPU DDP Constants
28
- RANK = int(os.getenv('RANK', -1))
29
- LOCAL_RANK = int(os.getenv('LOCAL_RANK', -1)) # https://pytorch.org/docs/stable/elastic/run.html
28
+ RANK = int(os.getenv("RANK", -1))
29
+ LOCAL_RANK = int(os.getenv("LOCAL_RANK", -1)) # https://pytorch.org/docs/stable/elastic/run.html
30
30
 
31
31
  # Other Constants
32
32
  FILE = Path(__file__).resolve()
33
33
  ROOT = FILE.parents[1] # YOLO
34
- ASSETS = ROOT / 'assets' # default images
35
- DEFAULT_CFG_PATH = ROOT / 'cfg/default.yaml'
34
+ ASSETS = ROOT / "assets" # default images
35
+ DEFAULT_CFG_PATH = ROOT / "cfg/default.yaml"
36
36
  NUM_THREADS = min(8, max(1, os.cpu_count() - 1)) # number of YOLOv5 multiprocessing threads
37
- AUTOINSTALL = str(os.getenv('YOLO_AUTOINSTALL', True)).lower() == 'true' # global auto-install mode
38
- VERBOSE = str(os.getenv('YOLO_VERBOSE', True)).lower() == 'true' # global verbose mode
39
- TQDM_BAR_FORMAT = '{l_bar}{bar:10}{r_bar}' if VERBOSE else None # tqdm bar format
40
- LOGGING_NAME = 'ultralytics'
41
- MACOS, LINUX, WINDOWS = (platform.system() == x for x in ['Darwin', 'Linux', 'Windows']) # environment booleans
42
- ARM64 = platform.machine() in ('arm64', 'aarch64') # ARM64 booleans
43
- HELP_MSG = \
44
- """
37
+ AUTOINSTALL = str(os.getenv("YOLO_AUTOINSTALL", True)).lower() == "true" # global auto-install mode
38
+ VERBOSE = str(os.getenv("YOLO_VERBOSE", True)).lower() == "true" # global verbose mode
39
+ TQDM_BAR_FORMAT = "{l_bar}{bar:10}{r_bar}" if VERBOSE else None # tqdm bar format
40
+ LOGGING_NAME = "ultralytics"
41
+ MACOS, LINUX, WINDOWS = (platform.system() == x for x in ["Darwin", "Linux", "Windows"]) # environment booleans
42
+ ARM64 = platform.machine() in ("arm64", "aarch64") # ARM64 booleans
43
+ HELP_MSG = """
45
44
  Usage examples for running YOLOv8:
46
45
 
47
46
  1. Install the ultralytics package:
@@ -99,12 +98,12 @@ HELP_MSG = \
99
98
  """
100
99
 
101
100
  # Settings
102
- torch.set_printoptions(linewidth=320, precision=4, profile='default')
103
- np.set_printoptions(linewidth=320, formatter={'float_kind': '{:11.5g}'.format}) # format short g, %precision=5
101
+ torch.set_printoptions(linewidth=320, precision=4, profile="default")
102
+ np.set_printoptions(linewidth=320, formatter={"float_kind": "{:11.5g}".format}) # format short g, %precision=5
104
103
  cv2.setNumThreads(0) # prevent OpenCV from multithreading (incompatible with PyTorch DataLoader)
105
- os.environ['NUMEXPR_MAX_THREADS'] = str(NUM_THREADS) # NumExpr max threads
106
- os.environ['CUBLAS_WORKSPACE_CONFIG'] = ':4096:8' # for deterministic training
107
- os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' # suppress verbose TF compiler warnings in Colab
104
+ os.environ["NUMEXPR_MAX_THREADS"] = str(NUM_THREADS) # NumExpr max threads
105
+ os.environ["CUBLAS_WORKSPACE_CONFIG"] = ":4096:8" # for deterministic training
106
+ os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2" # suppress verbose TF compiler warnings in Colab
108
107
 
109
108
 
110
109
  class TQDM(tqdm_original):
@@ -119,8 +118,8 @@ class TQDM(tqdm_original):
119
118
  def __init__(self, *args, **kwargs):
120
119
  """Initialize custom Ultralytics tqdm class with different default arguments."""
121
120
  # Set new default values (these can still be overridden when calling TQDM)
122
- kwargs['disable'] = not VERBOSE or kwargs.get('disable', False) # logical 'and' with default value if passed
123
- kwargs.setdefault('bar_format', TQDM_BAR_FORMAT) # override default value if passed
121
+ kwargs["disable"] = not VERBOSE or kwargs.get("disable", False) # logical 'and' with default value if passed
122
+ kwargs.setdefault("bar_format", TQDM_BAR_FORMAT) # override default value if passed
124
123
  super().__init__(*args, **kwargs)
125
124
 
126
125
 
@@ -134,14 +133,14 @@ class SimpleClass:
134
133
  attr = []
135
134
  for a in dir(self):
136
135
  v = getattr(self, a)
137
- if not callable(v) and not a.startswith('_'):
136
+ if not callable(v) and not a.startswith("_"):
138
137
  if isinstance(v, SimpleClass):
139
138
  # Display only the module and class name for subclasses
140
- s = f'{a}: {v.__module__}.{v.__class__.__name__} object'
139
+ s = f"{a}: {v.__module__}.{v.__class__.__name__} object"
141
140
  else:
142
- s = f'{a}: {repr(v)}'
141
+ s = f"{a}: {repr(v)}"
143
142
  attr.append(s)
144
- return f'{self.__module__}.{self.__class__.__name__} object with attributes:\n\n' + '\n'.join(attr)
143
+ return f"{self.__module__}.{self.__class__.__name__} object with attributes:\n\n" + "\n".join(attr)
145
144
 
146
145
  def __repr__(self):
147
146
  """Return a machine-readable string representation of the object."""
@@ -164,24 +163,26 @@ class IterableSimpleNamespace(SimpleNamespace):
164
163
 
165
164
  def __str__(self):
166
165
  """Return a human-readable string representation of the object."""
167
- return '\n'.join(f'{k}={v}' for k, v in vars(self).items())
166
+ return "\n".join(f"{k}={v}" for k, v in vars(self).items())
168
167
 
169
168
  def __getattr__(self, attr):
170
169
  """Custom attribute access error message with helpful information."""
171
170
  name = self.__class__.__name__
172
- raise AttributeError(f"""
171
+ raise AttributeError(
172
+ f"""
173
173
  '{name}' object has no attribute '{attr}'. This may be caused by a modified or out of date ultralytics
174
174
  'default.yaml' file.\nPlease update your code with 'pip install -U ultralytics' and if necessary replace
175
175
  {DEFAULT_CFG_PATH} with the latest version from
176
176
  https://github.com/ultralytics/ultralytics/blob/main/ultralytics/cfg/default.yaml
177
- """)
177
+ """
178
+ )
178
179
 
179
180
  def get(self, key, default=None):
180
181
  """Return the value of the specified key if it exists; otherwise, return the default value."""
181
182
  return getattr(self, key, default)
182
183
 
183
184
 
184
- def plt_settings(rcparams=None, backend='Agg'):
185
+ def plt_settings(rcparams=None, backend="Agg"):
185
186
  """
186
187
  Decorator to temporarily set rc parameters and the backend for a plotting function.
187
188
 
@@ -199,7 +200,7 @@ def plt_settings(rcparams=None, backend='Agg'):
199
200
  """
200
201
 
201
202
  if rcparams is None:
202
- rcparams = {'font.size': 11}
203
+ rcparams = {"font.size": 11}
203
204
 
204
205
  def decorator(func):
205
206
  """Decorator to apply temporary rc parameters and backend to a function."""
@@ -208,14 +209,14 @@ def plt_settings(rcparams=None, backend='Agg'):
208
209
  """Sets rc parameters and backend, calls the original function, and restores the settings."""
209
210
  original_backend = plt.get_backend()
210
211
  if backend != original_backend:
211
- plt.close('all') # auto-close()ing of figures upon backend switching is deprecated since 3.8
212
+ plt.close("all") # auto-close()ing of figures upon backend switching is deprecated since 3.8
212
213
  plt.switch_backend(backend)
213
214
 
214
215
  with plt.rc_context(rcparams):
215
216
  result = func(*args, **kwargs)
216
217
 
217
218
  if backend != original_backend:
218
- plt.close('all')
219
+ plt.close("all")
219
220
  plt.switch_backend(original_backend)
220
221
  return result
221
222
 
@@ -229,25 +230,26 @@ def set_logging(name=LOGGING_NAME, verbose=True):
229
230
  level = logging.INFO if verbose and RANK in {-1, 0} else logging.ERROR # rank in world for Multi-GPU trainings
230
231
 
231
232
  # Configure the console (stdout) encoding to UTF-8
232
- formatter = logging.Formatter('%(message)s') # Default formatter
233
- if WINDOWS and sys.stdout.encoding != 'utf-8':
233
+ formatter = logging.Formatter("%(message)s") # Default formatter
234
+ if WINDOWS and sys.stdout.encoding != "utf-8":
234
235
  try:
235
- if hasattr(sys.stdout, 'reconfigure'):
236
- sys.stdout.reconfigure(encoding='utf-8')
237
- elif hasattr(sys.stdout, 'buffer'):
236
+ if hasattr(sys.stdout, "reconfigure"):
237
+ sys.stdout.reconfigure(encoding="utf-8")
238
+ elif hasattr(sys.stdout, "buffer"):
238
239
  import io
239
- sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
240
+
241
+ sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
240
242
  else:
241
- sys.stdout.encoding = 'utf-8'
243
+ sys.stdout.encoding = "utf-8"
242
244
  except Exception as e:
243
- print(f'Creating custom formatter for non UTF-8 environments due to {e}')
245
+ print(f"Creating custom formatter for non UTF-8 environments due to {e}")
244
246
 
245
247
  class CustomFormatter(logging.Formatter):
246
-
247
248
  def format(self, record):
249
+ """Sets up logging with UTF-8 encoding and configurable verbosity."""
248
250
  return emojis(super().format(record))
249
251
 
250
- formatter = CustomFormatter('%(message)s') # Use CustomFormatter to eliminate UTF-8 output as last recourse
252
+ formatter = CustomFormatter("%(message)s") # Use CustomFormatter to eliminate UTF-8 output as last recourse
251
253
 
252
254
  # Create and configure the StreamHandler
253
255
  stream_handler = logging.StreamHandler(sys.stdout)
@@ -263,13 +265,13 @@ def set_logging(name=LOGGING_NAME, verbose=True):
263
265
 
264
266
  # Set logger
265
267
  LOGGER = set_logging(LOGGING_NAME, verbose=VERBOSE) # define globally (used in train.py, val.py, predict.py, etc.)
266
- for logger in 'sentry_sdk', 'urllib3.connectionpool':
268
+ for logger in "sentry_sdk", "urllib3.connectionpool":
267
269
  logging.getLogger(logger).setLevel(logging.CRITICAL + 1)
268
270
 
269
271
 
270
- def emojis(string=''):
272
+ def emojis(string=""):
271
273
  """Return platform-dependent emoji-safe version of string."""
272
- return string.encode().decode('ascii', 'ignore') if WINDOWS else string
274
+ return string.encode().decode("ascii", "ignore") if WINDOWS else string
273
275
 
274
276
 
275
277
  class ThreadingLocked:
@@ -309,7 +311,7 @@ class ThreadingLocked:
309
311
  return decorated
310
312
 
311
313
 
312
- def yaml_save(file='data.yaml', data=None, header=''):
314
+ def yaml_save(file="data.yaml", data=None, header=""):
313
315
  """
314
316
  Save YAML data to a file.
315
317
 
@@ -335,13 +337,13 @@ def yaml_save(file='data.yaml', data=None, header=''):
335
337
  data[k] = str(v)
336
338
 
337
339
  # Dump data to file in YAML format
338
- with open(file, 'w', errors='ignore', encoding='utf-8') as f:
340
+ with open(file, "w", errors="ignore", encoding="utf-8") as f:
339
341
  if header:
340
342
  f.write(header)
341
343
  yaml.safe_dump(data, f, sort_keys=False, allow_unicode=True)
342
344
 
343
345
 
344
- def yaml_load(file='data.yaml', append_filename=False):
346
+ def yaml_load(file="data.yaml", append_filename=False):
345
347
  """
346
348
  Load YAML data from a file.
347
349
 
@@ -352,18 +354,18 @@ def yaml_load(file='data.yaml', append_filename=False):
352
354
  Returns:
353
355
  (dict): YAML data and file name.
354
356
  """
355
- assert Path(file).suffix in ('.yaml', '.yml'), f'Attempting to load non-YAML file {file} with yaml_load()'
356
- with open(file, errors='ignore', encoding='utf-8') as f:
357
+ assert Path(file).suffix in (".yaml", ".yml"), f"Attempting to load non-YAML file {file} with yaml_load()"
358
+ with open(file, errors="ignore", encoding="utf-8") as f:
357
359
  s = f.read() # string
358
360
 
359
361
  # Remove special characters
360
362
  if not s.isprintable():
361
- s = re.sub(r'[^\x09\x0A\x0D\x20-\x7E\x85\xA0-\uD7FF\uE000-\uFFFD\U00010000-\U0010ffff]+', '', s)
363
+ s = re.sub(r"[^\x09\x0A\x0D\x20-\x7E\x85\xA0-\uD7FF\uE000-\uFFFD\U00010000-\U0010ffff]+", "", s)
362
364
 
363
365
  # Add YAML filename to dict and return
364
366
  data = yaml.safe_load(s) or {} # always return a dict (yaml.safe_load() may return None for empty files)
365
367
  if append_filename:
366
- data['yaml_file'] = str(file)
368
+ data["yaml_file"] = str(file)
367
369
  return data
368
370
 
369
371
 
@@ -385,7 +387,7 @@ def yaml_print(yaml_file: Union[str, Path, dict]) -> None:
385
387
  # Default configuration
386
388
  DEFAULT_CFG_DICT = yaml_load(DEFAULT_CFG_PATH)
387
389
  for k, v in DEFAULT_CFG_DICT.items():
388
- if isinstance(v, str) and v.lower() == 'none':
390
+ if isinstance(v, str) and v.lower() == "none":
389
391
  DEFAULT_CFG_DICT[k] = None
390
392
  DEFAULT_CFG_KEYS = DEFAULT_CFG_DICT.keys()
391
393
  DEFAULT_CFG = IterableSimpleNamespace(**DEFAULT_CFG_DICT)
@@ -399,8 +401,8 @@ def is_ubuntu() -> bool:
399
401
  (bool): True if OS is Ubuntu, False otherwise.
400
402
  """
401
403
  with contextlib.suppress(FileNotFoundError):
402
- with open('/etc/os-release') as f:
403
- return 'ID=ubuntu' in f.read()
404
+ with open("/etc/os-release") as f:
405
+ return "ID=ubuntu" in f.read()
404
406
  return False
405
407
 
406
408
 
@@ -411,7 +413,7 @@ def is_colab():
411
413
  Returns:
412
414
  (bool): True if running inside a Colab notebook, False otherwise.
413
415
  """
414
- return 'COLAB_RELEASE_TAG' in os.environ or 'COLAB_BACKEND_VERSION' in os.environ
416
+ return "COLAB_RELEASE_TAG" in os.environ or "COLAB_BACKEND_VERSION" in os.environ
415
417
 
416
418
 
417
419
  def is_kaggle():
@@ -421,7 +423,7 @@ def is_kaggle():
421
423
  Returns:
422
424
  (bool): True if running inside a Kaggle kernel, False otherwise.
423
425
  """
424
- return os.environ.get('PWD') == '/kaggle/working' and os.environ.get('KAGGLE_URL_BASE') == 'https://www.kaggle.com'
426
+ return os.environ.get("PWD") == "/kaggle/working" and os.environ.get("KAGGLE_URL_BASE") == "https://www.kaggle.com"
425
427
 
426
428
 
427
429
  def is_jupyter():
@@ -433,6 +435,7 @@ def is_jupyter():
433
435
  """
434
436
  with contextlib.suppress(Exception):
435
437
  from IPython import get_ipython
438
+
436
439
  return get_ipython() is not None
437
440
  return False
438
441
 
@@ -444,10 +447,10 @@ def is_docker() -> bool:
444
447
  Returns:
445
448
  (bool): True if the script is running inside a Docker container, False otherwise.
446
449
  """
447
- file = Path('/proc/self/cgroup')
450
+ file = Path("/proc/self/cgroup")
448
451
  if file.exists():
449
452
  with open(file) as f:
450
- return 'docker' in f.read()
453
+ return "docker" in f.read()
451
454
  else:
452
455
  return False
453
456
 
@@ -461,7 +464,7 @@ def is_online() -> bool:
461
464
  """
462
465
  import socket
463
466
 
464
- for host in '1.1.1.1', '8.8.8.8', '223.5.5.5': # Cloudflare, Google, AliDNS:
467
+ for host in "1.1.1.1", "8.8.8.8", "223.5.5.5": # Cloudflare, Google, AliDNS:
465
468
  try:
466
469
  test_connection = socket.create_connection(address=(host, 53), timeout=2)
467
470
  except (socket.timeout, socket.gaierror, OSError):
@@ -515,7 +518,7 @@ def is_pytest_running():
515
518
  Returns:
516
519
  (bool): True if pytest is running, False otherwise.
517
520
  """
518
- return ('PYTEST_CURRENT_TEST' in os.environ) or ('pytest' in sys.modules) or ('pytest' in Path(sys.argv[0]).stem)
521
+ return ("PYTEST_CURRENT_TEST" in os.environ) or ("pytest" in sys.modules) or ("pytest" in Path(sys.argv[0]).stem)
519
522
 
520
523
 
521
524
  def is_github_action_running() -> bool:
@@ -525,7 +528,7 @@ def is_github_action_running() -> bool:
525
528
  Returns:
526
529
  (bool): True if the current environment is a GitHub Actions runner, False otherwise.
527
530
  """
528
- return 'GITHUB_ACTIONS' in os.environ and 'GITHUB_WORKFLOW' in os.environ and 'RUNNER_OS' in os.environ
531
+ return "GITHUB_ACTIONS" in os.environ and "GITHUB_WORKFLOW" in os.environ and "RUNNER_OS" in os.environ
529
532
 
530
533
 
531
534
  def is_git_dir():
@@ -548,7 +551,7 @@ def get_git_dir():
548
551
  (Path | None): Git root directory if found or None if not found.
549
552
  """
550
553
  for d in Path(__file__).parents:
551
- if (d / '.git').is_dir():
554
+ if (d / ".git").is_dir():
552
555
  return d
553
556
 
554
557
 
@@ -561,7 +564,7 @@ def get_git_origin_url():
561
564
  """
562
565
  if is_git_dir():
563
566
  with contextlib.suppress(subprocess.CalledProcessError):
564
- origin = subprocess.check_output(['git', 'config', '--get', 'remote.origin.url'])
567
+ origin = subprocess.check_output(["git", "config", "--get", "remote.origin.url"])
565
568
  return origin.decode().strip()
566
569
 
567
570
 
@@ -574,7 +577,7 @@ def get_git_branch():
574
577
  """
575
578
  if is_git_dir():
576
579
  with contextlib.suppress(subprocess.CalledProcessError):
577
- origin = subprocess.check_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD'])
580
+ origin = subprocess.check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"])
578
581
  return origin.decode().strip()
579
582
 
580
583
 
@@ -601,11 +604,11 @@ def get_ubuntu_version():
601
604
  """
602
605
  if is_ubuntu():
603
606
  with contextlib.suppress(FileNotFoundError, AttributeError):
604
- with open('/etc/os-release') as f:
607
+ with open("/etc/os-release") as f:
605
608
  return re.search(r'VERSION_ID="(\d+\.\d+)"', f.read())[1]
606
609
 
607
610
 
608
- def get_user_config_dir(sub_dir='Ultralytics'):
611
+ def get_user_config_dir(sub_dir="Ultralytics"):
609
612
  """
610
613
  Get the user config directory.
611
614
 
@@ -617,19 +620,21 @@ def get_user_config_dir(sub_dir='Ultralytics'):
617
620
  """
618
621
  # Return the appropriate config directory for each operating system
619
622
  if WINDOWS:
620
- path = Path.home() / 'AppData' / 'Roaming' / sub_dir
623
+ path = Path.home() / "AppData" / "Roaming" / sub_dir
621
624
  elif MACOS: # macOS
622
- path = Path.home() / 'Library' / 'Application Support' / sub_dir
625
+ path = Path.home() / "Library" / "Application Support" / sub_dir
623
626
  elif LINUX:
624
- path = Path.home() / '.config' / sub_dir
627
+ path = Path.home() / ".config" / sub_dir
625
628
  else:
626
- raise ValueError(f'Unsupported operating system: {platform.system()}')
629
+ raise ValueError(f"Unsupported operating system: {platform.system()}")
627
630
 
628
631
  # GCP and AWS lambda fix, only /tmp is writeable
629
632
  if not is_dir_writeable(path.parent):
630
- LOGGER.warning(f"WARNING ⚠️ user config directory '{path}' is not writeable, defaulting to '/tmp' or CWD."
631
- 'Alternatively you can define a YOLO_CONFIG_DIR environment variable for this path.')
632
- path = Path('/tmp') / sub_dir if is_dir_writeable('/tmp') else Path().cwd() / sub_dir
633
+ LOGGER.warning(
634
+ f"WARNING ⚠️ user config directory '{path}' is not writeable, defaulting to '/tmp' or CWD."
635
+ "Alternatively you can define a YOLO_CONFIG_DIR environment variable for this path."
636
+ )
637
+ path = Path("/tmp") / sub_dir if is_dir_writeable("/tmp") else Path().cwd() / sub_dir
633
638
 
634
639
  # Create the subdirectory if it does not exist
635
640
  path.mkdir(parents=True, exist_ok=True)
@@ -637,8 +642,8 @@ def get_user_config_dir(sub_dir='Ultralytics'):
637
642
  return path
638
643
 
639
644
 
640
- USER_CONFIG_DIR = Path(os.getenv('YOLO_CONFIG_DIR') or get_user_config_dir()) # Ultralytics settings dir
641
- SETTINGS_YAML = USER_CONFIG_DIR / 'settings.yaml'
645
+ USER_CONFIG_DIR = Path(os.getenv("YOLO_CONFIG_DIR") or get_user_config_dir()) # Ultralytics settings dir
646
+ SETTINGS_YAML = USER_CONFIG_DIR / "settings.yaml"
642
647
 
643
648
 
644
649
  def colorstr(*input):
@@ -669,28 +674,29 @@ def colorstr(*input):
669
674
  >>> colorstr('blue', 'bold', 'hello world')
670
675
  >>> '\033[34m\033[1mhello world\033[0m'
671
676
  """
672
- *args, string = input if len(input) > 1 else ('blue', 'bold', input[0]) # color arguments, string
677
+ *args, string = input if len(input) > 1 else ("blue", "bold", input[0]) # color arguments, string
673
678
  colors = {
674
- 'black': '\033[30m', # basic colors
675
- 'red': '\033[31m',
676
- 'green': '\033[32m',
677
- 'yellow': '\033[33m',
678
- 'blue': '\033[34m',
679
- 'magenta': '\033[35m',
680
- 'cyan': '\033[36m',
681
- 'white': '\033[37m',
682
- 'bright_black': '\033[90m', # bright colors
683
- 'bright_red': '\033[91m',
684
- 'bright_green': '\033[92m',
685
- 'bright_yellow': '\033[93m',
686
- 'bright_blue': '\033[94m',
687
- 'bright_magenta': '\033[95m',
688
- 'bright_cyan': '\033[96m',
689
- 'bright_white': '\033[97m',
690
- 'end': '\033[0m', # misc
691
- 'bold': '\033[1m',
692
- 'underline': '\033[4m'}
693
- return ''.join(colors[x] for x in args) + f'{string}' + colors['end']
679
+ "black": "\033[30m", # basic colors
680
+ "red": "\033[31m",
681
+ "green": "\033[32m",
682
+ "yellow": "\033[33m",
683
+ "blue": "\033[34m",
684
+ "magenta": "\033[35m",
685
+ "cyan": "\033[36m",
686
+ "white": "\033[37m",
687
+ "bright_black": "\033[90m", # bright colors
688
+ "bright_red": "\033[91m",
689
+ "bright_green": "\033[92m",
690
+ "bright_yellow": "\033[93m",
691
+ "bright_blue": "\033[94m",
692
+ "bright_magenta": "\033[95m",
693
+ "bright_cyan": "\033[96m",
694
+ "bright_white": "\033[97m",
695
+ "end": "\033[0m", # misc
696
+ "bold": "\033[1m",
697
+ "underline": "\033[4m",
698
+ }
699
+ return "".join(colors[x] for x in args) + f"{string}" + colors["end"]
694
700
 
695
701
 
696
702
  def remove_colorstr(input_string):
@@ -707,8 +713,8 @@ def remove_colorstr(input_string):
707
713
  >>> remove_colorstr(colorstr('blue', 'bold', 'hello world'))
708
714
  >>> 'hello world'
709
715
  """
710
- ansi_escape = re.compile(r'\x1B\[[0-9;]*[A-Za-z]')
711
- return ansi_escape.sub('', input_string)
716
+ ansi_escape = re.compile(r"\x1B\[[0-9;]*[A-Za-z]")
717
+ return ansi_escape.sub("", input_string)
712
718
 
713
719
 
714
720
  class TryExcept(contextlib.ContextDecorator):
@@ -718,7 +724,7 @@ class TryExcept(contextlib.ContextDecorator):
718
724
  Use as @TryExcept() decorator or 'with TryExcept():' context manager.
719
725
  """
720
726
 
721
- def __init__(self, msg='', verbose=True):
727
+ def __init__(self, msg="", verbose=True):
722
728
  """Initialize TryExcept class with optional message and verbosity settings."""
723
729
  self.msg = msg
724
730
  self.verbose = verbose
@@ -743,7 +749,7 @@ def threaded(func):
743
749
 
744
750
  def wrapper(*args, **kwargs):
745
751
  """Multi-threads a given function based on 'threaded' kwarg and returns the thread or function result."""
746
- if kwargs.pop('threaded', True): # run in thread
752
+ if kwargs.pop("threaded", True): # run in thread
747
753
  thread = threading.Thread(target=func, args=args, kwargs=kwargs, daemon=True)
748
754
  thread.start()
749
755
  return thread
@@ -785,27 +791,28 @@ def set_sentry():
785
791
  Returns:
786
792
  dict: The modified event or None if the event should not be sent to Sentry.
787
793
  """
788
- if 'exc_info' in hint:
789
- exc_type, exc_value, tb = hint['exc_info']
790
- if exc_type in (KeyboardInterrupt, FileNotFoundError) \
791
- or 'out of memory' in str(exc_value):
794
+ if "exc_info" in hint:
795
+ exc_type, exc_value, tb = hint["exc_info"]
796
+ if exc_type in (KeyboardInterrupt, FileNotFoundError) or "out of memory" in str(exc_value):
792
797
  return None # do not send event
793
798
 
794
- event['tags'] = {
795
- 'sys_argv': sys.argv[0],
796
- 'sys_argv_name': Path(sys.argv[0]).name,
797
- 'install': 'git' if is_git_dir() else 'pip' if is_pip_package() else 'other',
798
- 'os': ENVIRONMENT}
799
+ event["tags"] = {
800
+ "sys_argv": sys.argv[0],
801
+ "sys_argv_name": Path(sys.argv[0]).name,
802
+ "install": "git" if is_git_dir() else "pip" if is_pip_package() else "other",
803
+ "os": ENVIRONMENT,
804
+ }
799
805
  return event
800
806
 
801
- if SETTINGS['sync'] and \
802
- RANK in (-1, 0) and \
803
- Path(sys.argv[0]).name == 'yolo' and \
804
- not TESTS_RUNNING and \
805
- ONLINE and \
806
- is_pip_package() and \
807
- not is_git_dir():
808
-
807
+ if (
808
+ SETTINGS["sync"]
809
+ and RANK in (-1, 0)
810
+ and Path(sys.argv[0]).name == "yolo"
811
+ and not TESTS_RUNNING
812
+ and ONLINE
813
+ and is_pip_package()
814
+ and not is_git_dir()
815
+ ):
809
816
  # If sentry_sdk package is not installed then return and do not use Sentry
810
817
  try:
811
818
  import sentry_sdk # noqa
@@ -813,14 +820,15 @@ def set_sentry():
813
820
  return
814
821
 
815
822
  sentry_sdk.init(
816
- dsn='https://5ff1556b71594bfea135ff0203a0d290@o4504521589325824.ingest.sentry.io/4504521592406016',
823
+ dsn="https://5ff1556b71594bfea135ff0203a0d290@o4504521589325824.ingest.sentry.io/4504521592406016",
817
824
  debug=False,
818
825
  traces_sample_rate=1.0,
819
826
  release=__version__,
820
- environment='production', # 'dev' or 'production'
827
+ environment="production", # 'dev' or 'production'
821
828
  before_send=before_send,
822
- ignore_errors=[KeyboardInterrupt, FileNotFoundError])
823
- sentry_sdk.set_user({'id': SETTINGS['uuid']}) # SHA-256 anonymized UUID hash
829
+ ignore_errors=[KeyboardInterrupt, FileNotFoundError],
830
+ )
831
+ sentry_sdk.set_user({"id": SETTINGS["uuid"]}) # SHA-256 anonymized UUID hash
824
832
 
825
833
 
826
834
  class SettingsManager(dict):
@@ -832,7 +840,7 @@ class SettingsManager(dict):
832
840
  version (str): Settings version. In case of local version mismatch, new default settings will be saved.
833
841
  """
834
842
 
835
- def __init__(self, file=SETTINGS_YAML, version='0.0.4'):
843
+ def __init__(self, file=SETTINGS_YAML, version="0.0.4"):
836
844
  """Initialize the SettingsManager with default settings, load and validate current settings from the YAML
837
845
  file.
838
846
  """
@@ -849,23 +857,24 @@ class SettingsManager(dict):
849
857
  self.file = Path(file)
850
858
  self.version = version
851
859
  self.defaults = {
852
- 'settings_version': version,
853
- 'datasets_dir': str(datasets_root / 'datasets'),
854
- 'weights_dir': str(root / 'weights'),
855
- 'runs_dir': str(root / 'runs'),
856
- 'uuid': hashlib.sha256(str(uuid.getnode()).encode()).hexdigest(),
857
- 'sync': True,
858
- 'api_key': '',
859
- 'openai_api_key': '',
860
- 'clearml': True, # integrations
861
- 'comet': True,
862
- 'dvc': True,
863
- 'hub': True,
864
- 'mlflow': True,
865
- 'neptune': True,
866
- 'raytune': True,
867
- 'tensorboard': True,
868
- 'wandb': True}
860
+ "settings_version": version,
861
+ "datasets_dir": str(datasets_root / "datasets"),
862
+ "weights_dir": str(root / "weights"),
863
+ "runs_dir": str(root / "runs"),
864
+ "uuid": hashlib.sha256(str(uuid.getnode()).encode()).hexdigest(),
865
+ "sync": True,
866
+ "api_key": "",
867
+ "openai_api_key": "",
868
+ "clearml": True, # integrations
869
+ "comet": True,
870
+ "dvc": True,
871
+ "hub": True,
872
+ "mlflow": True,
873
+ "neptune": True,
874
+ "raytune": True,
875
+ "tensorboard": True,
876
+ "wandb": True,
877
+ }
869
878
 
870
879
  super().__init__(copy.deepcopy(self.defaults))
871
880
 
@@ -876,13 +885,14 @@ class SettingsManager(dict):
876
885
  self.load()
877
886
  correct_keys = self.keys() == self.defaults.keys()
878
887
  correct_types = all(type(a) is type(b) for a, b in zip(self.values(), self.defaults.values()))
879
- correct_version = check_version(self['settings_version'], self.version)
888
+ correct_version = check_version(self["settings_version"], self.version)
880
889
  if not (correct_keys and correct_types and correct_version):
881
890
  LOGGER.warning(
882
- 'WARNING ⚠️ Ultralytics settings reset to default values. This may be due to a possible problem '
883
- 'with your settings or a recent ultralytics package update. '
891
+ "WARNING ⚠️ Ultralytics settings reset to default values. This may be due to a possible problem "
892
+ "with your settings or a recent ultralytics package update. "
884
893
  f"\nView settings with 'yolo settings' or at '{self.file}'"
885
- "\nUpdate settings with 'yolo settings key=value', i.e. 'yolo settings runs_dir=path/to/dir'.")
894
+ "\nUpdate settings with 'yolo settings key=value', i.e. 'yolo settings runs_dir=path/to/dir'."
895
+ )
886
896
  self.reset()
887
897
 
888
898
  def load(self):
@@ -909,14 +919,16 @@ def deprecation_warn(arg, new_arg, version=None):
909
919
  """Issue a deprecation warning when a deprecated argument is used, suggesting an updated argument."""
910
920
  if not version:
911
921
  version = float(__version__[:3]) + 0.2 # deprecate after 2nd major release
912
- LOGGER.warning(f"WARNING ⚠️ '{arg}' is deprecated and will be removed in 'ultralytics {version}' in the future. "
913
- f"Please use '{new_arg}' instead.")
922
+ LOGGER.warning(
923
+ f"WARNING ⚠️ '{arg}' is deprecated and will be removed in 'ultralytics {version}' in the future. "
924
+ f"Please use '{new_arg}' instead."
925
+ )
914
926
 
915
927
 
916
928
  def clean_url(url):
917
929
  """Strip auth from URL, i.e. https://url.com/file.txt?auth -> https://url.com/file.txt."""
918
- url = Path(url).as_posix().replace(':/', '://') # Pathlib turns :// -> :/, as_posix() for Windows
919
- return urllib.parse.unquote(url).split('?')[0] # '%2F' to '/', split https://url.com/file.txt?auth
930
+ url = Path(url).as_posix().replace(":/", "://") # Pathlib turns :// -> :/, as_posix() for Windows
931
+ return urllib.parse.unquote(url).split("?")[0] # '%2F' to '/', split https://url.com/file.txt?auth
920
932
 
921
933
 
922
934
  def url2file(url):
@@ -927,13 +939,22 @@ def url2file(url):
927
939
  # Run below code on utils init ------------------------------------------------------------------------------------
928
940
 
929
941
  # Check first-install steps
930
- PREFIX = colorstr('Ultralytics: ')
942
+ PREFIX = colorstr("Ultralytics: ")
931
943
  SETTINGS = SettingsManager() # initialize settings
932
- DATASETS_DIR = Path(SETTINGS['datasets_dir']) # global datasets directory
933
- WEIGHTS_DIR = Path(SETTINGS['weights_dir']) # global weights directory
934
- RUNS_DIR = Path(SETTINGS['runs_dir']) # global runs directory
935
- ENVIRONMENT = 'Colab' if is_colab() else 'Kaggle' if is_kaggle() else 'Jupyter' if is_jupyter() else \
936
- 'Docker' if is_docker() else platform.system()
944
+ DATASETS_DIR = Path(SETTINGS["datasets_dir"]) # global datasets directory
945
+ WEIGHTS_DIR = Path(SETTINGS["weights_dir"]) # global weights directory
946
+ RUNS_DIR = Path(SETTINGS["runs_dir"]) # global runs directory
947
+ ENVIRONMENT = (
948
+ "Colab"
949
+ if is_colab()
950
+ else "Kaggle"
951
+ if is_kaggle()
952
+ else "Jupyter"
953
+ if is_jupyter()
954
+ else "Docker"
955
+ if is_docker()
956
+ else platform.system()
957
+ )
937
958
  TESTS_RUNNING = is_pytest_running() or is_github_action_running()
938
959
  set_sentry()
939
960