YOLOv8-ultralytics-8.2.103部分代码阅读笔记-files.py
files.py
ultralytics\utils\files.py
目录
files.py
1.所需的库和模块
2.class WorkingDirectory(contextlib.ContextDecorator):
3.def spaces_in_path(path):
4.def increment_path(path, exist_ok=False, sep="", mkdir=False):
5.def file_age(path=__file__):
6.def file_date(path=__file__):
7.def file_size(path):
8.def get_latest_run(search_dir="."):
9.def update_models(model_names=("yolov8n.pt",), source_dir=Path("."), update_names=False):
1.所需的库和模块
# Ultralytics YOLO 🚀, AGPL-3.0 license
import contextlib
import glob
import os
import shutil
import tempfile
from contextlib import contextmanager
from datetime import datetime
from pathlib import Path
2.class WorkingDirectory(contextlib.ContextDecorator):
# 这段代码定义了一个名为 WorkingDirectory 的类,它是一个上下文管理器,用于临时改变当前工作目录。这个类继承自 contextlib.ContextDecorator ,使其可以被用作词法装饰器或上下文管理器。
# 类定义。
# WorkingDirectory :类名。 contextlib.ContextDecorator :父类,提供了上下文管理器的基础实现。
class WorkingDirectory(contextlib.ContextDecorator):
# 用于临时更改工作目录的上下文管理器和装饰器。
# 此类允许使用上下文管理器或装饰器临时更改工作目录。
# 它确保在上下文或装饰函数完成后恢复原始工作目录。
# 方法:
# __enter__ :将当前目录更改为指定目录。
# __exit__ :在退出上下文时恢复原始工作目录。
"""
A context manager and decorator for temporarily changing the working directory.
This class allows for the temporary change of the working directory using a context manager or decorator.
It ensures that the original working directory is restored after the context or decorated function completes.
Attributes:
dir (Path): The new directory to switch to.
cwd (Path): The original current working directory before the switch.
Methods:
__enter__: Changes the current directory to the specified directory.
__exit__: Restores the original working directory on context exit.
Examples:
Using as a context manager:
>>> with WorkingDirectory('/path/to/new/dir'):
>>> # Perform operations in the new directory
>>> pass
Using as a decorator:
>>> @WorkingDirectory('/path/to/new/dir')
>>> def some_function():
>>> # Perform operations in the new directory
>>> pass
"""
# 初始化方法 __init__ 。
# 1.new_dir :构造函数参数,表示要切换到的新目录。
def __init__(self, new_dir):
# 实例化时将工作目录设置为“new_dir”,以便与上下文管理器或装饰器一起使用。
"""Sets the working directory to 'new_dir' upon instantiation for use with context managers or decorators."""
# 属性初始化。
# 保存新目录的路径。
self.dir = new_dir # new dir
# current_working_directory = Path.cwd()
# 在Python中, cwd() 函数是 pathlib 模块中的一个方法,用于获取当前工作目录。这个方法是 Path 类的一个实例方法, Path 类是 pathlib 模块中用于处理文件系统路径的类。
# 功能描述 :
# Path.cwd() 方法返回一个 Path 对象,该对象代表当前工作目录的路径。
# 返回值 :
# Path.cwd() 方法返回的是 Path 对象,这个对象提供了许多方法来操作路径,例如 .resolve() 可以获取路径的绝对路径, .as_posix() 可以将路径转换为跨平台的字符串形式等。
# 异常处理 :
# Path.cwd() 方法通常不会抛出异常,因为它只是返回当前工作目录的路径。但是,如果系统出现问题,导致无法确定当前工作目录,可能会抛出异常,这种情况下应该进行异常处理。
# 保存当前工作目录的路径,并使用 Path.cwd().resolve() 来获取绝对路径。
self.cwd = Path.cwd().resolve() # current dir
# 进入上下文 __enter__ 。这个方法在进入上下文管理器时被调用。
def __enter__(self):
# 进入上下文后将当前工作目录更改为指定目录。
"""Changes the current working directory to the specified directory upon entering the context."""
# os.chdir(path)
# os.chdir() 函数是 Python 的标准库 os 模块中的一个函数,用于更改当前工作目录。
# 参数 :
# path :要更改到的目标目录的路径。
# 功能描述 :
# os.chdir(path) 函数将当前工作目录更改为 path 指定的目录。如果 path 不存在或无法访问,将抛出一个异常。
# 异常处理 :
# 当尝试更改到一个不存在或无法访问的目录时, os.chdir() 会抛出 FileNotFoundError 或 PermissionError 异常。因此,在实际使用中,可能需要捕获这些异常来处理错误情况
# 更改工作目录。使用 os.chdir 函数更改当前工作目录到 self.dir 指定的目录。
os.chdir(self.dir)
# 退出上下文 __exit__ 。这个方法在退出上下文管理器时被调用。
# 1.exc_type 、 2.exc_val 和 3.exc_tb 是异常相关的参数,分别代表 异常类型 、 异常值 和 异常的traceback 。
def __exit__(self, exc_type, exc_val, exc_tb): # noqa
# 退出上下文时恢复原始工作目录。
"""Restores the original working directory when exiting the context."""
# 恢复工作目录。使用 os.chdir 函数将当前工作目录恢复到 self.cwd 指定的目录。
os.chdir(self.cwd)
# 使用这个类,你可以在代码块中临时更改工作目录,而不影响外部目录。
3.def spaces_in_path(path):
# 这段代码定义了一个名为 spaces_in_path 的上下文管理器,它使用 contextmanager 装饰器来自 contextlib 模块。这个上下文管理器的目的是处理包含空格的文件路径,通过将空格替换为下划线,并在临时目录中复制文件或目录来避免路径中空格可能引起的问题。
# 上下文管理器定义。
# @contextmanager 装饰器,用于定义一个上下文管理器。
# spaces_in_path 函数名,接受一个参数。
# 1.path :表示可能包含空格的文件路径。
@contextmanager
def spaces_in_path(path):
# 上下文管理器处理名称中带有空格的路径。如果路径包含空格,它会用下划线替换它们,将文件/目录复制到新路径,执行上下文代码块,然后将文件/目录复制回其原始位置。
# Yields:
# (Path):如果存在空格,则用下划线替换临时路径中的空格,否则为原始路径。
"""
Context manager to handle paths with spaces in their names. If a path contains spaces, it replaces them with
underscores, copies the file/directory to the new path, executes the context code block, then copies the
file/directory back to its original location.
Args:
path (str | Path): The original path that may contain spaces.
Yields:
(Path): Temporary path with spaces replaced by underscores if spaces were present, otherwise the original path.
Examples:
Use the context manager to handle paths with spaces:
>>> from ultralytics.utils.files import spaces_in_path
>>> with spaces_in_path('/path/with spaces') as new_path:
>>> # Your code here
"""
# If path has spaces, replace them with underscores
# 检查路径中是否包含空格。将 path 转换为字符串并检查是否包含空格。
if " " in str(path):
# 确定输入路径类型。记录 path 是否为字符串类型,以便在上下文管理器的 yield 语句后返回相同类型的路径。
string = isinstance(path, str) # input type
# 转换路径为 Path 对象。
path = Path(path)
# Create a temporary directory and construct the new path
# tempfile.TemporaryDirectory()
# tempfile.TemporaryDirectory() 是 Python 标准库 tempfile 模块中的一个函数,用于创建一个临时目录。这个临时目录在创建时是空的,并且在使用完毕后可以自动删除。
# 函数定义 :
# with TemporaryDirectory() as tmp_dir:
# print(tmp_dir)
# 参数。TemporaryDirectory() 可以接受一些参数来定制临时目录的行为 :
# dir :指定一个特定的目录,在该目录下创建临时目录。如果没有指定,则使用系统默认的临时文件目录。
# prefix :指定临时目录的前缀。
# suffix :指定临时目录的后缀。
# ignore_cleanup_errors :一个布尔值,指定是否忽略清理时发生的错误,默认为 False 。
# cleanup :一个布尔值,指定是否在退出上下文管理器时清理临时目录,默认为 True 。
# mode :设置目录的权限模式,默认为 0o700 。
# 在示例中, TemporaryDirectory() 被用作上下文管理器,它创建了一个临时目录,并在 with 块中提供了这个目录的路径。当 with 块执行完毕后,临时目录及其内容将被自动删除。
# TemporaryDirectory() 是处理需要临时文件或目录的场合的有用工具,特别是在测试、临时文件处理或任何需要临时存储的场合。使用临时目录可以避免临时文件对主文件系统的污染,并确保资源在使用后被正确清理。
# 创建临时目录和新路径。
# 使用 tempfile.TemporaryDirectory() 创建一个临时目录。
with tempfile.TemporaryDirectory() as tmp_dir:
# 构造一个新的路径 tmp_path ,将原始路径中的文件名中的空格替换为下划线。
tmp_path = Path(tmp_dir) / path.name.replace(" ", "_")
# Copy file/directory
# 复制文件或目录。
# 如果 path 是目录,使用 shutil.copytree() 复制整个目录到临时路径。
if path.is_dir():
# shutil.copytree(src, dst, symlinks=False, ignore=None, dirs_exist_ok=False)
# shutil.copytree() 是 Python 标准库 shutil (shell utilities)模块中的一个函数,用于递归地复制一个目录到另一个位置。这个函数会复制目录中的所有内容,包括子目录和文件。
# 参数 :
# src :源目录的路径。
# dst :目标目录的路径。如果目标目录已经存在,并且 dirs_exist_ok 参数为 False ,则会抛出一个 FileExistsError 异常。
# symlinks :一个布尔值,指定是否复制符号链接。默认为 False ,即不复制符号链接。
# ignore :一个可选的回调函数,用于排除不需要复制的文件或目录。
# dirs_exist_ok :一个布尔值,指定如果目标目录已经存在,是否允许复制操作继续。默认为 False ,如果目标目录存在,则会抛出异常。
# shutil.copytree() 是一个强大的工具,用于复制整个目录树,常用于备份、同步文件或在测试中创建测试数据目录。
# tmp_path.mkdir(parents=True, exist_ok=True)
shutil.copytree(path, tmp_path)
# 如果 path 是文件,使用 shutil.copy2() 复制文件到临时路径。
elif path.is_file():
tmp_path.parent.mkdir(parents=True, exist_ok=True)
# shutil.copy2(src, dst, *, follow_symlinks=True)
# shutil.copy2() 是 Python 标准库 shutil (shell utilities)模块中的一个函数,用于复制文件,同时尝试保留原文件的元数据,如修改时间和权限等。
# 参数 :
# src :源文件的路径。
# dst :目标文件的路径。如果目标文件已经存在,将会被覆盖。
# follow_symlinks :一个布尔值,默认为 True ,表示是否跟随符号链接。如果设置为 False ,则会复制符号链接本身而不是链接指向的文件。
# 功能描述 :
# shutil.copy2() 函数将一个文件从 src 路径复制到 dst 路径,并尝试保留源文件的元数据。如果 follow_symlinks 参数为 True ,它将复制符号链接所指向的文件;如果为 False ,则复制符号链接本身。
# 异常处理 :
# shutil.copy2() 可能会抛出异常,如 FileNotFoundError (源文件不存在)、 PermissionError (没有权限写入目标文件)等。因此,在实际使用中,你可能需要捕获这些异常来处理错误情况:
# shutil.copy2() 是一个非常有用的函数,它在复制文件的同时保留了尽可能多的文件属性,这在需要保持文件完整性的场景中非常有用。
# shutil.copy2() 是一个方便的工具,用于在需要保留文件元数据的情况下复制文件。与 shutil.copy() 相比, shutil.copy2() 能够更完整地复制文件属性,因此在需要这些属性时应该优先使用 shutil.copy2() 。
shutil.copy2(path, tmp_path)
try:
# Yield the temporary path
# 提供临时路径。 yield 语句提供临时路径,如果原始 path 是字符串类型,则返回字符串类型的临时路径,否则返回 Path 对象。
yield str(tmp_path) if string else tmp_path
# 将文件或目录复制回原始位置。在 finally 块中,确保无论上下文代码块中发生什么,都会将临时目录中的文件或目录复制回原始位置。
finally:
# Copy file/directory back
if tmp_path.is_dir():
shutil.copytree(tmp_path, path, dirs_exist_ok=True)
elif tmp_path.is_file():
shutil.copy2(tmp_path, path) # Copy back the file
# 处理路径中不包含空格的情况。如果路径中不包含空格,则直接 yield 原始路径。
else:
# If there are no spaces, just yield the original path
yield path
# 使用这个上下文管理器,你可以确保在代码块中处理的路径不包含空格,从而避免某些操作系统或程序中可能由于路径中的空格引起的问题。
4.def increment_path(path, exist_ok=False, sep="", mkdir=False):
# 这段代码定义了一个名为 increment_path 的函数,其目的是在给定路径已存在的情况下,通过在路径后面添加一个数字后缀来生成一个新的路径,直到找到一个不存在的路径。这个函数可以用来避免文件或目录的覆盖。
# 函数定义。
# 1.path :要检查和增加后缀的文件或目录的路径。
# 2.exist_ok :一个布尔值,如果为 True ,则即使原始路径已存在也不会增加后缀。
# 3.sep :后缀数字前的分隔符,默认为空字符串。
# 4.mkdir :一个布尔值,如果为 True ,则会创建新的目录路径。
def increment_path(path, exist_ok=False, sep="", mkdir=False):
# 增加文件或目录路径,即 runs/exp --> runs/exp{sep}2、runs/exp{sep}3,... 等等。
# 如果路径存在且 `exist_ok` 不为 True,则通过在路径末尾附加数字和 `sep` 来增加路径。如果路径是文件,则将保留文件扩展名。如果路径是目录,则数字将直接附加到路径末尾。如果 `mkdir` 设置为 True,则如果路径尚不存在,则将创建为目录。
"""
Increments a file or directory path, i.e., runs/exp --> runs/exp{sep}2, runs/exp{sep}3, ... etc.
If the path exists and `exist_ok` is not True, the path will be incremented by appending a number and `sep` to
the end of the path. If the path is a file, the file extension will be preserved. If the path is a directory, the
number will be appended directly to the end of the path. If `mkdir` is set to True, the path will be created as a
directory if it does not already exist.
Args:
path (str | pathlib.Path): Path to increment.
exist_ok (bool): If True, the path will not be incremented and returned as-is.
sep (str): Separator to use between the path and the incrementation number.
mkdir (bool): Create a directory if it does not exist.
Returns:
(pathlib.Path): Incremented path.
Examples:
Increment a directory path:
>>> from pathlib import Path
>>> path = Path("runs/exp")
>>> new_path = increment_path(path)
>>> print(new_path)
runs/exp2
Increment a file path:
>>> path = Path("runs/exp/results.txt")
>>> new_path = increment_path(path)
>>> print(new_path)
runs/exp/results2.txt
"""
# 将输入的 path 转换为 Path 对象,使其与操作系统无关。
path = Path(path) # os-agnostic
# 检查路径是否存在,如果存在且 exist_ok 为 False ,则需要增加后缀。
if path.exists() and not exist_ok:
# 如果 path 是文件,则保存其后缀名,并移除后缀名以便添加数字后缀;如果 path 是目录,则保持不变。
path, suffix = (path.with_suffix(""), path.suffix) if path.is_file() else (path, "")
# Method 1
# 使用 range(2, 9999) 来生成数字后缀,从2开始以避免覆盖原始文件,并检查新路径是否存在。如果找到了一个不存在的路径,则退出循环。
for n in range(2, 9999):
p = f"{path}{sep}{n}{suffix}" # increment path
if not os.path.exists(p):
break
path = Path(p)
# 如果 mkdir 参数为 True ,则创建新的目录路径, parents=True 允许创建多级目录, exist_ok=True 忽略目录已存在的错误。
if mkdir:
# os.mkdir(path, mode=0o777, *, dir_fd=None, parents=False, exist_ok=False)
# 在Python中, mkdir() 函数是 os 模块中的一个函数,用于创建一个新目录。
# 参数说明 :
# path : 要创建的目录路径。
# mode : 可选参数,用于设置目录的权限模式,默认为 0o777 。在Unix和类Unix系统中有效。
# dir_fd : 可选参数,文件描述符;如果提供,表示 path 是相对于此文件描述符的。
# parents : 可选参数,布尔值;如果为 True ,则会创建父目录。
# exist_ok : 可选参数,布尔值;如果为 True ,当目录已存在时不会抛出异常。
# 注意事项 :
# 如果 parents=True , mkdir() 会递归地创建所有必需的父目录。
# 如果 exist_ok=True ,当目录已存在时, mkdir() 不会抛出 FileExistsError 异常。
# 在使用 mkdir() 时,需要确保程序有足够的权限来创建目录。
# 这个函数是处理文件系统操作的常用工具,可以帮助你管理目录结构。
path.mkdir(parents=True, exist_ok=True) # make directory
# 返回最终的路径,可能是原始路径(如果 exist_ok 为 True 且路径不存在),或者是一个增加了数字后缀的新路径。
return path
# 这个函数在处理文件和目录的创建时非常有用,特别是在需要确保不覆盖现有文件或目录的情况下。通过自动增加后缀,它可以确保新创建的文件或目录总是唯一的。
5.def file_age(path=__file__):
# 这段代码定义了一个名为 file_age 的函数,它的目的是计算指定文件自上次修改以来经过的天数。
# 函数定义。file_age 是函数名。
# 1.path :是函数的参数,默认值为 __file__ ,这意味着如果调用函数时没有指定路径,它将使用当前文件的路径。
def file_age(path=__file__):
# 返回自上次修改指定文件以来的天数。
"""Return days since the last modification of the specified file."""
# datetime.datetime.now([tz])
# datetime.now() 是 Python 中 datetime 模块的一个方法,用于获取当前的日期和时间。
# 参数说明 :
# tz (可选): 时区信息。如果提供, now() 方法将返回指定时区的当前时间。如果没有提供时区信息,将使用系统本地时区。
# 返回值 :
# 返回一个 datetime 对象,表示当前的日期和时间。
# 注意事项 :
# datetime.now() 返回的是本地时区的时间,如果你需要协调世界时(UTC),可以使用 datetime.utcnow() 方法。
# 如果你需要更精确的时间(包括微秒),可以使用 datetime.now(timezone.utc) 来获取。
# 在使用 datetime 模块之前,需要先导入该模块。
# datetime.now() 是处理日期和时间的常用方法,可以帮助你获取当前的日期和时间。
# datetime.datetime.fromtimestamp(timestamp[, tz])
# datetime.fromtimestamp() 是 Python 中 datetime 模块的一个方法,用于根据 Unix 时间戳(自1970年1月1日以来的秒数)来创建一个 datetime 对象。
# 参数说明 :
# timestamp : Unix 时间戳,表示自1970年1月1日(UTC)以来的秒数。
# tz (可选): 时区信息。如果提供,方法将返回指定时区对应的 datetime 对象。如果没有提供时区信息,将使用系统本地时区。
# 返回值 :
# 返回一个 datetime 对象,表示给定 Unix 时间戳对应的日期和时间。
# 注意事项 :
# datetime.fromtimestamp() 默认返回的是本地时区的时间,如果你需要协调世界时(UTC),可以提供一个时区参数。
# 如果你在处理时间戳时需要考虑时区,确保正确地使用 tz 参数。
# 在使用 datetime 模块之前,需要先导入该模块。
# datetime.fromtimestamp() 是一个非常有用的函数,它允许你将 Unix 时间戳转换为人类可读的日期和时间格式。
# os.stat(path, *, dir_fd=None, follow_symlinks=True, dir_fd=None)
# 在Python中, stat() 函数是 os 模块中的一个方法,用于获取文件或目录的状态信息。
# 参数说明 :
# path : 要获取状态信息的文件或目录的路径。
# dir_fd : 可选参数,文件描述符;如果提供,表示 path 是相对于此文件描述符的。
# follow_symlinks : 可选参数,布尔值;如果为 True (默认值),则 stat() 会跟随软链接,并返回链接目标的状态信息;如果为 False ,则返回链接本身的状态信息。
# 返回值 :
# 返回一个对象,该对象包含了文件或目录的许多属性,如修改时间、访问时间、文件大小等。
# 返回对象的属性 :
# 返回的对象包含以下属性(这些属性在不同的操作系统中可能有所不同) :
# st_mode : 文件模式(类型与权限)。
# st_ino : inode(节点)编号。
# st_dev : 设备编号。
# st_nlink : 硬链接数。
# st_uid : 文件所有者的ID。
# st_gid : 文件所有者组的ID。
# st_size : 文件大小,单位为字节。
# st_atime : 最后访问时间,以Unix时间戳表示。
# st_mtime : 最后修改时间,以Unix时间戳表示。
# st_ctime : 最后状态改变时间(inode修改时间),以Unix时间戳表示。
# 注意事项 :
# 使用 stat() 函数时,需要确保提供的路径是存在的,否则会抛出 FileNotFoundError 。
# stat() 函数返回的属性在不同的操作系统中可能有所不同,因此在编写跨平台代码时需要注意这一点。
# 在使用 os 模块之前,需要先导入该模块。
# stat() 函数是处理文件系统操作时常用的一个函数,它提供了获取文件或目录状态信息的直接方法。
# datetime.now() 获取当前的日期和时间。
# datetime.fromtimestamp(Path(path).stat().st_mtime) 将文件的最后修改时间(以时间戳形式)转换为 datetime 对象。
# Path(path) 是从 pathlib 模块创建的 Path 对象, stat() 方法获取文件的状态信息, st_mtime 是文件最后修改的时间戳。
# 两个 datetime 对象相减得到一个 timedelta 对象,表示两个时间点之间的差异。
dt = datetime.now() - datetime.fromtimestamp(Path(path).stat().st_mtime) # delta
# 返回 timedelta 对象中的天数部分。 注释掉的 + dt.seconds / 86400 部分如果取消注释,将会计算天数的小数部分,即包括小时、分钟和秒在内的完整天数。
return dt.days # + dt.seconds / 86400 # fractional days
# 这个函数可以很方便地用于检查文件的“年龄”,即自上次修改以来经过的时间。
6.def file_date(path=__file__):
# 这段代码定义了一个名为 file_date 的函数,它用于返回指定文件的最后修改日期,格式为 'YYYY-M-D' 。
# 函数定义。file_date 是函数名。
# 1.path :是函数的参数,默认值为 __file__ ,这意味着如果调用函数时没有指定路径,它将使用当前文件的路径。
def file_date(path=__file__):
# 以“YYYY-M-D”格式返回文件修改日期。
"""Returns the file modification date in 'YYYY-M-D' format."""
# Path(path) 是从 pathlib 模块创建的 Path 对象,它提供了面向对象的文件系统路径操作方法。
# Path(path).stat() 获取文件的状态信息,类似于 os.stat(path) 。
# st_mtime 是文件最后修改的时间戳, datetime.fromtimestamp() 方法将这个时间戳转换成 datetime 对象。
t = datetime.fromtimestamp(Path(path).stat().st_mtime)
# t.year 、 t.month 和 t.day 分别获取 datetime 对象的年、月、日。 使用 f-string(格式化字符串字面量)来格式化返回的日期字符串为 'YYYY-M-D' 格式。
return f"{t.year}-{t.month}-{t.day}"
# 函数中的 Path(path).stat().st_mtime 需要确保 path 是一个有效的文件路径,否则会抛出异常。返回的日期格式是固定的 'YYYY-M-D' ,如果你需要其他格式,可以修改 f-string 来适应你的需求。
# 这个函数可以很方便地用于获取文件的最后修改日期,并以一种简洁的格式返回。
7.def file_size(path):
# 这段代码定义了一个名为 file_size 的函数,它用于返回指定文件或目录的大小,单位为兆字节(MB)。
# 函数定义。file_size 是函数名。
# 1.path :是函数的参数,它接受一个字符串或 Path 对象,表示文件或目录的路径。
def file_size(path):
# 以兆字节 (MB) 为单位返回文件或目录的大小。
"""Returns the size of a file or directory in megabytes (MB)."""
# 检查 path 参数是否为字符串或 Path 对象。
if isinstance(path, (str, Path)):
# 定义了从字节到兆字节(MiB)的转换因子,因为1兆字节等于 2^20 字节。
mb = 1 << 20 # bytes to MiB (1024 ** 2)
# 将路径转换为 Path 对象,以便使用 pathlib 模块的方法。
path = Path(path)
# 检查路径是否指向一个文件。
if path.is_file():
# 如果是文件, path.stat().st_size 获取文件的大小(以字节为单位),然后除以 mb 转换为兆字节,并返回这个值。
return path.stat().st_size / mb
# 检查路径是否指向一个目录。
elif path.is_dir():
# 如果是目录, path.glob("**/*") 会递归地获取目录下所有文件的路径。
# sum(f.stat().st_size for f in path.glob("**/*") if f.is_file()) 计算所有文件的大小总和(以字节为单位)。
# 总大小除以 mb 转换为兆字节,并返回这个值。
return sum(f.stat().st_size for f in path.glob("**/*") if f.is_file()) / mb
# 如果路径既不是文件也不是目录,函数返回0.0。
return 0.0
# 函数中的 path.is_file() 和 path.is_dir() 方法需要确保 path 是一个有效的文件或目录路径,否则会抛出异常。函数中的 glob("**/*") 方法会递归地搜索目录下的所有文件,这可能在包含大量文件的目录中导致性能问题。
# 这个函数可以很方便地用于获取文件或目录的大小,并以兆字节为单位返回。
8.def get_latest_run(search_dir="."):
# 这段代码定义了一个名为 get_latest_run 的函数,它用于在指定目录及其子目录中查找最新的名为 last.pt 的文件。这个文件通常用于保存训练模型的状态,以便后续可以从中断的地方恢复训练。
# 函数定义。get_latest_run 是函数名。
# 1.search_dir :是函数的参数,默认值为 "." ,表示当前目录。它接受一个字符串,表示要搜索的目录路径。
def get_latest_run(search_dir="."):
# 返回指定目录中最新的“last.pt”文件的路径,以恢复训练。
"""Returns the path to the most recent 'last.pt' file in the specified directory for resuming training."""
# 使用 glob 模块的 glob 函数搜索 search_dir 目录及其所有子目录中匹配模式 last*.pt 的文件。 recursive=True 参数使得搜索是递归的,即包括所有子目录。
last_list = glob.glob(f"{search_dir}/**/last*.pt", recursive=True)
# max(last_list, key=os.path.getctime) 如果 last_list 不为空,使用 max 函数找到列表中创建时间(ctime)最新的文件路径。 key=os.path.getctime 指定 max 函数使用每个文件的创建时间作为比较的键。
# if last_list else "" 如果 last_list 为空(即没有找到任何文件),函数返回一个空字符串。
return max(last_list, key=os.path.getctime) if last_list else ""
# 函数中的 glob.glob 方法会搜索所有匹配的文件,如果目录中有很多文件,这可能会影响性能。 os.path.getctime 返回的是文件的创建时间,在某些操作系统上,这可能与文件的实际创建时间不同,或者在文件被修改后可能会改变。
# 这个函数可以很方便地用于在目录中查找最新的 last.pt 文件,以便恢复训练。
9.def update_models(model_names=("yolov8n.pt",), source_dir=Path("."), update_names=False):
# 这段代码定义了一个名为 update_models 的函数,它用于更新指定模型列表中的模型,并将它们保存到目标目录。
# 函数定义。update_models 是函数名。
# 1.model_names :是函数的参数,默认值为 ("yolov8n.pt",) ,表示要更新的模型文件名列表。
# 2.source_dir :是函数的参数,默认值为 Path(".") ,表示模型文件所在的源目录。
# 3.update_names :是函数的参数,默认值为 False ,表示是否更新模型中使用的类别名称。
def update_models(model_names=("yolov8n.pt",), source_dir=Path("."), update_names=False):
# 更新并重新保存“updated_models”子目录中的指定 YOLO 模型。
"""
Updates and re-saves specified YOLO models in an 'updated_models' subdirectory.
Args:
model_names (Tuple[str, ...]): Model filenames to update.
source_dir (Path): Directory containing models and target subdirectory.
update_names (bool): Update model names from a data YAML.
Examples:
Update specified YOLO models and save them in 'updated_models' subdirectory:
>>> from ultralytics.utils.files import update_models
>>> model_names = ("yolov8n.pt", "yolov8s.pt")
>>> update_models(model_names, source_dir=Path("/models"), update_names=True)
"""
# 导入模块。
# 导入 ultralytics 库中的 YOLO 类,用于加载和保存 YOLO 模型。
from ultralytics import YOLO
# 导入 default_class_names 函数,用于获取默认的类别名称。
from ultralytics.nn.autobackend import default_class_names
# 定义目标目录 target_dir ,它是源目录下的一个子目录 updated_models 。
target_dir = source_dir / "updated_models"
# 使用 mkdir 方法确保目标目录存在, parents=True 允许创建多级目录, exist_ok=True 避免在目录已存在时抛出异常。
target_dir.mkdir(parents=True, exist_ok=True) # Ensure target directory exists
# 遍历 model_names 列表中的每个模型文件名。
for model_name in model_names:
# 构造每个模型文件的完整路径 model_path 。
model_path = source_dir / model_name
# 打印正在加载的模型路径。
print(f"Loading model from {model_path}") # 从 {model_path} 加载模型。
# Load model
# 使用 YOLO 类加载模型。
model = YOLO(model_path)
# 调用 model.half() 方法将模型转换为半精度(FP16),以减少模型大小并可能加快推理速度。
model.half()
# 如果 update_names 参数为 True ,则更新模型中的类别名称为 coco8.yaml 文件中定义的默认类别名称。
if update_names: # update model names from a dataset YAML
# def default_class_names(data=None):
# -> 从一个输入的 YAML 文件中加载默认的类别名称,或者在加载过程中出现错误时返回一组默认的数值类别名称。返回值。函数返回一个包含类别名称的字典。如果成功加载了 YAML 文件并找到了 "names" 键,则返回该键对应的值;否则,返回一个包含默认类别名称的字典。
# -> return yaml_load(check_yaml(data))["names"] / return {i: f"class{i}" for i in range(999)} # return default if above errors
model.model.names = default_class_names("coco8.yaml")
# Define new save path
# 定义新的保存路径 save_path ,它是目标目录下的模型文件名。
save_path = target_dir / model_name
# Save model using model.save()
print(f"Re-saving {model_name} model to {save_path}") # 将 {model_name} 模型重新保存至 {save_path}。
# 使用 model.save() 方法保存模型到新路径, use_dill=False 表示不使用 dill 序列化库。
model.save(save_path, use_dill=False)
# 这个函数依赖于 ultralytics 库,所以在使用之前需要确保该库已正确安装。
# default_class_names 函数需要一个 YAML 文件路径作为参数,该文件应包含类别名称的定义。
# 函数中的 model.save() 方法需要 ultralytics 库支持的模型对象。
# 确保源目录和目标目录的路径正确,且有足够的权限进行文件操作。这个函数可以很方便地用于批量更新和保存 YOLO 模型,特别是当需要更新模型的类别名称或将模型转换为半精度时。