diff --git a/docs/build_docs.py b/docs/build_docs.py
index 47d328a3..4d7f9393 100644
--- a/docs/build_docs.py
+++ b/docs/build_docs.py
@@ -30,6 +30,7 @@ import shutil
import subprocess
from pathlib import Path
+from bs4 import BeautifulSoup
from tqdm import tqdm
os.environ["JUPYTER_PLATFORM_DIRS"] = "1" # fix DeprecationWarning: Jupyter is migrating to use standard platformdirs
@@ -96,8 +97,6 @@ def update_html_head(script=""):
def update_subdir_edit_links(subdir="", docs_url=""):
"""Update the HTML head section of each file."""
- from bs4 import BeautifulSoup
-
if str(subdir[0]) == "/":
subdir = str(subdir[0])[1:]
html_files = (SITE / subdir).rglob("*.html")
@@ -153,7 +152,7 @@ def update_markdown_files(md_filepath: Path):
def update_docs_html():
- """Updates titles, edit links and head sections of HTML documentation for improved accessibility and relevance."""
+ """Updates titles, edit links, head sections, and converts plaintext links in HTML documentation."""
update_page_title(SITE / "404.html", new_title="Ultralytics Docs - Not Found")
# Update edit links
@@ -162,12 +161,46 @@ def update_docs_html():
docs_url="https://github.com/ultralytics/hub-sdk/tree/main/docs/",
)
+ # Convert plaintext links to HTML hyperlinks
+ files_modified = 0
+ for html_file in tqdm(SITE.rglob("*.html"), desc="Converting plaintext links"):
+ with open(html_file, "r", encoding="utf-8") as file:
+ content = file.read()
+ updated_content = convert_plaintext_links_to_html(content)
+ if updated_content != content:
+ with open(html_file, "w", encoding="utf-8") as file:
+ file.write(updated_content)
+ files_modified += 1
+ print(f"Modified plaintext links in {files_modified} files.")
+
# Update HTML file head section
script = ""
if any(script):
update_html_head(script)
+def convert_plaintext_links_to_html(content):
+ """Convert plaintext links to HTML hyperlinks in the main content area only."""
+ soup = BeautifulSoup(content, "html.parser")
+
+ # Find the main content area (adjust this selector based on your HTML structure)
+ main_content = soup.find("main") or soup.find("div", class_="md-content")
+ if not main_content:
+ return content # Return original content if main content area not found
+
+ modified = False
+ for paragraph in main_content.find_all(["p", "li"]): # Focus on paragraphs and list items
+ for text_node in paragraph.find_all(string=True, recursive=False):
+ if text_node.parent.name not in {"a", "code"}: # Ignore links and code blocks
+ new_text = re.sub(r"(https?://\S+)", r'\1', str(text_node)) # note: reject http?
+ if ">> config_dict = cfg2dict('config.yaml')
- # Example usage with a file path
- config_dict = cfg2dict('config.yaml')
+ Convert a SimpleNamespace to a dictionary:
+ >>> from types import SimpleNamespace
+ >>> config_sn = SimpleNamespace(param1='value1', param2='value2')
+ >>> config_dict = cfg2dict(config_sn)
- # Example usage with a SimpleNamespace
- config_sn = SimpleNamespace(param1='value1', param2='value2')
- config_dict = cfg2dict(config_sn)
-
- # Example usage with a dictionary (returns the same dictionary)
- config_dict = cfg2dict({'param1': 'value1', 'param2': 'value2'})
- ```
+ Pass through an already existing dictionary:
+ >>> config_dict = cfg2dict({'param1': 'value1', 'param2': 'value2'})
Notes:
- If `cfg` is a path or a string, it will be loaded as YAML and converted to a dictionary.
@@ -228,9 +224,8 @@ def get_cfg(cfg: Union[str, Path, Dict, SimpleNamespace] = DEFAULT_CFG_DICT, ove
Load and merge configuration data from a file or dictionary, with optional overrides.
Args:
- cfg (str | Path | dict | SimpleNamespace, optional): Configuration data source. Defaults to `DEFAULT_CFG_DICT`.
- overrides (dict | None, optional): Dictionary containing key-value pairs to override the base configuration.
- Defaults to None.
+ cfg (str | Path | Dict | SimpleNamespace): Configuration data source.
+ overrides (Dict | None): Dictionary containing key-value pairs to override the base configuration.
Returns:
(SimpleNamespace): Namespace containing the merged training arguments.
@@ -238,23 +233,15 @@ def get_cfg(cfg: Union[str, Path, Dict, SimpleNamespace] = DEFAULT_CFG_DICT, ove
Notes:
- If both `cfg` and `overrides` are provided, the values in `overrides` will take precedence.
- Special handling ensures alignment and correctness of the configuration, such as converting numeric `project`
- and `name` to strings and validating the configuration keys and values.
+ and `name` to strings and validating configuration keys and values.
- Example:
- ```python
- from ultralytics.cfg import get_cfg
+ Examples:
+ Load default configuration:
+ >>> from ultralytics import get_cfg
+ >>> config = get_cfg()
- # Load default configuration
- config = get_cfg()
-
- # Load from a custom file with overrides
- config = get_cfg('path/to/config.yaml', overrides={'epochs': 50, 'batch_size': 16})
- ```
-
- Configuration dictionary merged with overrides:
- ```python
- {'epochs': 50, 'batch_size': 16, ...}
- ```
+ Load from a custom file with overrides:
+ >>> config = get_cfg('path/to/config.yaml', overrides={'epochs': 50, 'batch_size': 16})
"""
cfg = cfg2dict(cfg)
@@ -282,7 +269,26 @@ def get_cfg(cfg: Union[str, Path, Dict, SimpleNamespace] = DEFAULT_CFG_DICT, ove
def check_cfg(cfg, hard=True):
- """Validate Ultralytics configuration argument types and values, converting them if necessary."""
+ """
+ Checks configuration argument types and values for the Ultralytics library, ensuring correctness and converting them
+ if necessary.
+
+ Args:
+ cfg (Dict): Configuration dictionary to validate.
+ hard (bool): If True, raises exceptions for invalid types and values; if False, attempts to convert them.
+
+ Examples:
+ Validate a configuration with a mix of valid and invalid values:
+ >>> config = {
+ ... 'epochs': 50, # valid integer
+ ... 'lr0': 0.01, # valid float
+ ... 'momentum': 1.2, # invalid float (out of 0.0-1.0 range)
+ ... 'save': 'true', # invalid bool
+ ... }
+ >>> check_cfg(config, hard=False)
+ >>> print(config)
+ {'epochs': 50, 'lr0': 0.01, 'momentum': 1.2, 'save': False} # corrected 'save' key and retained other values
+ """
for k, v in cfg.items():
if v is not None: # None values may be from optional args
if k in CFG_FLOAT_KEYS and not isinstance(v, (int, float)):
@@ -318,7 +324,26 @@ def check_cfg(cfg, hard=True):
def get_save_dir(args, name=None):
- """Returns the directory path for saving outputs, derived from arguments or default settings."""
+ """
+ Returns the directory path for saving outputs, derived from arguments or default settings.
+
+ Args:
+ args (SimpleNamespace): Namespace object containing configurations such as 'project', 'name', 'task', 'mode', and
+ 'save_dir'.
+ name (str | None): Optional name for the output directory. If not provided, it defaults to 'args.name' or the
+ 'args.mode'.
+
+ Returns:
+ (Path): Directory path where outputs should be saved.
+
+ Examples:
+ Generate a save directory using provided arguments
+ >>> from types import SimpleNamespace
+ >>> args = SimpleNamespace(project='my_project', task='detect', mode='train', exist_ok=True)
+ >>> save_dir = get_save_dir(args)
+ >>> print(save_dir)
+ my_project/detect/train
+ """
if getattr(args, "save_dir", None):
save_dir = args.save_dir
@@ -333,7 +358,18 @@ def get_save_dir(args, name=None):
def _handle_deprecation(custom):
- """Handles deprecated configuration keys by mapping them to current equivalents with deprecation warnings."""
+ """
+ Handles deprecated configuration keys by mapping them to current equivalents with deprecation warnings.
+
+ Args:
+ custom (Dict): Configuration dictionary potentially containing deprecated keys.
+
+ Examples:
+ >>> custom_config = {"boxes": True, "hide_labels": "False", "line_thickness": 2}
+ >>> _handle_deprecation(custom_config)
+ >>> print(custom_config)
+ {'show_boxes': True, 'show_labels': True, 'line_width': 2}
+ """
for key in custom.copy().keys():
if key == "boxes":
@@ -354,35 +390,32 @@ def _handle_deprecation(custom):
def check_dict_alignment(base: Dict, custom: Dict, e=None):
"""
- Check for key alignment between custom and base configuration dictionaries, catering for deprecated keys and
- providing informative error messages for mismatched keys.
+ Check for key alignment between custom and base configuration dictionaries, handling deprecated keys and providing
+ informative error messages for mismatched keys.
Args:
- base (dict): The base configuration dictionary containing valid keys.
- custom (dict): The custom configuration dictionary to be checked for alignment.
- e (Exception, optional): An optional error instance passed by the calling function. Default is None.
+ base (Dict): The base configuration dictionary containing valid keys.
+ custom (Dict): The custom configuration dictionary to be checked for alignment.
+ e (Exception | None): Optional error instance passed by the calling function. Default is None.
Raises:
SystemExit: Terminates the program execution if mismatched keys are found.
Notes:
- - The function provides suggestions for mismatched keys based on their similarity to valid keys in the
- base configuration.
- - Deprecated keys in the custom configuration are automatically handled and replaced with their updated
- equivalents.
- - A detailed error message is printed for each mismatched key, helping users to quickly identify and correct
- their custom configurations.
+ - The function suggests corrections for mismatched keys based on similarity to valid keys.
+ - Deprecated keys in the custom configuration are automatically replaced with their updated equivalents.
+ - Detailed error messages are printed for each mismatched key to help users identify and correct their custom
+ configurations.
- Example:
- ```python
- base_cfg = {'epochs': 50, 'lr0': 0.01, 'batch_size': 16}
- custom_cfg = {'epoch': 100, 'lr': 0.02, 'batch_size': 32}
+ Examples:
+ >>> base_cfg = {'epochs': 50, 'lr0': 0.01, 'batch_size': 16}
+ >>> custom_cfg = {'epoch': 100, 'lr': 0.02, 'batch_size': 32}
- try:
- check_dict_alignment(base_cfg, custom_cfg)
- except SystemExit:
- # Handle the error or correct the configuration
- ```
+ >>> try:
+ ... check_dict_alignment(base_cfg, custom_cfg)
+ ... except SystemExit:
+ ... # Handle the error or correct the configuration
+ ... pass
"""
custom = _handle_deprecation(custom)
base_keys, custom_keys = (set(x.keys()) for x in (base, custom))
@@ -401,30 +434,29 @@ def check_dict_alignment(base: Dict, custom: Dict, e=None):
def merge_equals_args(args: List[str]) -> List[str]:
"""
- Merges arguments around isolated '=' args in a list of strings. The function considers cases where the first
- argument ends with '=' or the second starts with '=', as well as when the middle one is an equals sign.
+ Merges arguments around isolated '=' in a list of strings.
Args:
- args (List[str]): A list of strings where each element is an argument.
+ args (List[str]): A list of strings where each element represents an argument.
Returns:
(List[str]): A list of strings where the arguments around isolated '=' are merged.
- Example:
- The function modifies the argument list as follows:
- ```python
- args = ["arg1", "=", "value"]
- new_args = merge_equals_args(args)
- print(new_args) # Output: ["arg1=value"]
+ Examples:
+ Merge arguments where equals sign is separated:
+ >>> args = ["arg1", "=", "value"]
+ >>> merge_equals_args(args)
+ ["arg1=value"]
- args = ["arg1=", "value"]
- new_args = merge_equals_args(args)
- print(new_args) # Output: ["arg1=value"]
+ Merge arguments where equals sign is at the end of the first argument:
+ >>> args = ["arg1=", "value"]
+ >>> merge_equals_args(args)
+ ["arg1=value"]
- args = ["arg1", "=value"]
- new_args = merge_equals_args(args)
- print(new_args) # Output: ["arg1=value"]
- ```
+ Merge arguments where equals sign is at the beginning of the second argument:
+ >>> args = ["arg1", "=value"]
+ >>> merge_equals_args(args)
+ ["arg1=value"]
"""
new_args = []
for i, arg in enumerate(args):
@@ -445,16 +477,13 @@ def handle_yolo_hub(args: List[str]) -> None:
"""
Handle Ultralytics HUB command-line interface (CLI) commands.
- This function processes Ultralytics HUB CLI commands such as login and logout. It should be called when executing
- a script with arguments related to HUB authentication.
+ This function processes Ultralytics HUB CLI commands such as login and logout. It should be called when executing a
+ script with arguments related to HUB authentication.
Args:
args (List[str]): A list of command line arguments.
- Returns:
- None
-
- Example:
+ Examples:
```bash
yolo hub login YOUR_API_KEY
```
@@ -480,13 +509,9 @@ def handle_yolo_settings(args: List[str]) -> None:
Args:
args (List[str]): A list of command line arguments for YOLO settings management.
- Returns:
- None
-
- Example:
- ```bash
- yolo settings reset
- ```
+ Examples:
+ Reset YOLO settings:
+ >>> yolo settings reset
Notes:
For more information on handling YOLO settings, visit:
@@ -511,21 +536,58 @@ def handle_yolo_settings(args: List[str]) -> None:
def handle_explorer():
- """Open the Ultralytics Explorer GUI for dataset exploration and analysis."""
+ """
+ Open the Ultralytics Explorer GUI for dataset exploration and analysis.
+
+ This function launches a graphical user interface that provides tools for interacting with and analyzing datasets
+ using the Ultralytics Explorer API.
+
+ Examples:
+ Start the Ultralytics Explorer:
+ >>> handle_explorer()
+ """
checks.check_requirements("streamlit>=1.29.0")
LOGGER.info("💡 Loading Explorer dashboard...")
subprocess.run(["streamlit", "run", ROOT / "data/explorer/gui/dash.py", "--server.maxMessageSize", "2048"])
def handle_streamlit_inference():
- """Open the Ultralytics Live Inference streamlit app for real time object detection."""
+ """
+ Open the Ultralytics Live Inference streamlit app for real-time object detection.
+
+ This function initializes and runs a Streamlit application designed for performing live object detection using
+ Ultralytics models.
+
+ References:
+ - Streamlit documentation: https://docs.streamlit.io/
+ - Ultralytics: https://docs.ultralytics.com
+
+ Examples:
+ To run the live inference Streamlit app, execute:
+ >>> handle_streamlit_inference()
+ """
checks.check_requirements("streamlit>=1.29.0")
LOGGER.info("💡 Loading Ultralytics Live Inference app...")
subprocess.run(["streamlit", "run", ROOT / "solutions/streamlit_inference.py", "--server.headless", "true"])
def parse_key_value_pair(pair):
- """Parse one 'key=value' pair and return key and value."""
+ """
+ Parse a 'key=value' pair and return the key and value.
+
+ Args:
+ pair (str): The 'key=value' string to be parsed.
+
+ Returns:
+ (tuple[str, str]): A tuple containing the key and value as separate strings.
+
+ Examples:
+ >>> key, value = parse_key_value_pair("model=yolov8n.pt")
+ >>> key
+ 'model'
+ >>> value
+ 'yolov8n.pt
+ """
k, v = pair.split("=", 1) # split on first '=' sign
k, v = k.strip(), v.strip() # remove spaces
assert v, f"missing '{k}' value"
@@ -533,7 +595,29 @@ def parse_key_value_pair(pair):
def smart_value(v):
- """Convert a string to its appropriate type (int, float, bool, None, etc.)."""
+ """
+ Convert a string representation of a value into its appropriate Python type (int, float, bool, None, etc.).
+
+ Args:
+ v (str): String representation of the value to be converted.
+
+ Returns:
+ (Any): The converted value, which can be of type int, float, bool, None, or the original string if no conversion
+ is applicable.
+
+ Examples:
+ Convert a string to various types:
+ >>> smart_value("42")
+ 42
+ >>> smart_value("3.14")
+ 3.14
+ >>> smart_value("True")
+ True
+ >>> smart_value("None")
+ None
+ >>> smart_value("some_string")
+ 'some_string'
+ """
v_lower = v.lower()
if v_lower == "none":
return None
@@ -551,31 +635,26 @@ def entrypoint(debug=""):
"""
Ultralytics entrypoint function for parsing and executing command-line arguments.
- This function serves as the main entry point for the Ultralytics CLI, parsing command-line arguments and
+ This function serves as the main entry point for the Ultralytics CLI, parsing command-line arguments and
executing the corresponding tasks such as training, validation, prediction, exporting models, and more.
Args:
- debug (str, optional): Space-separated string of command-line arguments for debugging purposes. Default is "".
+ debug (str, optional): Space-separated string of command-line arguments for debugging purposes.
- Returns:
- (None): This function does not return any value.
+ Examples:
+ Train a detection model for 10 epochs with an initial learning_rate of 0.01:
+ >>> entrypoint("train data=coco8.yaml model=yolov8n.pt epochs=10 lr0=0.01")
+
+ Predict a YouTube video using a pretrained segmentation model at image size 320:
+ >>> entrypoint("predict model=yolov8n-seg.pt source='https://youtu.be/LNwODJXcvt4' imgsz=320")
+
+ Validate a pretrained detection model at batch-size 1 and image size 640:
+ >>> entrypoint("val model=yolov8n.pt data=coco8.yaml batch=1 imgsz=640")
Notes:
- For a list of all available commands and their arguments, see the provided help messages and the Ultralytics
documentation at https://docs.ultralytics.com.
- If no arguments are passed, the function will display the usage help message.
-
- Example:
- ```python
- # Train a detection model for 10 epochs with an initial learning_rate of 0.01
- entrypoint("train data=coco8.yaml model=yolov8n.pt epochs=10 lr0=0.01")
-
- # Predict a YouTube video using a pretrained segmentation model at image size 320
- entrypoint("predict model=yolov8n-seg.pt source='https://youtu.be/LNwODJXcvt4' imgsz=320")
-
- # Validate a pretrained detection model at batch-size 1 and image size 640
- entrypoint("val model=yolov8n.pt data=coco8.yaml batch=1 imgsz=640")
- ```
"""
args = (debug.split(" ") if debug else ARGV)[1:]
if not args: # no arguments passed
@@ -713,7 +792,18 @@ def entrypoint(debug=""):
# Special modes --------------------------------------------------------------------------------------------------------
def copy_default_cfg():
- """Copy and create a new default configuration file with '_copy' appended to its name, providing usage example."""
+ """
+ Copy and create a new default configuration file with '_copy' appended to its name, providing a usage example.
+
+ This function duplicates the existing default configuration file and appends '_copy' to its name in the current
+ working directory.
+
+ Examples:
+ Copy the default configuration file and use it in a YOLO command:
+ >>> copy_default_cfg()
+ >>> # Example YOLO command with this new custom cfg:
+ >>> # yolo cfg='default_copy.yaml' imgsz=320 batch=8
+ """
new_file = Path.cwd() / DEFAULT_CFG_PATH.name.replace(".yaml", "_copy.yaml")
shutil.copy2(DEFAULT_CFG_PATH, new_file)
LOGGER.info(
diff --git a/ultralytics/utils/torch_utils.py b/ultralytics/utils/torch_utils.py
index db17813f..21973d7e 100644
--- a/ultralytics/utils/torch_utils.py
+++ b/ultralytics/utils/torch_utils.py
@@ -21,6 +21,7 @@ from ultralytics.utils import (
DEFAULT_CFG_DICT,
DEFAULT_CFG_KEYS,
LOGGER,
+ NUM_THREADS,
PYTHON_VERSION,
TORCHVISION_VERSION,
__version__,
@@ -172,6 +173,8 @@ def select_device(device="", batch=0, newline=False, verbose=True):
s += f"CPU ({get_cpu_info()})\n"
arg = "cpu"
+ if arg in {"cpu", "mps"}:
+ torch.set_num_threads(NUM_THREADS) # reset OMP_NUM_THREADS for cpu training
if verbose:
LOGGER.info(s if newline else s.rstrip())
return torch.device(arg)