Bootstrap

YOLOv11-ultralytics-8.3.67部分代码阅读笔记-checks.py

checks.py

ultralytics\utils\checks.py

目录

checks.py

1.所需的库和模块

2.def parse_requirements(file_path=ROOT.parent / "requirements.txt", package=""): 

3.def parse_version(version="0.0.0") -> tuple: 

4.def is_ascii(s) -> bool: 

5.def check_imgsz(imgsz, stride=32, min_dim=1, max_dim=2, floor=0): 

6.def check_version(current: str = "0.0.0", required: str = "0.0.0", name: str = "version", hard: bool = False, verbose: bool = False, msg: str = "",) -> bool: 

7.def check_latest_pypi_version(package_name="ultralytics"): 

8.def check_pip_update_available(): 

9.def check_font(font="Arial.ttf"): 

10.def check_python(minimum: str = "3.8.0", hard: bool = True, verbose: bool = False) -> bool: 

11.def check_requirements(requirements=ROOT.parent / "requirements.txt", exclude=(), install=True, cmds=""): 

12.def check_torchvision(): 

13.def check_suffix(file="yolo11n.pt", suffix=".pt", msg=""): 

14.def check_yolov5u_filename(file: str, verbose: bool = True): 

15.def check_model_file_from_stem(model="yolo11n"):

16.def check_file(file, suffix="", download=True, download_dir=".", hard=True): 

17.def check_yaml(file, suffix=(".yaml", ".yml"), hard=True): 

18.def check_is_path_safe(basedir, path): 

19.def check_imshow(warn=False): 

20.def check_yolo(verbose=True, device=""): 

21.def collect_system_info(): 

22.def check_amp(model): 

23.def git_describe(path=ROOT): 

24.def print_args(args: Optional[dict] = None, show_file=True, show_func=False): 

25.def cuda_device_count() -> int:

26.def cuda_is_available() -> bool: 

27.def is_sudo_available() -> bool: 

28.Run checks and define constants / Define constants

 


1.所需的库和模块

# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license

import glob
import inspect
import math
import os
import platform
import re
import shutil
import subprocess
import time
from importlib import metadata
from pathlib import Path
from typing import Optional

import cv2
import numpy as np
import requests
import torch

from ultralytics.utils import (
    ARM64,
    ASSETS,
    AUTOINSTALL,
    IS_COLAB,
    IS_GIT_DIR,
    IS_KAGGLE,
    IS_PIP_PACKAGE,
    LINUX,
    LOGGER,
    MACOS,
    ONLINE,
    PYTHON_VERSION,
    RKNN_CHIPS,
    ROOT,
    TORCHVISION_VERSION,
    USER_CONFIG_DIR,
    WINDOWS,
    Retry,
    SimpleNamespace,
    ThreadingLocked,
    TryExcept,
    clean_url,
    colorstr,
    downloads,
    emojis,
    is_github_action_running,
    url2file,
)

2.def parse_requirements(file_path=ROOT.parent / "requirements.txt", package=""): 

# 这段代码定义了一个名为 parse_requirements 的函数,用于解析Python依赖项。它可以从一个 requirements.txt 文件或通过包名从已安装的Python包中提取依赖项。
# 定义了一个函数 parse_requirements ,它接受两个参数。
# 1.file_path : requirements.txt 文件的路径,默认为 ROOT.parent / "requirements.txt" 。
# 2.package :可选的包名,如果提供,则从该包的元数据中提取依赖项。
def parse_requirements(file_path=ROOT.parent / "requirements.txt", package=""):
    # 解析 requirements.txt 文件,忽略以 '#' 开头的行和 '#' 之后的任何文本。
    """
    Parse a requirements.txt file, ignoring lines that start with '#' and any text after '#'.

    Args:
        file_path (Path): Path to the requirements.txt file.
        package (str, optional): Python package to use instead of requirements.txt file, i.e. package='ultralytics'.

    Returns:
        (List[Dict[str, str]]): List of parsed requirements as dictionaries with `name` and `specifier` keys.

    Example:
        ```python
        from ultralytics.utils.checks import parse_requirements

        parse_requirements(package="ultralytics")
        ```
    """
    # 如果提供了 package 参数。
    if package:

        # metadata.distribution(distribution_name)
        # metadata.distribution() 函数是 Python 标准库 importlib.metadata 模块中的一个函数,用于获取指定分发包(distribution package)的元数据。
        # 参数 :
        # distribution_name :一个字符串参数,表示要查询的分发包的名称。
        # 返回值 :
        # 函数返回一个 Distribution 实例,该实例描述了指定的分发包。
        # 异常 :
        # 如果指定的分发包没有在当前 Python 环境中安装,函数将引发 PackageNotFoundError 异常。
        # Distribution 类 :
        # Distribution 是一个抽象对象,代表 Python 分发包的元数据。通过 Distribution 实例,可以访问分发包的各种元数据,例如版本号、依赖关系、许可证等。
        # 注意事项 :
        # importlib.metadata 模块在 Python 3.10 版本中引入,用于取代 pkg_resources 模块中的类似功能。
        # Distribution 实例上的元数据可以通过 dist.metadata 访问,它是一个包含解析元数据的字典。
        # 该函数默认在文件系统和 zip 归档中查找分发包的元数据,其搜索范围默认为 sys.path 。
        # 通过使用 metadata.distribution() 函数,你可以方便地获取已安装 Python 包的元数据,这对于包管理和依赖检查非常有用。

        # 使用 metadata.distribution(package).requires 获取该包的依赖项。 使用列表推导式过滤掉包含 extra == 的依赖项(这些通常是可选依赖项)。
        requires = [x for x in metadata.distribution(package).requires if "extra == " not in x]
    # 如果没有提供 package 参数。
    else:
        # 从指定的 file_path 读取 requirements.txt 文件的内容,并按行分割成列表。
        requires = Path(file_path).read_text().splitlines()

    # 初始化一个空列表 requirements ,用于 存储解析后的依赖项 。
    requirements = []
    # 遍历 requires 列表中的每一行。
    for line in requires:
        # 使用 strip() 去除行首和行尾的空白字符。
        line = line.strip()
        # 如果行不为空且不以 # 开头(即不是注释行)。
        if line and not line.startswith("#"):
            # 使用 split("#")[0].strip() 去除行内的注释。
            line = line.split("#")[0].strip()  # ignore inline comments
            # 使用正则表达式 re.match 匹配依赖项的名称和版本规范。
            # ([a-zA-Z0-9-_]+) :匹配依赖项的名称。
            # \s*([<>!=~]+.*)? :匹配版本规范(可选)。
            if match := re.match(r"([a-zA-Z0-9-_]+)\s*([<>!=~]+.*)?", line):

                # SimpleNamespace
                # SimpleNamespace 是 Python 标准库 types 模块中的一个类,它提供了一个简单的命名空间对象,可以用来存储属性。这个类的设计目的是创建一个轻量级的容器,用于存储和访问任意的属性,而不需要定义一个完整的类。
                # 以下是 SimpleNamespace 的一些特点 :
                # 无需定义类 : 你不需要定义一个完整的类,就可以创建一个带有任意属性的对象。
                # 动态属性 : 可以动态地添加、删除和修改属性。
                # 易于访问 : 属性可以通过点( . )操作符直接访问,就像普通的类实例一样。
                # 不可变 : SimpleNamespace 实例是不可变的,这意味着一旦创建,你不能添加新的属性。
                # 易于转换 : SimpleNamespace 实例可以通过 vars() 函数轻松转换为字典。
                # 下面是一个使用 SimpleNamespace 的简单示例 :
                # from types import SimpleNamespace
                # # 创建一个SimpleNamespace实例
                # ns = SimpleNamespace(name='Kimi', age=5)
                # # 访问属性
                # print(ns.name)  # 输出: Kimi
                # print(ns.age)   # 输出: 5
                # # 尝试添加新属性将导致错误
                # # ns.new_attr = 'new value'  # 这将抛出 AttributeError
                #  SimpleNamespace 通常用于临时存储数据或作为函数参数传递一组命名参数,特别是在参数数量不确定或参数较为简单的情况下。它提供了一种比完整类定义更简洁、更灵活的方式来处理命名空间。

                # 如果匹配成功,将依赖项的名称和版本规范存储到一个 SimpleNamespace 对象中,并将其添加到 requirements 列表中。
                requirements.append(SimpleNamespace(name=match[1], specifier=match[2].strip() if match[2] else ""))

    # 返回解析后的依赖项列表 requirements 。
    return requirements
# 这段代码的主要功能是。获取依赖项:如果提供了 package 参数,则从该包的元数据中提取依赖项。如果没有提供 package 参数,则从指定的 requirements.txt 文件中读取依赖项。解析依赖项:去除注释行和行内的注释。使用正则表达式提取依赖项的名称和版本规范。将解析后的依赖项存储到一个 SimpleNamespace 对象中。返回解析后的依赖项:返回一个包含解析后的依赖项的列表。通过这个函数,可以方便地解析和处理Python项目的依赖项,无论是从文件中读取还是从已安装的包中提取。
# def parse_requirements(file_path=ROOT.parent / "requirements.txt", package=""):
# -> 用于解析Python依赖项。它可以从一个 requirements.txt 文件或通过包名从已安装的Python包中提取依赖项。返回解析后的依赖项列表 requirements 。
# -> return requirements

3.def parse_version(version="0.0.0") -> tuple: 

# 这段代码定义了一个名为 parse_version 的函数,用于解析版本号字符串,并将其转换为一个包含三个整数的元组。
# 定义了一个名为 parse_version 的函数,它接受一个参数。
# 1.version :默认值为 "0.0.0" 。
# 函数的返回类型为 tuple 。
def parse_version(version="0.0.0") -> tuple:
    # 将版本字符串转换为整数元组,忽略附加到版本的任何额外非数字字符串。此函数替换已弃用的“pkg_resources.parse_version(v)”。
    """
    Convert a version string to a tuple of integers, ignoring any extra non-numeric string attached to the version. This
    function replaces deprecated 'pkg_resources.parse_version(v)'.

    Args:
        version (str): Version string, i.e. '2.0.1+cpu'

    Returns:
        (tuple): Tuple of integers representing the numeric part of the version and the extra string, i.e. (2, 0, 1)
    """
    # 开始一个 try 块,用于捕获可能发生的异常。
    try:
        # re.findall(r"\d+", version) :使用正则表达式 \d+ 查找版本号字符串中的所有数字序列,返回一个列表。
        # [:3] :取列表的前三个元素,确保只提取 主版本号 、 次版本号 和 修订号 。
        # map(int, ...) :将列表中的每个字符串转换为整数。
        # tuple(...) :将结果转换为元组。
        # 例如,对于版本号 "2.0.1+cpu" , re.findall(r"\d+", version) 会返回 ['2', '0', '1'] ,然后转换为 (2, 0, 1) 。
        return tuple(map(int, re.findall(r"\d+", version)[:3]))  # '2.0.1+cpu' -> (2, 0, 1)
    # 捕获 try 块中可能发生的任何异常,并将其存储在变量 e 中。
    except Exception as e:
        # 如果发生异常,使用 LOGGER.warning 记录警告信息,提示解析失败,并返回 (0, 0, 0) 。
        LOGGER.warning(f"WARNING ⚠️ failure for parse_version({version}), returning (0, 0, 0): {e}")    # 警告 ⚠️ parse_version({version}) 失败,返回 (0, 0, 0): {e}。
        # 返回默认值 (0, 0, 0) 。
        return 0, 0, 0
# parse_version 函数用于解析版本号字符串,并将其转换为一个包含三个整数的元组。它通过正则表达式提取数字,并使用异常处理确保在解析失败时返回默认值。这种设计使得函数在处理各种输入时更加健壮和可靠。

4.def is_ascii(s) -> bool: 

# 这段代码定义了一个名为 is_ascii 的函数,用于检查一个字符串是否仅由 ASCII 字符组成。
# 定义了一个函数 is_ascii ,接收一个参数 1.s ,并返回一个布尔值( True 或 False )。
# 函数的返回类型注解为 bool ,表示该函数返回一个布尔值。
def is_ascii(s) -> bool:
    # 检查字符串是否仅由 ASCII 字符组成。
    """
    Check if a string is composed of only ASCII characters.

    Args:
        s (str): String to be checked.

    Returns:
        (bool): True if the string is composed only of ASCII characters, False otherwise.
    """
    # Convert list, tuple, None, etc. to string
    # 将输入参数 s 转换为字符串。这一步确保即使输入是一个列表、元组、 None 或其他类型,也会被正确处理。例如 :如果输入是 None ,则转换为字符串 "None" 。 如果输入是一个列表,如 [1, 2, 3] ,则转换为字符串 "[1, 2, 3]" 。
    s = str(s)

    # Check if the string is composed of only ASCII characters

    # ord(c)
    # ord() 是 Python 的内置函数,用于返回一个字符(即字符串长度为1的单个字符)的 Unicode 编码。这个编码是一个整数,表示该字符在 Unicode 标准中的编号。
    # 参数 :
    # c :一个长度为1的字符串,即单个字符。
    # 返回值 :
    # 返回整数,c 的 Unicode 编码。
    # ord() 函数常用于需要将字符转换为其对应的整数编码的场景,比如在处理字符编码、文件编码转换或者网络传输时。

    # 使用 all 函数和生成器表达式检查字符串 s 中的每个字符是否都是 ASCII 字符。
    # ord(c) :获取字符 c 的 Unicode 码点。
    # ord(c) < 128 :检查字符 c 的 Unicode 码点是否小于 128。ASCII 字符的 Unicode 码点范围是 0 到 127。
    # all(...) :确保字符串中的所有字符都满足上述条件。如果所有字符的码点都小于 128,则返回 True ,否则返回 False 。
    return all(ord(c) < 128 for c in s)
# is_ascii 函数通过将输入转换为字符串,并检查每个字符的 Unicode 码点是否小于 128,来判断字符串是否仅由 ASCII 字符组成。这种方法简单高效,适用于各种输入类型(如字符串、列表、元组等)。

5.def check_imgsz(imgsz, stride=32, min_dim=1, max_dim=2, floor=0): 

# 这段代码定义了一个名为 check_imgsz 的函数,用于验证和调整图像尺寸,确保其在每个维度上都是给定步幅(stride)的倍数。如果图像尺寸不是步幅的倍数,则会更新为大于或等于给定下限(floor)的最近的步幅倍数。
# 定义了一个函数 check_imgsz ,接收以下参数 :
# 1.imgsz :图像尺寸,可以是整数、列表、元组或字符串。
# 2.stride :步幅,默认为 32。
# 3.min_dim :最小维度数,默认为 1。
# 4.max_dim :最大维度数,默认为 2。
# 5.floor :图像尺寸的下限,默认为 0。
def check_imgsz(imgsz, stride=32, min_dim=1, max_dim=2, floor=0):
    # 验证图像大小是否是每个维度中给定步长的倍数。如果图像大小不是步长的倍数,则将其更新为大于或等于给定下限值的步长的最近倍数。
    """
    Verify image size is a multiple of the given stride in each dimension. If the image size is not a multiple of the
    stride, update it to the nearest multiple of the stride that is greater than or equal to the given floor value.

    Args:
        imgsz (int | cList[int]): Image size.
        stride (int): Stride value.
        min_dim (int): Minimum number of dimensions.
        max_dim (int): Maximum number of dimensions.
        floor (int): Minimum allowed value for image size.

    Returns:
        (List[int]): Updated image size.
    """
    # Convert stride to integer if it is a tensor    如果步幅是张量,则将其转换为整数。
    # 如果 stride 是一个 PyTorch 张量,则取其最大值并转换为整数。如果 stride 已经是整数,则直接使用。
    stride = int(stride.max() if isinstance(stride, torch.Tensor) else stride)

    # 这段代码的作用是将输入的图像尺寸 imgsz 转换为一个统一的列表格式,以便后续处理。它支持多种输入类型,包括整数、列表、元组和字符串,并在输入类型无效时抛出错误。
    # Convert image size to list if it is an integer    如果图像大小是整数,则将其转换为列表。
    # 如果 imgsz 是一个整数,则将其转换为一个单元素列表。例如,如果 imgsz=640 ,则转换为 [640] 。
    if isinstance(imgsz, int):
        imgsz = [imgsz]
    # 如果 imgsz 是一个列表或元组,则将其转换为列表。例如,如果 imgsz=(640, 640) ,则转换为 [640, 640] 。
    elif isinstance(imgsz, (list, tuple)):
        imgsz = list(imgsz)
    # 如果 imgsz 是一个字符串。
    elif isinstance(imgsz, str):  # i.e. '640' or '[640,640]'
        # 如果字符串表示一个整数(例如 '640' ),则将其转换为整数,并包装为单元素列表 [640] 。
        # 如果字符串表示一个列表(例如 '[640,640]' ),则使用 eval 将其解析为列表 [640, 640] 。
        imgsz = [int(imgsz)] if imgsz.isnumeric() else eval(imgsz)
    # 如果 imgsz 的类型不是整数、列表、元组或字符串。
    else:
        # 则抛出 TypeError ,说明输入类型无效,并提示用户有效的输入类型。
        raise TypeError(
            f"'imgsz={imgsz}' is of invalid type {type(imgsz).__name__}. "    # 'imgsz={imgsz}' 的类型为无效类型 {type(imgsz).__name__}。
            f"Valid imgsz types are int i.e. 'imgsz=640' or list i.e. 'imgsz=[640,640]'"    # 有效的 imgsz 类型是 int,即“imgsz=640”或列表,即“imgsz=[640,640]”。
        )
    # 这段代码通过将输入的图像尺寸 imgsz 转换为统一的列表格式,确保后续处理的便利性和一致性。它支持多种输入类型,并在输入类型无效时提供明确的错误提示。这种设计提高了函数的灵活性和健壮性,能够适应不同的输入场景。

    # 这段代码的作用是根据 max_dim 参数限制图像尺寸 imgsz 的维度数。如果 imgsz 的维度数超过了 max_dim ,则会根据 max_dim 的值进行相应的处理。
    # Apply max_dim
    # 检查 imgsz 的维度数是否超过了 max_dim 。 max_dim 是一个参数,用于指定图像尺寸的最大维度数。
    if len(imgsz) > max_dim:
        # 定义一个错误信息 msg ,说明在不同场景下 imgsz 的有效格式。
        # 在训练 ( train ) 和验证 ( val ) 阶段, imgsz 必须是整数。
        # 在预测 ( predict ) 和导出 ( export ) 阶段, imgsz 可以是整数或 [h, w] 列表。
        msg = (
            "'train' and 'val' imgsz must be an integer, while 'predict' and 'export' imgsz may be a [h, w] list "    # 'train' 和 'val' imgsz 必须是整数,
            "or an integer, i.e. 'yolo export imgsz=640,480' or 'yolo export imgsz=640'"    # 而 'predict' 和 'export' imgsz 可以是 [h, w] 列表或整数,即 'yolo export imgsz=640,480' 或 'yolo export imgsz=640' 。
        )
        # 如果 max_dim 不等于 1,则抛出 ValueError ,说明 imgsz 的维度数超过了允许的最大值,并提供详细的错误信息。
        if max_dim != 1:
            raise ValueError(f"imgsz={imgsz} is not a valid image size. {msg}")    # imgsz={imgsz} 不是有效的图像大小。{msg}。
        # 如果 max_dim 等于 1,则发出警告,说明 imgsz 的维度数超过了允许的最大值,并将 imgsz 更新为最大值。警告信息中包含更新后的值和详细的说明。
        LOGGER.warning(f"WARNING ⚠️ updating to 'imgsz={max(imgsz)}'. {msg}")    # 警告⚠️更新为“imgsz={max(imgsz)}”。{msg}。
        # 将 imgsz 更新为最大值,并将其包装为单元素列表。例如,如果 imgsz=[640, 480, 320] ,则更新为 [640] 。
        imgsz = [max(imgsz)]
    # 这段代码通过检查 imgsz 的维度数是否超过了 max_dim ,确保图像尺寸的维度数符合要求。如果维度数超过了允许的最大值,则根据 max_dim 的值决定是否抛出错误或发出警告,并将 imgsz 更新为最大值。这种设计确保了图像尺寸的灵活性和兼容性,同时避免了因维度数过多而导致的错误。
    # Make image size a multiple of the stride
    # 将每个维度的图像尺寸调整为步幅的倍数。 使用 math.ceil(x / stride) 计算大于或等于 x / stride 的最小整数。 乘以步幅 stride ,得到最近的步幅倍数。 使用 max(..., floor) 确保调整后的尺寸不小于 floor 。
    sz = [max(math.ceil(x / stride) * stride, floor) for x in imgsz]

    # Print warning message if image size was updated
    # 如果调整后的图像尺寸 sz 与原始尺寸 imgsz 不同,则发出警告,说明图像尺寸已被更新。
    if sz != imgsz:
        LOGGER.warning(f"WARNING ⚠️ imgsz={imgsz} must be multiple of max stride {stride}, updating to {sz}")    # 警告 ⚠️ imgsz={imgsz} 必须是最大步幅 {stride} 的倍数,更新为 {sz} 。

    # Add missing dimensions if necessary
    # 根据 min_dim 的值,确保图像尺寸的维度数符合要求。
    # 如果 min_dim 等于 2 且 sz 只有一个元素,则将 sz 扩展为 [sz[0], sz[0]] 。
    # 如果 min_dim 等于 1 且 sz 只有一个元素,则直接返回 sz[0] 。
    # 否则,返回原始的 sz 。
    sz = [sz[0], sz[0]] if min_dim == 2 and len(sz) == 1 else sz[0] if min_dim == 1 and len(sz) == 1 else sz

    # 返回调整后的图像尺寸 sz 。
    return sz
# check_imgsz 函数用于验证和调整图像尺寸,确保其在每个维度上都是给定步幅的倍数。它支持多种输入类型(如整数、列表、元组、字符串),并根据需要进行调整。函数还考虑了最小和最大维度数的限制,并在调整图像尺寸时发出警告。这种设计确保了图像尺寸的灵活性和兼容性,同时满足模型对图像尺寸的要求。

6.def check_version(current: str = "0.0.0", required: str = "0.0.0", name: str = "version", hard: bool = False, verbose: bool = False, msg: str = "",) -> bool: 

# 这段代码定义了一个名为 check_version 的函数,用于检查当前版本是否满足指定的版本要求。
# 定义了 check_version 函数,接受以下参数 :
# 1.current :当前版本号,默认为 "0.0.0" 。
# 2.required :所需版本号,默认为 "0.0.0" 。
# 3.name :版本名称,默认为 "version" 。
# 4.hard :是否严格检查,默认为 False 。如果为 True ,不满足版本要求时会抛出异常。
# 5.verbose :是否打印详细信息,默认为 False 。
# 6.msg :附加消息,默认为空字符串。
# 函数返回一个布尔值,表示当前版本是否满足所需版本要求。
def check_version(
    current: str = "0.0.0",
    required: str = "0.0.0",
    name: str = "version",
    hard: bool = False,
    verbose: bool = False,
    msg: str = "",
) -> bool:
    # 对照所需版本或范围检查当前版本。
    """
    Check current version against the required version or range.

    Args:
        current (str): Current version or package name to get version from.
        required (str): Required version or range (in pip-style format).
        name (str, optional): Name to be used in warning message.
        hard (bool, optional): If True, raise an AssertionError if the requirement is not met.
        verbose (bool, optional): If True, print warning message if requirement is not met.
        msg (str, optional): Extra message to display if verbose.

    Returns:
        (bool): True if requirement is met, False otherwise.

    Example:
        ```python
        # Check if current version is exactly 22.04
        check_version(current="22.04", required="==22.04")

        # Check if current version is greater than or equal to 22.04
        check_version(current="22.10", required="22.04")  # assumes '>=' inequality if none passed

        # Check if current version is less than or equal to 22.04
        check_version(current="22.04", required="<=22.04")

        # Check if current version is between 20.04 (inclusive) and 22.04 (exclusive)
        check_version(current="21.10", required=">20.04,<22.04")
        ```
    """
    # 这段代码是 check_version 函数的一部分,主要负责处理 current 参数的两种特殊情况:一是 current 为空或 None ,二是 current 不是版本号而是包名。
    # 检查 current 是否为空字符串或  None  。如果是,说明传入的当前版本号无效。
    if not current:  # if current is '' or None
        # 使用 LOGGER.warning 记录一条警告信息,提示用户传入的 current 和 required 参数无效,需要检查这些值。
        LOGGER.warning(f"WARNING ⚠️ invalid check_version({current}, {required}) requested, please check values.")    # 警告⚠️请求的 check_version({current}, {required}) 无效,请检查值。
        # 在这种情况下,函数返回 True ,表示版本检查通过。这可能是因为在某些情况下,空的 current 值被视为默认通过检查。
        return True
    # 如果 current 的第一个字符不是数字,说明 current 可能是一个包名而不是版本号。例如, current 可能是 'ultralytics' 。
    elif not current[0].isdigit():  # current is package name rather than version string, i.e. current='ultralytics'
        # 开始一个 try 块,尝试执行以下代码。如果在执行过程中发生异常,将捕获并处理。
        try:
            # 将 current 的值赋给 name 变量,这样 name 就保存了包名。
            name = current  # assigned package name to 'name' arg

            # importlib.metadata.version(distribution_name)
            # metadata.version() 函数是 Python 标准库 importlib.metadata 模块中的一个函数,用于获取指定分发包的已安装版本号。
            # 参数 :
            # distribution_name :一个字符串,表示要查询的分发包的名称。
            # 返回值 :
            # 该函数返回一个字符串,表示指定分发包的版本号。
            # 异常 :
            # PackageNotFoundError :如果指定的分发包没有在当前Python环境中安装,会抛出这个异常。
            # 这个函数提供了一种简单而现代的方式来获取已安装包的版本信息,有助于在开发和生产环境中管理和验证依赖项的版本。

            # 使用 metadata.version 函数,通过包名获取该包的实际版本号,并将这个版本号赋值给 current 。这样, current 就从包名变成了版本号。
            current = metadata.version(current)  # get version string from package name
        # 如果在尝试获取包的版本号时,抛出了 PackageNotFoundError 异常,说明指定的包没有安装。
        except metadata.PackageNotFoundError as e:
            # 检查 hard 参数是否为 True 。如果 hard 为 True ,表示在版本检查失败时应该抛出异常。
            if hard:
                # 抛出一个 ModuleNotFoundError 异常,提示用户所需的包未安装。异常消息中包含了包名,并且使用了 emojis 函数来添加表情符号。 from e 表示这个异常是由之前的 PackageNotFoundError 异常引起的。
                # def emojis(string=""): -> 在Windows系统上移除字符串中的表情符号,而在非Windows系统上保持字符串不变。 -> return string.encode().decode("ascii", "ignore") if WINDOWS else string
                raise ModuleNotFoundError(emojis(f"WARNING ⚠️ {current} package is required but not installed")) from e    # 警告 ⚠️ 需要 {current} 包但尚未安装。
            # 如果 hard 为 False ,表示在版本检查失败时不抛出异常,而是以其他方式处理。
            else:
                # 返回 False ,表示版本检查未通过。在这种情况下,不会抛出异常,而是通过返回值来表示检查结果。
                return False
    # 这段代码处理了 current 参数的两种特殊情况。如果 current 为空或 None ,会记录警告并返回 True 。如果 current 是一个包名而不是版本号,会尝试获取该包的实际版本号。如果包未安装且 hard 为 True ,会抛出异常;如果 hard 为 False ,则返回 False 。

    # 这段代码继续处理 check_version 函数中的 required 参数,主要负责检查 required 是否为空或包含特定的平台条件。
    # 检查 required 是否为空字符串或 None 。如果是,说明没有指定所需的版本号。
    if not required:  # if required is '' or None
        # 在这种情况下,函数返回 True ,表示版本检查通过。这可能是因为在某些情况下,没有指定所需的版本号被视为默认通过检查。
        return True

    # 检查 required 中是否包含 sys_platform ,这表示版本要求可能与特定的操作系统平台有关。例如, required 可能是 '<2.4.0,>=1.8.0; sys_platform == "win32"' 。
    if "sys_platform" in required and (  # i.e. required='<2.4.0,>=1.8.0; sys_platform == "win32"'
        # 检查当前操作系统是否为Windows( WINDOWS 为 True ),并且 required 中是否不包含 "win32" 。如果这两个条件都满足,说明当前操作系统是Windows,但 required 中没有指定Windows平台的要求。
        (WINDOWS and "win32" not in required)
        # 检查当前操作系统是否为Linux( LINUX 为 True ),并且 required 中是否不包含 "linux" 。如果这两个条件都满足,说明当前操作系统是Linux,但 required 中没有指定Linux平台的要求。
        or (LINUX and "linux" not in required)
        # 检查当前操作系统是否为macOS( MACOS 为 True ),并且 required 中是否不包含 "macos" 和 "darwin" 。如果这两个条件都满足,说明当前操作系统是macOS,但 required 中没有指定macOS平台的要求。
        or (MACOS and "macos" not in required and "darwin" not in required)
    ):
        # 如果上述任何一个条件满足,说明当前操作系统与 required 中指定的平台不匹配,因此返回 True ,表示版本检查通过。
        return True
    # 这段代码处理了 required 参数的两种特殊情况。如果 required 为空或 None ,会直接返回 True 。如果 required 中包含 sys_platform ,并且当前操作系统与 required 中指定的平台不匹配,也会返回 True 。这样可以确保在没有版本要求或平台不匹配的情况下,版本检查不会失败。

    # 这段代码是 check_version 函数的核心部分,负责解析 required 中的版本要求,并与 current 版本号进行比较,以确定当前版本是否满足所需版本要求。
    # 初始化变量。
    # 用于存储操作符(如 == 、 != 、 >= 等)。
    op = ""
    # 用于存储版本号。
    version = ""
    # 用于存储版本检查的结果,默认为 True ,表示版本检查通过。
    result = True
    # 使用 parse_version 函数将 current 版本号解析为元组形式,例如 '1.2.3' 会被解析为 (1, 2, 3) 。
    # def parse_version(version="0.0.0") -> tuple: -> 用于解析版本号字符串,并将其转换为一个包含三个整数的元组。 -> return tuple(map(int, re.findall(r"\d+", version)[:3]))  # '2.0.1+cpu' -> (2, 0, 1) / return 0, 0, 0
    c = parse_version(current)  # '1.2.3' -> (1, 2, 3)
    # 将 required 中的版本要求按逗号分割,并去掉首尾空格,然后遍历每个要求。例如, required 为 '>=1.2.3,<2.0.0' 时,会分割为 ['>=1.2.3', '<2.0.0'] 。
    for r in required.strip(",").split(","):
        # 使用正则表达式 re.match 匹配操作符和版本号。正则表达式 ([^0-9]*)([\d.]+) 的作用是 :
        # ([^0-9]*) :匹配任意非数字字符,捕获为 操作符 op 。
        # ([\d.]+) :匹配一个或多个数字和点,捕获为 版本号 version 。例如,对于 '>=22.04' ,会匹配出 op='>=' 和 version='22.04' 。
        op, version = re.match(r"([^0-9]*)([\d.]+)", r).groups()  # split '>=22.04' -> ('>=', '22.04')
        # 如果操作符 op 为空,则默认设置为 >= 。
        if not op:
            op = ">="  # assume >= if no op passed
        # 使用 parse_version 函数将 version 版本号解析为元组形式,例如 '1.2.3' 会被解析为 (1, 2, 3) 。
        v = parse_version(version)  # '1.2.3' -> (1, 2, 3)
        # 如果操作符为 == 且当前版本号 c 不等于所需版本号 v ,则将 result 设置为 False 。
        if op == "==" and c != v:
            result = False
        # 如果操作符为 != 且当前版本号 c 等于所需版本号 v ,则将 result 设置为 False 。
        elif op == "!=" and c == v:
            result = False
        # 如果操作符为 >= 且当前版本号 c 小于所需版本号 v ,则将 result 设置为 False 。
        elif op == ">=" and not (c >= v):
            result = False
        # 如果操作符为 <= 且当前版本号 c 大于所需版本号 v ,则将 result 设置为 False 。
        elif op == "<=" and not (c <= v):
            result = False
        # 如果操作符为 > 且当前版本号 c 小于等于所需版本号 v ,则将 result 设置为 False 。
        elif op == ">" and not (c > v):
            result = False
        # 如果操作符为 < 且当前版本号 c 大于等于所需版本号 v ,则将 result 设置为 False 。
        elif op == "<" and not (c < v):
            result = False
    # 如果 result 为 False ,说明当前版本不满足所需版本要求。
    if not result:
        # 生成警告信息,提示所需的版本号和当前安装的版本号,以及自定义消息 msg 。
        warning = f"WARNING ⚠️ {name}{op}{version} is required, but {name}=={current} is currently installed {msg}"    # 警告 ⚠️ 需要 {name}{op}{version},但当前已安装 {name}=={current} {msg}。
        # 如果 hard 为 True ,表示在版本检查失败时应该抛出异常。
        if hard:
            # 抛出一个 ModuleNotFoundError 异常,提示用户所需的版本号未满足。异常消息中包含了警告信息,并且使用了 emojis 函数来添加表情符号。
            raise ModuleNotFoundError(emojis(warning))  # assert version requirements met
        # 如果 verbose 为 True ,表示在版本检查失败时应该输出详细信息。
        if verbose:
            # 使用 LOGGER.warning 记录警告信息。
            LOGGER.warning(warning)
    # 返回版本检查的结果。如果 result 为 True ,表示版本检查通过;如果为 False ,表示版本检查未通过。
    return result
    # 这段代码通过解析 required 中的版本要求,并与 current 版本号进行比较,来确定当前版本是否满足所需版本要求。它支持多种比较操作符,并且可以根据 hard 和 verbose 参数的设置,在版本检查失败时抛出异常或输出详细信息。
# 这段代码实现了一个版本检查函数 check_version ,它通过解析版本号和比较操作符来判断当前版本是否满足所需的版本要求。函数支持多种比较操作符,如 == 、 != 、 >= 、 <= 、 > 和 < ,并且可以根据参数设置在版本不满足要求时进行警告或抛出异常。此外,函数还支持根据操作系统平台进行版本检查,以及处理包名和版本号的转换。
# def check_version(current: str = "0.0.0", required: str = "0.0.0", name: str = "version", hard: bool = False, verbose: bool = False, msg: str = "",) -> bool:
# -> 用于检查当前版本是否满足指定的版本要求。返回版本检查的结果。如果 result 为 True ,表示版本检查通过;如果为 False ,表示版本检查未通过。
# -> return result

7.def check_latest_pypi_version(package_name="ultralytics"): 

# 这段代码定义了一个名为 check_latest_pypi_version 的函数,用于查询 PyPI(Python Package Index)上指定包的最新版本,而无需下载或安装该包。
# 定义了一个函数 check_latest_pypi_version ,接收一个参数。
# 1.package_name :默认值为 "ultralytics" ,表示要查询的 PyPI 包的名称。
def check_latest_pypi_version(package_name="ultralytics"):
    # 返回 PyPI 包的最新版本,无需下载或安装。
    """
    Returns the latest version of a PyPI package without downloading or installing it.

    Args:
        package_name (str): The name of the package to find the latest version for.

    Returns:
        (str): The latest version of the package.
    """
    # 使用 requests.packages.urllib3.disable_warnings() 禁用 urllib3 的安全警告(如 InsecureRequestWarning )。这通常用于避免在查询过程中出现不必要的警告信息。
    try:
        requests.packages.urllib3.disable_warnings()  # Disable the InsecureRequestWarning
        # 使用 requests.get 发起一个 HTTP GET 请求,查询 PyPI 上指定包的 JSON 数据。请求的 URL 格式为 https://pypi.org/pypi/{package_name}/json`,其中 {package_name}` 是包的名称。 设置请求超时时间为 3 秒,以避免请求过长时间未响应。
        response = requests.get(f"https://pypi.org/pypi/{package_name}/json", timeout=3)
        # 检查 HTTP 响应的状态码是否为 200(表示请求成功)。
        if response.status_code == 200:
            # 如果请求成功,则解析响应的 JSON 数据,并返回包的最新版本号。版本号存储在 JSON 数据的 "info" 字段中的 "version" 键中。
            return response.json()["info"]["version"]
    # 如果在查询过程中发生任何异常(如网络错误、解析错误等)。
    except Exception:
        # 则捕获异常并返回 None 。
        return None
# check_latest_pypi_version 函数通过查询 PyPI 上的 JSON 数据,获取指定包的最新版本号,而无需下载或安装该包。它通过禁用不必要的安全警告,并设置请求超时时间,确保查询过程的高效性和稳定性。如果查询成功,函数返回包的最新版本号;如果发生异常,则返回 None 。

8.def check_pip_update_available(): 

# 这段代码定义了一个名为 check_pip_update_available 的函数,用于检查 PyPI 上是否有 ultralytics 包的新版本可用。
# 定义了一个函数 check_pip_update_available ,该函数不接收任何参数。
def check_pip_update_available():
    # 检查 PyPI 上是否有新版本的 ultralytics 包。
    """
    Checks if a new version of the ultralytics package is available on PyPI.

    Returns:
        (bool): True if an update is available, False otherwise.
    """
    #  检查两个条件。
    # ONLINE :表示当前环境是否在线,能够访问 PyPI。
    # IS_PIP_PACKAGE :表示当前是否是通过 pip 安装的 ultralytics 包。
    if ONLINE and IS_PIP_PACKAGE:
        # 从 ultralytics 包中导入当前版本号 __version__ 。
        try:
            # __version__ -> "8.3.67"
            from ultralytics import __version__

            # 调用 check_latest_pypi_version 函数,获取 PyPI 上 ultralytics 包的最新版本号。
            latest = check_latest_pypi_version()
            # 使用 check_version 函数检查当前版本号 __version__ 是否小于最新版本号 latest 。 check_version 函数的第二个参数是一个版本比较表达式,如 "<1.0.0" 。
            if check_version(__version__, f"<{latest}"):  # check if current version is < latest version
                # 如果当前版本小于最新版本,则打印一条日志信息,提示用户有新版本可用,并建议用户通过 pip install -U ultralytics 更新到最新版本。
                LOGGER.info(
                    f"New https://pypi.org/project/ultralytics/{latest} available 😃 "    # 新的 https://pypi.org/project/ultralytics/{latest} 可用 😃 。
                    f"Update with 'pip install -U ultralytics'"    # 使用“pip install -U ultralytics”进行更新。
                )
                # 返回 True ,表示有新版本可用。
                return True
        # 如果在检查过程中发生任何异常,则捕获异常并忽略,继续执行后续代码。
        except Exception:
            pass
    # 如果没有新版本可用,或者在检查过程中发生异常,则返回 False 。
    return False
# check_pip_update_available 函数通过查询 PyPI 上的最新版本号,并与当前安装的版本号进行比较,检查是否有新的 ultralytics 包版本可用。如果有新版本可用,则提示用户更新,并返回 True ;否则返回 False 。该函数在在线环境且通过 pip 安装的包中有效,确保用户能够及时获取最新的包版本。

9.def check_font(font="Arial.ttf"): 

# 这段代码定义了一个名为 check_font 的函数,用于检查指定字体文件是否存在,并在需要时从网络下载该字体文件。函数使用了 ThreadingLocked 装饰器,确保在多线程环境中线程安全地执行。
# 使用 ThreadingLocked 装饰器,确保函数在多线程环境中线程安全地执行。
@ThreadingLocked()
#  定义了一个函数 check_font ,接收一个参数。
# 1.font :默认值为 "Arial.ttf" ,表示要检查的字体文件名。
def check_font(font="Arial.ttf"):
    # 如果字体尚不存在,则在本地查找字体或下载到用户的配置目录。
    """
    Find font locally or download to user's configuration directory if it does not already exist.

    Args:
        font (str): Path or name of font.

    Returns:
        file (Path): Resolved font file path.
    """
    # 从 matplotlib 模块中导入 font_manager ,用于查找系统中的字体文件。
    from matplotlib import font_manager

    # Check USER_CONFIG_DIR
    # 检查用户配置目录( USER_CONFIG_DIR )中是否存在指定的字体文件。
    # 获取字体文件的名称(不包含路径)。
    name = Path(font).name
    # 构建字体文件的完整路径。
    file = USER_CONFIG_DIR / name
    if file.exists():
        # 如果文件存在,则返回该路径。
        return file

    # Check system fonts
    # 使用 font_manager.findSystemFonts() 查找系统中的所有字体文件,并筛选出包含指定字体名称的文件路径。
    # font in s :检查字体文件名是否包含在路径中。
    matches = [s for s in font_manager.findSystemFonts() if font in s]
    # 如果找到匹配的字体文件。
    if any(matches):
        # 则返回第一个匹配项。
        return matches[0]

    # Download to USER_CONFIG_DIR if missing
    # 如果字体文件在用户配置目录和系统中都未找到,则准备从网络下载字体文件。
    url = f"https://github.com/ultralytics/assets/releases/download/v0.0.0/{name}"
    # 检查 URL 是否有效。
    if downloads.is_url(url, check=True):
        # 如果 URL 有效,则调用 downloads.safe_download 下载字体文件到用户配置目录中的指定路径。
        downloads.safe_download(url=url, file=file)
        # 下载完成后,返回字体文件的路径。
        return file
# check_font 函数通过以下步骤确保指定的字体文件可用。检查用户配置目录中是否存在字体文件。如果未找到,则在系统中查找匹配的字体文件。如果仍未找到,则从网络下载字体文件到用户配置目录。该函数使用了 ThreadingLocked 装饰器,确保在多线程环境中线程安全地执行,避免多个线程同时下载同一字体文件。这种设计确保了字体文件的可用性,同时提高了代码的健壮性和效率。

10.def check_python(minimum: str = "3.8.0", hard: bool = True, verbose: bool = False) -> bool: 

# 这段代码定义了一个名为 check_python 的函数,用于检查当前运行的 Python 版本是否满足指定的最低版本要求。
# 定义了一个函数 check_python ,接收以下参数 :
# 1.minimum :指定所需的最低 Python 版本,默认为 "3.8.0" 。
# 2.hard :一个布尔值,表示是否强制检查。如果为 True ,则在版本不满足要求时会抛出异常;如果为 False ,则返回 False 。
# 3.verbose :一个布尔值,表示是否打印详细的版本检查信息。
# 函数返回一个布尔值,表示当前 Python 版本是否满足要求。
def check_python(minimum: str = "3.8.0", hard: bool = True, verbose: bool = False) -> bool:
    # 检查当前 Python 版本是否符合最低版本要求。
    """
    Check current python version against the required minimum version.

    Args:
        minimum (str): Required minimum version of python.
        hard (bool, optional): If True, raise an AssertionError if the requirement is not met.
        verbose (bool, optional): If True, print warning message if requirement is not met.

    Returns:
        (bool): Whether the installed Python version meets the minimum constraints.
    """
    # 调用 check_version 函数,检查当前 Python 版本( PYTHON_VERSION )是否满足指定的最低版本要求( minimum )。
    # check_version 函数的参数 :
    # PYTHON_VERSION :当前运行的 Python 版本。
    # minimum :所需的最低版本。
    # name="Python" :指定检查的软件名称为 "Python" 。
    # hard=hard :是否强制检查。
    # verbose=verbose :是否打印详细信息。
    # check_version 函数返回一个布尔值,表示版本是否满足要求。 check_python 函数直接返回该布尔值。
    return check_version(PYTHON_VERSION, minimum, name="Python", hard=hard, verbose=verbose)
# check_python 函数通过调用 check_version 函数,检查当前运行的 Python 版本是否满足指定的最低版本要求。它支持强制检查(抛出异常)和详细信息打印,确保在运行代码之前,Python 版本符合要求。这种设计提高了代码的兼容性和健壮性,避免因版本不兼容而导致的错误。

11.def check_requirements(requirements=ROOT.parent / "requirements.txt", exclude=(), install=True, cmds=""): 

# 这段代码定义了一个名为 check_requirements 的函数,用于检查和安装Python项目的依赖项。它使用了 TryExcept 上下文管理器来捕获和处理异常,并且支持自动安装缺失的依赖项。
@TryExcept()
# 定义了一个函数 check_requirements ,它接受以下参数 :
# 1.requirements :依赖项的来源,可以是一个 Path 对象(指向 requirements.txt 文件)或一个字符串(直接指定依赖项)。
# 2.exclude :一个元组,包含需要排除的依赖项名称。
# 3.install :一个布尔值,指示是否自动安装缺失的依赖项,默认为 True 。
# 4.cmds :一个字符串,包含额外的pip安装命令参数。
def check_requirements(requirements=ROOT.parent / "requirements.txt", exclude=(), install=True, cmds=""):
    # 检查已安装的依赖项是否满足 YOLOv8 要求,并尝试在需要时自动更新。
    """
    Check if installed dependencies meet YOLOv8 requirements and attempt to auto-update if needed.

    Args:
        requirements (Union[Path, str, List[str]]): Path to a requirements.txt file, a single package requirement as a
            string, or a list of package requirements as strings.
        exclude (Tuple[str]): Tuple of package names to exclude from checking.
        install (bool): If True, attempt to auto-update packages that don't meet requirements.
        cmds (str): Additional commands to pass to the pip install command when auto-updating.

    Example:
        ```python
        from ultralytics.utils.checks import check_requirements

        # Check a requirements.txt file
        check_requirements("path/to/requirements.txt")

        # Check a single package
        check_requirements("ultralytics>=8.0.0")

        # Check multiple packages
        check_requirements(["numpy", "ultralytics>=8.0.0"])
        ```
    """
    # 使用 colorstr 函数定义一个红色加粗的前缀字符串 "requirements:" ,用于在终端中打印彩色消息。
    prefix = colorstr("red", "bold", "requirements:")
    # 检查依赖项来源。
    # 如果 requirements 是一个 Path 对象。
    if isinstance(requirements, Path):  # requirements.txt file
        # 使用 resolve() 方法解析路径,确保路径是绝对路径。
        file = requirements.resolve()
        # 使用 assert 检查文件是否存在。
        assert file.exists(), f"{prefix} {file} not found, check failed."    # 未找到 {prefix} {file},检查失败。
        # 调用 parse_requirements(file) 解析 requirements.txt 文件,生成依赖项列表,并排除 exclude 中的依赖项。
        # def parse_requirements(file_path=ROOT.parent / "requirements.txt", package=""):
        # -> 用于解析Python依赖项。它可以从一个 requirements.txt 文件或通过包名从已安装的Python包中提取依赖项。返回解析后的依赖项列表 requirements 。
        # -> return requirements
        requirements = [f"{x.name}{x.specifier}" for x in parse_requirements(file) if x.name not in exclude]
    # 如果 requirements 是一个字符串,将其转换为一个包含单个元素的列表。
    elif isinstance(requirements, str):
        requirements = [requirements]

    # 解析依赖项。
    # 这段代码的作用是从给定的依赖项列表 requirements 中检查每个依赖项是否已正确安装且版本符合要求。如果某个依赖项未安装或版本不符合要求,则将其添加到 pkgs 列表中,以便后续进行安装或提示用户。
    # 初始化一个空列表 pkgs ,用于 存储未安装或版本不满足要求的依赖项 。
    pkgs = []
    # 遍历传入的依赖项列表 requirements , r 表示 当前处理的依赖项 。
    for r in requirements:
        # 使用 split("/")[-1] 提取依赖项字符串的最后一部分,这通常包含依赖项的名称。 使用 replace(".git", "") 去除可能存在的 .git 后缀。例如,将`git+https://org/repo.git`处理为`repo`。
        r_stripped = r.split("/")[-1].replace(".git", "")  # replace git+https://org/repo.git -> 'repo'
        # 使用正则表达式 re.match 解析处理后的依赖项字符串 r_stripped 。 正则表达式 ([a-zA-Z0-9-_]+) 匹配依赖项的名称。 正则表达式 ([<>!=~]+.*)? 匹配版本规范(可选)。 match 是一个匹配对象,包含解析后的依赖项名称和版本规范。
        match = re.match(r"([a-zA-Z0-9-_]+)([<>!=~]+.*)?", r_stripped)
        # 从匹配对象 match 中提取 依赖项的名称 name 和 版本规范 required 。 如果版本规范存在,则使用 strip() 去除首尾空白字符;否则, required 为空字符串。
        name, required = match[1], match[2].strip() if match[2] else ""
        # 检查依赖项版本。
        try:

            # importlib.metadata.version(distribution_name)
            # metadata.version() 函数是 Python 标准库 importlib.metadata 模块中的一个函数,用于获取指定分发包的已安装版本号。
            # 参数 :
            # distribution_name :一个字符串,表示要查询的分发包的名称。
            # 返回值 :
            # 该函数返回一个字符串,表示指定分发包的版本号。
            # 异常 :
            # PackageNotFoundError :如果指定的分发包没有在当前Python环境中安装,会抛出这个异常。
            # 这个函数提供了一种简单而现代的方式来获取已安装包的版本信息,有助于在开发和生产环境中管理和验证依赖项的版本。

            # 使用 metadata.version(name) 获取已安装的包 name 的版本。 使用 check_version 函数检查已安装的版本是否满足 required 中的版本规范。 如果版本不满足要求, check_version 函数会抛出 AssertionError 。
            # def check_version(current: str = "0.0.0", required: str = "0.0.0", name: str = "version", hard: bool = False, verbose: bool = False, msg: str = "",) -> bool:
            # -> 用于检查当前版本是否满足指定的版本要求。返回版本检查的结果。如果 result 为 True ,表示版本检查通过;如果为 False ,表示版本检查未通过。
            # -> return result
            assert check_version(metadata.version(name), required)  # exception if requirements not met
        # 捕获异常并记录缺失或不符合要求的依赖项。
        # 如果捕获到 AssertionError 或 metadata.PackageNotFoundError (表示包未安装)。
        except (AssertionError, metadata.PackageNotFoundError):
            # 将当前依赖项 r 添加到 pkgs 列表中,表示该依赖项需要安装或更新。
            pkgs.append(r)
    # 这段代码的主要功能是。处理依赖项字符串:提取依赖项的名称,并去除可能的 .git 后缀。解析依赖项:使用正则表达式解析依赖项的名称和版本规范。检查依赖项版本:获取已安装的包版本,并检查是否满足版本规范。记录缺失或不符合要求的依赖项:如果依赖项未安装或版本不符合要求,将其添加到 pkgs 列表中。通过这段代码,可以有效地检查项目依赖项的安装状态和版本是否符合要求,为后续的自动安装或用户提示提供依据。

    # 这段代码定义了一个名为 attempt_install 的函数,用于尝试安装Python包,并在安装失败时进行重试。该函数使用了 @Retry 装饰器来实现重试机制。
    # 使用 @Retry 装饰器,设置最多重试2次,每次重试之间延迟1秒。
    # class Retry(contextlib.ContextDecorator):
    # -> 这个类的作用是作为一个装饰器,用于为函数提供重试机制。当被装饰的函数抛出异常时, Retry 装饰器会根据指定的次数和延迟进行重试。
    # -> def __init__(self, times=3, delay=2):
    @Retry(times=2, delay=1)
    # 定义了 attempt_install 函数,它接受两个参数。
    # 1.packages :要安装的包的名称或列表,以空格分隔。
    # 2.commands :额外的命令行参数,例如 --upgrade 或 --user 。
    def attempt_install(packages, commands):
        # 尝试 pip install 命令,如果失败则重试。
        """Attempt pip install command with retries on failure."""

        # subprocess.check_output(cmd, *args, **kwargs)
        # check_output() 函数是 Python 标准库 subprocess 模块中的一个函数,它用于执行指定的命令并获取命令的输出。如果命令执行成功, check_output() 会返回命令的输出;如果命令执行失败(即返回非零退出状态),则会抛出一个 CalledProcessError 异常。
        # 参数说明 :
        # cmd :要执行的命令,可以是字符串或者字符串列表。如果是字符串,会被 shell 解释,这与 shell=True 相同;如果是字符串列表,则直接传递给底层的 execvp() 函数。
        # *args :传递给 subprocess.Popen() 的其他参数。
        # **kwargs :传递给 subprocess.Popen() 的其他关键字参数。常用的关键字参数包括 :
        # shell :如果为 True ,则 cmd 会被 shell 解释。默认为 False 。
        # stdout :子进程的 stdout 管道。默认为 subprocess.PIPE ,即捕获输出。
        # stderr :子进程的 stderr 管道。默认为 subprocess.PIPE ,即捕获错误输出。
        # universal_newlines 或 text :如果设置为 True ,则 check_output() 返回一个字符串而不是字节对象。在 Python 3.7 及更高版本中, text 参数被引入, universal_newlines 被废弃。
        # 返回值 :
        # 返回执行命令后的标准输出(stdout)。
        # 异常 :
        # 如果命令返回非零退出状态, check_output() 会抛出 subprocess.CalledProcessError 异常。

        # 使用 subprocess.check_output 执行 pip install 命令。
        # 构造命令字符串 f"pip install --no-cache-dir {packages} {commands}" 。
        # --no-cache-dir :禁用pip的缓存,确保每次都从源安装。
        # {packages} :要安装的包的名称或列表。
        # {commands} :额外的命令行参数。
        # shell=True :允许通过shell执行命令。
        # subprocess.check_output 返回命令的输出,使用 .decode() 将其从字节转换为字符串。
        return subprocess.check_output(f"pip install --no-cache-dir {packages} {commands}", shell=True).decode()
    # 这段代码的主要功能是。重试机制:使用 @Retry 装饰器,设置最多重试2次,每次重试之间延迟1秒。执行 pip install 命令:构造并执行 pip install 命令,安装指定的包。禁用pip的缓存,确保每次都从源安装。支持额外的命令行参数,例如 --upgrade 或 --user 。返回安装结果:返回 pip install 命令的输出,方便后续处理或日志记录。通过这个函数,可以更可靠地安装Python包,特别是在网络不稳定或包安装失败的情况下,重试机制可以提高安装的成功率。

    # 这段代码的主要功能是根据检查到的缺失依赖项(存储在 pkgs 列表中),决定是否自动安装这些依赖项,并记录相应的日志信息。
    # 构造安装命令的字符串。使用列表推导式和 join 方法,将 pkgs 列表中的每个包名用双引号括起来,并用空格连接成一个字符串。 这是为了构造一个适合在命令行中使用的包名列表字符串,例如 "pkg1" "pkg2" 。
    s = " ".join(f'"{x}"' for x in pkgs)  # console string
    # 检查是否有缺失的依赖项。如果字符串 s 不为空,说明 pkgs 列表中有缺失的依赖项。
    if s:
        # 检查是否启用了自动安装功能。
        # install :函数参数,指示是否允许自动安装。
        # AUTOINSTALL :环境变量,指示是否启用自动安装功能。
        if install and AUTOINSTALL:  # check environment variable
            # 记录缺失依赖项信息。
            # 计算缺失依赖项的数量 n 。
            n = len(pkgs)  # number of packages updates
            # 使用 LOGGER.info 记录一条信息,提示缺失的依赖项,并说明将尝试自动更新。
            # prefix :前面定义的彩色前缀字符串,用于在终端中打印彩色消息。
            # requirement{'s' * (n > 1)} :根据 n 的值动态添加复数形式的 s 。
            LOGGER.info(f"{prefix} Ultralytics requirement{'s' * (n > 1)} {pkgs} not found, attempting AutoUpdate...")    # 未找到 {prefix} Ultralytics 要求 {'s' * (n > 1)} {pkgs},正在尝试自动更新...
            # 尝试自动安装缺失的依赖项。
            # 使用 try 块尝试自动安装缺失的依赖项。
            try:
                # 记录开始时间。
                t = time.time()
                # 检查是否处于在线状态,如果离线则跳过自动更新。
                assert ONLINE, "AutoUpdate skipped (offline)"    # 跳过自动更新(离线)。
                # 调用 attempt_install 函数尝试安装缺失的依赖项,并记录安装过程的输出。
                LOGGER.info(attempt_install(s, cmds))
                # 计算安装所花费的时间。
                dt = time.time() - t
                # 使用 LOGGER.info 记录成功消息,包括安装时间、安装的包数以及包名。提示用户重启运行时或重新运行命令以使更新生效。
                LOGGER.info(
                    f"{prefix} AutoUpdate success ✅ {dt:.1f}s, installed {n} package{'s' * (n > 1)}: {pkgs}\n"    # {prefix} 自动更新成功✅ {dt:.1f}s,安装了 {n} 个软件包{'s' * (n > 1)}:{pkgs}。
                    f"{prefix} ⚠️ {colorstr('bold', 'Restart runtime or rerun command for updates to take effect')}\n"    # {prefix} ⚠️ {colorstr('bold', '重新启动运行时或重新运行命令以使更新生效')。
                )
            # 捕获安装过程中的异常。
            except Exception as e:
                # 如果在安装过程中发生异常,捕获异常并使用 LOGGER.warning 记录警告信息。
                LOGGER.warning(f"{prefix} ❌ {e}")
                # 返回 False ,表示自动安装失败。
                return False
        # 如果未启用自动安装。
        else:
            # 如果未启用自动安装功能( install 为 False 或 AUTOINSTALL 为 False ),直接返回 False ,表示未安装缺失的依赖项。
            return False

    # 如果 pkgs 列表为空(即没有缺失的依赖项),返回 True ,表示所有依赖项都已满足。
    return True
    # 这段代码的主要功能是。构造安装命令的字符串:将缺失的依赖项列表转换为适合命令行使用的字符串。检查是否有缺失的依赖项:如果没有缺失的依赖项,直接返回 True 。检查是否启用自动安装:如果未启用自动安装功能,返回 False 。尝试自动安装缺失的依赖项:使用 attempt_install 函数尝试安装缺失的依赖项。记录安装过程的输出和结果。如果安装成功,返回 True ;如果安装失败,返回 False 。通过这段代码,可以实现对缺失依赖项的自动安装功能,并提供详细的日志记录,方便用户了解安装过程和结果。
# 这段代码的主要功能是。解析依赖项:从 requirements.txt 文件或直接指定的依赖项中解析依赖项列表。排除指定的依赖项。检查依赖项:检查已安装的包版本是否满足要求。如果版本不满足要求或包未安装,记录缺失的依赖项。自动安装缺失的依赖项:如果启用了自动安装( install 和 AUTOINSTALL 为 True ),尝试安装缺失的依赖项。使用 Retry 装饰器支持重试机制。返回检查结果:如果所有依赖项都已满足或安装成功,返回 True 。如果存在未满足的依赖项且未启用自动安装,返回 False 。通过这个函数,可以方便地检查和安装项目的依赖项,确保项目能够正常运行。
# def check_requirements(requirements=ROOT.parent / "requirements.txt", exclude=(), install=True, cmds=""):
# -> 用于检查和安装Python项目的依赖项。返回 False ,表示自动安装失败。如果未启用自动安装功能( install 为 False 或 AUTOINSTALL 为 False ),直接返回 False ,表示未安装缺失的依赖项。如果 pkgs 列表为空(即没有缺失的依赖项),返回 True ,表示所有依赖项都已满足。
# -> return False / return False / return True

12.def check_torchvision(): 

# 这段代码定义了一个名为 check_torchvision 的函数,用于检查当前安装的 PyTorch 和 torchvision 的版本是否兼容。
# 定义了一个函数 check_torchvision ,该函数不接收任何参数。
def check_torchvision():
    # 检查已安装的 PyTorch 和 Torchvision 版本以确保它们兼容。
    # 此函数检查已安装的 PyTorch 和 Torchvision 版本,并根据提供的兼容性表(基于 https://github.com/pytorch/vision#installation)警告它们是否不兼容。
    # 兼容性表是一个字典,其中的键是 PyTorch 版本,值是兼容 Torchvision 版本的列表。
    """
    Checks the installed versions of PyTorch and Torchvision to ensure they're compatible.

    This function checks the installed versions of PyTorch and Torchvision, and warns if they're incompatible according
    to the provided compatibility table based on:
    https://github.com/pytorch/vision#installation.

    The compatibility table is a dictionary where the keys are PyTorch versions and the values are lists of compatible
    Torchvision versions.
    """
    # Compatibility table
    # 定义了一个字典 compatibility_table ,用于存储 PyTorch 和 torchvision 的版本兼容性信息。键是 PyTorch 的版本号(主版本号和次版本号),值是与该 PyTorch 版本兼容的 torchvision 版本号列表。
    compatibility_table = {
        "2.5": ["0.20"],
        "2.4": ["0.19"],
        "2.3": ["0.18"],
        "2.2": ["0.17"],
        "2.1": ["0.16"],
        "2.0": ["0.15"],
        "1.13": ["0.14"],
        "1.12": ["0.13"],
    }

    # Extract only the major and minor versions
    # 提取 PyTorch 版本号的主版本号和次版本号。例如,如果 torch.__version__ 是 "2.5.1+cu124" ,则提取的版本号为 "2.5" 。
    v_torch = ".".join(torch.__version__.split("+")[0].split(".")[:2])
    # 检查提取的 PyTorch 版本号是否在兼容性表中。
    if v_torch in compatibility_table:
        # 如果 PyTorch 版本号在兼容性表中,则获取与该版本兼容的 torchvision 版本号列表。
        compatible_versions = compatibility_table[v_torch]
        # 提取 torchvision 版本号的主版本号和次版本号。例如,如果 TORCHVISION_VERSION 是 "0.20.1+cu124" ,则提取的版本号为 "0.20" 。
        v_torchvision = ".".join(TORCHVISION_VERSION.split("+")[0].split(".")[:2])
        # 检查当前安装的 torchvision 版本号是否与兼容性表中的任何一个版本号匹配。如果都不匹配,则说明当前安装的 torchvision 版本与 PyTorch 版本不兼容。
        if all(v_torchvision != v for v in compatible_versions):
            # 如果版本不兼容,则打印警告信息,提示用户当前安装的 torchvision 版本与 PyTorch 版本不兼容,并建议用户通过以下命令修复或更新 :
            # pip install torchvision=={compatible_versions[0]} :安装与当前 PyTorch 版本兼容的 torchvision 版本。
            # pip install -U torch torchvision :更新 PyTorch 和 torchvision 到最新版本。
            print(
                f"WARNING ⚠️ torchvision=={v_torchvision} is incompatible with torch=={v_torch}.\n"    # 警告⚠️ torchvision=={v_torchvision} 与 torch=={v_torch} 不兼容。
                f"Run 'pip install torchvision=={compatible_versions[0]}' to fix torchvision or "    # 运行“pip install torchvision=={compatible_versions[0]}”以修复 torchvision 或运行“pip install -U torch torchvision”以更新两者。
                "'pip install -U torch torchvision' to update both.\n"
                "For a full compatibility table see https://github.com/pytorch/vision#installation"    # 有关完整兼容性表,请参阅 https://github.com/pytorch/vision#installation。
            )
# check_torchvision 函数通过检查 PyTorch 和 torchvision 的版本号,确保它们之间的兼容性。它使用一个预定义的兼容性表来验证当前安装的版本是否匹配。如果不匹配,则打印警告信息并提供修复建议。这种设计有助于避免因版本不兼容而导致的运行时错误,确保深度学习项目的稳定性和兼容性。

13.def check_suffix(file="yolo11n.pt", suffix=".pt", msg=""): 

# 这段代码定义了一个名为 check_suffix 的函数,用于检查文件的后缀是否符合指定的允许后缀列表。如果文件的后缀不符合要求,函数会抛出一个 AssertionError 。
# 定义了 check_suffix 函数,它接受以下参数 :
# 1.file :文件名或文件名列表,可以是一个字符串或一个包含多个文件名的列表/元组。
# 2.suffix :允许的文件后缀,可以是一个字符串或一个包含多个后缀的元组。
# 3.msg :可选的错误消息前缀,用于在抛出异常时提供额外的上下文信息。
def check_suffix(file="yolo11n.pt", suffix=".pt", msg=""):
    # 检查文件是否有可接受的后缀。
    """Check file(s) for acceptable suffix."""
    # 如果 file 和 suffix 都不为空,则继续执行后续逻辑。
    if file and suffix:
        # 如果 suffix 是一个字符串。
        if isinstance(suffix, str):
            # 将其转换为一个包含单个元素的元组。这使得后续的逻辑可以统一处理单个后缀和多个后缀的情况。
            suffix = (suffix,)
        # 如果 file 是一个列表或元组,则直接遍历其中的每个文件名。 如果 file 是一个字符串,则将其视为单个文件名,并将其放在一个列表中进行遍历。
        for f in file if isinstance(file, (list, tuple)) else [file]:
            # 使用 Path(f).suffix 获取文件的后缀。 使用 lower() 将后缀转换为小写,以确保比较时不区分大小写。 使用 strip() 去除后缀首尾的空白字符。
            s = Path(f).suffix.lower().strip()  # file suffix
            # 如果文件后缀不为空( len(s) > 0 )。
            if len(s):
                # 则检查该后缀是否在允许的后缀列表 suffix 中。
                # 如果文件后缀不在允许的后缀列表中,抛出一个 AssertionError ,并提供一个包含错误信息的消息。消息中包含可选的 msg 前缀、文件名 f 、允许的后缀列表 suffix 和实际的文件后缀 s 。
                assert s in suffix, f"{msg}{f} acceptable suffix is {suffix}, not {s}"    # {msg}{f} 可接受的后缀是 {suffix},而不是 {s}。
# 这段代码的主要功能是。检查文件后缀:确保文件的后缀符合指定的允许后缀列表。支持单个文件和文件列表:可以处理单个文件名或包含多个文件名的列表/元组。支持单个后缀和多个后缀:允许后缀可以是一个字符串或一个包含多个后缀的元组。提供详细的错误信息:如果文件后缀不符合要求,抛出一个 AssertionError ,并提供详细的错误信息,方便调试和问题定位。通过这个函数,可以方便地验证文件的后缀是否符合预期,确保文件类型正确。

14.def check_yolov5u_filename(file: str, verbose: bool = True): 

# 这段代码定义了一个名为 check_yolov5u_filename 的函数,用于将旧版YOLOv5文件名替换为更新后的YOLOv5u文件名。它还提供了一个提示信息,建议用户使用新的文件名,因为YOLOv5u模型在性能上有所改进。
# 定义了 check_yolov5u_filename 函数,它接受以下参数 :
# 1.file :文件名,通常是YOLOv5模型的文件名。
# 2.verbose :一个布尔值,指示是否打印提示信息,默认为 True 。
def check_yolov5u_filename(file: str, verbose: bool = True):
    # 用更新的 YOLOv5u 文件名替换旧的 YOLOv5 文件名。
    """Replace legacy YOLOv5 filenames with updated YOLOv5u filenames."""
    # 如果文件名中包含 yolov3 或 yolov5 ,则继续执行后续逻辑。
    if "yolov3" in file or "yolov5" in file:
        # 如果文件名中包含 u.yaml 。
        if "u.yaml" in file:
            # 则将其替换为 .yaml 。例如, yolov5nu.yaml 将被替换为 yolov5n.yaml 。
            file = file.replace("u.yaml", ".yaml")  # i.e. yolov5nu.yaml -> yolov5n.yaml
        # 处理 .pt 文件名。
        # 如果文件名中包含 .pt 且不包含 u ,则使用正则表达式进行替换。
        elif ".pt" in file and "u" not in file:
            original_file = file
            # 将 yolov5n.pt 替换为 yolov5nu.pt 。
            file = re.sub(r"(.*yolov5([nsmlx]))\.pt", "\\1u.pt", file)  # i.e. yolov5n.pt -> yolov5nu.pt
            # 将 yolov5n6.pt 替换为 yolov5n6u.pt 。
            file = re.sub(r"(.*yolov5([nsmlx])6)\.pt", "\\1u.pt", file)  # i.e. yolov5n6.pt -> yolov5n6u.pt
            # 将 yolov3-spp.pt 替换为 yolov3-sppu.pt 。
            file = re.sub(r"(.*yolov3(|-tiny|-spp))\.pt", "\\1u.pt", file)  # i.e. yolov3-spp.pt -> yolov3-sppu.pt
            # 打印提示信息。
            # 如果文件名发生了变化( file != original_file )且 verbose 为 True 。
            if file != original_file and verbose:
                # 则使用 LOGGER.info 打印一条提示信息,建议用户使用新的文件名。 提示信息中包含了一个链接,指向`https://github.com/ultralytics/ultralytics`,说明YOLOv5u模型在性能上有所改进。
                LOGGER.info(
                    f"PRO TIP 💡 Replace 'model={original_file}' with new 'model={file}'.\nYOLOv5 'u' models are "    # 专业提示💡 将“model={original_file}”替换为新的“model={file}”。
                    f"trained with https://github.com/ultralytics/ultralytics and feature improved performance vs "    # YOLOv5“u”模型使用 https://github.com/ultralytics/ultralytics 进行训练,
                    f"standard YOLOv5 models trained with https://github.com/ultralytics/yolov5.\n"    # 与使用 https://github.com/ultralytics/yolov5 进行训练的标准 YOLOv5 模型相比,其性能有所提升。
                )
    # 返回更新后的文件名。
    return file
# 这段代码的主要功能是。检查文件名是否包含YOLOv3或YOLOv5:如果文件名中包含 yolov3 或 yolov5 ,则继续处理。处理 u.yaml 文件名:将 u.yaml 替换为 .yaml 。处理 .pt 文件名:使用正则表达式将旧版YOLOv5文件名替换为YOLOv5u文件名。打印提示信息:如果文件名发生了变化且 verbose 为 True ,打印一条提示信息,建议用户使用新的文件名。返回更新后的文件名:返回更新后的文件名,以便后续使用。通过这个函数,可以方便地将旧版YOLOv5文件名替换为更新后的YOLOv5u文件名,并提供性能改进的提示信息。

15.def check_model_file_from_stem(model="yolo11n"):

# 这段代码定义了一个名为 check_model_file_from_stem 的函数,用于根据模型的名称(或“stem”)返回完整的模型文件名。如果输入的模型名称没有文件扩展名,并且是一个有效的模型名称,则函数会自动为其添加 .pt 扩展名。
# 定义了一个函数 check_model_file_from_stem ,接收一个参数。
# 1.model :默认值为 "yolo11n" ,表示模型的名称或路径。
def check_model_file_from_stem(model="yolo11n"):
    # 从有效的模型词干返回模型文件名。
    """Return a model filename from a valid model stem."""

    # Path(path)
    # 在Python中, Path 是 pathlib 模块中的一个类,用于表示文件系统路径。 pathlib 是一个现代的文件路径操作库,它提供了面向对象的方式来处理文件和目录路径。
    # 导入 Path 类 : from pathlib import Path。
    # 可以使用 Path 类来创建一个路径对象。这个对象可以是一个文件或者目录的路径。
    # 路径操作 :
    # Path 对象提供了许多方法来操作路径。
    # p.exists() :检查路径是否存在。
    # p.is_file() :检查路径是否指向一个文件。
    # p.is_dir() :检查路径是否指向一个目录。
    # p.resolve() :解析路径,返回绝对路径。
    # p.parent :返回路径的父目录。
    # p.name :返回路径的最后一部分(文件名)。
    # p.suffix :返回文件的后缀名。
    # p.stem :返回文件名不包括后缀的部分。

    # 检查以下条件。
    # model 不为空。
    # Path(model).suffix 为空,即输入的 model 没有文件扩展名。
    # Path(model).stem 在 downloads.GITHUB_ASSETS_STEMS 中,即输入的模型名称是一个有效的模型名称。
    # GITHUB_ASSETS_STEMS -> 一个包含所有YOLO模型文件和其他资产文件名的茎(stem)的列表。
    if model and not Path(model).suffix and Path(model).stem in downloads.GITHUB_ASSETS_STEMS:
        # 如果条件满足,则使用 Path(model).with_suffix(".pt") 为模型名称添加 .pt 扩展名,并返回完整的文件路径。例如,输入 "yolo11n" 会返回 Path("yolo11n.pt") 。
        return Path(model).with_suffix(".pt")  # add suffix, i.e. yolo11n -> yolo11n.pt
    # 如果条件不满足。
    else:
        # 则直接返回原始的 model 输入。
        return model
# check_model_file_from_stem 函数通过检查输入的模型名称是否有效且没有文件扩展名,自动为其添加 .pt 扩展名。这种设计确保了用户可以使用简化的模型名称(如 "yolo11n" ),而函数会自动处理并返回完整的模型文件名。如果输入的模型名称已经包含扩展名或无效,则直接返回原始输入。这提高了代码的灵活性和用户体验。

16.def check_file(file, suffix="", download=True, download_dir=".", hard=True): 

# 这段代码定义了一个名为 check_file 的函数,用于检查文件是否存在,如果不存在则尝试下载文件,并返回文件的路径。
# 定义了 check_file 函数,它接受以下参数 :
# 1.file :文件名或URL。
# 2.suffix :允许的文件后缀,默认为空字符串。
# 3.download :是否尝试下载文件,默认为 True 。
# 4.download_dir :下载目录,默认为当前目录( "." )。
# 5.hard :是否严格检查文件存在性,默认为 True 。
def check_file(file, suffix="", download=True, download_dir=".", hard=True):
    # 搜索/下载文件(如有必要)并返回路径。
    """Search/download file (if necessary) and return path."""
    # 调用 check_suffix 函数检查文件的后缀是否符合指定的允许后缀列表。如果 suffix 为空,则此步骤可选。
    # def check_suffix(file="yolo11n.pt", suffix=".pt", msg=""): -> 用于检查文件的后缀是否符合指定的允许后缀列表。如果文件的后缀不符合要求,函数会抛出一个 AssertionError 。
    check_suffix(file, suffix)  # optional
    # 将 file 转换为字符串,并去除首尾的空白字符。
    file = str(file).strip()  # convert to string and strip spaces
    # 调用 check_yolov5u_filename 函数,将旧版YOLOv5文件名替换为更新后的YOLOv5u文件名。
    file = check_yolov5u_filename(file)  # yolov5n -> yolov5nu
    # 检查文件是否存在。
    # 如果 file 为空,或者文件路径中不包含 :// 且文件已存在,或者文件路径以 grpc:// 开头(表示gRPC Triton图像)
    if (
        not file
        or ("://" not in file and Path(file).exists())  # '://' check required in Windows Python<3.10
        or file.lower().startswith("grpc://")
    ):  # file exists or gRPC Triton images
        # 则直接返回 file 。
        return file
    # 如果启用了下载功能( download=True )且文件路径以 https:// 、 http:// 、 rtsp:// 、 rtmp:// 或 tcp:// 开头,则尝试下载文件。
    elif download and file.lower().startswith(("https://", "http://", "rtsp://", "rtmp://", "tcp://")):  # download
        # 将文件路径保存为 url 。
        url = file  # warning: Pathlib turns :// -> :/
        # 使用 url2file 函数提取文件名,并将其保存到指定的下载目录 download_dir 中。
        # def url2file(url): -> 用于将URL转换为文件名。使用 clean_url(url) 清理URL,去除查询参数(如 ?auth )。 将清理后的URL字符串转换为 Path 对象。 使用 Path 对象的 .name 属性提取文件名部分。 -> return Path(clean_url(url)).name
        file = Path(download_dir) / url2file(file)  # '%2F' to '/', split https://url.com/file.txt?auth
        # 如果文件已存在,记录一条信息,说明文件已本地存在。
        if file.exists():
            # def clean_url(url): -> 用于清理URL,去除其中的查询参数(如 ?auth )。使用 unquote(url) 将URL中的百分号编码(如 %2F )转换为普通字符(如 / )。 使用 split("?")[0] 将URL分割为两部分,取第一部分(即去除查询参数后的URL)。 -> return unquote(url).split("?")[0]  # '%2F' to '/', split https://url.com/file.txt?auth
            LOGGER.info(f"Found {clean_url(url)} locally at {file}")  # file already exists    在本地的 {file} 处找到了 {clean_url(url)}。
        # 如果文件不存在,调用 downloads.safe_download 函数下载文件。
        else:
            downloads.safe_download(url=url, file=file, unzip=False)
        # 返回下载后的文件路径,将其转换为字符串形式。
        return str(file)
    # 如果文件路径不是URL且文件不存在,则尝试在项目根目录 ROOT 及其父目录中搜索文件。
    else:  # search
        # 这行代码的作用是尝试在指定的目录及其子目录中查找文件,并返回匹配的文件路径列表。它使用了 glob.glob 函数来实现这一功能。
        # 第一部分 :在项目根目录及其子目录中查找文件。
        # glob.glob(str(ROOT / "**" / file), recursive=True)
        # ROOT :项目根目录的路径。
        # ROOT / "**" / file :构造一个路径字符串,表示在 ROOT 目录及其所有子目录中查找文件 file 。
        # recursive=True :启用递归查找,即在所有子目录中查找匹配的文件。
        # str(...) :将路径对象转换为字符串,因为 glob.glob 需要一个字符串路径。
        # 第二部分 :在项目根目录的父目录中查找文件。
        # glob.glob(str(ROOT.parent / file))
        # ROOT.parent :项目根目录的父目录。
        # ROOT.parent / file :构造一个路径字符串,表示在 ROOT.parent 目录中查找文件 file 。
        # str(...) :将路径对象转换为字符串,因为 glob.glob 需要一个字符串路径。
        # 逻辑或操作, or 。
        # 如果第一部分( glob.glob(str(ROOT / "**" / file), recursive=True) )返回的列表不为空,则 files 将被赋值为该列表。
        # 如果第一部分返回的列表为空,则尝试第二部分( glob.glob(str(ROOT.parent / file)) )的结果。
        # 这行代码的主要功能是。递归查找文件:在项目根目录及其所有子目录中查找指定的文件。父目录查找:如果在项目根目录及其子目录中未找到文件,则在项目根目录的父目录中查找。返回匹配的文件路径列表:如果找到匹配的文件,返回文件路径列表;否则返回空列表。通过这种逻辑,可以确保在多个可能的目录中找到目标文件,提高代码的灵活性和健壮性。
        files = glob.glob(str(ROOT / "**" / file), recursive=True) or glob.glob(str(ROOT.parent / file))  # find file
        # 如果未找到文件且 hard=True ,抛出 FileNotFoundError 。
        if not files and hard:
            raise FileNotFoundError(f"'{file}' does not exist")    # ‘{file}’不存在。
        # 如果找到多个匹配的文件且 hard=True ,抛出 FileNotFoundError ,提示用户指定确切路径。
        elif len(files) > 1 and hard:
            raise FileNotFoundError(f"Multiple files match '{file}', specify exact path: {files}")    # 多个文件与“{file}”匹配,请指定确切路径:{files}。
        # 如果找到文件,返回第一个匹配的文件路径。 如果未找到文件,返回空列表 [] 。
        return files[0] if len(files) else []  # return file
# 这段代码的主要功能是。检查文件后缀:确保文件的后缀符合指定的允许后缀列表。处理文件名:转换为字符串并去除首尾空白字符。将旧版YOLOv5文件名替换为更新后的YOLOv5u文件名。检查文件是否存在:如果文件已存在或路径为gRPC Triton图像,直接返回文件路径。下载文件:如果文件路径为URL且启用了下载功能,尝试下载文件。搜索文件:如果文件路径不是URL且文件不存在,尝试在项目根目录及其父目录中搜索文件。返回文件路径:返回找到的文件路径或抛出异常。通过这个函数,可以方便地检查文件是否存在,如果不存在则尝试下载文件,或者在本地搜索文件。

17.def check_yaml(file, suffix=(".yaml", ".yml"), hard=True): 

# 这段代码定义了一个名为 check_yaml 的函数,用于检查YAML文件是否存在,如果不存在则尝试下载文件,并返回文件的路径。它还检查文件的后缀是否符合指定的允许后缀列表。
# 定义了 check_yaml 函数,它接受以下参数 :
# 1.file :YAML文件的路径或URL。
# 2.suffix :允许的文件后缀,默认为 (".yaml", ".yml") 。
# 3.hard :是否严格检查文件存在性,默认为 True 。
def check_yaml(file, suffix=(".yaml", ".yml"), hard=True):
    # 搜索/下载 YAML 文件(如有必要)并返回路径,检查后缀。
    """Search/download YAML file (if necessary) and return path, checking suffix."""
    # 调用 check_file 函数,检查文件是否存在,如果不存在则尝试下载文件,并返回文件的路径。 check_file 函数的参数 :
    # file :YAML文件的路径或URL。
    # suffix :允许的文件后缀。
    # hard :是否严格检查文件存在性。
    # def check_file(file, suffix="", download=True, download_dir=".", hard=True):
    # -> 用于检查文件是否存在,如果不存在则尝试下载文件,并返回文件的路径。则直接返回 file 。返回下载后的文件路径,将其转换为字符串形式。如果找到文件,返回第一个匹配的文件路径。 如果未找到文件,返回空列表 [] 。
    # -> return file / return str(file) / return files[0] if len(files) else []  # return file
    return check_file(file, suffix, hard=hard)
# 这段代码的主要功能是。检查文件后缀:确保文件的后缀符合指定的允许后缀列表。检查文件是否存在:如果文件已存在,直接返回文件路径。下载文件:如果文件不存在且启用了下载功能,尝试从指定的URL下载文件。返回文件路径:返回文件的路径。通过这个函数,可以方便地检查YAML文件是否存在,如果不存在则尝试下载文件,确保文件的后缀符合要求。这在处理配置文件时非常有用。

18.def check_is_path_safe(basedir, path): 

# 这段代码定义了一个名为 check_is_path_safe 的函数,用于检查一个路径是否安全,即该路径是否位于指定的基础目录( basedir )内部。
# 定义了一个函数 check_is_path_safe ,接收两个参数。
# 1.basedir :基础目录路径。
# 2.path :需要检查的目标路径。
def check_is_path_safe(basedir, path):
    # 检查解析的路径是否位于预期目录下,以防止路径遍历。
    """
    Check if the resolved path is under the intended directory to prevent path traversal.

    Args:
        basedir (Path | str): The intended directory.
        path (Path | str): The path to check.

    Returns:
        (bool): True if the path is safe, False otherwise.
    """
    # 使用 Path(basedir).resolve() 解析基础目录路径 ,将其转换为绝对路径,并解析所有符号链接。 resolve() 方法确保路径是绝对路径,且符号链接被解析为实际路径。
    base_dir_resolved = Path(basedir).resolve()
    # 使用 Path(path).resolve() 解析目标路径 ,将其转换为绝对路径,并解析所有符号链接。
    path_resolved = Path(path).resolve()

    # 检查目标路径是否安全。
    # path_resolved.exists() :检查目标路径是否存在。
    # path_resolved.parts[: len(base_dir_resolved.parts)] == base_dir_resolved.parts :比较目标路径的前缀部分是否与基础目录的路径部分完全一致。 parts 是一个元组,包含路径的各个部分(例如, /home/user 的 parts 是 ('/home', 'user') )。
    # 如果 目标路径存在 且 其前缀部分与基础目录的路径部分完全一致 ,则返回 True ,表示路径安全;否则返回 False 。
    return path_resolved.exists() and path_resolved.parts[: len(base_dir_resolved.parts)] == base_dir_resolved.parts
# check_is_path_safe 函数通过解析基础目录和目标路径为绝对路径,并比较目标路径的前缀部分是否与基础目录的路径部分一致,来判断目标路径是否位于基础目录内部。这种设计可以有效防止路径遍历攻击(例如,用户尝试访问超出指定目录范围的文件),确保路径的安全性。

19.def check_imshow(warn=False): 

# 这段代码定义了一个名为 check_imshow 的函数,用于检查当前环境是否支持图像显示。它通过尝试使用 OpenCV 的 imshow 函数显示一个小型图像来验证环境是否支持图像显示。
# 定义了一个函数 check_imshow ,接收一个参数。
# 1.warn :默认值为 False 。如果 warn 为 True ,则在环境不支持图像显示时打印警告信息。
def check_imshow(warn=False):
    # 检查环境是否支持图像显示。
    """Check if environment supports image displays."""
    # 在 Linux 系统上,检查以下条件。
    try:
        if LINUX:
            # 确保当前环境不是 Google Colab 或 Kaggle,因为这些环境通常不支持直接的图像显示。
            assert not IS_COLAB and not IS_KAGGLE
            # 确保 DISPLAY 环境变量已设置,这是在 Linux 上显示图形界面的必要条件。
            assert "DISPLAY" in os.environ, "The DISPLAY environment variable isn't set."    # 未设置 DISPLAY 环境变量。
        # 使用 OpenCV 的 imshow 函数显示一个小型图像(8x8 像素,3 通道,黑色背景)。这一步用于测试环境是否支持图像显示。
        cv2.imshow("test", np.zeros((8, 8, 3), dtype=np.uint8))  # show a small 8-pixel image
        # 调用 cv2.waitKey(1) 等待 1 毫秒,确保图像窗口有足够的时间显示。这一步是必要的,因为 imshow 是非阻塞的。
        cv2.waitKey(1)
        # 关闭所有 OpenCV 创建的窗口,清理资源。
        cv2.destroyAllWindows()
        # 再次调用 cv2.waitKey(1) ,确保窗口完全关闭。
        cv2.waitKey(1)
        # 如果上述步骤没有抛出异常,则返回 True ,表示环境支持图像显示。
        return True
    # 捕获在测试过程中可能发生的任何异常。
    except Exception as e:
        # 如果 warn 参数为 True ,则打印警告信息,说明当前环境不支持图像显示,并输出异常信息。
        if warn:
            LOGGER.warning(f"WARNING ⚠️ Environment does not support cv2.imshow() or PIL Image.show()\n{e}")    # 警告⚠️ 环境不支持 cv2.imshow() 或 PIL Image.show()\n{e}。
        # 返回 False ,表示环境不支持图像显示。
        return False
# check_imshow 函数通过尝试使用 OpenCV 的 imshow 函数显示一个小型图像,验证当前环境是否支持图像显示。它还考虑了 Linux 系统上的特殊条件,如 DISPLAY 环境变量的设置。如果测试失败,函数可以选择性地打印警告信息,并返回 False ,表示环境不支持图像显示。这种设计确保了代码在不同环境中能够正确地检测图像显示功能的支持情况。

20.def check_yolo(verbose=True, device=""): 

# 这段代码定义了一个名为 check_yolo 的函数,用于生成关于 YOLO 软件和硬件环境的摘要信息。它提供了系统资源(如 CPU、内存和磁盘使用情况)的简要概述,并确保环境设置正确。
# 定义了一个函数 check_yolo ,接收两个参数。
# 1.verbose :一个布尔值,默认为 True ,表示是否打印详细的系统信息。
# 2.device :一个字符串,默认为空,表示要使用的设备(如 "cpu" 或 "cuda" )。
def check_yolo(verbose=True, device=""):
    # 返回人类可读的 YOLO 软件和硬件摘要。
    """Return a human-readable YOLO software and hardware summary."""
    # 导入 psutil 模块,用于获取系统资源信息(如 CPU、内存和磁盘使用情况)。
    import psutil

    # 从 ultralytics.utils.torch_utils 模块中导入 select_device 函数,用于选择合适的设备(CPU 或 GPU)。
    from ultralytics.utils.torch_utils import select_device

    # 如果当前环境是 Google Colab 。
    if IS_COLAB:

        # shutil.rmtree(path, ignore_errors=False, onerror=None)
        # shutil.rmtree 是 Python 标准库 shutil 模块中的一个函数,用于递归地删除一个目录以及其中的所有内容。这个函数会删除指定的目录,包括其中的所有子目录和文件,以及这些子目录和文件的所有内容。
        # 参数 :
        # path : 要删除的目录的路径。这个路径可以是字符串或者 Path 对象。
        # ignore_errors (可选): 如果设置为 True ,则在删除文件或目录时,如果遇到任何错误(例如权限错误), shutil.rmtree 会忽略这些错误。默认值为 False ,即在遇到错误时会抛出异常。
        # onerror (可选): 一个回调函数,用于处理在删除过程中遇到的错误。
        # 这个回调函数会接收三个参数 :函数(即 onerror 本身)、路径和异常信息。如果提供了 onerror 回调函数,那么即使 ignore_errors 设置为 False ,也不会抛出异常,而是调用回调函数来处理错误。
        # 注意事项 :
        # 使用 shutil.rmtree 时要非常小心,因为它会永久删除文件和目录,这个操作是不可逆的。
        # 确保在调用 shutil.rmtree 之前,你确实希望删除指定的目录及其所有内容。
        # 如果你只想删除空目录,可以使用 os.rmdir 或 pathlib.Path.rmdir 方法。 shutil.rmtree 是一个强大的工具,但也需要谨慎使用,以避免意外删除重要数据。

        # 则删除 Colab 默认的 /sample_data 目录。 shutil.rmtree 用于递归删除目录, ignore_errors=True 表示忽略删除过程中可能出现的错误。
        shutil.rmtree("sample_data", ignore_errors=True)  # remove colab /sample_data directory

    # 如果 verbose 为 True ,则收集系统信息。
    if verbose:
        # System info
        # 定义 1 GiB 的字节数( 1 << 30 )。
        gib = 1 << 30  # bytes per GiB

        # psutil.virtual_memory()
        # psutil.virtual_memory() 是一个函数,属于 psutil 库,用于获取系统虚拟内存(RAM)的使用情况。
        # 参数 :无参数。
        # 返回值 :
        # 该函数返回一个命名元组( psutil._common.smem ),其中包含了以下属性 :
        # total :总物理内存大小,单位为字节。
        # available :可供分配的内存大小,单位为字节,这个值是系统认为可用的内存,包括缓存和缓冲区占用的内存。
        # percent :已使用内存的百分比。
        # used :已使用的内存大小,单位为字节。
        # free :空闲的内存大小,单位为字节。
        # active :当前正在使用或最近使用的内存,单位为字节。
        # inactive :标记为未使用的内存,单位为字节。
        # buffers :缓存数据,如文件系统元数据,单位为字节。
        # cached :缓存数据,单位为字节。
        # shared :可由多个进程共享的内存,单位为字节。
        # slab :用于内核数据结构的内存,单位为字节。

        # 获取系统总内存(以字节为单位)。
        ram = psutil.virtual_memory().total

        # shutil.disk_usage(path)
        # shutil.disk_usage(path) 是 Python 标准库 shutil 模块中的一个函数,用于获取指定路径的磁盘使用情况统计信息。
        # 参数 :
        # path :一个表示文件系统路径的字符串或路径对象。在 Windows 上,这个路径必须代表一个目录;在 Unix 系统上,它可以是文件或目录。
        # 返回值 :
        # 该函数返回一个命名元组(namedtuple),包含以下属性 :
        # total :表示文件系统的总空间量,单位为字节。
        # used :表示已使用的磁盘空间量,单位为字节。
        # free :表示可用的磁盘空间量,单位为字节。
        # 注意事项 :
        # 在 Unix 文件系统中, path 必须指向一个已挂载文件系统分区中的路径。在这些平台上,CPython 不会尝试从未挂载的文件系统中获取磁盘使用信息。
        # 从 Python 3.8 开始,在 Windows 上, path 可以是一个文件或目录。 shutil.disk_usage() 提供的信息可以帮助开发者监控磁盘空间使用情况,或者在需要时优化磁盘使用。

        # 获取根目录 / 的 磁盘总容量 、 已使用量 和 剩余空间 。
        total, used, free = shutil.disk_usage("/")
        # 构建一个字符串 s ,包含 CPU 数量 、 总内存 和 磁盘使用情况 。
        s = f"({os.cpu_count()} CPUs, {ram / gib:.1f} GB RAM, {(total - free) / gib:.1f}/{total / gib:.1f} GB disk)"
        # 如果在 Jupyter Notebook 环境中运行,尝试导入 IPython.display 并清除之前的输出。如果 IPython 未安装,则忽略错误。
        try:
            from IPython import display

            # display.clear_output(wait=False)
            # display.clear_output() 是 IPython 库中的一个函数,用于清除 Jupyter Notebook 或 JupyterLab 界面中当前单元格的输出。这个函数属于 IPython.display 模块,它允许用户在交互式环境中控制输出的显示。
            # 参数 :
            # wait :一个布尔值,指定是否等待用户确认后再清除输出。如果设置为 True ,则会等待用户输入,通常是按下键盘上的任意键或者点击界面后,输出才会被清除。默认值为 False 。
            # 返回值 :该函数不返回任何值,它的作用是直接在 Jupyter 环境中清除输出区域。
            # 请注意,display.clear_output() 只在 Jupyter 环境中有效,如果你在标准的 Python 脚本或其他 IDE 中使用这个函数,它不会有任何效果。

            display.clear_output()  # clear display if notebook
        except ImportError:
            pass
    # 如果 verbose 为 False ,则不打印系统信息,将 s 设置为空字符串。
    else:
        s = ""

    # 调用 select_device 函数,选择合适的设备(CPU 或 GPU)。 newline=False 表示在打印设备信息时不换行。
    # # def select_device(device="", batch=0, newline=False, verbose=True):
    # -> 用于根据用户指定的设备字符串(如 "cpu" 、 "cuda" 、 "mps" 等)选择合适的计算设备(CPU、GPU 或 MPS),并进行一系列的检查和配置,以确保设备选择的正确性和兼容性。返回一个 torch.device 对象,表示 最终选择的设备 。
    # -> return torch.device(arg)
    select_device(device=device, newline=False)
    # 使用 LOGGER.info 打印一条日志信息,表示环境设置完成,并附上系统信息(如果 verbose 为 True )。
    LOGGER.info(f"Setup complete ✅ {s}")    # 设置完成✅{s}。
# check_yolo 函数通过收集系统资源信息(如 CPU、内存和磁盘使用情况),并确保环境设置正确,生成关于 YOLO 软件和硬件环境的摘要信息。它支持在 Google Colab 和 Jupyter Notebook 环境中运行,并可以选择性地打印详细的系统信息。这种设计有助于用户快速了解当前环境的配置情况,确保 YOLO 模型能够顺利运行。

21.def collect_system_info(): 

# 这段代码定义了一个名为 collect_system_info 的函数,用于收集和打印系统相关信息,包括操作系统、Python 版本、内存、CPU、GPU 和 CUDA 等信息。此外,它还会检查安装的 Python 包版本是否符合要求,并在 GitHub Actions 环境中收集额外的信息。
# 定义了一个函数 collect_system_info ,该函数不接收任何参数。
def collect_system_info():
    # 收集并打印相关系统信息,包括 OS、Python、RAM、CPU 和 CUDA。
    """Collect and print relevant system information including OS, Python, RAM, CPU, and CUDA."""
    # 导入 psutil 模块,用于获取系统资源信息(如内存和 CPU 使用情况)。
    import psutil

    # 从 ultralytics.utils 模块中导入 ENVIRONMENT ,避免循环导入。
    # ENVIRONMENT -> 用于 标识当前运行环境 。通过一系列条件判断,确定当前环境是 Colab、Kaggle、Jupyter、Docker 还是其他操作系统(如 Windows、Linux 或 macOS)。
    from ultralytics.utils import ENVIRONMENT  # scope to avoid circular import
    # 从 ultralytics.utils.torch_utils 模块中导入 get_cpu_info 和 get_gpu_info 函数,用于获取 CPU 和 GPU 信息。
    from ultralytics.utils.torch_utils import get_cpu_info, get_gpu_info

    # 定义一个变量 gib ,表示 1 GiB 的字节数( 1 << 30 )。
    gib = 1 << 30  # bytes per GiB
    # 检查是否支持 CUDA。如果 torch 已导入且支持 CUDA,则 cuda 为 True 。
    cuda = torch and torch.cuda.is_available()
    # 调用 check_yolo 函数,确保环境设置正确。
    check_yolo()
    # 获取根目录 / 的磁盘总容量、已使用量和剩余空间。
    total, used, free = shutil.disk_usage("/")

    # 构建一个字典 info_dict ,包含系统相关信息。
    info_dict = {
        # 操作系统版本。
        "OS": platform.platform(),
        # 运行环境。
        "Environment": ENVIRONMENT,
        # Python 版本。
        "Python": PYTHON_VERSION,
        # 安装方式(Git、pip 或其他)。
        "Install": "git" if IS_GIT_DIR else "pip" if IS_PIP_PACKAGE else "other",
        # 总内存和磁盘使用情况。
        "RAM": f"{psutil.virtual_memory().total / gib:.2f} GB",
        "Disk": f"{(total - free) / gib:.1f}/{total / gib:.1f} GB",
        # CPU 信息和核心数。
        "CPU": get_cpu_info(),
        "CPU count": os.cpu_count(),
        # GPU 信息(如果支持 CUDA)。
        "GPU": get_gpu_info(index=0) if cuda else None,
        # GPU 数量(如果支持 CUDA)。
        "GPU count": torch.cuda.device_count() if cuda else None,
        # CUDA 版本(如果支持 CUDA)。
        "CUDA": torch.version.cuda if cuda else None,
    }
    # 使用 LOGGER.info 打印系统信息,格式化为每行一个键值对。
    LOGGER.info("\n" + "\n".join(f"{k:<20}{v}" for k, v in info_dict.items()) + "\n")

    # 这段代码的作用是收集和记录与 ultralytics 包相关的依赖项信息,并在 GitHub Actions 环境中收集额外的运行信息。
    # 初始化一个空字典 package_info ,用于存储 ultralytics 包的依赖项信息。
    package_info = {}
    # 遍历 ultralytics 包的依赖项。 parse_requirements 是一个函数,用于解析指定包的依赖项。 r 是一个表示依赖项的对象,包含 依赖项的名称 和 版本要求 。
    # def parse_requirements(file_path=ROOT.parent / "requirements.txt", package=""):
    # -> 用于解析Python依赖项。它可以从一个 requirements.txt 文件或通过包名从已安装的Python包中提取依赖项。返回解析后的依赖项列表 requirements 。
    # -> return requirements
    for r in parse_requirements(package="ultralytics"):
        # 尝试获取当前安装的依赖项版本。
        try:

            # importlib.metadata.version(distribution_name)
            # metadata.version() 函数是 Python 标准库 importlib.metadata 模块中的一个函数,用于获取指定分发包的已安装版本号。
            # 参数 :
            # distribution_name :一个字符串,表示要查询的分发包的名称。
            # 返回值 :
            # 该函数返回一个字符串,表示指定分发包的版本号。
            # 异常 :
            # PackageNotFoundError :如果指定的分发包没有在当前Python环境中安装,会抛出这个异常。
            # 这个函数提供了一种简单而现代的方式来获取已安装包的版本信息,有助于在开发和生产环境中管理和验证依赖项的版本。

            # 获取指定包的当前安装版本。
            current = metadata.version(r.name)
            # 检查当前安装的版本是否满足依赖项的版本要求。
            # check_version(current, str(r.specifier), name=r.name, hard=True) :调用 check_version 函数,检查当前版本是否满足依赖项的版本要求。
            # 如果满足要求,则 is_met 为 "✅ " ;否则为 "❌ " 。
            is_met = "✅ " if check_version(current, str(r.specifier), name=r.name, hard=True) else "❌ "
        # 如果依赖项未安装,则捕获 metadata.PackageNotFoundError 异常。
        except metadata.PackageNotFoundError:
            # 并设置 current 为 "(not installed)" , is_met 为 "❌ " 。
            current = "(not installed)"
            is_met = "❌ "
        # 将依赖项的检查结果存储到 package_info 字典中。格式为 "{is_met}{current}{r.specifier}" ,例如 "✅ 1.0.0>=1.0.0" 或 "❌ (not installed)>=1.0.0" 。
        package_info[r.name] = f"{is_met}{current}{r.specifier}"
        # 使用 LOGGER.info 打印依赖项的名称和检查结果,格式化为每行一个键值对。
        LOGGER.info(f"{r.name:<20}{package_info[r.name]}")

    # 将 依赖项信息 存储到 info_dict 字典中,键为 "Package Info" 。
    info_dict["Package Info"] = package_info

    # 检查是否在 GitHub Actions 环境中运行。 is_github_action_running 是一个函数,用于检测当前环境是否为 GitHub Actions 。
    if is_github_action_running():
        # 如果在 GitHub Actions 环境中运行,则收集以下 GitHub Actions 的环境变量信息。
        github_info = {
            # 运行器的操作系统。
            "RUNNER_OS": os.getenv("RUNNER_OS"),
            # 触发工作流的事件名称。
            "GITHUB_EVENT_NAME": os.getenv("GITHUB_EVENT_NAME"),
            # 工作流的名称。
            "GITHUB_WORKFLOW": os.getenv("GITHUB_WORKFLOW"),
            # 触发工作流的用户。
            "GITHUB_ACTOR": os.getenv("GITHUB_ACTOR"),
            # 仓库的名称。
            "GITHUB_REPOSITORY": os.getenv("GITHUB_REPOSITORY"),
            # 仓库的所有者。
            "GITHUB_REPOSITORY_OWNER": os.getenv("GITHUB_REPOSITORY_OWNER"),
        }
        # 使用 LOGGER.info 打印 GitHub Actions 的环境变量信息,格式化为每行一个键值对。
        LOGGER.info("\n" + "\n".join(f"{k}: {v}" for k, v in github_info.items()))
        # 将 GitHub Actions 的 环境变量信息 存储到 info_dict 字典中,键为 "GitHub Info" 。
        info_dict["GitHub Info"] = github_info
    # 这段代码通过解析 ultralytics 包的依赖项,检查每个依赖项的当前版本是否满足要求,并将结果存储到 package_info 字典中。它还支持在 GitHub Actions 环境中收集额外的运行信息,并将这些信息存储到 info_dict 字典中。这种设计有助于用户快速了解当前环境的配置情况,确保依赖项版本的正确性,同时在 GitHub Actions 环境中提供详细的运行上下文信息。

    # 返回包含系统信息的字典 info_dict 。
    return info_dict
# collect_system_info 函数通过收集系统相关信息(如操作系统、Python 版本、内存、CPU、GPU 和 CUDA 等),并检查安装的 Python 包版本是否符合要求,生成详细的系统信息摘要。它还支持在 GitHub Actions 环境中收集额外的信息。这种设计有助于用户快速了解当前环境的配置情况,确保 YOLO 模型能够顺利运行。

22.def check_amp(model): 

# 这段代码定义了一个名为 check_amp 的函数,用于检查当前环境是否支持自动混合精度(AMP)训练。它通过一系列检查确保在使用 AMP 时不会出现数值不稳定(如 NaN 损失)或性能下降(如零 mAP 结果)的问题。
# 定义了一个函数 check_amp ,接收一个参数。
# 1.model :要检查的 PyTorch 模型。
def check_amp(model):
    # 检查 YOLO11 模型的 PyTorch 自动混合精度 (AMP) 功能。如果检查失败,则意味着系统中的 AMP 存在异常,可能导致 NaN 损失或零 mAP 结果,因此训练期间将禁用 AMP。
    """
    Checks the PyTorch Automatic Mixed Precision (AMP) functionality of a YOLO11 model. If the checks fail, it means
    there are anomalies with AMP on the system that may cause NaN losses or zero-mAP results, so AMP will be disabled
    during training.

    Args:
        model (nn.Module): A YOLO11 model instance.

    Example:
        ```python
        from ultralytics import YOLO
        from ultralytics.utils.checks import check_amp

        model = YOLO("yolo11n.pt").model.cuda()
        check_amp(model)
        ```

    Returns:
        (bool): Returns True if the AMP functionality works correctly with YOLO11 model, else False.
    """
    # 从 ultralytics.utils.torch_utils 模块中导入 autocast ,用于在 AMP 模式下运行模型。
    from ultralytics.utils.torch_utils import autocast

    # 这段代码的作用是检查当前模型的设备是否支持自动混合精度(AMP)训练。它通过检查设备类型和 GPU 型号,确定是否启用 AMP 。
    # 获取模型的设备信息。通过访问模型的第一个参数的 .device 属性,确定模型当前运行的设备(如 CPU、GPU 或 MPS)。
    device = next(model.parameters()).device  # get model device
    # 定义一个前缀字符串 prefix ,用于日志输出,便于区分 AMP 相关的日志信息。 colorstr 是一个函数,用于为字符串添加颜色,便于在终端中突出显示。
    prefix = colorstr("AMP: ")
    # 检查设备类型是否为 CPU 或 MPS。
    if device.type in {"cpu", "mps"}:
        # 如果模型运行在这些设备上,则直接返回 False ,因为 AMP 仅在 CUDA 设备上支持。
        return False  # AMP only used on CUDA devices
    # 定义一个正则表达式 pattern ,用于匹配已知在 AMP 模式下可能出现问题的 GPU 型号。这些型号通常在 AMP 模式下会出现数值不稳定(如 NaN 损失)或性能下降(如零 mAP 结果)的问题。
    else:
        # GPUs that have issues with AMP
        pattern = re.compile(
            r"(nvidia|geforce|quadro|tesla).*?(1660|1650|1630|t400|t550|t600|t1000|t1200|t2000|k40m)", re.IGNORECASE
        )

        # 获取当前设备的 GPU 名称。 torch.cuda.get_device_name(device) 返回当前设备的 GPU 名称。
        gpu = torch.cuda.get_device_name(device)
        # 使用正则表达式 pattern 检查当前 GPU 名称是否在已知问题列表中。
        if bool(pattern.search(gpu)):
            # 如果匹配成功( pattern.search(gpu) 返回非空),则打印警告信息,说明当前 GPU 在 AMP 模式下可能出现问题。
            LOGGER.warning(
                # 警告信息中包含 GPU 名称和具体问题描述。
                f"{prefix}checks failed ❌. AMP training on {gpu} GPU may cause "    # {prefix} 检查失败 ❌。在 {gpu} GPU 上进行 AMP 训练可能会导致 NaN 损失或零 mAP 结果,因此在训练期间将禁用 AMP。
                f"NaN losses or zero-mAP results, so AMP will be disabled during training."
            )
            # 并返回 False ,表示不支持 AMP。
            return False
    # 这段代码通过检查模型的设备类型和 GPU 型号,确定是否支持自动混合精度(AMP)训练。它确保只有在支持 AMP 的 CUDA 设备上才会启用 AMP,并且排除了已知在 AMP 模式下可能出现问题的 GPU 型号。这种设计有助于避免在训练过程中出现数值不稳定或性能下降的问题,提高训练的稳定性和可靠性。

    # 这段代码定义了一个名为 amp_allclose 的内部函数,用于比较在 FP32(单精度浮点)和 AMP(自动混合精度)模式下模型的推理结果是否接近。
    # 定义了一个内部函数 amp_allclose ,接收两个参数。
    # 1.m :要检查的模型。
    # 2.im :用于推理的输入图像路径。
    def amp_allclose(m, im):
        # 所有 FP32 与 AMP 结果均接近。
        """All close FP32 vs AMP results."""
        # 创建一个包含 8 个相同图像的批次。这一步是为了模拟批量推理,确保结果的稳定性。
        batch = [im] * 8
        # 计算最大图像尺寸 imgsz 。
        # model.stride.max() :获取模型的最大步幅。
        # int(model.stride.max() * 4) :将最大步幅乘以 4,确保图像尺寸足够大,以覆盖模型的所有特征层。
        # max(256, ...) :确保图像尺寸至少为 256,以避免过小的图像尺寸导致问题。
        imgsz = max(256, int(model.stride.max() * 4))  # max stride P5-32 and P6-64
        # 在 FP32 模式下运行模型,获取推理结果。
        # m(batch, imgsz=imgsz, device=device, verbose=False) :调用模型 m ,传入批次 batch 、图像尺寸 imgsz 和设备 device ,并禁用详细输出。
        # [0].boxes.data :提取推理结果中的边界框数据。
        a = m(batch, imgsz=imgsz, device=device, verbose=False)[0].boxes.data  # FP32 inference
        # 在 AMP 模式下运行模型,获取推理结果。
        # 使用 autocast(enabled=True) 上下文管理器启用 AMP 模式。
        # # def autocast(enabled: bool, device: str = "cuda"):
        # -> 用于根据 PyTorch 的版本动态选择混合精度训练的上下文管理器。如果当前 PyTorch 版本为 1.13 或更高版本,返回 torch.amp.autocast 上下文管理器。返回 torch.cuda.amp.autocast ,并传递 enabled 参数,用于控制是否启用混合精度训练。
        # -> return torch.amp.autocast(device, enabled=enabled) / return torch.cuda.amp.autocast(enabled)
        with autocast(enabled=True):
            # 调用模型 m ,传入相同的参数,获取 AMP 模式下的推理结果。
            b = m(batch, imgsz=imgsz, device=device, verbose=False)[0].boxes.data  # AMP inference
        # 删除模型对象,释放内存。
        del m

        # torch.allclose(actual, desired, rtol=1e-05, atol=1e-08, equal_nan=False)
        # torch.allclose() 是 PyTorch 库中的一个函数,用于检查两个张量(tensor)是否在元素级别上近似相等。这个函数主要用于数值计算中,由于浮点数的精度限制,直接比较两个浮点数是否相等可能不总是可靠,因此使用 torch.allclose() 来检查它们是否足够接近。
        # 参数 :
        # actual :实际的张量。
        # desired :期望的张量。
        # rtol (relative tolerance):相对容差,默认值为 1e-05 。
        # atol (absolute tolerance):绝对容差,默认值为 1e-08 。
        # equal_nan :是否将 NaN 视为相等,默认为 False 。
        # 返回值 :
        # 返回一个布尔值,如果两个张量在指定的容差内近似相等,则返回 True ;否则返回 False 。
        # 注意事项 :
        # 如果两个张量的形状不同, torch.allclose() 会直接返回 False 。
        # rtol 和 atol 参数用于控制近似相等的严格程度。 rtol 是相对容差,适用于比例误差; atol 是绝对容差,适用于固定误差。
        # 如果设置 equal_nan=True ,则两个张量中对应位置的 NaN 值也会被视为相等。
        # torch.allclose() 在机器学习和数值计算中非常有用,特别是在比较模型预测结果和真实值时,或者在验证算法实现的正确性时。

        # 比较 FP32 和 AMP 模式下的推理结果。
        # a.shape == b.shape :检查两个结果的形状是否一致。
        # torch.allclose(a, b.float(), atol=0.5) :使用 torch.allclose 检查两个结果是否在绝对容忍度为 0.5 的范围内接近。
        # 如果两个条件都满足,则返回 True ,表示结果接近;否则返回 False 。
        return a.shape == b.shape and torch.allclose(a, b.float(), atol=0.5)  # close to 0.5 absolute tolerance
    # amp_allclose 函数通过比较 FP32 和 AMP 模式下的推理结果,验证 AMP 模式是否能够稳定地运行模型。它通过创建一个包含多个相同图像的批次,确保结果的稳定性,并使用 torch.allclose 检查两个结果是否在指定的容忍度范围内接近。这种设计有助于在启用 AMP 时避免数值不稳定或性能下降的问题。

    # 这段代码是 check_amp 函数的一部分,用于运行自动混合精度(AMP)检查,并根据检查结果决定是否启用 AMP。
    # 定义一个变量 im ,表示用于 AMP 检查的图像路径。这里使用了 ASSETS 路径下的 bus.jpg 图像。
    im = ASSETS / "bus.jpg"  # image to check
    # 使用 LOGGER.info 打印一条日志信息,表示正在运行 AMP 检查。 prefix 是之前定义的 "AMP: " ,用于标识 AMP 相关的日志信息。
    LOGGER.info(f"{prefix}running Automatic Mixed Precision (AMP) checks...")    # {prefix} 正在运行自动混合精度 (AMP) 检查...
    # 定义一条警告信息 warning_msg ,提示用户在遇到问题时可以禁用 AMP。
    warning_msg = "Setting 'amp=True'. If you experience zero-mAP or NaN losses you can disable AMP with amp=False."    # 设置“amp=True”。如果您遇到零 mAP 或 NaN 损失,则可以使用 amp=False 禁用 AMP。
    # 尝试加载 YOLO 模型,并使用 amp_allclose 函数检查 FP32 和 AMP 模式下的推理结果是否接近。
    try:
        from ultralytics import YOLO

        # YOLO("yolo11n.pt") :加载预训练的 YOLO 模型。
        # amp_allclose(YOLO("yolo11n.pt"), im) :比较 FP32 和 AMP 模式下的推理结果。
        assert amp_allclose(YOLO("yolo11n.pt"), im)
        # 如果检查通过,则打印通过信息。
        LOGGER.info(f"{prefix}checks passed ✅")    # {prefix}检查已通过✅。
    # 如果在加载模型时遇到 ConnectionError 。
    except ConnectionError:
        # 则打印警告信息,说明无法下载模型,跳过检查。
        LOGGER.warning(
            f"{prefix}checks skipped ⚠️. Offline and unable to download YOLO11n for AMP checks. {warning_msg}"    # {prefix} 检查已跳过 ⚠️。离线且无法下载 YOLO11n 进行 AMP 检查。{warning_msg}。
        )
    # 如果在加载模型时遇到 AttributeError 或 ModuleNotFoundError 。
    except (AttributeError, ModuleNotFoundError):
        # 则打印警告信息,说明可能由于 ultralytics 包的修改导致无法加载模型,跳过检查。
        LOGGER.warning(
            f"{prefix}checks skipped ⚠️. "    # {prefix}检查已跳过⚠️。
            f"Unable to load YOLO11n for AMP checks due to possible Ultralytics package modifications. {warning_msg}"    # 由于 Ultralytics 软件包可能存在修改,因此无法加载 YOLO11n 进行 AMP 检查。{warning_msg} 。
        )
    # 如果 amp_allclose 检查失败(抛出 AssertionError )。
    except AssertionError:
        # 则打印警告信息,说明在 AMP 模式下检测到异常,可能导致 NaN 损失或零 mAP 结果,因此在训练期间将禁用 AMP。
        LOGGER.warning(
            f"{prefix}checks failed ❌. Anomalies were detected with AMP on your system that may lead to "    # {prefix} 检查失败 ❌。
            f"NaN losses or zero-mAP results, so AMP will be disabled during training."    # 您的系统上检测到 AMP 异常,可能导致 NaN 损失或零 mAP 结果,因此训练期间将禁用 AMP。
        )
        # 返回 False ,表示不支持 AMP。
        return False
    #  如果所有检查通过,则返回 True ,表示支持 AMP。
    return True
    # 这段代码通过运行 AMP 检查,确保在启用 AMP 时不会出现数值不稳定或性能下降的问题。它通过加载预训练的 YOLO 模型,并比较 FP32 和 AMP 模式下的推理结果来验证 AMP 的稳定性。如果检查通过,则返回 True ;否则返回 False 。这种设计有助于在训练过程中避免因 AMP 导致的问题。
# check_amp 函数通过一系列检查确保当前环境是否支持自动混合精度(AMP)训练。它首先检查设备是否支持 AMP,然后检查 GPU 型号是否已知存在问题。接着,它通过比较 FP32 和 AMP 模式下的推理结果来验证 AMP 的稳定性。如果检查通过,则返回 True ;否则返回 False 。这种设计有助于避免在使用 AMP 时出现数值不稳定或性能下降的问题。

23.def git_describe(path=ROOT): 

# 这段代码定义了一个名为 git_describe 的函数,用于获取指定目录的 Git 描述信息。这个描述信息通常是一个人类可读的版本号,例如 v5.0-5-g3e25f1e ,表示当前提交相对于最近的标签的版本信息。
# 定义了一个函数 git_describe ,接收一个参数。
# 1.path :默认值为 ROOT ,表示要检查的目录路径。 ROOT 是一个全局变量,指向项目的根目录。
def git_describe(path=ROOT):  # path must be a directory
    # 返回人类可读的 git 描述,即 v5.0-5-g3e25f1e https://git-scm.com/docs/git-describe。
    """Return human-readable git description, i.e. v5.0-5-g3e25f1e https://git-scm.com/docs/git-describe."""
    # 使用 subprocess.check_output 执行 Git 命令 git describe --tags --long --always ,并获取输出。
    try:
        # git -C {path} :指定 Git 命令在指定目录 path 下执行。
        # describe --tags --long --always :生成一个描述信息,包含 最近的标签 、 自该标签以来的提交数量 和 当前提交的哈希值 。
        # shell=True :允许通过 shell 执行命令。
        # .decode() :将输出从字节解码为字符串。
        # [:-1] :去掉字符串末尾的换行符。
        return subprocess.check_output(f"git -C {path} describe --tags --long --always", shell=True).decode()[:-1]
    # 如果在执行 Git 命令时发生任何异常(例如,目录不是 Git 仓库或命令执行失败)。
    except Exception:
        # 则捕获异常并返回空字符串。
        return ""
# git_describe 函数通过执行 Git 命令 git describe ,获取指定目录的 Git 描述信息。这个描述信息通常是一个人类可读的版本号,例如 v5.0-5-g3e25f1e ,表示当前提交相对于最近的标签的版本信息。这种设计有助于在代码中动态获取版本信息,便于版本管理和调试。

24.def print_args(args: Optional[dict] = None, show_file=True, show_func=False): 

# 这段代码定义了一个名为 print_args 的函数,用于打印函数的参数及其值。它支持可选的参数字典,并可以选择性地显示文件名和函数名。
# 定义了一个函数 print_args ,接收以下参数 :
# 1.args :一个可选的字典,包含要打印的参数及其值。默认为 None ,表示自动获取当前函数的参数。
# 2.show_file :一个布尔值,表示是否显示文件名。默认为 True 。
# 3.show_func :一个布尔值,表示是否显示函数名。默认为 False 。
def print_args(args: Optional[dict] = None, show_file=True, show_func=False):
    # 打印函数参数(可选参数字典)。
    """Print function arguments (optional args dict)."""

    # 定义了一个内部函数 strip_auth ,用于清理可能包含认证信息的长 URL。
    def strip_auth(v):
        # 通过剥离潜在的身份验证信息来清理较长的 Ultralytics HUB URL。
        """Clean longer Ultralytics HUB URLs by stripping potential authentication information."""
        # 如果输入是一个字符串且以 http 开头且长度超过 100 个字符,则调用 clean_url 函数清理 URL;否则直接返回原始值。
        return clean_url(v) if (isinstance(v, str) and v.startswith("http") and len(v) > 100) else v

    # frame = inspect.currentframe()
    # inspect.currentframe() 是 Python inspect 模块中的一个函数,它用于获取当前的栈帧(stack frame)。栈帧是程序执行期间的一个上下文,包含了局部变量、函数的参数、返回值等信息。这个函数返回当前的栈帧对象,你可以用这个对象来获取当前函数调用的各种信息。
    # 参数 :无参数。
    # 返回值 :
    # 该函数返回当前的栈帧对象。栈帧对象的属性 :
    # 栈帧对象包含多个属性,以下是一些常用的属性 :
    # f_back :指向上一个栈帧,即当前栈帧的调用者。
    # f_builtins :当前栈帧中可见的内置命名空间。
    # f_code :代码对象,包含编译后的代码。
    # f_globals :全局命名空间,即当前栈帧中可见的全局变量。
    # f_lasti :最后尝试执行的字节码指令的索引。
    # f_lineno :当前行号。
    # f_locals :局部变量的字典,包含当前栈帧中定义的局部变量。
    # 注意事项 :
    # inspect.currentframe() 返回的栈帧对象通常用于调试和分析程序的运行状态。
    # 栈帧对象包含的信息非常丰富,可以通过 inspect 模块中的其他函数来进一步获取,例如 inspect.getframeinfo() 、 inspect.stack() 、 inspect.trace() 等。
    # 在使用 inspect.currentframe() 时需要注意,频繁地操作栈帧可能会对性能有一定影响,因此建议在调试时使用,而不是在生产环境中使用。
    # inspect.currentframe() 是 Python 反射(reflection)功能的一部分,它允许程序在运行时检查和修改自身的结构和行为。

    # 使用 inspect.currentframe().f_back 获取调用当前函数的上一个帧(即调用者的信息)。
    x = inspect.currentframe().f_back  # previous frame

    # frame_info = inspect.getframeinfo(frame, context=1)
    # inspect.getframeinfo() 是 Python 标准库 inspect 模块中的一个函数,它用于获取指定栈帧的详细信息。这个函数需要一个栈帧对象作为参数,通常与 inspect.currentframe() 结合使用来获取当前的栈帧信息。
    # 参数 :
    # frame :一个栈帧对象,通常通过 inspect.currentframe() 获取。
    # context :一个整数,表示返回的帧信息中应包含多少层上级帧的信息。默认值为 0,意味着只返回传入的帧信息。
    # 返回值 :
    # 该函数返回一个命名元组( FrameInfo ),其中包含了以下属性 :
    # filename :栈帧中的代码文件名。
    # lineno :栈帧中的当前行号。
    # function :栈帧中的当前函数名。
    # code_context :一个包含栈帧代码上下文的列表,每个元素是一个元组,包含行号和代码行。
    # 注意事项 :
    # inspect.getframeinfo() 可以用于调试和分析程序的运行状态,尤其是在需要获取函数调用堆栈时。
    # 由于获取栈帧信息可能会有一定的性能开销,因此建议在调试时使用,而不是在生产环境中频繁使用。
    # code_context 属性可以用于获取代码的上下文,这对于生成更详细的调试信息非常有用。
    # inspect.getframeinfo() 是 Python 反射(reflection)功能的一部分,它允许程序在运行时检查和修改自身的结构和行为。

    # 使用 inspect.getframeinfo(x) 获取调用者的信息,包括 文件名 、 行号 、 函数名 等。
    file, _, func, _, _ = inspect.getframeinfo(x)
    # 如果 args 为 None 。
    if args is None:  # get args automatically

        # arg_values = inspect.getargvalues(frame)
        # inspect.getargvalues() 是 Python inspect 模块中的一个函数,用于获取指定栈帧中函数的参数信息。这个函数返回一个命名元组,其中包含了函数的参数名称、 * 和 ** 参数的名字、关键字参数的名字,以及局部变量的字典。
        # 参数 :
        # frame :一个栈帧对象,通常通过 inspect.currentframe() 或 inspect.stack() 获取。
        # 返回值 :
        # 该函数返回一个命名元组 ArgInfo ,包含以下属性 :
        # args :一个列表,包含所有位置参数的名称。
        # varargs :一个字符串,表示 * 形式的参数名称,或者 None 如果没有。
        # keywords :一个字符串,表示 ** 形式的参数名称,或者 None 如果没有。
        # locals :一个字典,包含函数调用时的局部变量。
        # 注意事项 :
        # inspect.getargvalues() 可以用于调试和分析程序的运行状态,尤其是在需要获取函数调用参数时。
        # 由于获取栈帧信息可能会有一定的性能开销,因此建议在调试时使用,而不是在生产环境中频繁使用。
        # 在 Python 3.3 及以上版本中, inspect.getargvalues() 函数因疏忽在 Python 3.5 中被错误地标记为弃用,但实际上它仍然是一个有效的函数。
        # inspect.getargvalues()   是 Python 反射(reflection)功能的一部分,它允许程序在运行时检查和修改自身的结构和行为。

        # 则自动获取调用者的参数及其值。
        # 使用 inspect.getargvalues(x) 获取调用者的参数信息。
        args, _, _, frm = inspect.getargvalues(x)
        # 构建一个字典 args ,包含调用者的 参数 及其 值 。
        args = {k: v for k, v in frm.items() if k in args}
    # 尝试将文件路径解析为相对于项目根目录的路径,并去掉文件扩展名。
    try:
        # 使用 Path(file).resolve() 获取绝对路径。
        # 使用 relative_to(ROOT) 将路径解析为相对于项目根目录的路径。
        # 使用 with_suffix("") 去掉文件扩展名。
        file = Path(file).resolve().relative_to(ROOT).with_suffix("")
    # 如果解析失败(抛出 ValueError )。
    except ValueError:
        # 则只保留文件名(去掉扩展名)。
        file = Path(file).stem
    # 构建日志信息的前缀。
    # 如果 show_file 为 True ,则包含文件名。
    # 如果 show_func 为 True ,则包含函数名。
    s = (f"{file}: " if show_file else "") + (f"{func}: " if show_func else "")
    # 使用 LOGGER.info 打印日志信息,格式化为每行一个键值对。
    # 使用 colorstr 为前缀添加颜色。
    # 使用 strip_auth 清理可能包含认证信息的值。
    # 使用 ", ".join(...) 将参数及其值拼接为一个字符串。
    LOGGER.info(colorstr(s) + ", ".join(f"{k}={strip_auth(v)}" for k, v in args.items()))
# print_args 函数通过自动获取或接收参数字典,打印函数的参数及其值。它支持选择性地显示文件名和函数名,并清理可能包含认证信息的长 URL。这种设计有助于在调试和日志记录中快速了解函数的调用参数,提高代码的可读性和可维护性。

25.def cuda_device_count() -> int:

# 这段代码定义了一个名为 cuda_device_count 的函数,用于获取系统中可用的 NVIDIA GPU 数量。它通过调用 nvidia-smi 命令并解析其输出来实现这一功能。
# 定义了一个函数 cuda_device_count ,返回类型为 int ,表示系统中可用的 NVIDIA GPU 数量。
def cuda_device_count() -> int:
    # 获取环境中可用的 NVIDIA GPU 数量。
    """
    Get the number of NVIDIA GPUs available in the environment.

    Returns:
        (int): The number of NVIDIA GPUs available.
    """
    try:

        # subprocess.check_output(*popenargs, input=None, timeout=None, **kwargs)
        # subprocess.check_output 是 Python 标准库 subprocess 模块中的一个函数,用于执行一个命令并获取它的输出。如果命令执行失败(即返回一个非零退出状态),该函数会抛出一个 CalledProcessError 异常。
        # 参数 :
        # *popenargs :这是一组参数,它们将被传递给 subprocess.Popen 构造函数。通常,这些参数包括命令和它的参数,类似于在 shell 中执行命令的方式。
        # input :(可选)一个字符串或字节序列,将被发送到 subprocess 的 stdin。
        # timeout :(可选)一个浮点数或整数,指定等待 subprocess 完成的秒数。如果超时,将抛出 TimeoutExpired 异常。
        # **kwargs :(可选)其他参数将被传递给 subprocess.Popen 构造函数。
        # 返回值 :
        # 函数返回 subprocess 的 stdout 输出,作为一个字节串(bytes)。
        # 异常 :
        # CalledProcessError :如果 subprocess 返回一个非零退出状态。
        # TimeoutExpired :如果 subprocess 超过指定的 timeout 时间。
        # 注意事项 :
        # 默认情况下, check_output 函数返回的输出是字节串。如果你需要字符串,可以指定 text=True 参数(在 Python 3.7+ 中)或者使用 universal_newlines=True 参数(在 Python 3.6 及以下版本中)。
        # 使用 shell=True 参数时需要特别小心,因为它可能会使程序容易受到 shell 注入攻击。只有当你能够完全信任输入数据时,才应该使用 shell=True 。
        # timeout 参数在 Python 3.3 及以上版本中可用。

        # Run the nvidia-smi command and capture its output
        # 使用 subprocess.check_output 执行 nvidia-smi 命令并捕获其输出。
        # ["nvidia-smi", "--query-gpu=count", "--format=csv,noheader,nounits"] :指定 nvidia-smi 命令及其参数,用于查询 GPU 数量。
        # --query-gpu=count :查询 GPU 的数量。
        # --format=csv,noheader,nounits :以 CSV 格式输出,不包含表头和单位。
        # encoding="utf-8" :指定输出的编码为 UTF-8。
        output = subprocess.check_output(
            ["nvidia-smi", "--query-gpu=count", "--format=csv,noheader,nounits"], encoding="utf-8"
        )

        # Take the first line and strip any leading/trailing white space
        # 去掉输出的首尾空白字符,并取第一行内容。 nvidia-smi 的输出通常只有一行,但这里仍然使用 split("\n")[0] 以确保只处理第一行。
        first_line = output.strip().split("\n")[0]

        # 将第一行内容转换为整数并返回。这一步假设 nvidia-smi 的输出是一个有效的整数,表示 GPU 的数量。
        return int(first_line)
    # 捕获可能发生的异常。
    # subprocess.CalledProcessError : nvidia-smi 命令执行失败。
    # FileNotFoundError : nvidia-smi 命令未找到。
    # ValueError :输出无法转换为整数。
    except (subprocess.CalledProcessError, FileNotFoundError, ValueError):
        # If the command fails, nvidia-smi is not found, or output is not an integer, assume no GPUs are available
        # 如果发生任何异常,则返回 0 ,表示没有可用的 GPU。
        return 0
# cuda_device_count 函数通过调用 nvidia-smi 命令并解析其输出,获取系统中可用的 NVIDIA GPU 数量。它处理了可能的异常情况,确保在命令失败或输出无效时返回 0 。这种设计有助于在代码中动态获取 GPU 数量,便于资源管理和调试。

26.def cuda_is_available() -> bool: 

# 这段代码定义了一个名为 cuda_is_available 的函数,用于检查系统中是否有可用的 NVIDIA GPU。它通过调用 cuda_device_count 函数来实现这一功能。
# 定义了一个函数 cuda_is_available ,返回类型为 bool ,表示系统中是否有可用的 NVIDIA GPU。
def cuda_is_available() -> bool:
    # 检查环境中是否可用 CUDA。
    """
    Check if CUDA is available in the environment.

    Returns:
        (bool): True if one or more NVIDIA GPUs are available, False otherwise.
    """
    # 调用 cuda_device_count 函数,获取系统中可用的 NVIDIA GPU 数量。
    # 如果 GPU 数量大于 0,则返回 True ,表示系统中有可用的 GPU;否则返回 False 。
    return cuda_device_count() > 0
# cuda_is_available 函数通过调用 cuda_device_count 函数,检查系统中是否有可用的 NVIDIA GPU。这种设计简洁明了,便于在代码中快速判断是否可以使用 GPU 加速计算。


# 这段代码定义了一个名为 is_rockchip 的函数,用于检测当前运行环境是否在Rockchip SoC(System on Chip,系统级芯片)上。
# 定义了 is_rockchip 函数,它不接受任何参数。
def is_rockchip():
    # 检查当前环境是否在 Rockchip SoC 上运行。
    """Check if the current environment is running on a Rockchip SoC."""
    # 检查当前操作系统是否为Linux( LINUX 为 True )。
    # 检查当前架构是否为ARM64( ARM64 为 True )。
    # 如果这两个条件都满足,继续执行后续逻辑。
    if LINUX and ARM64:
        # 尝试打开 /proc/device-tree/compatible 文件,该文件包含设备树的兼容性信息。
        try:
            with open("/proc/device-tree/compatible") as f:
                # 读取文件内容并存储在变量 dev_str 中。
                dev_str = f.read()
                # 使用 split(",") 将 dev_str 按逗号分割成多个部分。 使用 *_, soc 将最后一个部分赋值给变量 soc ,其他部分丢弃。
                *_, soc = dev_str.split(",")
                # 使用 replace("\x00", "") 去除 soc 中的空字符( \x00 )。检查清理后的 soc 是否在 RKNN_CHIPS 列表中。
                if soc.replace("\x00", "") in RKNN_CHIPS:
                    # 如果匹配成功,返回 True ,表示当前环境运行在Rockchip SoC上。
                    return True
        # 如果在尝试打开或读取 /proc/device-tree/compatible 文件时发生 OSError ,捕获异常。
        except OSError:
            # 并返回 False 。
            return False
    # 如果当前操作系统不是Linux或架构不是ARM64。
    else:
        # 直接返回 False 。
        return False
# 这段代码的主要功能是。检查操作系统和架构:确保当前环境是Linux系统且架构为ARM64。读取设备树兼容性信息:从 /proc/device-tree/compatible 文件中读取设备树的兼容性信息。解析设备树信息:提取设备树信息中的SoC名称。检查SoC是否为Rockchip:将提取的SoC名称与 RKNN_CHIPS 列表中的Rockchip SoC名称进行匹配。返回检测结果:如果匹配成功,返回 True ;否则返回 False 。通过这个函数,可以方便地检测当前运行环境是否在Rockchip SoC上,适用于需要特定硬件支持的场景。

27.def is_sudo_available() -> bool: 

# 这段代码定义了一个名为 is_sudo_available 的函数,用于检查当前系统是否支持 sudo 命令。它通过尝试运行 sudo --version 命令并检查其返回码来实现这一功能。
# 定义了一个函数 is_sudo_available ,返回类型为 bool ,表示系统中是否支持 sudo 命令。
def is_sudo_available() -> bool:
    # 检查环境中是否可以使用 sudo 命令。
    """
    Check if the sudo command is available in the environment.

    Returns:
        (bool): True if the sudo command is available, False otherwise.
    """
    # 如果当前系统是 Windows。
    if WINDOWS:
        # 则直接返回 False ,因为 Windows 系统不支持 sudo 命令。
        return False
    # 定义要执行的命令 sudo --version ,用于检查 sudo 命令是否存在并能正常运行。
    cmd = "sudo --version"
    # 使用 subprocess.run 执行命令 sudo --version 。
    # shell=True :允许通过 shell 执行命令。
    # stdout=subprocess.DEVNULL :将标准输出重定向到 /dev/null ,即丢弃输出。
    # stderr=subprocess.DEVNULL :将标准错误重定向到 /dev/null ,即丢弃错误信息。
    # 检查命令的返回码是否为 0 :
    # 如果返回码为 0 ,表示命令执行成功, sudo 命令可用,返回 True 。
    # 如果返回码不为 0 ,表示命令执行失败, sudo 命令不可用,返回 False 。
    return subprocess.run(cmd, shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode == 0
# is_sudo_available 函数通过尝试运行 sudo --version 命令并检查其返回码,判断系统中是否支持 sudo 命令。它特别处理了 Windows 系统,因为 Windows 不支持 sudo 命令。这种设计简洁明了,便于在代码中快速判断是否可以使用 sudo 命令。
# sudo 命令是 Linux 和 Unix 系统中一个非常重要的工具,用于以超级用户(root)或其他用户的身份执行命令。
# 定义 : sudo 是 "superuser do" 的缩写,意为“以超级用户身份执行”。它允许普通用户在需要时临时获得超级用户的权限,以执行需要管理员权限的命令。 sudo 命令通过配置文件 /etc/sudoers 来管理用户的权限,确保只有授权用户才能执行特定的命令。

28.Run checks and define constants / Define constants

# 这段代码的作用是运行一系列检查,并定义一些与 Python 版本相关的常量。
# Run checks and define constants    运行检查并定义常量。
# 调用 check_python 函数,检查当前 Python 版本是否满足最低版本要求(3.8)。设置 hard=False 表示不强制检查,即使版本不满足要求也不会抛出异常。设置 verbose=True 表示打印详细的版本检查信息。
check_python("3.8", hard=False, verbose=True)  # check python version
# 调用 check_torchvision 函数,检查当前安装的 PyTorch 和 torchvision 的版本是否兼容。如果版本不兼容,会打印警告信息。
check_torchvision()  # check torch-torchvision compatibility

# Define constants    定义常量。
# 调用 check_python 函数,检查当前 Python 版本是否至少为 3.10。设置 hard=False 表示不强制检查,返回一个布尔值,表示当前版本是否满足要求。将结果存储在常量 IS_PYTHON_MINIMUM_3_10 中。
IS_PYTHON_MINIMUM_3_10 = check_python("3.10", hard=False)
# 检查当前 Python 版本是否以 "3.12" 开头。使用 startswith 方法检查 PYTHON_VERSION 是否以 "3.12" 开头,并将结果存储在常量 IS_PYTHON_3_12 中。
IS_PYTHON_3_12 = PYTHON_VERSION.startswith("3.12")
# 这段代码通过运行一系列检查,确保当前环境的 Python 版本和 PyTorch/torchvision 版本符合要求。同时,它定义了两个与 Python 版本相关的常量: IS_PYTHON_MINIMUM_3_10 :表示当前 Python 版本是否至少为 3.10。 IS_PYTHON_3_12 :表示当前 Python 版本是否为 3.12。这些常量可以在后续代码中用于条件判断,以确保某些功能或特性仅在支持的 Python 版本中启用。

 

;