ultralytics 8.0.239 Ultralytics Actions and hub-sdk adoption (#7431)
Signed-off-by: Glenn Jocher <glenn.jocher@ultralytics.com> Co-authored-by: UltralyticsAssistant <web@ultralytics.com> Co-authored-by: Burhan <62214284+Burhan-Q@users.noreply.github.com> Co-authored-by: Kayzwer <68285002+Kayzwer@users.noreply.github.com>
This commit is contained in:
parent
e795277391
commit
fe27db2f6e
139 changed files with 6870 additions and 5125 deletions
|
|
@ -25,23 +25,22 @@ from tqdm import tqdm as tqdm_original
|
|||
from ultralytics import __version__
|
||||
|
||||
# PyTorch Multi-GPU DDP Constants
|
||||
RANK = int(os.getenv('RANK', -1))
|
||||
LOCAL_RANK = int(os.getenv('LOCAL_RANK', -1)) # https://pytorch.org/docs/stable/elastic/run.html
|
||||
RANK = int(os.getenv("RANK", -1))
|
||||
LOCAL_RANK = int(os.getenv("LOCAL_RANK", -1)) # https://pytorch.org/docs/stable/elastic/run.html
|
||||
|
||||
# Other Constants
|
||||
FILE = Path(__file__).resolve()
|
||||
ROOT = FILE.parents[1] # YOLO
|
||||
ASSETS = ROOT / 'assets' # default images
|
||||
DEFAULT_CFG_PATH = ROOT / 'cfg/default.yaml'
|
||||
ASSETS = ROOT / "assets" # default images
|
||||
DEFAULT_CFG_PATH = ROOT / "cfg/default.yaml"
|
||||
NUM_THREADS = min(8, max(1, os.cpu_count() - 1)) # number of YOLOv5 multiprocessing threads
|
||||
AUTOINSTALL = str(os.getenv('YOLO_AUTOINSTALL', True)).lower() == 'true' # global auto-install mode
|
||||
VERBOSE = str(os.getenv('YOLO_VERBOSE', True)).lower() == 'true' # global verbose mode
|
||||
TQDM_BAR_FORMAT = '{l_bar}{bar:10}{r_bar}' if VERBOSE else None # tqdm bar format
|
||||
LOGGING_NAME = 'ultralytics'
|
||||
MACOS, LINUX, WINDOWS = (platform.system() == x for x in ['Darwin', 'Linux', 'Windows']) # environment booleans
|
||||
ARM64 = platform.machine() in ('arm64', 'aarch64') # ARM64 booleans
|
||||
HELP_MSG = \
|
||||
"""
|
||||
AUTOINSTALL = str(os.getenv("YOLO_AUTOINSTALL", True)).lower() == "true" # global auto-install mode
|
||||
VERBOSE = str(os.getenv("YOLO_VERBOSE", True)).lower() == "true" # global verbose mode
|
||||
TQDM_BAR_FORMAT = "{l_bar}{bar:10}{r_bar}" if VERBOSE else None # tqdm bar format
|
||||
LOGGING_NAME = "ultralytics"
|
||||
MACOS, LINUX, WINDOWS = (platform.system() == x for x in ["Darwin", "Linux", "Windows"]) # environment booleans
|
||||
ARM64 = platform.machine() in ("arm64", "aarch64") # ARM64 booleans
|
||||
HELP_MSG = """
|
||||
Usage examples for running YOLOv8:
|
||||
|
||||
1. Install the ultralytics package:
|
||||
|
|
@ -99,12 +98,12 @@ HELP_MSG = \
|
|||
"""
|
||||
|
||||
# Settings
|
||||
torch.set_printoptions(linewidth=320, precision=4, profile='default')
|
||||
np.set_printoptions(linewidth=320, formatter={'float_kind': '{:11.5g}'.format}) # format short g, %precision=5
|
||||
torch.set_printoptions(linewidth=320, precision=4, profile="default")
|
||||
np.set_printoptions(linewidth=320, formatter={"float_kind": "{:11.5g}".format}) # format short g, %precision=5
|
||||
cv2.setNumThreads(0) # prevent OpenCV from multithreading (incompatible with PyTorch DataLoader)
|
||||
os.environ['NUMEXPR_MAX_THREADS'] = str(NUM_THREADS) # NumExpr max threads
|
||||
os.environ['CUBLAS_WORKSPACE_CONFIG'] = ':4096:8' # for deterministic training
|
||||
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' # suppress verbose TF compiler warnings in Colab
|
||||
os.environ["NUMEXPR_MAX_THREADS"] = str(NUM_THREADS) # NumExpr max threads
|
||||
os.environ["CUBLAS_WORKSPACE_CONFIG"] = ":4096:8" # for deterministic training
|
||||
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2" # suppress verbose TF compiler warnings in Colab
|
||||
|
||||
|
||||
class TQDM(tqdm_original):
|
||||
|
|
@ -119,8 +118,8 @@ class TQDM(tqdm_original):
|
|||
def __init__(self, *args, **kwargs):
|
||||
"""Initialize custom Ultralytics tqdm class with different default arguments."""
|
||||
# Set new default values (these can still be overridden when calling TQDM)
|
||||
kwargs['disable'] = not VERBOSE or kwargs.get('disable', False) # logical 'and' with default value if passed
|
||||
kwargs.setdefault('bar_format', TQDM_BAR_FORMAT) # override default value if passed
|
||||
kwargs["disable"] = not VERBOSE or kwargs.get("disable", False) # logical 'and' with default value if passed
|
||||
kwargs.setdefault("bar_format", TQDM_BAR_FORMAT) # override default value if passed
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
|
||||
|
|
@ -134,14 +133,14 @@ class SimpleClass:
|
|||
attr = []
|
||||
for a in dir(self):
|
||||
v = getattr(self, a)
|
||||
if not callable(v) and not a.startswith('_'):
|
||||
if not callable(v) and not a.startswith("_"):
|
||||
if isinstance(v, SimpleClass):
|
||||
# Display only the module and class name for subclasses
|
||||
s = f'{a}: {v.__module__}.{v.__class__.__name__} object'
|
||||
s = f"{a}: {v.__module__}.{v.__class__.__name__} object"
|
||||
else:
|
||||
s = f'{a}: {repr(v)}'
|
||||
s = f"{a}: {repr(v)}"
|
||||
attr.append(s)
|
||||
return f'{self.__module__}.{self.__class__.__name__} object with attributes:\n\n' + '\n'.join(attr)
|
||||
return f"{self.__module__}.{self.__class__.__name__} object with attributes:\n\n" + "\n".join(attr)
|
||||
|
||||
def __repr__(self):
|
||||
"""Return a machine-readable string representation of the object."""
|
||||
|
|
@ -164,24 +163,26 @@ class IterableSimpleNamespace(SimpleNamespace):
|
|||
|
||||
def __str__(self):
|
||||
"""Return a human-readable string representation of the object."""
|
||||
return '\n'.join(f'{k}={v}' for k, v in vars(self).items())
|
||||
return "\n".join(f"{k}={v}" for k, v in vars(self).items())
|
||||
|
||||
def __getattr__(self, attr):
|
||||
"""Custom attribute access error message with helpful information."""
|
||||
name = self.__class__.__name__
|
||||
raise AttributeError(f"""
|
||||
raise AttributeError(
|
||||
f"""
|
||||
'{name}' object has no attribute '{attr}'. This may be caused by a modified or out of date ultralytics
|
||||
'default.yaml' file.\nPlease update your code with 'pip install -U ultralytics' and if necessary replace
|
||||
{DEFAULT_CFG_PATH} with the latest version from
|
||||
https://github.com/ultralytics/ultralytics/blob/main/ultralytics/cfg/default.yaml
|
||||
""")
|
||||
"""
|
||||
)
|
||||
|
||||
def get(self, key, default=None):
|
||||
"""Return the value of the specified key if it exists; otherwise, return the default value."""
|
||||
return getattr(self, key, default)
|
||||
|
||||
|
||||
def plt_settings(rcparams=None, backend='Agg'):
|
||||
def plt_settings(rcparams=None, backend="Agg"):
|
||||
"""
|
||||
Decorator to temporarily set rc parameters and the backend for a plotting function.
|
||||
|
||||
|
|
@ -199,7 +200,7 @@ def plt_settings(rcparams=None, backend='Agg'):
|
|||
"""
|
||||
|
||||
if rcparams is None:
|
||||
rcparams = {'font.size': 11}
|
||||
rcparams = {"font.size": 11}
|
||||
|
||||
def decorator(func):
|
||||
"""Decorator to apply temporary rc parameters and backend to a function."""
|
||||
|
|
@ -208,14 +209,14 @@ def plt_settings(rcparams=None, backend='Agg'):
|
|||
"""Sets rc parameters and backend, calls the original function, and restores the settings."""
|
||||
original_backend = plt.get_backend()
|
||||
if backend != original_backend:
|
||||
plt.close('all') # auto-close()ing of figures upon backend switching is deprecated since 3.8
|
||||
plt.close("all") # auto-close()ing of figures upon backend switching is deprecated since 3.8
|
||||
plt.switch_backend(backend)
|
||||
|
||||
with plt.rc_context(rcparams):
|
||||
result = func(*args, **kwargs)
|
||||
|
||||
if backend != original_backend:
|
||||
plt.close('all')
|
||||
plt.close("all")
|
||||
plt.switch_backend(original_backend)
|
||||
return result
|
||||
|
||||
|
|
@ -229,26 +230,26 @@ def set_logging(name=LOGGING_NAME, verbose=True):
|
|||
level = logging.INFO if verbose and RANK in {-1, 0} else logging.ERROR # rank in world for Multi-GPU trainings
|
||||
|
||||
# Configure the console (stdout) encoding to UTF-8
|
||||
formatter = logging.Formatter('%(message)s') # Default formatter
|
||||
if WINDOWS and sys.stdout.encoding != 'utf-8':
|
||||
formatter = logging.Formatter("%(message)s") # Default formatter
|
||||
if WINDOWS and sys.stdout.encoding != "utf-8":
|
||||
try:
|
||||
if hasattr(sys.stdout, 'reconfigure'):
|
||||
sys.stdout.reconfigure(encoding='utf-8')
|
||||
elif hasattr(sys.stdout, 'buffer'):
|
||||
if hasattr(sys.stdout, "reconfigure"):
|
||||
sys.stdout.reconfigure(encoding="utf-8")
|
||||
elif hasattr(sys.stdout, "buffer"):
|
||||
import io
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
|
||||
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
|
||||
else:
|
||||
sys.stdout.encoding = 'utf-8'
|
||||
sys.stdout.encoding = "utf-8"
|
||||
except Exception as e:
|
||||
print(f'Creating custom formatter for non UTF-8 environments due to {e}')
|
||||
print(f"Creating custom formatter for non UTF-8 environments due to {e}")
|
||||
|
||||
class CustomFormatter(logging.Formatter):
|
||||
|
||||
def format(self, record):
|
||||
"""Sets up logging with UTF-8 encoding and configurable verbosity."""
|
||||
return emojis(super().format(record))
|
||||
|
||||
formatter = CustomFormatter('%(message)s') # Use CustomFormatter to eliminate UTF-8 output as last recourse
|
||||
formatter = CustomFormatter("%(message)s") # Use CustomFormatter to eliminate UTF-8 output as last recourse
|
||||
|
||||
# Create and configure the StreamHandler
|
||||
stream_handler = logging.StreamHandler(sys.stdout)
|
||||
|
|
@ -264,13 +265,13 @@ def set_logging(name=LOGGING_NAME, verbose=True):
|
|||
|
||||
# Set logger
|
||||
LOGGER = set_logging(LOGGING_NAME, verbose=VERBOSE) # define globally (used in train.py, val.py, predict.py, etc.)
|
||||
for logger in 'sentry_sdk', 'urllib3.connectionpool':
|
||||
for logger in "sentry_sdk", "urllib3.connectionpool":
|
||||
logging.getLogger(logger).setLevel(logging.CRITICAL + 1)
|
||||
|
||||
|
||||
def emojis(string=''):
|
||||
def emojis(string=""):
|
||||
"""Return platform-dependent emoji-safe version of string."""
|
||||
return string.encode().decode('ascii', 'ignore') if WINDOWS else string
|
||||
return string.encode().decode("ascii", "ignore") if WINDOWS else string
|
||||
|
||||
|
||||
class ThreadingLocked:
|
||||
|
|
@ -310,7 +311,7 @@ class ThreadingLocked:
|
|||
return decorated
|
||||
|
||||
|
||||
def yaml_save(file='data.yaml', data=None, header=''):
|
||||
def yaml_save(file="data.yaml", data=None, header=""):
|
||||
"""
|
||||
Save YAML data to a file.
|
||||
|
||||
|
|
@ -336,13 +337,13 @@ def yaml_save(file='data.yaml', data=None, header=''):
|
|||
data[k] = str(v)
|
||||
|
||||
# Dump data to file in YAML format
|
||||
with open(file, 'w', errors='ignore', encoding='utf-8') as f:
|
||||
with open(file, "w", errors="ignore", encoding="utf-8") as f:
|
||||
if header:
|
||||
f.write(header)
|
||||
yaml.safe_dump(data, f, sort_keys=False, allow_unicode=True)
|
||||
|
||||
|
||||
def yaml_load(file='data.yaml', append_filename=False):
|
||||
def yaml_load(file="data.yaml", append_filename=False):
|
||||
"""
|
||||
Load YAML data from a file.
|
||||
|
||||
|
|
@ -353,18 +354,18 @@ def yaml_load(file='data.yaml', append_filename=False):
|
|||
Returns:
|
||||
(dict): YAML data and file name.
|
||||
"""
|
||||
assert Path(file).suffix in ('.yaml', '.yml'), f'Attempting to load non-YAML file {file} with yaml_load()'
|
||||
with open(file, errors='ignore', encoding='utf-8') as f:
|
||||
assert Path(file).suffix in (".yaml", ".yml"), f"Attempting to load non-YAML file {file} with yaml_load()"
|
||||
with open(file, errors="ignore", encoding="utf-8") as f:
|
||||
s = f.read() # string
|
||||
|
||||
# Remove special characters
|
||||
if not s.isprintable():
|
||||
s = re.sub(r'[^\x09\x0A\x0D\x20-\x7E\x85\xA0-\uD7FF\uE000-\uFFFD\U00010000-\U0010ffff]+', '', s)
|
||||
s = re.sub(r"[^\x09\x0A\x0D\x20-\x7E\x85\xA0-\uD7FF\uE000-\uFFFD\U00010000-\U0010ffff]+", "", s)
|
||||
|
||||
# Add YAML filename to dict and return
|
||||
data = yaml.safe_load(s) or {} # always return a dict (yaml.safe_load() may return None for empty files)
|
||||
if append_filename:
|
||||
data['yaml_file'] = str(file)
|
||||
data["yaml_file"] = str(file)
|
||||
return data
|
||||
|
||||
|
||||
|
|
@ -386,7 +387,7 @@ def yaml_print(yaml_file: Union[str, Path, dict]) -> None:
|
|||
# Default configuration
|
||||
DEFAULT_CFG_DICT = yaml_load(DEFAULT_CFG_PATH)
|
||||
for k, v in DEFAULT_CFG_DICT.items():
|
||||
if isinstance(v, str) and v.lower() == 'none':
|
||||
if isinstance(v, str) and v.lower() == "none":
|
||||
DEFAULT_CFG_DICT[k] = None
|
||||
DEFAULT_CFG_KEYS = DEFAULT_CFG_DICT.keys()
|
||||
DEFAULT_CFG = IterableSimpleNamespace(**DEFAULT_CFG_DICT)
|
||||
|
|
@ -400,8 +401,8 @@ def is_ubuntu() -> bool:
|
|||
(bool): True if OS is Ubuntu, False otherwise.
|
||||
"""
|
||||
with contextlib.suppress(FileNotFoundError):
|
||||
with open('/etc/os-release') as f:
|
||||
return 'ID=ubuntu' in f.read()
|
||||
with open("/etc/os-release") as f:
|
||||
return "ID=ubuntu" in f.read()
|
||||
return False
|
||||
|
||||
|
||||
|
|
@ -412,7 +413,7 @@ def is_colab():
|
|||
Returns:
|
||||
(bool): True if running inside a Colab notebook, False otherwise.
|
||||
"""
|
||||
return 'COLAB_RELEASE_TAG' in os.environ or 'COLAB_BACKEND_VERSION' in os.environ
|
||||
return "COLAB_RELEASE_TAG" in os.environ or "COLAB_BACKEND_VERSION" in os.environ
|
||||
|
||||
|
||||
def is_kaggle():
|
||||
|
|
@ -422,7 +423,7 @@ def is_kaggle():
|
|||
Returns:
|
||||
(bool): True if running inside a Kaggle kernel, False otherwise.
|
||||
"""
|
||||
return os.environ.get('PWD') == '/kaggle/working' and os.environ.get('KAGGLE_URL_BASE') == 'https://www.kaggle.com'
|
||||
return os.environ.get("PWD") == "/kaggle/working" and os.environ.get("KAGGLE_URL_BASE") == "https://www.kaggle.com"
|
||||
|
||||
|
||||
def is_jupyter():
|
||||
|
|
@ -434,6 +435,7 @@ def is_jupyter():
|
|||
"""
|
||||
with contextlib.suppress(Exception):
|
||||
from IPython import get_ipython
|
||||
|
||||
return get_ipython() is not None
|
||||
return False
|
||||
|
||||
|
|
@ -445,10 +447,10 @@ def is_docker() -> bool:
|
|||
Returns:
|
||||
(bool): True if the script is running inside a Docker container, False otherwise.
|
||||
"""
|
||||
file = Path('/proc/self/cgroup')
|
||||
file = Path("/proc/self/cgroup")
|
||||
if file.exists():
|
||||
with open(file) as f:
|
||||
return 'docker' in f.read()
|
||||
return "docker" in f.read()
|
||||
else:
|
||||
return False
|
||||
|
||||
|
|
@ -462,7 +464,7 @@ def is_online() -> bool:
|
|||
"""
|
||||
import socket
|
||||
|
||||
for host in '1.1.1.1', '8.8.8.8', '223.5.5.5': # Cloudflare, Google, AliDNS:
|
||||
for host in "1.1.1.1", "8.8.8.8", "223.5.5.5": # Cloudflare, Google, AliDNS:
|
||||
try:
|
||||
test_connection = socket.create_connection(address=(host, 53), timeout=2)
|
||||
except (socket.timeout, socket.gaierror, OSError):
|
||||
|
|
@ -516,7 +518,7 @@ def is_pytest_running():
|
|||
Returns:
|
||||
(bool): True if pytest is running, False otherwise.
|
||||
"""
|
||||
return ('PYTEST_CURRENT_TEST' in os.environ) or ('pytest' in sys.modules) or ('pytest' in Path(sys.argv[0]).stem)
|
||||
return ("PYTEST_CURRENT_TEST" in os.environ) or ("pytest" in sys.modules) or ("pytest" in Path(sys.argv[0]).stem)
|
||||
|
||||
|
||||
def is_github_action_running() -> bool:
|
||||
|
|
@ -526,7 +528,7 @@ def is_github_action_running() -> bool:
|
|||
Returns:
|
||||
(bool): True if the current environment is a GitHub Actions runner, False otherwise.
|
||||
"""
|
||||
return 'GITHUB_ACTIONS' in os.environ and 'GITHUB_WORKFLOW' in os.environ and 'RUNNER_OS' in os.environ
|
||||
return "GITHUB_ACTIONS" in os.environ and "GITHUB_WORKFLOW" in os.environ and "RUNNER_OS" in os.environ
|
||||
|
||||
|
||||
def is_git_dir():
|
||||
|
|
@ -549,7 +551,7 @@ def get_git_dir():
|
|||
(Path | None): Git root directory if found or None if not found.
|
||||
"""
|
||||
for d in Path(__file__).parents:
|
||||
if (d / '.git').is_dir():
|
||||
if (d / ".git").is_dir():
|
||||
return d
|
||||
|
||||
|
||||
|
|
@ -562,7 +564,7 @@ def get_git_origin_url():
|
|||
"""
|
||||
if is_git_dir():
|
||||
with contextlib.suppress(subprocess.CalledProcessError):
|
||||
origin = subprocess.check_output(['git', 'config', '--get', 'remote.origin.url'])
|
||||
origin = subprocess.check_output(["git", "config", "--get", "remote.origin.url"])
|
||||
return origin.decode().strip()
|
||||
|
||||
|
||||
|
|
@ -575,7 +577,7 @@ def get_git_branch():
|
|||
"""
|
||||
if is_git_dir():
|
||||
with contextlib.suppress(subprocess.CalledProcessError):
|
||||
origin = subprocess.check_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD'])
|
||||
origin = subprocess.check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"])
|
||||
return origin.decode().strip()
|
||||
|
||||
|
||||
|
|
@ -602,11 +604,11 @@ def get_ubuntu_version():
|
|||
"""
|
||||
if is_ubuntu():
|
||||
with contextlib.suppress(FileNotFoundError, AttributeError):
|
||||
with open('/etc/os-release') as f:
|
||||
with open("/etc/os-release") as f:
|
||||
return re.search(r'VERSION_ID="(\d+\.\d+)"', f.read())[1]
|
||||
|
||||
|
||||
def get_user_config_dir(sub_dir='Ultralytics'):
|
||||
def get_user_config_dir(sub_dir="Ultralytics"):
|
||||
"""
|
||||
Get the user config directory.
|
||||
|
||||
|
|
@ -618,19 +620,21 @@ def get_user_config_dir(sub_dir='Ultralytics'):
|
|||
"""
|
||||
# Return the appropriate config directory for each operating system
|
||||
if WINDOWS:
|
||||
path = Path.home() / 'AppData' / 'Roaming' / sub_dir
|
||||
path = Path.home() / "AppData" / "Roaming" / sub_dir
|
||||
elif MACOS: # macOS
|
||||
path = Path.home() / 'Library' / 'Application Support' / sub_dir
|
||||
path = Path.home() / "Library" / "Application Support" / sub_dir
|
||||
elif LINUX:
|
||||
path = Path.home() / '.config' / sub_dir
|
||||
path = Path.home() / ".config" / sub_dir
|
||||
else:
|
||||
raise ValueError(f'Unsupported operating system: {platform.system()}')
|
||||
raise ValueError(f"Unsupported operating system: {platform.system()}")
|
||||
|
||||
# GCP and AWS lambda fix, only /tmp is writeable
|
||||
if not is_dir_writeable(path.parent):
|
||||
LOGGER.warning(f"WARNING ⚠️ user config directory '{path}' is not writeable, defaulting to '/tmp' or CWD."
|
||||
'Alternatively you can define a YOLO_CONFIG_DIR environment variable for this path.')
|
||||
path = Path('/tmp') / sub_dir if is_dir_writeable('/tmp') else Path().cwd() / sub_dir
|
||||
LOGGER.warning(
|
||||
f"WARNING ⚠️ user config directory '{path}' is not writeable, defaulting to '/tmp' or CWD."
|
||||
"Alternatively you can define a YOLO_CONFIG_DIR environment variable for this path."
|
||||
)
|
||||
path = Path("/tmp") / sub_dir if is_dir_writeable("/tmp") else Path().cwd() / sub_dir
|
||||
|
||||
# Create the subdirectory if it does not exist
|
||||
path.mkdir(parents=True, exist_ok=True)
|
||||
|
|
@ -638,8 +642,8 @@ def get_user_config_dir(sub_dir='Ultralytics'):
|
|||
return path
|
||||
|
||||
|
||||
USER_CONFIG_DIR = Path(os.getenv('YOLO_CONFIG_DIR') or get_user_config_dir()) # Ultralytics settings dir
|
||||
SETTINGS_YAML = USER_CONFIG_DIR / 'settings.yaml'
|
||||
USER_CONFIG_DIR = Path(os.getenv("YOLO_CONFIG_DIR") or get_user_config_dir()) # Ultralytics settings dir
|
||||
SETTINGS_YAML = USER_CONFIG_DIR / "settings.yaml"
|
||||
|
||||
|
||||
def colorstr(*input):
|
||||
|
|
@ -670,28 +674,29 @@ def colorstr(*input):
|
|||
>>> colorstr('blue', 'bold', 'hello world')
|
||||
>>> '\033[34m\033[1mhello world\033[0m'
|
||||
"""
|
||||
*args, string = input if len(input) > 1 else ('blue', 'bold', input[0]) # color arguments, string
|
||||
*args, string = input if len(input) > 1 else ("blue", "bold", input[0]) # color arguments, string
|
||||
colors = {
|
||||
'black': '\033[30m', # basic colors
|
||||
'red': '\033[31m',
|
||||
'green': '\033[32m',
|
||||
'yellow': '\033[33m',
|
||||
'blue': '\033[34m',
|
||||
'magenta': '\033[35m',
|
||||
'cyan': '\033[36m',
|
||||
'white': '\033[37m',
|
||||
'bright_black': '\033[90m', # bright colors
|
||||
'bright_red': '\033[91m',
|
||||
'bright_green': '\033[92m',
|
||||
'bright_yellow': '\033[93m',
|
||||
'bright_blue': '\033[94m',
|
||||
'bright_magenta': '\033[95m',
|
||||
'bright_cyan': '\033[96m',
|
||||
'bright_white': '\033[97m',
|
||||
'end': '\033[0m', # misc
|
||||
'bold': '\033[1m',
|
||||
'underline': '\033[4m'}
|
||||
return ''.join(colors[x] for x in args) + f'{string}' + colors['end']
|
||||
"black": "\033[30m", # basic colors
|
||||
"red": "\033[31m",
|
||||
"green": "\033[32m",
|
||||
"yellow": "\033[33m",
|
||||
"blue": "\033[34m",
|
||||
"magenta": "\033[35m",
|
||||
"cyan": "\033[36m",
|
||||
"white": "\033[37m",
|
||||
"bright_black": "\033[90m", # bright colors
|
||||
"bright_red": "\033[91m",
|
||||
"bright_green": "\033[92m",
|
||||
"bright_yellow": "\033[93m",
|
||||
"bright_blue": "\033[94m",
|
||||
"bright_magenta": "\033[95m",
|
||||
"bright_cyan": "\033[96m",
|
||||
"bright_white": "\033[97m",
|
||||
"end": "\033[0m", # misc
|
||||
"bold": "\033[1m",
|
||||
"underline": "\033[4m",
|
||||
}
|
||||
return "".join(colors[x] for x in args) + f"{string}" + colors["end"]
|
||||
|
||||
|
||||
def remove_colorstr(input_string):
|
||||
|
|
@ -708,8 +713,8 @@ def remove_colorstr(input_string):
|
|||
>>> remove_colorstr(colorstr('blue', 'bold', 'hello world'))
|
||||
>>> 'hello world'
|
||||
"""
|
||||
ansi_escape = re.compile(r'\x1B\[[0-9;]*[A-Za-z]')
|
||||
return ansi_escape.sub('', input_string)
|
||||
ansi_escape = re.compile(r"\x1B\[[0-9;]*[A-Za-z]")
|
||||
return ansi_escape.sub("", input_string)
|
||||
|
||||
|
||||
class TryExcept(contextlib.ContextDecorator):
|
||||
|
|
@ -719,7 +724,7 @@ class TryExcept(contextlib.ContextDecorator):
|
|||
Use as @TryExcept() decorator or 'with TryExcept():' context manager.
|
||||
"""
|
||||
|
||||
def __init__(self, msg='', verbose=True):
|
||||
def __init__(self, msg="", verbose=True):
|
||||
"""Initialize TryExcept class with optional message and verbosity settings."""
|
||||
self.msg = msg
|
||||
self.verbose = verbose
|
||||
|
|
@ -744,7 +749,7 @@ def threaded(func):
|
|||
|
||||
def wrapper(*args, **kwargs):
|
||||
"""Multi-threads a given function based on 'threaded' kwarg and returns the thread or function result."""
|
||||
if kwargs.pop('threaded', True): # run in thread
|
||||
if kwargs.pop("threaded", True): # run in thread
|
||||
thread = threading.Thread(target=func, args=args, kwargs=kwargs, daemon=True)
|
||||
thread.start()
|
||||
return thread
|
||||
|
|
@ -786,27 +791,28 @@ def set_sentry():
|
|||
Returns:
|
||||
dict: The modified event or None if the event should not be sent to Sentry.
|
||||
"""
|
||||
if 'exc_info' in hint:
|
||||
exc_type, exc_value, tb = hint['exc_info']
|
||||
if exc_type in (KeyboardInterrupt, FileNotFoundError) \
|
||||
or 'out of memory' in str(exc_value):
|
||||
if "exc_info" in hint:
|
||||
exc_type, exc_value, tb = hint["exc_info"]
|
||||
if exc_type in (KeyboardInterrupt, FileNotFoundError) or "out of memory" in str(exc_value):
|
||||
return None # do not send event
|
||||
|
||||
event['tags'] = {
|
||||
'sys_argv': sys.argv[0],
|
||||
'sys_argv_name': Path(sys.argv[0]).name,
|
||||
'install': 'git' if is_git_dir() else 'pip' if is_pip_package() else 'other',
|
||||
'os': ENVIRONMENT}
|
||||
event["tags"] = {
|
||||
"sys_argv": sys.argv[0],
|
||||
"sys_argv_name": Path(sys.argv[0]).name,
|
||||
"install": "git" if is_git_dir() else "pip" if is_pip_package() else "other",
|
||||
"os": ENVIRONMENT,
|
||||
}
|
||||
return event
|
||||
|
||||
if SETTINGS['sync'] and \
|
||||
RANK in (-1, 0) and \
|
||||
Path(sys.argv[0]).name == 'yolo' and \
|
||||
not TESTS_RUNNING and \
|
||||
ONLINE and \
|
||||
is_pip_package() and \
|
||||
not is_git_dir():
|
||||
|
||||
if (
|
||||
SETTINGS["sync"]
|
||||
and RANK in (-1, 0)
|
||||
and Path(sys.argv[0]).name == "yolo"
|
||||
and not TESTS_RUNNING
|
||||
and ONLINE
|
||||
and is_pip_package()
|
||||
and not is_git_dir()
|
||||
):
|
||||
# If sentry_sdk package is not installed then return and do not use Sentry
|
||||
try:
|
||||
import sentry_sdk # noqa
|
||||
|
|
@ -814,14 +820,15 @@ def set_sentry():
|
|||
return
|
||||
|
||||
sentry_sdk.init(
|
||||
dsn='https://5ff1556b71594bfea135ff0203a0d290@o4504521589325824.ingest.sentry.io/4504521592406016',
|
||||
dsn="https://5ff1556b71594bfea135ff0203a0d290@o4504521589325824.ingest.sentry.io/4504521592406016",
|
||||
debug=False,
|
||||
traces_sample_rate=1.0,
|
||||
release=__version__,
|
||||
environment='production', # 'dev' or 'production'
|
||||
environment="production", # 'dev' or 'production'
|
||||
before_send=before_send,
|
||||
ignore_errors=[KeyboardInterrupt, FileNotFoundError])
|
||||
sentry_sdk.set_user({'id': SETTINGS['uuid']}) # SHA-256 anonymized UUID hash
|
||||
ignore_errors=[KeyboardInterrupt, FileNotFoundError],
|
||||
)
|
||||
sentry_sdk.set_user({"id": SETTINGS["uuid"]}) # SHA-256 anonymized UUID hash
|
||||
|
||||
|
||||
class SettingsManager(dict):
|
||||
|
|
@ -833,7 +840,7 @@ class SettingsManager(dict):
|
|||
version (str): Settings version. In case of local version mismatch, new default settings will be saved.
|
||||
"""
|
||||
|
||||
def __init__(self, file=SETTINGS_YAML, version='0.0.4'):
|
||||
def __init__(self, file=SETTINGS_YAML, version="0.0.4"):
|
||||
"""Initialize the SettingsManager with default settings, load and validate current settings from the YAML
|
||||
file.
|
||||
"""
|
||||
|
|
@ -850,23 +857,24 @@ class SettingsManager(dict):
|
|||
self.file = Path(file)
|
||||
self.version = version
|
||||
self.defaults = {
|
||||
'settings_version': version,
|
||||
'datasets_dir': str(datasets_root / 'datasets'),
|
||||
'weights_dir': str(root / 'weights'),
|
||||
'runs_dir': str(root / 'runs'),
|
||||
'uuid': hashlib.sha256(str(uuid.getnode()).encode()).hexdigest(),
|
||||
'sync': True,
|
||||
'api_key': '',
|
||||
'openai_api_key': '',
|
||||
'clearml': True, # integrations
|
||||
'comet': True,
|
||||
'dvc': True,
|
||||
'hub': True,
|
||||
'mlflow': True,
|
||||
'neptune': True,
|
||||
'raytune': True,
|
||||
'tensorboard': True,
|
||||
'wandb': True}
|
||||
"settings_version": version,
|
||||
"datasets_dir": str(datasets_root / "datasets"),
|
||||
"weights_dir": str(root / "weights"),
|
||||
"runs_dir": str(root / "runs"),
|
||||
"uuid": hashlib.sha256(str(uuid.getnode()).encode()).hexdigest(),
|
||||
"sync": True,
|
||||
"api_key": "",
|
||||
"openai_api_key": "",
|
||||
"clearml": True, # integrations
|
||||
"comet": True,
|
||||
"dvc": True,
|
||||
"hub": True,
|
||||
"mlflow": True,
|
||||
"neptune": True,
|
||||
"raytune": True,
|
||||
"tensorboard": True,
|
||||
"wandb": True,
|
||||
}
|
||||
|
||||
super().__init__(copy.deepcopy(self.defaults))
|
||||
|
||||
|
|
@ -877,13 +885,14 @@ class SettingsManager(dict):
|
|||
self.load()
|
||||
correct_keys = self.keys() == self.defaults.keys()
|
||||
correct_types = all(type(a) is type(b) for a, b in zip(self.values(), self.defaults.values()))
|
||||
correct_version = check_version(self['settings_version'], self.version)
|
||||
correct_version = check_version(self["settings_version"], self.version)
|
||||
if not (correct_keys and correct_types and correct_version):
|
||||
LOGGER.warning(
|
||||
'WARNING ⚠️ Ultralytics settings reset to default values. This may be due to a possible problem '
|
||||
'with your settings or a recent ultralytics package update. '
|
||||
"WARNING ⚠️ Ultralytics settings reset to default values. This may be due to a possible problem "
|
||||
"with your settings or a recent ultralytics package update. "
|
||||
f"\nView settings with 'yolo settings' or at '{self.file}'"
|
||||
"\nUpdate settings with 'yolo settings key=value', i.e. 'yolo settings runs_dir=path/to/dir'.")
|
||||
"\nUpdate settings with 'yolo settings key=value', i.e. 'yolo settings runs_dir=path/to/dir'."
|
||||
)
|
||||
self.reset()
|
||||
|
||||
def load(self):
|
||||
|
|
@ -910,14 +919,16 @@ def deprecation_warn(arg, new_arg, version=None):
|
|||
"""Issue a deprecation warning when a deprecated argument is used, suggesting an updated argument."""
|
||||
if not version:
|
||||
version = float(__version__[:3]) + 0.2 # deprecate after 2nd major release
|
||||
LOGGER.warning(f"WARNING ⚠️ '{arg}' is deprecated and will be removed in 'ultralytics {version}' in the future. "
|
||||
f"Please use '{new_arg}' instead.")
|
||||
LOGGER.warning(
|
||||
f"WARNING ⚠️ '{arg}' is deprecated and will be removed in 'ultralytics {version}' in the future. "
|
||||
f"Please use '{new_arg}' instead."
|
||||
)
|
||||
|
||||
|
||||
def clean_url(url):
|
||||
"""Strip auth from URL, i.e. https://url.com/file.txt?auth -> https://url.com/file.txt."""
|
||||
url = Path(url).as_posix().replace(':/', '://') # Pathlib turns :// -> :/, as_posix() for Windows
|
||||
return urllib.parse.unquote(url).split('?')[0] # '%2F' to '/', split https://url.com/file.txt?auth
|
||||
url = Path(url).as_posix().replace(":/", "://") # Pathlib turns :// -> :/, as_posix() for Windows
|
||||
return urllib.parse.unquote(url).split("?")[0] # '%2F' to '/', split https://url.com/file.txt?auth
|
||||
|
||||
|
||||
def url2file(url):
|
||||
|
|
@ -928,13 +939,22 @@ def url2file(url):
|
|||
# Run below code on utils init ------------------------------------------------------------------------------------
|
||||
|
||||
# Check first-install steps
|
||||
PREFIX = colorstr('Ultralytics: ')
|
||||
PREFIX = colorstr("Ultralytics: ")
|
||||
SETTINGS = SettingsManager() # initialize settings
|
||||
DATASETS_DIR = Path(SETTINGS['datasets_dir']) # global datasets directory
|
||||
WEIGHTS_DIR = Path(SETTINGS['weights_dir']) # global weights directory
|
||||
RUNS_DIR = Path(SETTINGS['runs_dir']) # global runs directory
|
||||
ENVIRONMENT = 'Colab' if is_colab() else 'Kaggle' if is_kaggle() else 'Jupyter' if is_jupyter() else \
|
||||
'Docker' if is_docker() else platform.system()
|
||||
DATASETS_DIR = Path(SETTINGS["datasets_dir"]) # global datasets directory
|
||||
WEIGHTS_DIR = Path(SETTINGS["weights_dir"]) # global weights directory
|
||||
RUNS_DIR = Path(SETTINGS["runs_dir"]) # global runs directory
|
||||
ENVIRONMENT = (
|
||||
"Colab"
|
||||
if is_colab()
|
||||
else "Kaggle"
|
||||
if is_kaggle()
|
||||
else "Jupyter"
|
||||
if is_jupyter()
|
||||
else "Docker"
|
||||
if is_docker()
|
||||
else platform.system()
|
||||
)
|
||||
TESTS_RUNNING = is_pytest_running() or is_github_action_running()
|
||||
set_sentry()
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue