YOLOv10-1.1部分代码阅读笔记-plotting.py

plotting.py

ultralytics\utils\plotting.py

目录

plotting.py

1.所需的库和模块

2.class Colors: 

3.class Annotator: 

4.def plot_labels(boxes, cls, names=(), save_dir=Path(""), on_plot=None): 

5.def save_one_box(xyxy, im, file=Path("im.jpg"), gain=1.02, pad=10, square=False, BGR=False, save=True): 

6.def plot_images(images, batch_idx, cls, bboxes=np.zeros(0, dtype=np.float32), confs=None, masks=np.zeros(0, dtype=np.uint8), kpts=np.zeros((0, 51), dtype=np.float32), paths=None, fname="images.jpg", names=None, on_plot=None, max_subplots=16, save=True, conf_thres=0.25,): 

7.def plot_results(file="path/to/results.csv", dir="", segment=False, pose=False, classify=False, on_plot=None): 

8.def plt_color_scatter(v, f, bins=20, cmap="viridis", alpha=0.8, edgecolors="none"): 

9.def plot_tune_results(csv_file="tune_results.csv"): 

10.def output_to_target(output, max_det=300): 

11.def output_to_rotated_target(output, max_det=300): 

12.def feature_visualization(x, module_type, stage, n=32, save_dir=Path("runs/detect/exp")): 


1.所需的库和模块

# Ultralytics YOLO 🚀, AGPL-3.0 licenseimport contextlib
import math
import warnings
from pathlib import Pathimport cv2
import matplotlib.pyplot as plt
import numpy as np
import torch
from PIL import Image, ImageDraw, ImageFont
from PIL import __version__ as pil_versionfrom ultralytics.utils import LOGGER, TryExcept, ops, plt_settings, threaded
from .checks import check_font, check_version, is_ascii
from .files import increment_path

2.class Colors: 

# 这段代码定义了一个名为 Colors 的类,用于生成和管理颜色。
# 定义一个名为 Colors 的类。
class Colors:# Ultralytics 默认调色板 https://ultralytics.com/。# 此类提供使用 Ultralytics 调色板的方法,包括将十六进制颜色代码转换为 RGB 值。"""Ultralytics default color palette https://ultralytics.com/.This class provides methods to work with the Ultralytics color palette, including converting hex color codes toRGB values.Attributes:palette (list of tuple): List of RGB color values.n (int): The number of colors in the palette.pose_palette (np.ndarray): A specific color palette array with dtype np.uint8."""# 定义类的初始化方法 __init__ ,用于在创建类实例时执行初始化操作。def __init__(self):# 将颜色初始化为 hex = matplotlib.colors.TABLEAU_COLORS.values()。"""Initialize colors as hex = matplotlib.colors.TABLEAU_COLORS.values()."""# 定义一个元组 hexs ,包含一系列十六进制颜色代码。这些颜色代码用于生成颜色调色板。hexs = ("FF3838","FF9D97","FF701F","FFB21D","CFD231","48F90A","92CC17","3DDB86","1A9334","00D4BB","2C99A8","00C2FF","344593","6473FF","0018EC","8438FF","520085","CB38FF","FF95C8","FF37C7",)# 使用列表推导式,将 hexs 中的每个十六进制颜色代码转换为 RGB 格式的颜色,并存储在 self.palette 列表中。 self.hex2rgb 是一个静态方法,用于将十六进制颜色代码转换为 RGB 格式。self.palette = [self.hex2rgb(f"#{c}") for c in hexs]# 计算颜色调色板的长度,并将其存储在 self.n 中。self.n = len(self.palette)# 定义一个 NumPy 数组 self.pose_palette ,包含一系列预定义的颜色,用于姿态估计等任务。这些颜色以 RGB 格式存储,并指定数据类型为 np.uint8 。self.pose_palette = np.array([[255, 128, 0],[255, 153, 51],[255, 178, 102],[230, 230, 0],[255, 153, 255],[153, 204, 255],[255, 102, 255],[255, 51, 255],[102, 178, 255],[51, 153, 255],[255, 153, 153],[255, 102, 102],[255, 51, 51],[153, 255, 153],[102, 255, 102],[51, 255, 51],[0, 255, 0],[0, 0, 255],[255, 0, 0],[255, 255, 255],],dtype=np.uint8,)# 定义一个 __call__ 方法,使类实例可以像函数一样被调用。该方法接收两个参数。# 1.i :颜色索引。# 2.bgr :一个布尔值,用于指定返回的颜色格式是 BGR 还是 RGB,默认为 RGB。def __call__(self, i, bgr=False):# 将十六进制颜色代码转换为 RGB 值。"""Converts hex color codes to RGB values."""# 根据索引 i 获取颜色调色板中的颜色。使用 int(i) % self.n 确保索引在调色板范围内循环。c = self.palette[int(i) % self.n]# 如果 bgr 为 True ,则返回 BGR 格式的颜色;否则返回 RGB 格式的颜色。return (c[2], c[1], c[0]) if bgr else c# 定义一个静态方法 hex2rgb ,该方法不需要访问类的实例属性。@staticmethod# 定义静态方法 hex2rgb ,用于将十六进制颜色代码转换为 RGB 格式的颜色。def hex2rgb(h):# 将十六进制颜色代码转换为 RGB 值(即默认 PIL 顺序)。"""Converts hex color codes to RGB values (i.e. default PIL order)."""# num = int(x, base=10)# int() 是 Python 中的一个内置函数,用于将一个整数或字符串转换成一个整数类型。如果转换成功,返回整数类型的结果;如果转换失败,会抛出一个 ValueError 异常。# 参数 :# x :要转换的值,可以是一个字符串或者一个数字。# base :(可选)进制基数,用于字符串转换时指定数字的进制,默认为 10 进制。# 返回值 :# 返回一个整数类型的值。# 注意事项 :# 如果字符串包含非数字字符(除了首字符为正负号外), int() 函数将抛出 ValueError 。# 如果传入的是浮点数, int() 函数会直接去掉小数部分,只保留整数部分。# 如果 base 参数指定了进制基数, x 必须是一个符合该进制表示的有效字符串。# int() 函数是处理数字和字符串转换时的基础工具,常用于数据类型转换、解析用户输入、文件解析等多种场景。# 将十六进制颜色代码 h 转换为 RGB 格式的颜色。通过切片和 int 函数将每个两位的十六进制数转换为整数,并返回一个元组。return tuple(int(h[1 + i : 1 + i + 2], 16) for i in (0, 2, 4))
# 这个 Colors 类提供了一种方便的方式来生成和管理颜色。它包含一个预定义的颜色调色板,并提供了一个方法 __call__ ,可以通过索引获取颜色,并支持返回 RGB 或 BGR 格式的颜色。此外,类中还包含一个静态方法 hex2rgb ,用于将十六进制颜色代码转换为 RGB 格式的颜色。这种设计使得类在处理颜色时更加灵活和方便,适用于各种需要颜色管理的场景,如图像处理、可视化等。# 调用 Colors 类的构造函数 __init__ ,创建一个 Colors 类的实例,并将该实例赋值给变量 colors 。
colors = Colors()  # create instance for 'from utils.plots import colors'
# 这行代码通过创建 Colors 类的实例,使得可以在代码中方便地调用 Colors 类的方法和属性,获取预定义的颜色。这种设计模式使得颜色管理更加灵活和方便,适用于各种需要颜色管理的场景,如图像处理、可视化等。通过 colors 实例,可以轻松地获取颜色调色板中的颜色,并支持返回 RGB 或 BGR 格式的颜色。

3.class Annotator: 

# 这段代码定义了一个名为 Annotator 的类,用于在图像上添加注释,如绘制边界框、关键点、文本等。
# 定义了一个名为 Annotator 的类。
class Annotator:# Ultralytics Annotator 用于训练/验证马赛克和 JPG 以及预测注释。"""Ultralytics Annotator for train/val mosaics and JPGs and predictions annotations.Attributes:im (Image.Image or numpy array): The image to annotate.pil (bool): Whether to use PIL or cv2 for drawing annotations.font (ImageFont.truetype or ImageFont.load_default): Font used for text annotations.lw (float): Line width for drawing.skeleton (List[List[int]]): Skeleton structure for keypoints.limb_color (List[int]): Color palette for limbs.kpt_color (List[int]): Color palette for keypoints."""# 这段代码是 Annotator 类的初始化方法 __init__ ,用于设置图像注释的基础属性和参数。# 定义了 Annotator 类的初始化方法,接收以下参数 :# 1.im :输入图像,可以是PIL图像或NumPy数组。# 2.line_width :绘制线条的宽度,默认为 None 。# 3.font_size :字体大小,默认为 None 。# 4.font :字体文件,默认为 Arial.ttf 。# 5.pil :是否使用PIL库进行图像处理,默认为 False 。# 6.example :示例文本,默认为 "abc" ,用于检测是否需要支持非ASCII字符。def __init__(self, im, line_width=None, font_size=None, font="Arial.ttf", pil=False, example="abc"):# 使用图像和线宽以及关键点和肢体的调色板初始化 Annotator 类。"""Initialize the Annotator class with image and line width along with color palette for keypoints and limbs."""# 这四行代码是 Annotator 类初始化方法 __init__ 的一部分,主要功能是进行一些初始设置,包括判断字符编码、确定使用的图像处理库以及设置线宽。# 检查示例文本 example 是否包含非ASCII字符。# is_ascii(example) :调用 is_ascii 函数检查 example 字符串中的所有字符是否都是ASCII字符。如果 example 中的所有字符都是ASCII字符,该函数返回 True ;否则返回 False 。# not is_ascii(example) :对 is_ascii 函数的结果取反。如果 example 包含非ASCII字符, non_ascii 将被设置为 True ,表示需要支持非拉丁语系的标签,如亚洲、阿拉伯、西里尔等语系的字符。# def is_ascii(s) -> bool: -> 检查一个给定的字符串 s 是否只包含 ASCII 字符。使用生成器表达式和 all 函数来检查字符串 s 中的每个字符是否都是 ASCII 字符。 -> return all(ord(c) < 128 for c in s)non_ascii = not is_ascii(example)  # non-latin labels, i.e. asian, arabic, cyrillic# 检查输入图像 im 是否为PIL库的 Image.Image 类型。# isinstance(im, Image.Image) :使用 isinstance 函数检查 im 是否是 Image.Image 类型的实例。如果是,返回 True ;否则返回 False 。# input_is_pil :将检查结果赋值给 input_is_pil 变量。如果 im 是PIL图像, input_is_pil 将为 True 。input_is_pil = isinstance(im, Image.Image)# 确定是否使用PIL库进行图像处理。# pil :传入的参数 pil ,如果用户明确指定使用PIL库,则 pil 为 True 。# non_ascii :如果示例文本包含非ASCII字符,需要使用PIL库来支持非拉丁语系的标签。# input_is_pil :如果输入图像已经是PIL图像,也使用PIL库进行处理。# self.pil :将这三个条件进行逻辑或操作,如果任何一个条件为 True ,则 self.pil 将被设置为 True ,表示使用PIL库进行图像处理。self.pil = pil or non_ascii or input_is_pil# 设置线宽 lw 。# line_width :传入的参数 line_width ,如果用户指定了线宽,则使用该值。# im.size if input_is_pil else im.shape :根据 im 的类型(PIL图像或NumPy数组),获取图像的尺寸。如果 im 是PIL图像,使用 im.size ;如果是NumPy数组,使用 im.shape 。# sum(im.size if input_is_pil else im.shape) / 2 * 0.003 :计算图像宽度和高度之和的一半,然后乘以0.003,得到一个基础线宽值。# round(...) :将基础线宽值四舍五入到最接近的整数。# max(..., 2) :确保线宽至少为2。# self.lw :将最终的线宽值赋值给 self.lw 。self.lw = line_width or max(round(sum(im.size if input_is_pil else im.shape) / 2 * 0.003), 2)# 这四行代码完成了以下初始设置,检查示例文本是否包含非ASCII字符,以决定是否需要支持非拉丁语系的标签。检查输入图像是否为PIL图像,以决定是否使用PIL库进行处理。根据用户指定的参数、非ASCII字符检查结果和输入图像类型,确定是否使用PIL库进行图像处理。设置线宽,如果用户未指定线宽,则根据图像尺寸计算一个默认值,确保线宽至少为2。# 这段代码是 Annotator 类初始化方法 __init__ 的一部分,主要处理当决定使用PIL库进行图像处理时的初始化设置。# 检查是否决定使用PIL库进行图像处理。如果 self.pil 为 True ,则执行以下操作。if self.pil:  # use PIL# 根据输入图像的类型,将图像转换为PIL图像格式。# input_is_pil :如果输入图像已经是PIL图像( input_is_pil 为 True ),则直接使用 im 。# Image.fromarray(im) :如果输入图像是NumPy数组,则使用 Image.fromarray 方法将其转换为PIL图像。# self.im :将转换后的PIL图像赋值给 self.im 。self.im = im if input_is_pil else Image.fromarray(im)# 创建一个 ImageDraw 对象,用于在PIL图像上进行绘制操作。 ImageDraw.Draw(self.im) 返回一个可以在 self.im 上绘制图形和文本的对象,赋值给 self.draw 。self.draw = ImageDraw.Draw(self.im)# 开始一个 try-except 块,用于处理字体加载的逻辑,并捕获可能出现的异常。try:# 选择合适的字体文件。# non_ascii :如果 non_ascii 为 True ,表示需要支持非ASCII字符,使用 Arial.Unicode.ttf 字体。# font :如果 non_ascii 为 False ,使用传入的 font 参数指定的字体文件。# check_font :调用 check_font 函数检查并返回字体文件的路径。# def check_font(font="Arial.ttf"):# -> 用于检查和获取指定字体文件的路径。如果文件存在,则直接返回该路径,表示找到了字体文件。如果列表不为空,表示找到了至少一个匹配的字体文件路径,则返回列表中的第一个路径,表示找到了字体文件。返回字体文件的路径。# -> return file / return matches[0] / return filefont = check_font("Arial.Unicode.ttf" if non_ascii else font)# 计算字体大小。# font_size :如果用户指定了字体大小,则使用该值。# sum(self.im.size) / 2 * 0.035 :如果用户未指定字体大小,根据图像尺寸计算一个默认值。计算方法是将图像宽度和高度之和的一半乘以0.035。# round(...) :将计算结果四舍五入到最接近的整数。# max(..., 12) :确保字体大小至少为12。# size :将最终的字体大小赋值给 size 。size = font_size or max(round(sum(self.im.size) / 2 * 0.035), 12)# 加载字体。# ImageFont.truetype(str(font), size) :使用 ImageFont.truetype 方法加载指定路径的字体文件,并设置字体大小。# self.font :将加载的字体对象赋值给 self.font 。self.font = ImageFont.truetype(str(font), size)# 处理字体加载过程中可能出现的异常。except Exception:# 如果在加载字体时发生任何异常,使用 ImageFont.load_default() 加载默认字体,并赋值给 self.font 。self.font = ImageFont.load_default()# Deprecation fix for w, h = getsize(string) -> _, _, w, h = getbox(string)# 处理PIL库版本9.2.0及以上版本中 getsize 方法的弃用问题。# 调用 check_version 函数检查PIL库的版本是否大于或等于9.2.0。# 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 ,表示版本检查是否通过。# -> return resultif check_version(pil_version, "9.2.0"):# getbbox(text, mode='', direction=None, features=None, language=None, stroke_width=0, anchor=None)# ImageFont.truetype.getbbox() 是 PIL (Pillow) 库中 ImageFont 模块的一个方法,用于获取给定文本在指定字体下的边界框。这个方法在 PIL 9.2.0 及以上版本中引入,返回一个四元组,表示文本的边界框的左上角和右下角的坐标。# 参数 :# text (str) :要渲染的文本。# mode (str, optional) :某些图形驱动程序使用它来指示驱动程序喜欢哪种模式;如果为空,渲染器可能返回任一模式。默认值为 '' 。# direction (str, optional) :文本的方向,可以是 None 、 'rtl' (从右到左)或 'ttb' (从上到下)。默认值为 None 。# features (list, optional) :用于控制字体渲染的 OpenType 特性列表。默认值为 None 。# language (str, optional) :文本的语言。不同的语言可以使用不同的字形形状或连字。此参数告诉字体文本使用的是哪种语言,并根据需要应用正确的替换(如果可用)。默认值为 None 。# stroke_width (int, optional) :文本笔划的宽度。默认值为 0 。# anchor (str, optional) :文本锚点对齐方式。确定锚点相对于文本的相对位置。默认对齐方式为左上角。默认值为 None 。# 返回值 :# tuple :返回一个四元组 (left, top, right, bottom) ,表示文本的边界框的左上角和右下角的坐标。# 说明 :# getbbox 方法返回的四元组 (left, top, right, bottom) 表示文本的边界框的左上角和右下角的坐标。# left 和 top 是文本左上角的坐标, right 和 bottom 是文本右下角的坐标。# 这个方法对于需要在图形界面或绘图应用中精确控制文本的位置和大小非常有用。# 版本兼容性 :# getbbox 方法在 PIL 9.2.0 及以上版本中可用。如果使用的是较旧版本的 PIL,可以使用 getsize 方法来获取文本的宽度和高度,但 getsize 方法返回的是一个二元组 (width, height) ,而不是四元组。# 如果版本符合条件,将 getsize 方法重写为使用 getbbox 方法返回文本的宽度和高度。self.font.getsize = lambda x: self.font.getbbox(x)[2:4]  # text width, height# 这段代码完将输入图像转换为PIL图像格式。创建一个 ImageDraw 对象,用于在PIL图像上进行绘制操作。选择合适的字体文件,并计算字体大小。加载字体,如果加载失败则使用默认字体。处理PIL库版本9.2.0及以上版本中 getsize 方法的弃用问题。# 这段代码是 Annotator 类初始化方法 __init__ 的一部分,主要处理当决定使用OpenCV库进行图像处理时的初始化设置。# 如果 self.pil 为 False ,即不使用PIL库,那么将使用OpenCV库进行图像处理。else:  # use cv2# 检查输入图像 im 的数据是否是连续的。# im.data.contiguous :检查图像数据是否在内存中是连续的。# assert :如果图像数据不是连续的,抛出一个 AssertionError ,并提供错误信息。错误信息提示用户可以使用 np.ascontiguousarray(im) 将图像转换为连续数组。assert im.data.contiguous, "Image not contiguous. Apply np.ascontiguousarray(im) to Annotator input images."    # 图像不连续。将 np.ascontiguousarray(im) 应用于 Annotator 输入图像。# 确保图像数据是可以写入的。# im.flags.writeable :检查图像数据是否可以写入。# im.copy() :如果图像数据不能写入,创建一个可写入的副本。# self.im :将最终的图像赋值给 self.im 。self.im = im if im.flags.writeable else im.copy()# 设置字体厚度 tf 。# self.lw - 1 :计算线宽减1。# max(..., 1) :确保字体厚度至少为1。# self.tf :将最终的字体厚度赋值给 self.tf 。self.tf = max(self.lw - 1, 1)  # font thickness# 设置字体缩放比例 sf 。# self.lw / 3 :计算线宽除以3。# self.sf :将最终的字体缩放比例赋值给 self.sf 。self.sf = self.lw / 3  # font scale# 这段代码检查输入图像的数据是否连续,如果不是,则抛出错误并提示用户如何处理。确保图像数据是可以写入的,如果不能写入,则创建一个可写入的副本。设置字体厚度 tf ,确保其至少为1。设置字体缩放比例 sf ,根据线宽计算一个合适的值。这些设置为后续使用OpenCV库进行图像绘制操作提供了基础。# 这段代码是 Annotator 类初始化方法 __init__ 的一部分,主要处理与人体姿态估计相关的初始化设置。具体来说,它定义了人体关键点的连接骨架、肢体颜色和关键点颜色。# Pose# 定义了人体关键点的连接骨架。# self.skeleton :一个列表,每个元素是一个包含两个整数的列表,表示两个关键点之间的连接。# 每个子列表 [a, b] 表示关键点 a 和关键点 b 之间的连接线。# 例如, [16, 14] 表示关键点 16 和关键点 14 之间有一条连接线。self.skeleton = [[16, 14],[14, 12],[17, 15],[15, 13],[12, 13],[6, 12],[7, 13],[6, 7],[6, 8],[7, 9],[8, 10],[9, 11],[2, 3],[1, 2],[1, 3],[2, 4],[3, 5],[4, 6],[5, 7],]# 定义了肢体颜色。# colors.pose_palette :一个预定义的颜色调色板,用于人体姿态估计。# self.limb_color :从颜色调色板中选择特定的颜色,用于绘制肢体连接线。# 每个颜色索引对应一个特定的颜色值,这些颜色值用于绘制不同的肢体连接线。# 例如, [9, 9, 9, 9, 7, 7, 7, 0, 0, 0, 0, 0, 16, 16, 16, 16, 16, 16, 16] 表示不同肢体连接线的颜色索引。self.limb_color = colors.pose_palette[[9, 9, 9, 9, 7, 7, 7, 0, 0, 0, 0, 0, 16, 16, 16, 16, 16, 16, 16]]# 定义了关键点颜色。# colors.pose_palette :一个预定义的颜色调色板,用于人体姿态估计。# self.kpt_color :从颜色调色板中选择特定的颜色,用于绘制关键点。# 每个颜色索引对应一个特定的颜色值,这些颜色值用于绘制不同的关键点。# 例如, [16, 16, 16, 16, 16, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9] 表示不同关键点的颜色索引。self.kpt_color = colors.pose_palette[[16, 16, 16, 16, 16, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9]]# 这段代码定义了人体关键点的连接骨架,用于绘制人体姿态的骨架。定义了肢体颜色,用于在图像上绘制不同肢体部分的连接线。定义了关键点颜色,用于在图像上绘制不同关键点。这些设置为后续在图像上绘制人体姿态估计结果提供了基础。# __init__ 方法初始化了 Annotator 类,设置了图像处理库(PIL或OpenCV)、线宽、字体等基础属性。 根据输入图像的类型和是否包含非ASCII字符,选择使用PIL库或OpenCV库进行图像处理。 初始化了人体姿态的关键点连接骨架和颜色设置,为后续的姿态绘制提供了基础。# 这段代码定义了 Annotator 类的一个方法 box_label ,用于在图像上绘制带有标签的边界框。这个方法支持两种情况:使用PIL库和使用OpenCV库。# 这行定义了一个名为 box_label 的方法,接受五个参数。# 1.box :边界框的坐标,可以是列表或PyTorch张量。# 2.label :边界框的标签文本,默认为空字符串。# 3.color :边界框的颜色,默认为灰色 (128, 128, 128) 。# 4.txt_color :标签文本的颜色,默认为白色 (255, 255, 255) 。# 5.rotated :是否绘制旋转的边界框,默认为 False 。def box_label(self, box, label="", color=(128, 128, 128), txt_color=(255, 255, 255), rotated=False):# 为带有标签的图像添加一个 xyxy 框。"""Add one xyxy box to image with label."""# 检查 box 是否为PyTorch张量,如果是,则将其转换为列表。if isinstance(box, torch.Tensor):box = box.tolist()# 检查是否使用PIL库或标签是否包含非ASCII字符。如果任一条件为真,则使用PIL库进行绘制。if self.pil or not is_ascii(label):# 检查是否绘制旋转的边界框。if rotated:# 获取旋转边界框的第一个点。p1 = box[0]# NOTE: PIL-version polygon needs tuple type.# ImageDraw.polygon(outline, fill=None, width=0)# 在Python的PIL库(现在更多的是其分支Pillow)中, ImageDraw.polygon() 函数用于在图像上绘制一个多边形。# 参数说明 :# outline : 多边形顶点的序列,以坐标对 (x, y) 的形式给出。这些坐标对可以是元组列表、列表列表或者其他可迭代对象。# fill : 填充颜色(可选)。如果为 None ,则多边形不会填充。# width : 多边形边界线的宽度,默认为0,即不绘制边界线。# 返回值 :该函数没有返回值,它直接在 ImageDraw 对象关联的图像上进行绘制。# ImageDraw.polygon() 函数是Pillow库中绘制多边形的常用方法,适用于需要绘制复杂形状或自定义多边形图形的场景。# 使用 polygon 方法绘制旋转的多边形边界框。这里将 box 中的每个点转换为元组类型,因为PIL的 polygon 方法需要元组类型的点。self.draw.polygon([tuple(b) for b in box], width=self.lw, outline=color)# 非旋转边界框。else:# 获取边界框的左上角点。p1 = (box[0], box[1])# 使用 rectangle 方法绘制矩形边界框。self.draw.rectangle(box, width=self.lw, outline=color)  # box# 绘制标签。if label:# 使用 getsize 方法获取标签文本的宽度 w 和高度 h 。w, h = self.font.getsize(label)  # text width, height# 检查标签是否可以放在边界框的上方。如果 p1[1] - h 大于等于0,表示标签可以放在边界框的上方。# 这行代码的作用是判断标签文本是否可以放置在边界框的上方。# p1 边界框的左上角点的坐标,是一个包含两个元素的元组 (x, y) 。 h 标签文本的高度。 p1[1] 边界框左上角点的 y 坐标。# p1[1] - h :计算标签文本放置在边界框上方时的 y 坐标。# p1[1] - h >= 0 :检查计算得到的 y 坐标是否大于等于0。如果大于等于0,表示标签文本可以完全放置在边界框的上方,不会超出图像的上边界。# 示例 :# 假设边界框的左上角点 p1 为 (10, 20) ,标签文本的高度 h 为 15 : p1[1] - h = 20 - 15 = 5 ,因为 5 >= 0 ,所以 outside 为 True ,标签文本可以放置在边界框的上方。# 如果边界框的左上角点 p1 为 (10, 10) ,标签文本的高度 h 为 15 : p1[1] - h = 10 - 15 = -5 ,因为 -5 < 0 ,所以 outside 为 False ,标签文本不能放置在边界框的上方,需要放置在边界框的下方。outside = p1[1] - h >= 0  # label fits outside box# 绘制标签的背景矩形。如果 outside 为 True ,背景矩形的顶部坐标为 p1[1] - h ;否则,背景矩形的底部坐标为 p1[1] + h + 1 。self.draw.rectangle((p1[0], p1[1] - h if outside else p1[1], p1[0] + w + 1, p1[1] + 1 if outside else p1[1] + h + 1),fill=color,)# self.draw.text((box[0], box[1]), label, fill=txt_color, font=self.font, anchor='ls')  # for PIL>8.0# 在背景矩形上绘制标签文本。如果 outside 为 True ,文本的顶部坐标为 p1[1] - h ;否则,文本的底部坐标为  p1[1] + h + 2  。self.draw.text((p1[0], p1[1] - h if outside else p1[1]), label, fill=txt_color, font=self.font)# 这行代码表示如果 self.pil 为 False ,即不使用PIL库,那么将使用OpenCV库进行绘制。else:  # cv2# 检查是否绘制旋转的边界框。if rotated:# 获取旋转边界框的第一个点,并将其转换为整数列表。p1 = [int(b) for b in box[0]]# NOTE: cv2-version polylines needs np.asarray type.# cv2.polylines(img, pts, isClosed, color[, thickness[, lineType[, shift]]])# cv2.polylines() 函数是 OpenCV 库中用于绘制一系列多边形线条的函数。这个函数可以绘制一个或多个多边形的轮廓,可以是开放的也可以是闭合的。# 参数 :# img :目标图像,必须是 8 位或浮点型(单通道或3通道)。# pts :一个列表,其中每个元素是一个 NumPy 数组,代表多边形的顶点。如果 isClosed 参数为 True ,则每个数组代表一个闭合的多边形;如果为 False ,则代表一系列线段。# isClosed :布尔值,指示每个多边形是否闭合。# color :线条的颜色,在 BGR 格式下。对于灰度图像,只需提供一个值。# thickness :可选参数,指定线条的粗细。正数表示线条的粗细, cv2.FILLED 表示填充多边形。# lineType :可选参数,指定线条的类型,可以是以下值之一 : cv2.LINE_4 - 4连通性线(8位宽度)。 cv2.LINE_8 - 8连通性线(8位宽度)。 cv2.LINE_AA - 抗锯齿线。# shift :可选参数,表示顶点坐标的小数点位数。如果顶点坐标是浮点数,这个参数可以用来确定坐标的小数位数。# 返回值 :该函数没有返回值,它直接在输入图像 img 上进行绘制。# 说明 :# pts 参数是一个列表,列表中的每个元素是一个 NumPy 数组,代表多边形的顶点。每个数组的形状应该是 (n, 1, 2) ,其中 n 是顶点的数量。# 如果 isClosed 参数为 True ,则每个多边形的最后一个顶点会与第一个顶点连接起来。# thickness 参数如果为负值(例如 -1 ),则多边形会被填充。# lineType 参数影响线条的绘制方式, cv2.LINE_AA 提供了抗锯齿的效果,使得线条更加平滑。# shift 参数用于处理浮点数坐标,如果顶点坐标是浮点数,可以通过这个参数来确定坐标的小数位数,然后向下取整到最近的整数。# 这个函数常用于在图像上绘制形状,如在计算机视觉任务中标记检测到的对象轮廓。# 使用 polylines 方法绘制旋转的多边形边界框。 box 中的每个点需要转换为NumPy数组,并且数据类型为整数。cv2.polylines(self.im, [np.asarray(box, dtype=int)], True, color, self.lw)# 非旋转边界框。else:# 获取边界框的左上角点 p1 和右下角点 p2 ,并将其转换为整数元组。p1, p2 = (int(box[0]), int(box[1])), (int(box[2]), int(box[3]))# 使用 rectangle 方法绘制矩形边界框。 lineType=cv2.LINE_AA 表示使用抗锯齿线条。cv2.rectangle(self.im, p1, p2, color, thickness=self.lw, lineType=cv2.LINE_AA)# 绘制标签。if label:# 使用 getTextSize 方法获取标签文本的宽度 w 和高度 h 。 fontScale=self.sf 和 thickness=self.tf 分别表示字体缩放比例和字体厚度。w, h = cv2.getTextSize(label, 0, fontScale=self.sf, thickness=self.tf)[0]  # text width, height# 检查标签是否可以放在边界框的上方。如果 p1[1] - h 大于等于3,表示标签可以放在边界框的上方。outside = p1[1] - h >= 3# 计算标签背景矩形的右下角点 p2 。如果 outside 为 True ,背景矩形的顶部坐标为 p1[1] - h - 3 ;否则,背景矩形的底部坐标为 p1[1] + h + 3 。p2 = p1[0] + w, p1[1] - h - 3 if outside else p1[1] + h + 3# 绘制标签的背景矩形。 -1 表示填充矩形。cv2.rectangle(self.im, p1, p2, color, -1, cv2.LINE_AA)  # filled# 在背景矩形上绘制标签文本。如果 outside 为 True ,文本的顶部坐标为 p1[1] - 2 ;否则,文本的底部坐标为 p1[1] + h + 2 。cv2.putText(self.im,label,(p1[0], p1[1] - 2 if outside else p1[1] + h + 2),0,self.sf,txt_color,thickness=self.tf,lineType=cv2.LINE_AA,)# box_label 方法根据是否使用PIL库或OpenCV库,分别绘制带有标签的边界框。这个方法支持绘制旋转和非旋转的边界框,并根据标签的大小和位置自动调整标签的显示位置。这些功能使得在图像上标注对象变得非常灵活和方便。# 在图像处理中,坐标 (0, 0) 通常表示图像的左上角。这是图像坐标系的原点。具体来说 :# x 坐标 :表示水平方向的位置,从左到右增加。# y 坐标 :表示垂直方向的位置,从上到下增加。# 图像坐标系示意图 :# (0, 0) -----------------> (width, 0)#    |                         |#    |                         |#    |                         |#    |                         |#    |                         |#    |                         |#    |                         |#    |                         |#    |                         |#    |                         |# (0, height) -------------> (width, height)# 左上角 : (0, 0) ,这是图像的起点。# 右上角 : (width, 0) ,宽度为图像的宽度。# 左下角 : (0, height) ,高度为图像的高度。# 右下角 : (width, height) ,宽度和高度分别为图像的宽度和高度。# 这段代码定义了 Annotator 类的一个方法 masks ,用于在图像上绘制掩码。这个方法支持PIL库和OpenCV库,可以处理多个掩码并将其叠加到图像上。# 这行定义了一个名为 masks 的方法,接受五个参数。# 1.masks :预测的掩码,形状为 [n, h, w] ,其中 n 是掩码的数量, h 和 w 是图像的高度和宽度。# 2.colors :每个掩码的颜色,形状为 [n, 3] ,每个颜色是一个 RGB 值。# 3.im_gpu :图像在 GPU 上的表示,形状为 [3, h, w] ,范围为 [0, 1] 。# 4.alpha :掩码的透明度,范围为 [0, 1] ,默认值为 0.5 。# 5.retina_masks :是否使用高分辨率掩码, 默认为 False 。def masks(self, masks, colors, im_gpu, alpha=0.5, retina_masks=False):# 在图像上绘制蒙版。# 参数:# mask (tensor):cuda 上的预测蒙版,形状:[n, h, w]# colors (List[List[Int]]):预测蒙版的颜色,[[r, g, b] * n]# im_gpu (tensor):图像在 cuda 中,形状:[3, h, w],范围:[0, 1]"""Plot masks on image.Args:masks (tensor): Predicted masks on cuda, shape: [n, h, w]colors (List[List[Int]]): Colors for predicted masks, [[r, g, b] * n]im_gpu (tensor): Image is in cuda, shape: [3, h, w], range: [0, 1]alpha (float): Mask transparency: 0.0 fully transparent, 1.0 opaqueretina_masks (bool): Whether to use high resolution masks or not. Defaults to False."""# 这段代码是 Annotator 类中 masks 方法的一部分,用于在图像上绘制掩码。具体来说,它处理了PIL图像的转换、没有掩码的情况、图像和掩码的设备同步、颜色和掩码的处理。# 如果使用PIL库。if self.pil:# Convert to numpy first# 将PIL图像转换为NumPy数组并复制一份。这一步是为了确保后续操作可以在NumPy数组上进行。self.im = np.asarray(self.im).copy()# 如果没有掩码( masks 列表为空)。if len(masks) == 0:# 直接将图像从GPU转换为NumPy数组并乘以255,然后赋值给 self.im 。这里使用 permute(1, 2, 0) 将图像的通道顺序从 [3, h, w] 转换为 [h, w, 3] , contiguous() 确保内存是连续的, cpu().numpy() 将Tensor转换为NumPy数组。self.im[:] = im_gpu.permute(1, 2, 0).contiguous().cpu().numpy() * 255# 如果图像和掩码不在同一个设备上(例如,一个在CPU,一个在GPU)。if im_gpu.device != masks.device:# 将图像移动到掩码所在的设备。这一步确保后续操作可以在同一个设备上进行,避免设备不匹配的错误。im_gpu = im_gpu.to(masks.device)# 将颜色列表转换为Tensor,并归一化到 [0, 1] 范围。颜色列表 colors 的形状为 [n, 3] ,其中 n 是掩码的数量,3表示RGB三个通道。colors = torch.tensor(colors, device=masks.device, dtype=torch.float32) / 255.0  # shape(n,3)# 调整颜色Tensor的形状,从 [n, 3] 变为 [n, 1, 1, 3] 。这一步是为了在后续的逐元素乘法中,颜色Tensor的形状能够与掩码Tensor的形状匹配。colors = colors[:, None, None]  # shape(n,1,1,3)# 调整掩码Tensor的形状,从 [n, h, w] 变为 [n, h, w, 1] 。这一步是为了在后续的逐元素乘法中,掩码Tensor的形状能够与颜色Tensor的形状匹配。masks = masks.unsqueeze(3)  # shape(n,h,w,1)# 计算每个掩码的颜色表示。这里使用逐元素乘法,将掩码Tensor与颜色Tensor相乘,并考虑透明度 alpha 。最终得到的 masks_color 的形状为 [n, h, w, 3] ,表示每个掩码的颜色表示。masks_color = masks * (colors * alpha)  # shape(n,h,w,3)# 这段代码完成了以下功能,PIL图像转换:如果使用PIL库,将PIL图像转换为NumPy数组。处理没有掩码的情况:如果没有掩码,直接将图像从GPU转换为NumPy数组并赋值给 self.im 。设备同步:确保图像和掩码在同一个设备上,避免设备不匹配的错误。颜色处理:将颜色列表转换为Tensor并归一化,调整颜色和掩码的形状,以便进行逐元素乘法。计算掩码颜色:计算每个掩码的颜色表示,考虑透明度 alpha 。这些步骤为后续的掩码叠加和图像绘制提供了基础。# np.cumprod(a, axis=None, dtype=None, out=None, *, where=None)# np.cumprod() 是 NumPy 库中的一个函数,它用于计算输入数组中元素的累积乘积。累积乘积是指从数组的第一个元素开始,逐个将元素与之前所有元素的乘积相乘的结果。# 参数说明 :# a : 输入数组。# axis : 沿哪个轴计算累积乘积。如果为 None ,则计算扁平化的数组的累积乘积。# dtype : 输出数组的数据类型。如果为 None ,则数据类型与输入数组相同。# out : 用于存放结果的输出数组。# where : 一个布尔数组,与输入数组形状相同,用来选择性地计算累积乘积。# 返回值 :# 返回一个新的数组,其形状与输入数组相同,包含了输入数组沿指定轴的累积乘积。# 这段代码继续 masks 方法的实现,主要处理掩码的叠加和图像的最终合成。# 计算逆透明度掩码,用于在叠加多个掩码时保持背景的透明度。 masks * alpha :将每个掩码乘以透明度 alpha 。 1 - masks * alpha :计算每个掩码的逆透明度。# .cumprod(0) :沿着第一个维度(即掩码的维度)计算累积乘积。这一步确保在叠加多个掩码时,后面的掩码不会覆盖前面掩码的透明部分。inv_alpha_masks = (1 - masks * alpha).cumprod(0)  # shape(n,h,w,1)# 计算所有掩码的最大颜色值,用于最终的叠加效果。 masks_color.max(dim=0).values :沿着第一个维度(即掩码的维度)计算最大值,得到每个像素位置的最大颜色值。mcs = masks_color.max(dim=0).values  # shape(n,h,w,3)# 沿着第一个维度(即颜色通道维度)进行翻转。将张量的通道顺序从 [R, G, B] 翻转为 [B, G, R] 。 im_gpu 形状不变,只是通道顺序改变。# im_gpu 的初始形状是 [3, h, w] ,其中 : 3 表示图像的三个颜色通道(RGB)。 h 表示图像的高度。 w 表示图像的宽度。# flip(dims=[0]) flip 方法用于沿着指定的维度翻转张量。参数 dims=[0] 表示沿着第一个维度(即颜色通道维度)进行翻转。 这会将第一个通道和第三个通道交换位置,中间的通道保持不变。因此, im_gpu.flip(dims=[0]) 将张量的通道顺序从 [R, G, B] 翻转为 [B, G, R] 。最终形状经过 flip(dims=[0]) 处理后, im_gpu 的形状不会改变,仍然是 [3, h, w] 。只是通道的顺序发生了变化。im_gpu = im_gpu.flip(dims=[0])  # flip channel# 调整维度顺序并确保内存是连续的。 im_gpu.permute(1, 2, 0) 将张量的形状从 [3, h, w] 转换为 [h, w, 3] 。im_gpu = im_gpu.permute(1, 2, 0).contiguous()  # shape(h,w,3)# 将图像和掩码进行叠加,考虑透明度。 im_gpu * inv_alpha_masks[-1] :将图像乘以逆透明度掩码的最后一个值,确保背景的透明度。 im_gpu * inv_alpha_masks[-1] + mcs :将处理后的图像与最大颜色值相加,得到最终的叠加效果。im_gpu = im_gpu * inv_alpha_masks[-1] + mcs# 将结果乘以255,转换为整数范围。im_mask = im_gpu * 255# 将Tensor转换为NumPy数组,并确保数据类型为 uint8 。im_mask_np = im_mask.byte().cpu().numpy()# 如果使用高分辨率掩码,直接赋值;否则,调整图像大小以匹配原始图像的尺寸。# def scale_image(masks, im0_shape, ratio_pad=None): -> 用于将图像掩码(masks)从一个形状( im1_shape )缩放到另一个形状( im0_shape )。返回缩放后的 masks 。 -> return masksself.im[:] = im_mask_np if retina_masks else ops.scale_image(im_mask_np, self.im.shape)# 如果使用PIL库,将NumPy数组转换回PIL图像并更新 self.draw 对象。if self.pil:# Convert im back to PIL and update draw# 将NumPy数组转换为PIL图像,并更新 self.im 和 self.draw 。self.fromarray(self.im)# 这段代码完成了以下功能,逆透明度掩码:计算逆透明度掩码,用于在叠加多个掩码时保持背景的透明度。最大颜色值:计算所有掩码的最大颜色值,用于最终的叠加效果。通道顺序调整:将图像的通道顺序从 [3, h, w] 转换为 [h, w, 3] 。叠加掩码和图像:将图像和掩码进行叠加,考虑透明度,并将结果转换为NumPy数组。更新PIL图像:如果使用PIL库,将NumPy数组转换回PIL图像并更新绘制对象。这些步骤确保了掩码能够正确地叠加到图像上,并且最终结果可以用于进一步的图像处理或显示。# masks 方法的主要功能是在图像上绘制多个掩码,并考虑透明度和颜色。这个方法支持PIL库和OpenCV库,可以处理没有掩码的情况,并确保图像和掩码在同一个设备上。通过逐元素乘法和逆透明度掩码的计算,方法能够有效地叠加多个掩码,最终将结果赋值给 self.im 。如果使用PIL库,还会将结果转换回PIL图像并更新绘制对象。# 这段代码定义了 Annotator 类的一个方法 kpts ,用于在图像上绘制关键点(keypoints)和关键点之间的连接线。这个方法支持PIL库和OpenCV库,可以处理不同形状的关键点数据,并根据关键点的置信度过滤掉不准确的关键点。# 这行定义了一个名为 kpts 的方法,接受四个参数。# 1.kpts :关键点数据,形状为 [nkpt, ndim] ,其中 nkpt 是关键点的数量, ndim 是每个关键点的维度(2或3,分别表示 (x, y) 或 (x, y, confidence) )。# 2.shape :图像的形状,格式为 (height, width) ,默认为 (640, 640) 。# 3.radius :绘制关键点时的圆半径,默认为 5 。# 4.kpt_line :是否绘制关键点之间的连接线,默认为 True 。def kpts(self, kpts, shape=(640, 640), radius=5, kpt_line=True):# 在图像上绘制关键点。# 参数:# kpts(张量):形状为 [17, 3] 的预测关键点。每个关键点都有 (x, y, 置信度)。# shape(元组):图像形状为元组 (h, w),其中 h 是高度,w 是宽度。"""Plot keypoints on the image.Args:kpts (tensor): Predicted keypoints with shape [17, 3]. Each keypoint has (x, y, confidence).shape (tuple): Image shape as a tuple (h, w), where h is the height and w is the width.radius (int, optional): Radius of the drawn keypoints. Default is 5.kpt_line (bool, optional): If True, the function will draw lines connecting keypointsfor human pose. Default is True.Note:`kpt_line=True` currently only supports human pose plotting."""# 如果使用PIL库。if self.pil:# Convert to numpy first# 将PIL图像转换为NumPy数组并复制一份。这一步是为了确保后续操作可以在NumPy数组上进行。self.im = np.asarray(self.im).copy()# 获取 关键点的数量 nkpt 和 每个关键点的维度 ndim 。nkpt, ndim = kpts.shape# 检查是否为人体姿态关键点,人体姿态通常有17个关键点,每个关键点的维度可以是2或3。is_pose = nkpt == 17 and ndim in {2, 3}# 如果 kpt_line 为 True ,则只有在 is_pose 为 True 时才绘制关键点之间的连接线。kpt_line &= is_pose  # `kpt_line=True` for now only supports human pose plotting# 用于遍历关键点数据 kpts ,每次迭代中, i 是当前关键点的索引, k 是当前关键点的数据。for i, k in enumerate(kpts):# 选择关键点的颜色。如果 is_pose 为 True ,使用预定义的颜色 self.kpt_color ;否则,使用 colors(i) 生成的颜色。color_k = [int(x) for x in self.kpt_color[i]] if is_pose else colors(i)# 获取关键点的坐标。x_coord, y_coord = k[0], k[1]# 检查关键点的坐标是否在图像范围内。# 用于检查关键点的坐标是否在图像的有效范围内。它确保关键点的坐标不是图像边界的整数倍,从而避免绘制在图像边界上的关键点。# x_coord % shape[1] != 0 :检查 x 坐标是否不是图像宽度的整数倍。如果 x_coord 是 shape[1] 的整数倍,说明该关键点位于图像的右边界上。# y_coord % shape[0] != 0 :检查 y 坐标是否不是图像高度的整数倍。如果 y_coord 是 shape[0] 的整数倍,说明该关键点位于图像的下边界上。# 确保关键点的坐标既不在图像的右边界上,也不在图像的下边界上。只有满足这个条件的关键点才会被绘制。if x_coord % shape[1] != 0 and y_coord % shape[0] != 0:# 如果关键点有置信度信息,检查置信度是否大于0.5,如果小于0.5则跳过该关键点。if len(k) == 3:conf = k[2]if conf < 0.5:continue# 使用OpenCV绘制关键点。cv2.circle(self.im, (int(x_coord), int(y_coord)), radius, color_k, -1, lineType=cv2.LINE_AA)# 如果 kpt_line 为 True ,则绘制关键点之间的连接线。if kpt_line:# 获取每个关键点的维度。ndim = kpts.shape[-1]# 遍历预定义的骨架连接。for i, sk in enumerate(self.skeleton):# 获取连接线的两个端点的坐标。pos1 = (int(kpts[(sk[0] - 1), 0]), int(kpts[(sk[0] - 1), 1]))pos2 = (int(kpts[(sk[1] - 1), 0]), int(kpts[(sk[1] - 1), 1]))# 如果关键点有置信度信息,检查两个端点的置信度是否都大于0.5,如果有一个小于0.5则跳过该连接线。if ndim == 3:conf1 = kpts[(sk[0] - 1), 2]conf2 = kpts[(sk[1] - 1), 2]if conf1 < 0.5 or conf2 < 0.5:continue# 检查两个端点的坐标是否在图像范围内。# 用于检查一个点 pos1 的坐标是否位于图像的边界上或超出图像的范围。# pos1[0] % shape[1] == 0 :如果 x 坐标是图像宽度的整数倍,说明该点位于图像的右边界上。# pos1[1] % shape[0] == 0 :如果 y 坐标是图像高度的整数倍,说明该点位于图像的下边界上。# pos1[0] < 0 :如果 x 坐标小于 0,说明该点位于图像的左侧边界外。# pos1[1] < 0 :如果 y 坐标小于 0,说明该点位于图像的上边界外。# 通常情况下,检查点是否在图像的边界外应该包括所有四个方向:左侧、上侧、右侧和下侧。然而,代码片段 if pos1[0] % shape[1] == 0 or pos1[1] % shape[0] == 0 or pos1[0] < 0 or pos1[1] < 0: 实际上并没有直接检查点是否在右侧和下侧边界外,这可能是一个疏忽或特定应用场景下的简化。# 确保点在图像的有效范围内通常需要检查所有四个边界。如果应用场景需要严格的边界检查,建议使用完整的条件来确保点不会超出图像的任何边界。if pos1[0] % shape[1] == 0 or pos1[1] % shape[0] == 0 or pos1[0] < 0 or pos1[1] < 0:continueif pos2[0] % shape[1] == 0 or pos2[1] % shape[0] == 0 or pos2[0] < 0 or pos2[1] < 0:continue# 使用OpenCV绘制连接线。cv2.line(self.im, pos1, pos2, [int(x) for x in self.limb_color[i]], thickness=2, lineType=cv2.LINE_AA)# 如果使用PIL库,将NumPy数组转换回PIL图像并更新 self.draw 对象。if self.pil:# Convert im back to PIL and update draw# 将NumPy数组转换为PIL图像,并更新 self.im 和 self.draw 。self.fromarray(self.im)# kpts 方法的主要功能是在图像上绘制关键点和关键点之间的连接线。这个方法支持PIL库和OpenCV库,可以处理不同形状的关键点数据,并根据关键点的置信度过滤掉不准确的关键点。通过预定义的骨架连接,方法能够有效地绘制人体姿态的关键点和连接线。这些步骤确保了关键点能够正确地绘制到图像上,并且最终结果可以用于进一步的图像处理或显示。# 这段代码定义了 Annotator 类的一个方法 rectangle ,用于在图像上绘制矩形。这个方法使用 PIL 库的 ImageDraw 模块来绘制矩形。# 这行定义了一个名为 rectangle 的方法,接受四个参数。# 1.xy :矩形的坐标,格式为 [x0, y0, x1, y1] ,其中 (x0, y0) 是矩形的左上角坐标, (x1, y1) 是矩形的右下角坐标。# 2.fill :矩形的填充颜色,可以是颜色名称(如 'red' )、十六进制颜色代码(如 #FF0000 )或 RGB 元组(如 (255, 0, 0) )。默认为 None ,表示不填充。# 3.outline :矩形的轮廓颜色,可以是颜色名称、十六进制颜色代码或 RGB 元组。默认为 None ,表示不绘制轮廓。# 4.width :轮廓的宽度,默认为 1 。def rectangle(self, xy, fill=None, outline=None, width=1):# 将矩形添加到图像(仅限 PIL)。"""Add rectangle to image (PIL-only)."""# 使用 ImageDraw 的 rectangle 方法绘制矩形。  self.draw : ImageDraw.Draw   对象,用于在图像上绘制图形。self.draw.rectangle(xy, fill, outline, width)# rectangle 方法的主要功能是在图像上绘制矩形。这个方法使用 PIL 库的 ImageDraw 模块,支持填充颜色、轮廓颜色和轮廓宽度的设置。通过传入矩形的坐标和相关参数,可以灵活地在图像上绘制矩形。这些功能使得在图像上标注对象变得非常灵活和方便。# 这段代码定义了 Annotator 类的一个方法 text ,用于在图像上绘制文本。这个方法支持PIL库和OpenCV库,可以处理文本的对齐、背景框的绘制以及多行文本的处理。# 这行定义了一个名为 text 的方法,接受五个参数。# 1.xy :文本的起始坐标,格式为 [x, y] 。# 2.text :要绘制的文本内容。# 3.txt_color :文本颜色,默认为白色 (255, 255, 255) 。# 4.anchor :文本的对齐方式,默认为 "top" ,表示文本从顶部开始绘制。如果为 "bottom" ,则从底部开始绘制。# 5.box_style :是否绘制文本背景框,默认为 False 。def text(self, xy, text, txt_color=(255, 255, 255), anchor="top", box_style=False):# 使用 PIL 或 cv2 向图像添加文本。"""Adds text to an image using PIL or cv2."""# 如果 anchor 为 "bottom" ,则调整 y 坐标,使文本从底部开始绘制。if anchor == "bottom":  # start y from font bottom# 获取文本的宽度 w 和高度 h 。w, h = self.font.getsize(text)  # text width, height# 调整 y 坐标,使其从文本的底部开始绘制。xy[1] += 1 - h# 使用PIL库绘制文本。# 如果 self.pil 为 True ,则使用 PIL 库进行绘制。if self.pil:# 如果 box_style 为 True ,则绘制文本背景框。if box_style:# 使用 self.font.getsize(text) 获取文本的宽度 w 和高度 h 。w, h = self.font.getsize(text)# 使用 self.draw.rectangle 方法绘制背景框。背景框的坐标为 (xy[0], xy[1], xy[0] + w + 1, xy[1] + h + 1) ,填充颜色为 txt_color 。self.draw.rectangle((xy[0], xy[1], xy[0] + w + 1, xy[1] + h + 1), fill=txt_color)# Using `txt_color` for background and draw fg with white color# 将文本颜色设置为白色,以便在背景框上显示。txt_color = (255, 255, 255)# 检查文本是否包含换行符 \n 。if "\n" in text:# 将文本按换行符分割成多行。lines = text.split("\n")# 获取文本的高度 h 。_, h = self.font.getsize(text)# 遍历每一行文本。for line in lines:# 使用 self.draw.text 方法绘制当前行文本。self.draw.text(xy, line, fill=txt_color, font=self.font)# 调整 y 坐标,以便绘制下一行文本。xy[1] += h# 如果文本不包含换行符,直接绘制单行文本。else:# 使用 self.draw.text 方法绘制文本。self.draw.text(xy, text, fill=txt_color, font=self.font)# 使用OpenCV库绘制文本。# 如果 self.pil 为 False ,则使用 OpenCV 库进行绘制。else:# 如果 box_style 为 True ,则绘制文本背景框。if box_style:# 使用 cv2.getTextSize 方法获取文本的宽度 w 和高度 h 。 fontScale=self.sf 和 thickness=self.tf 分别表示字体缩放比例和字体厚度。w, h = cv2.getTextSize(text, 0, fontScale=self.sf, thickness=self.tf)[0]  # text width, height# 为什么选择3个像素?# 视觉美观 :3个像素的空间可以确保文本不会紧贴图像的顶部边界,从而看起来更加美观和自然。# 文本可读性 :3个像素的空间可以提供足够的缓冲区,避免文本与图像边界重叠,提高文本的可读性。# 经验选择 :3个像素是一个经验值,通常在大多数情况下都能提供良好的视觉效果。这个值可以根据具体的应用场景和图像分辨率进行调整。# 检查文本是否可以放在起始点的上方。如果 xy[1] - h 大于等于 3,表示文本可以放在起始点的上方。outside = xy[1] - h >= 3# 计算背景框的右下角坐标。如果 outside 为 True ,背景框的顶部坐标为 xy[1] - h - 3 ;否则,背景框的底部坐标为 xy[1] + h + 3 。p2 = xy[0] + w, xy[1] - h - 3 if outside else xy[1] + h + 3# 使用 cv2.rectangle 方法绘制背景框。 -1 表示填充矩形。cv2.rectangle(self.im, xy, p2, txt_color, -1, cv2.LINE_AA)  # filled# Using `txt_color` for background and draw fg with white color# 将文本颜色设置为白色,以便在背景框上显示。txt_color = (255, 255, 255)# 使用 cv2.putText 方法在图像上绘制文本。# self.im :图像数组。 text :要绘制的文本内容。 xy :文本的起始坐标。 0 :字体类型(默认为 cv2.FONT_HERSHEY_SIMPLEX )。 self.sf :字体缩放比例。 txt_color :文本颜色。 thickness=self.tf :字体厚度。 lineType=cv2.LINE_AA :线条类型,使用抗锯齿。cv2.putText(self.im, text, xy, 0, self.sf, txt_color, thickness=self.tf, lineType=cv2.LINE_AA)# text 方法的主要功能是在图像上绘制文本。这个方法支持PIL库和OpenCV库,可以处理文本的对齐、背景框的绘制以及多行文本的处理。处理 anchor 为 "bottom" 的情况:调整 y 坐标,使文本从底部开始绘制。使用PIL库绘制文本:绘制背景框。处理多行文本,逐行绘制。绘制单行文本。使用OpenCV库绘制文本:绘制背景框。绘制文本。这些功能使得在图像上标注文本变得非常灵活和方便,特别是在处理多行文本和背景框时。# 这段代码定义了 Annotator 类的一个方法 fromarray ,用于将 NumPy 数组转换为 PIL 图像,并更新 Annotator 实例中的图像和绘图对象。# 这行定义了一个名为 fromarray 的方法,接受一个参数。# 1.im :输入图像,可以是 NumPy 数组或 PIL 图像。def fromarray(self, im):# 从 numpy 数组更新 self.im。"""Update self.im from a numpy array."""# 检查输入图像的类型并进行转换。# isinstance(im, Image.Image) :检查输入图像 im 是否已经是 PIL 图像。# im if isinstance(im, Image.Image) else Image.fromarray(im) :如果 im 是 PIL 图像,则直接使用 im ;否则,使用 Image.fromarray(im) 将 NumPy 数组转换为 PIL 图像。# self.im = ... :将转换后的图像赋值给 self.im 。self.im = im if isinstance(im, Image.Image) else Image.fromarray(im)# 创建绘图对象。# ImageDraw.Draw(self.im) :使用 ImageDraw.Draw 方法创建一个绘图对象,该对象可以在 self.im 上进行绘制操作。# self.draw = ... :将创建的绘图对象赋值给 self.draw 。self.draw = ImageDraw.Draw(self.im)# fromarray 方法的主要功能是将输入图像(可以是 NumPy 数组或 PIL 图像)转换为 PIL 图像,并更新 Annotator 实例中的图像和绘图对象。检查输入图像的类型:如果输入图像是 PIL 图像,则直接使用;否则,将 NumPy 数组转换为 PIL 图像。创建绘图对象:使用 ImageDraw.Draw 方法创建一个绘图对象,以便在图像上进行绘制操作。这些功能使得 Annotator 类可以灵活地处理不同类型的输入图像,并在图像上进行各种标注和绘制操作。# 这段代码定义了 Annotator 类的一个方法 result ,用于返回当前图像的 NumPy 数组表示。这个方法在处理完所有标注和绘制操作后调用,以便将最终的图像转换为 NumPy 数组,方便后续的处理或显示。# 这个方法没有参数,直接返回当前图像的 NumPy 数组表示。def result(self):# 以数组形式返回带注释的图像。"""Return annotated image as array."""# 返回图像的 NumPy 数组表示。# 使用 np.asarray 方法将 self.im (PIL 图像)转换为 NumPy 数组。 返回转换后的 NumPy 数组。return np.asarray(self.im)# result 方法的主要功能是将当前图像(PIL 图像)转换为 NumPy 数组。这个方法在处理完所有标注和绘制操作后调用,以便将最终的图像转换为 NumPy 数组,方便后续的处理或显示。转换图像:使用 np.asarray 方法将 self.im (PIL 图像)转换为 NumPy 数组。返回结果:返回转换后的 NumPy 数组。# 这段代码定义了 Annotator 类的一个方法 show ,用于显示当前图像。这个方法将图像转换为 NumPy 数组,然后使用 PIL 库的 Image.fromarray 方法将数组转换回 PIL 图像,并调用 show 方法显示图像。# 1.title :显示图像时的窗口标题,默认为 None 。def show(self, title=None):# 显示带注释的图像。"""Show the annotated image."""# 转换图像并显示。# np.asarray(self.im) :将 self.im (PIL 图像)转换为 NumPy 数组。# [..., ::-1] :使用切片操作 [::-1] 翻转数组的最后一个维度,将 RGB 通道转换为 BGR 通道。这是因为 PIL 图像使用 RGB 格式,而 OpenCV 和许多其他图像显示库使用 BGR 格式。# Image.fromarray(...) :将转换后的 NumPy 数组转换回 PIL 图像。# .show(title) :使用 PIL 图像的 show 方法显示图像。如果 title 不为 None ,则使用 title 作为窗口标题。Image.fromarray(np.asarray(self.im)[..., ::-1]).show(title)# show 方法的主要功能是显示当前图像。转换图像:将 self.im (PIL 图像)转换为 NumPy 数组,并翻转通道顺序。显示图像:将转换后的 NumPy 数组转换回 PIL 图像,并使用 show 方法显示图像。# 这段代码定义了 Annotator 类的一个方法 save ,用于将当前图像保存到指定的文件中。这个方法使用 OpenCV 库的 cv2.imwrite 函数来保存图像。# 1.filename :要保存的图像文件名,默认为 "image.jpg" 。def save(self, filename="image.jpg"):# 将带注释的图像保存至‘文件名’。"""Save the annotated image to 'filename'."""# 保存图像。# np.asarray(self.im) :将 self.im (PIL 图像)转换为 NumPy 数组。这是因为 cv2.imwrite 函数需要一个 NumPy 数组作为输入。# cv2.imwrite(filename, ...) :使用 OpenCV 的 cv2.imwrite 函数将 NumPy 数组保存到指定的文件中。 filename 参数指定了保存的文件名和路径。cv2.imwrite(filename, np.asarray(self.im))# save 方法的主要功能是将当前图像保存到指定的文件中。转换图像:将 self.im (PIL 图像)转换为 NumPy 数组。保存图像:使用 cv2.imwrite 函数将 NumPy 数组保存到指定的文件中。# 这段代码定义了 Annotator 类的一个方法 draw_region ,用于在图像上绘制一个多边形区域。这个方法使用 OpenCV 库的 cv2.polylines 函数来绘制多边形。# 1.reg_pts :多边形的顶点坐标,格式为 [[x1, y1], [x2, y2], ..., [xn, yn]] 。默认为 None 。# 2.color :多边形的颜色,格式为 (B, G, R) 。默认为绿色 (0, 255, 0) 。# 3.thickness :多边形线条的厚度。默认为 5 。def draw_region(self, reg_pts=None, color=(0, 255, 0), thickness=5):# 绘制区域线。"""Draw region line.Args:reg_pts (list): Region Points (for line 2 points, for region 4 points)color (tuple): Region Color valuethickness (int): Region area thickness value"""# cv2.polylines(img, pts, isClosed, color[, thickness[, lineType[, shift]]])# cv2.polylines() 函数是 OpenCV 库中用于绘制一系列多边形线条的函数。这个函数可以绘制一个或多个多边形的轮廓,可以是开放的也可以是闭合的。# 参数 :# img :目标图像,必须是 8 位或浮点型(单通道或3通道)。# pts :一个列表,其中每个元素是一个 NumPy 数组,代表多边形的顶点。如果 isClosed 参数为 True ,则每个数组代表一个闭合的多边形;如果为 False ,则代表一系列线段。# isClosed :布尔值,指示每个多边形是否闭合。# color :线条的颜色,在 BGR 格式下。对于灰度图像,只需提供一个值。# thickness :可选参数,指定线条的粗细。正数表示线条的粗细, cv2.FILLED 表示填充多边形。# lineType :可选参数,指定线条的类型,可以是以下值之一 : cv2.LINE_4 - 4连通性线(8位宽度)。 cv2.LINE_8 - 8连通性线(8位宽度)。 cv2.LINE_AA - 抗锯齿线。# shift :可选参数,表示顶点坐标的小数点位数。如果顶点坐标是浮点数,这个参数可以用来确定坐标的小数位数。# 返回值 :该函数没有返回值,它直接在输入图像 img 上进行绘制。# 说明 :# pts 参数是一个列表,列表中的每个元素是一个 NumPy 数组,代表多边形的顶点。每个数组的形状应该是 (n, 1, 2) ,其中 n 是顶点的数量。# 如果 isClosed 参数为 True ,则每个多边形的最后一个顶点会与第一个顶点连接起来。# thickness 参数如果为负值(例如 -1 ),则多边形会被填充。# lineType 参数影响线条的绘制方式, cv2.LINE_AA 提供了抗锯齿的效果,使得线条更加平滑。# shift 参数用于处理浮点数坐标,如果顶点坐标是浮点数,可以通过这个参数来确定坐标的小数位数,然后向下取整到最近的整数。# 这个函数常用于在图像上绘制形状,如在计算机视觉任务中标记检测到的对象轮廓。# 绘制多边形区域。# np.array(reg_pts, dtype=np.int32) :将多边形的顶点坐标 reg_pts 转换为 NumPy 数组,并确保数据类型为 np.int32 。这是因为 cv2.polylines 函数需要顶点坐标为整数类型。# [np.array(reg_pts, dtype=np.int32)] :将顶点坐标数组包装在一个列表中,因为 cv2.polylines 函数需要一个顶点坐标列表。# isClosed=True :表示多边形是闭合的,即最后一个顶点会连接到第一个顶点。# color :多边形的颜色,格式为 (B, G, R) 。# thickness :多边形线条的厚度。cv2.polylines(self.im, [np.array(reg_pts, dtype=np.int32)], isClosed=True, color=color, thickness=thickness)# draw_region 方法的主要功能是在图像上绘制一个多边形区域。转换顶点坐标:将多边形的顶点坐标 reg_pts 转换为 NumPy 数组,并确保数据类型为 np.int32 。绘制多边形:使用 cv2.polylines 函数在图像上绘制多边形,指定顶点坐标、颜色和线条厚度。# 这段代码定义了 Annotator 类的一个方法 draw_centroid_and_tracks ,用于在图像上绘制轨迹点和轨迹线。这个方法使用 OpenCV 库的 cv2.polylines 和 cv2.circle 函数来绘制轨迹线和轨迹点的中心。# 1.track :轨迹点的坐标列表,格式为 [[x1, y1], [x2, y2], ..., [xn, yn]] 。# 2.color :轨迹线和中心点的颜色,格式为 (B, G, R) 。默认为紫色 (255, 0, 255) 。# 3.track_thickness :轨迹线的厚度。默认为 2 。def draw_centroid_and_tracks(self, track, color=(255, 0, 255), track_thickness=2):# 绘制质心点和轨迹。"""Draw centroid point and track trails.Args:track (list): object tracking points for trails displaycolor (tuple): tracks line colortrack_thickness (int): track line thickness value"""# np.hstack(tup)# np.hstack() 是 NumPy 库中的一个函数,用于水平(按列顺序)堆叠数组。# 参数 :# tup :一个元组或列表,包含要水平堆叠的数组。这些数组必须有相同的形状,除了第二维(列)。# 返回值 :# 返回一个数组,它是输入数组水平堆叠的结果。# 说明 :# np.hstack() 函数将多个数组水平(沿着第二维)堆叠起来。这意味着所有输入数组的第一维(行)必须相同,而第二维(列)可以不同。# 如果输入数组的维度大于2,那么除了第一维和第二维之外,其他维度的大小必须相同。# 该函数常用于将具有相同行数的多个数组合并为一个更宽的数组。# 转换轨迹点并绘制轨迹线。# np.hstack(track) :将轨迹点列表 track 水平堆叠成一个 NumPy 数组。# .astype(np.int32) :将数组的数据类型转换为 np.int32 ,因为 cv2.polylines 函数需要整数类型的坐标。# .reshape((-1, 1, 2)) :将数组重新塑形为 (-1, 1, 2) 的形状,其中 -1 表示自动计算行数, 1 表示每个点的维度为 1, 2 表示每个点有两个坐标(x, y)。points = np.hstack(track).astype(np.int32).reshape((-1, 1, 2))# 使用 cv2.polylines 函数在图像上绘制轨迹线。 self.im :图像数组。 [points] :轨迹点的坐标列表。 isClosed=False :表示轨迹线不闭合。 color :轨迹线的颜色。 thickness=track_thickness :轨迹线的厚度。cv2.polylines(self.im, [points], isClosed=False, color=color, thickness=track_thickness)# 绘制轨迹点的中心。# int(track[-1][0]), int(track[-1][1]) :获取轨迹点列表中最后一个点的坐标,并转换为整数。# track_thickness * 2 :计算中心点的半径,半径为轨迹线厚度的两倍。# cv2.circle(self.im, (int(track[-1][0]), int(track[-1][1])), track_thickness * 2, color, -1) :使用 cv2.circle 函数在图像上绘制中心点。 self.im 图像数组。 (int(track[-1][0]), int(track[-1][1])) 中心点的坐标。 track_thickness * 2 中心点的半径。 color 中心点的颜色。 -1 表示填充圆。cv2.circle(self.im, (int(track[-1][0]), int(track[-1][1])), track_thickness * 2, color, -1)# draw_centroid_and_tracks 方法的主要功能是在图像上绘制轨迹点和轨迹线。转换轨迹点:将轨迹点列表转换为 NumPy 数组,并确保数据类型为 np.int32 ,然后重新塑形为 (-1, 1, 2) 的形状。绘制轨迹线:使用 cv2.polylines 函数在图像上绘制轨迹线。绘制轨迹点的中心:使用 cv2.circle 函数在图像上绘制轨迹点的中心。# 这段代码定义了 Annotator 类的一个方法 count_labels ,用于在图像上绘制计数标签。这个方法使用 OpenCV 库的 cv2.rectangle 和 cv2.putText 函数来绘制背景框和文本。# 1.counts :要显示的计数值,默认为 0 。# 2.count_txt_size :文本的大小,默认为 2 。# 3.color :背景框的颜色,默认为白色 (255, 255, 255) 。# 4.txt_color :文本的颜色,默认为黑色 (0, 0, 0) 。def count_labels(self, counts=0, count_txt_size=2, color=(255, 255, 255), txt_color=(0, 0, 0)):# 绘制对象计数器的计数。"""Plot counts for object counter.Args:counts (int): objects counts valuecount_txt_size (int): text size for counts displaycolor (tuple): background color of counts displaytxt_color (tuple): text color of counts display"""# 设置文本字体大小和厚度。# 将传入的 count_txt_size 赋值给 self.tf ,表示文本的字体大小。self.tf = count_txt_size# 如果 self.tf 为 0 ,则使用图像尺寸计算一个默认的字体大小。 0.002 * (self.im.shape[0] + self.im.shape[1]) / 2 计算图像宽度和高度的平均值的 0.2%,然后四舍五入并加 1。tl = self.tf or round(0.002 * (self.im.shape[0] + self.im.shape[1]) / 2) + 1# 确保字体厚度至少为 1。tf = max(tl - 1, 1)# Get text size for in_count and out_count# 获取文本尺寸。# 使用 cv2.getTextSize 方法获取文本的宽度和高度。 fontScale=tl / 2 和 thickness=tf 分别表示字体缩放比例和字体厚度。t_size_in = cv2.getTextSize(str(counts), 0, fontScale=tl / 2, thickness=tf)[0]# Calculate positions for counts label# 计算文本位置。# 获取文本的宽度。text_width = t_size_in[0]# 计算文本的 x 坐标,使其居中显示。# 这行代码用于计算文本的 x 坐标,使其在图像中居中显示。# self.im.shape[1] :获取图像的宽度。在 OpenCV 中,图像数组的形状为 (height, width, channels) ,其中 shape[1] 表示宽度。# text_width :文本的宽度,通常通过 cv2.getTextSize 方法获取。# (self.im.shape[1] - text_width) :计算图像宽度减去文本宽度的差值。# // 2 :将差值除以 2,取整数部分,得到文本的居中 x 坐标。# 示例 :假设图像的宽度为 640 像素,文本的宽度为 100 像素。那么 :# self.im.shape[1] = 640# text_width = 100# text_x = (640 - 100) // 2 = 540 // 2 = 270# 因此,文本的起始 x 坐标为 270,这将使文本在图像的水平方向上居中显示。text_x = (self.im.shape[1] - text_width) // 2  # Center x-coordinate# 获取文本的高度。text_y = t_size_in[1]# Create a rounded rectangle for in_count# cv2.rectangle(img, pt1, pt2, color[, thickness[, lineType[, shift]]])# cv2.rectangle() 函数是 OpenCV 库中用于在图像上绘制矩形的函数。# 参数 :# img :目标图像,必须是 8 位或浮点型(单通道或3通道)。# pt1 和 pt2 :矩形的两个对角点的坐标。这两个参数是包含两个点坐标的元组或数组,每个点的坐标由 (x, y) 组成。 pt1 是矩形的一个角点, pt2 是对角点。# color :矩形边框的颜色,在 BGR 格式下。对于灰度图像,只需提供一个值。# thickness :可选参数,指定边框的粗细。正数表示边框的粗细, cv2.FILLED (或 -1 )表示填充矩形内部。# lineType :可选参数,指定线条的类型,可以是以下值之一 : cv2.LINE_4 - 4连通性线(8位宽度)。 cv2.LINE_8 - 8连通性线(8位宽度)。 cv2.LINE_AA - 抗锯齿线。# shift :可选参数,表示坐标值的小数点位数。如果坐标是浮点数,这个参数可以用来确定坐标的小数位数。# 返回值 :# 该函数没有返回值,它直接在输入图像 img 上进行绘制。# 说明 :# pt1 和 pt2 参数定义了矩形的两个对角点,OpenCV 会自动计算出矩形的其他两个角点。# 如果 thickness 参数为正数,则绘制的是一个只有边框的矩形;如果为 cv2.FILLED 或 -1 ,则矩形内部会被填充。# lineType 参数影响线条的绘制方式, cv2.LINE_AA 提供了抗锯齿的效果,使得线条更加平滑。# shift 参数用于处理浮点数坐标,如果坐标是浮点数,可以通过这个参数来确定坐标的小数位数,然后向下取整到最近的整数。# 这个函数常用于在图像上标记区域,例如在计算机视觉任务中标记检测到的对象的边界框。# 绘制背景框。使用 cv2.rectangle 方法绘制背景框。 -1 表示填充矩形。cv2.rectangle(self.im, (text_x - 5, text_y - 5), (text_x + text_width + 7, text_y + t_size_in[1] + 7), color, -1)# 绘制文本。使用 cv2.putText 方法在图像上绘制文本。 (text_x, text_y + t_size_in[1]) :文本的起始坐标。 tl / 2 :字体缩放比例。cv2.putText(self.im, str(counts), (text_x, text_y + t_size_in[1]), 0, tl / 2, txt_color, self.tf, lineType=cv2.LINE_AA)# count_labels 方法的主要功能是在图像上绘制计数标签。设置文本字体大小和厚度:根据传入的 count_txt_size 或图像尺寸计算字体大小和厚度。获取文本尺寸:使用 cv2.getTextSize 方法获取文本的宽度和高度。计算文本位置:计算文本的 x 坐标,使其居中显示。绘制背景框:使用 cv2.rectangle 方法绘制背景框。绘制文本:使用 cv2.putText 方法在图像上绘制文本。# 这段代码定义了 Annotator 类的一个静态方法 estimate_pose_angle ,用于计算三个点之间的角度。这个方法使用 NumPy 库来处理向量运算和三角函数计算。@staticmethod# 1.a 、 2.b 、 3.c :三个点的坐标,每个点是一个包含两个元素的列表或元组,表示 (x, y) 。def estimate_pose_angle(a, b, c):# 计算物体的姿势角度。"""Calculate the pose angle for object.Args:a (float) : The value of pose point ab (float): The value of pose point bc (float): The value o pose point cReturns:angle (degree): Degree value of angle between three points"""# 将输入的点坐标 a 、 b 、 c 转换为 NumPy 数组,以便进行向量运算。a, b, c = np.array(a), np.array(b), np.array(c)# 计算两个向量的弧度差。# np.arctan2(c[1] - b[1], c[0] - b[0]) :计算向量 bc 的弧度。# np.arctan2(a[1] - b[1], a[0] - b[0]) :计算向量 ba 的弧度。# radians = ... - ... :计算两个向量的弧度差。radians = np.arctan2(c[1] - b[1], c[0] - b[0]) - np.arctan2(a[1] - b[1], a[0] - b[0])# 计算角度。将弧度差转换为角度,并取绝对值。angle = np.abs(radians * 180.0 / np.pi)# 处理角度超过 180 度的情况。# 如果计算的角度大于 180 度,说明角度在 180 度到 360 度之间。if angle > 180.0:# 将角度转换为 0 到 180 度之间的值。angle = 360 - angle# 返回计算得到的角度。return angle# estimate_pose_angle 方法的主要功能是计算三个点之间的角度。转换点坐标:将输入的点坐标转换为 NumPy 数组。计算弧度差:计算两个向量的弧度差。计算角度:将弧度差转换为角度,并取绝对值。处理角度超过 180 度的情况:将角度转换为 0 到 180 度之间的值。返回角度:返回计算得到的角度。# 这段代码定义了 Annotator 类的一个方法 draw_specific_points ,用于在图像上绘制特定的关键点。这个方法使用 OpenCV 库的 cv2.circle 函数来绘制关键点。# 1.keypoints :关键点的坐标列表,每个关键点是一个包含两个或三个元素的列表或元组,表示 (x, y) 或 (x, y, confidence) 。# 2.indices :要绘制的关键点的索引列表,默认为 [2, 5, 7] 。# 3.shape :图像的形状,格式为 (height, width) ,默认为 (640, 640) 。# 4.radius :绘制关键点时的圆半径,默认为 2 。def draw_specific_points(self, keypoints, indices=[2, 5, 7], shape=(640, 640), radius=2):# 绘制特定关键点,用于gym步数计数。"""Draw specific keypoints for gym steps counting.Args:keypoints (list): list of keypoints data to be plottedindices (list): keypoints ids list to be plottedshape (tuple): imgsz for model inferenceradius (int): Keypoint radius value"""# 遍历关键点。使用 enumerate 遍历 keypoints , i 是关键点的索引, k 是关键点的坐标。for i, k in enumerate(keypoints):# 检查关键点索引是否在指定的索引列表中。# 如果当前关键点的索引 i 在 indices 列表中,则进行绘制。if i in indices:# 获取关键点的 x 和 y 坐标。x_coord, y_coord = k[0], k[1]# 检查关键点的 x 坐标是否不是图像宽度的整数倍,y 坐标是否不是图像高度的整数倍。这确保关键点不在图像的边界上。if x_coord % shape[1] != 0 and y_coord % shape[0] != 0:# 检查关键点的置信度。如果关键点有置信度信息(即 len(k) == 3 ),检查置信度是否大于或等于 0.5。如果置信度小于 0.5,则跳过该关键点。if len(k) == 3:conf = k[2]if conf < 0.5:continue# 绘制关键点。使用 cv2.circle 函数在图像上绘制关键点。# self.im :图像数组。 (int(x_coord), int(y_coord)) :关键点的坐标。 radius :圆的半径。 (0, 255, 0) :圆的颜色,绿色。 -1 :表示填充圆。 lineType=cv2.LINE_AA :使用抗锯齿线条。cv2.circle(self.im, (int(x_coord), int(y_coord)), radius, (0, 255, 0), -1, lineType=cv2.LINE_AA)# 返回绘制后的图像数组。return self.im# draw_specific_points 方法的主要功能是在图像上绘制特定的关键点。遍历关键点:使用 enumerate 遍历关键点列表。检查关键点索引:如果关键点的索引在指定的索引列表中,则进行绘制。获取关键点坐标:获取关键点的 x 和 y 坐标。检查关键点是否在图像范围内:确保关键点不在图像的边界上。检查关键点的置信度:如果关键点有置信度信息,检查置信度是否大于或等于 0.5。绘制关键点:使用 cv2.circle 函数在图像上绘制关键点。返回图像:返回绘制后的图像数组。# 这段代码定义了 Annotator 类的一个方法 plot_angle_and_count_and_stage ,用于在图像上绘制角度、计数和阶段信息。这个方法使用 OpenCV 库的 cv2.rectangle 和 cv2.putText 函数来绘制背景框和文本。# 1.angle_text :要显示的角度文本,格式为浮点数。# 2.count_text :要显示的计数文本,格式为字符串。# 3.stage_text :要显示的阶段文本,格式为字符串。# 4.center_kpt :文本的中心关键点坐标,格式为 (x, y) 。# 5.line_thickness :文本和背景框的线条厚度,默认为 2 。def plot_angle_and_count_and_stage(self, angle_text, count_text, stage_text, center_kpt, line_thickness=2):# 绘制姿势角度、计数值和步数阶段。"""Plot the pose angle, count value and step stage.Args:angle_text (str): angle value for workout monitoringcount_text (str): counts value for workout monitoringstage_text (str): stage decision for workout monitoringcenter_kpt (int): centroid pose index for workout monitoringline_thickness (int): thickness for text display"""# 格式化文本和设置字体大小。# 将 angle_text 、 count_text 和 stage_text 格式化为字符串。angle_text, count_text, stage_text = (f" {angle_text:.2f}", f"Steps : {count_text}", f" {stage_text}")# 根据 line_thickness 计算字体大小,确保字体大小与线条厚度成正比。font_scale = 0.6 + (line_thickness / 10.0)# 这段代码是 plot_angle_and_count_and_stage 方法的一部分,用于在图像上绘制角度信息。具体步骤包括计算文本的尺寸、确定文本和背景框的位置,以及绘制背景框和文本。# Draw angle# 计算角度文本的尺寸。使用 cv2.getTextSize 方法获取角度文本的宽度和高度。# angle_text :要绘制的角度文本。 0 :字体类型(默认为 cv2.FONT_HERSHEY_SIMPLEX )。 font_scale :字体缩放比例。 line_thickness :字体厚度。 (angle_text_width, angle_text_height) :获取文本的宽度和高度。(angle_text_width, angle_text_height), _ = cv2.getTextSize(angle_text, 0, font_scale, line_thickness)# 确定角度文本的位置。 center_kpt :文本的中心关键点坐标,格式为 (x, y) 。 angle_text_position :角度文本的起始位置,使用中心关键点的坐标。angle_text_position = (int(center_kpt[0]), int(center_kpt[1]))# 计算背景框的位置。背景框的起始位置,位于文本上方,留出 5 像素的间距。angle_background_position = (angle_text_position[0], angle_text_position[1] - angle_text_height - 5)# 计算背景框的大小。背景框的大小,宽度和高度分别增加 10 像素(每边 5 像素),并根据线条厚度增加额外的间距。angle_background_size = (angle_text_width + 2 * 5, angle_text_height + 2 * 5 + (line_thickness * 2))# 绘制背景框。使用 cv2.rectangle 方法绘制背景框。cv2.rectangle(# 图像数组。self.im,# 背景框的起始位置。angle_background_position,(# 背景框的右边界。angle_background_position[0] + angle_background_size[0],# 背景框的下边界。angle_background_position[1] + angle_background_size[1],),# 背景框的颜色(白色)。(255, 255, 255),# 表示填充矩形。-1,)# 绘制角度文本。使用 cv2.putText 方法在图像上绘制角度文本。# self.im :图像数组。 angle_text :要绘制的角度文本。 angle_text_position :文本的起始位置。 0 :字体类型(默认为 cv2.FONT_HERSHEY_SIMPLEX )。 font_scale :字体缩放比例。 (0, 0, 0) :文本颜色(黑色)。 line_thickness :字体厚度。 lineType=cv2.LINE_AA :使用抗锯齿线条。cv2.putText(self.im, angle_text, angle_text_position, 0, font_scale, (0, 0, 0), line_thickness)# 这段代码的主要功能是在图像上绘制角度信息。计算角度文本的尺寸:使用 cv2.getTextSize 获取文本的宽度和高度。确定角度文本的位置:使用中心关键点的坐标确定文本的起始位置。计算背景框的位置和大小:根据文本尺寸和间距计算背景框的起始位置和大小。绘制背景框:使用 cv2.rectangle 绘制白色背景框。绘制角度文本:使用 cv2.putText 绘制黑色角度文本。# 这段代码是 plot_angle_and_count_and_stage 方法的一部分,用于在图像上绘制计数信息。具体步骤包括计算文本的尺寸、确定文本和背景框的位置,以及绘制背景框和文本。# Draw Counts# 计算计数文本的尺寸。使用 cv2.getTextSize 方法获取计数文本的宽度和高度。# count_text :要绘制的计数文本。 0 :字体类型(默认为 cv2.FONT_HERSHEY_SIMPLEX )。 font_scale :字体缩放比例。 line_thickness :字体厚度。 (count_text_width, count_text_height) :获取文本的宽度和高度。(count_text_width, count_text_height), _ = cv2.getTextSize(count_text, 0, font_scale, line_thickness)# 确定计数文本的位置。 angle_text_position :角度文本的起始位置。 angle_text_height :角度文本的高度。 count_text_position :计数文本的起始位置,位于角度文本下方,留出 20 像素的间距。count_text_position = (angle_text_position[0], angle_text_position[1] + angle_text_height + 20)# 计算背景框的位置。 angle_background_position :角度背景框的起始位置。 angle_background_size[1] :角度背景框的高度。 count_background_position :计数背景框的起始位置,位于角度背景框下方,留出 5 像素的间距。count_background_position = (angle_background_position[0],angle_background_position[1] + angle_background_size[1] + 5,)# 计算背景框的大小。背景框的大小,宽度和高度分别增加 10 像素(每边 5 像素),并根据线条厚度增加额外的间距。count_background_size = (count_text_width + 10, count_text_height + 10 + (line_thickness * 2))# 绘制背景框。使用 cv2.rectangle 方法绘制背景框。cv2.rectangle(# 图像数组。self.im,# 背景框的起始位置。count_background_position,(# 背景框的右边界。count_background_position[0] + count_background_size[0],# 背景框的下边界。count_background_position[1] + count_background_size[1],),# 背景框的颜色(白色)。(255, 255, 255),# 表示填充矩形。-1,)# 绘制计数文本。使用 cv2.putText 方法在图像上绘制计数文本。# self.im :图像数组。 count_text :要绘制的计数文本。 count_text_position :文本的起始位置。 0 :字体类型(默认为 cv2.FONT_HERSHEY_SIMPLEX )。 font_scale :字体缩放比例。 (0, 0, 0) :文本颜色(黑色)。 line_thickness :字体厚度。 lineType=cv2.LINE_AA :使用抗锯齿线条。cv2.putText(self.im, count_text, count_text_position, 0, font_scale, (0, 0, 0), line_thickness)# 这段代码的主要功能是在图像上绘制计数信息。计算计数文本的尺寸:使用 cv2.getTextSize 获取文本的宽度和高度。确定计数文本的位置:根据角度文本的位置和高度确定计数文本的起始位置。计算背景框的位置和大小:根据计数文本的尺寸和间距计算背景框的起始位置和大小。绘制背景框:使用 cv2.rectangle 绘制白色背景框。绘制计数文本:使用 cv2.putText 绘制黑色计数文本。# 这段代码是 plot_angle_and_count_and_stage 方法的一部分,用于在图像上绘制阶段信息。具体步骤包括计算文本的尺寸、确定文本和背景框的位置,以及绘制背景框和文本。# Draw Stage# 计算阶段文本的尺寸。使用 cv2.getTextSize 方法获取阶段文本的宽度和高度。# stage_text :要绘制的阶段文本。 0 :字体类型(默认为 cv2.FONT_HERSHEY_SIMPLEX )。 font_scale :字体缩放比例。 line_thickness :字体厚度。 (stage_text_width, stage_text_height) :获取文本的宽度和高度。(stage_text_width, stage_text_height), _ = cv2.getTextSize(stage_text, 0, font_scale, line_thickness)# 确定阶段文本的位置。 center_kpt :文本的中心关键点坐标,格式为 (x, y) 。 angle_text_height :角度文本的高度。 count_text_height :计数文本的高度。 stage_text_position :阶段文本的起始位置,位于角度文本和计数文本下方,留出 40 像素的间距。stage_text_position = (int(center_kpt[0]), int(center_kpt[1]) + angle_text_height + count_text_height + 40)# 计算背景框的位置。背景框的起始位置,位于阶段文本上方,留出 5 像素的间距。stage_background_position = (stage_text_position[0], stage_text_position[1] - stage_text_height - 5)# 计算背景框的大小。背景框的大小,宽度和高度分别增加 10 像素(每边 5 像素)。stage_background_size = (stage_text_width + 10, stage_text_height + 10)# 绘制背景框。使用 cv2.rectangle 方法绘制背景框。cv2.rectangle(# 图像数组。self.im,# 背景框的起始位置。stage_background_position,(# 背景框的右边界。stage_background_position[0] + stage_background_size[0],# 背景框的下边界。stage_background_position[1] + stage_background_size[1],),# 背景框的颜色(白色)。(255, 255, 255),# 表示填充矩形。-1,)# 绘制阶段文本。使用 cv2.putText 方法在图像上绘制阶段文本。 self.im :图像数组。 stage_text :要绘制的阶段文本。 stage_text_position :文本的起始位置。 0 :字体类型(默认为 cv2.FONT_HERSHEY_SIMPLEX )。 font_scale :字体缩放比例。 (0, 0, 0) :文本颜色(黑色)。 line_thickness :字体厚度。 lineType=cv2.LINE_AA :使用抗锯齿线条。cv2.putText(self.im, stage_text, stage_text_position, 0, font_scale, (0, 0, 0), line_thickness)# 这段代码的主要功能是在图像上绘制阶段信息。计算阶段文本的尺寸:使用 cv2.getTextSize 获取文本的宽度和高度。确定阶段文本的位置:根据角度文本和计数文本的位置和高度确定阶段文本的起始位置。计算背景框的位置和大小:根据阶段文本的尺寸和间距计算背景框的起始位置和大小。绘制背景框:使用 cv2.rectangle 绘制白色背景框。绘制阶段文本:使用 cv2.putText 绘制黑色阶段文本。# plot_angle_and_count_and_stage 方法的主要功能是在图像上绘制角度、计数和阶段信息。格式化文本和设置字体大小:格式化角度、计数和阶段文本,并设置字体大小。绘制角度信息:计算角度文本的位置和背景框的大小,绘制背景框和文本。绘制计数信息:计算计数文本的位置和背景框的大小,绘制背景框和文本。绘制阶段信息:计算阶段文本的位置和背景框的大小,绘制背景框和文本。# 这段代码定义了 Annotator 类的一个方法 seg_bbox ,用于在图像上绘制分割掩码的边界框,并在边界框附近添加标签。这个方法使用 OpenCV 库的 cv2.polylines 、 cv2.rectangle 和 cv2.putText 函数来绘制多边形、背景框和文本。# 1.mask :分割掩码的顶点坐标列表,格式为 [[x1, y1], [x2, y2], ..., [xn, yn]] 。# 2.mask_color :多边形的颜色,默认为紫色 (255, 0, 255) 。# 3.det_label :检测标签,格式为字符串。默认为 None 。# 4.track_label :跟踪标签,格式为字符串。默认为 None 。def seg_bbox(self, mask, mask_color=(255, 0, 255), det_label=None, track_label=None):# 用于以边界框形状绘制分割对象的函数。"""Function for drawing segmented object in bounding box shape.Args:mask (list): masks data list for instance segmentation area plottingmask_color (tuple): mask foreground colordet_label (str): Detection label texttrack_label (str): Tracking label text"""# 绘制多边形边界框。使用 cv2.polylines 方法绘制多边形边界框。 self.im :图像数组。 [np.int32([mask])] :将顶点坐标列表转换为 NumPy 数组,并确保数据类型为 np.int32 。 isClosed=True :表示多边形是闭合的。 color=mask_color :多边形的颜色。 thickness=2 :多边形线条的厚度。cv2.polylines(self.im, [np.int32([mask])], isClosed=True, color=mask_color, thickness=2)# 选择标签。根据 track_label 和 det_label 选择要显示的标签。如果 track_label 不为 None ,则使用 track_label ;否则,使用 det_label 。label = f"Track ID: {track_label}" if track_label else det_label# 计算标签文本的尺寸。使用 cv2.getTextSize 方法获取标签文本的宽度和高度。# label :要绘制的标签文本。 0 :字体类型(默认为 cv2.FONT_HERSHEY_SIMPLEX )。 0.7 :字体缩放比例。 1 :字体厚度。 text_size :获取文本的宽度和高度。text_size, _ = cv2.getTextSize(label, 0, 0.7, 1)# 计算背景框的位置。使用 cv2.rectangle 方法绘制背景框。cv2.rectangle(# 图像数组。self.im,# 背景框的起始位置,位于多边形的第一个顶点上方,留出 10 像素的间距。(int(mask[0][0]) - text_size[0] // 2 - 10, int(mask[0][1]) - text_size[1] - 10),# 背景框的下边界,位于多边形的第一个顶点下方,留出 5 像素的间距。(int(mask[0][0]) + text_size[0] // 2 + 5, int(mask[0][1] + 5)),# 背景框的颜色。mask_color,# 表示填充矩形。-1,)# 绘制标签文本。使用 cv2.putText 方法在图像上绘制标签文本。cv2.putText(# (int(mask[0][0]) - text_size[0] // 2, int(mask[0][1]) - 5) :文本的起始位置,位于多边形的第一个顶点上方,留出 5 像素的间距。self.im, label, (int(mask[0][0]) - text_size[0] // 2, int(mask[0][1]) - 5), 0, 0.7, (255, 255, 255), 2)# seg_bbox 方法的主要功能是在图像上绘制分割掩码的边界框,并在边界框附近添加标签。绘制多边形边界框:使用 cv2.polylines 绘制多边形。选择标签:根据 track_label 和 det_label 选择要显示的标签。计算标签文本的尺寸:使用 cv2.getTextSize 获取文本的宽度和高度。计算背景框的位置:根据多边形的第一个顶点和文本尺寸计算背景框的位置。绘制背景框:使用 cv2.rectangle 绘制背景框。绘制标签文本:使用 cv2.putText 绘制标签文本。# 这段代码定义了 Annotator 类的一个方法 plot_distance_and_line ,用于在图像上绘制两个质心之间的距离和连接线。这个方法使用 OpenCV 库的 cv2.getTextSize 、 cv2.rectangle 、 cv2.putText 、 cv2.line 和 cv2.circle 函数来绘制文本、背景框、连接线和质心。# 1.distance_m :两个质心之间的距离(米)。# 2.distance_mm :两个质心之间的距离(毫米)。# 3.centroids :两个质心的坐标列表,格式为 [(x1, y1), (x2, y2)] 。# 4.line_color :连接线的颜色。# 5.centroid_color :质心的颜色。def plot_distance_and_line(self, distance_m, distance_mm, centroids, line_color, centroid_color):# 在框架上绘制距离和线。"""Plot the distance and line on frame.Args:distance_m (float): Distance between two bbox centroids in meters.distance_mm (float): Distance between two bbox centroids in millimeters.centroids (list): Bounding box centroids data.line_color (RGB): Distance line color.centroid_color (RGB): Bounding box centroid color."""# 这段代码是 plot_distance_and_line 方法的一部分,用于在图像上绘制两个质心之间的距离(以米为单位)。具体步骤包括计算文本的尺寸、确定文本和背景框的位置,以及绘制背景框和文本。# 绘制距离(米)。# (cv2.Size(width, height), baseline) = cv2.getTextSize(text, fontFace, fontScale, thickness)# cv2.getTextSize 函数是 OpenCV 库中的一个函数,它用于计算给定文本的尺寸(宽度和高度)。这个函数在绘制文本之前非常有用,因为它可以帮助你确定文本的尺寸,从而可以进行适当的定位和布局。# 参数 :# text :要测量的文本字符串。# fontFace :字体类型,可以是 OpenCV 预定义的字体,如 cv2.FONT_HERSHEY_SIMPLEX 、 cv2.FONT_HERSHEY_PLAIN 等。# fontScale :字体缩放因子,用于调整字体大小。# thickness :字体线条的厚度。# 返回值 :# cv2.Size(width, height) :一个 cv2.Size 对象,包含文本的宽度和高度。# baseline :文本基线(即文本底部到基线的距离),这个值可以用来确定文本的位置,以确保基线对齐。# 计算文本的尺寸。使用 cv2.getTextSize 方法获取文本的宽度和高度。 (text_width_m, text_height_m) :获取文本的宽度和高度。(text_width_m, text_height_m), _ = cv2.getTextSize(# f"Distance M: {distance_m:.2f}m" :格式化的文本,显示距离(以米为单位),保留两位小数。# cv2.FONT_HERSHEY_SIMPLEX :字体类型。# 0.8 :字体缩放比例。# 2 :字体厚度。f"Distance M: {distance_m:.2f}m", cv2.FONT_HERSHEY_SIMPLEX, 0.8, 2)# 绘制背景框。使用 cv2.rectangle 方法绘制背景框。 (15, 25) :背景框的起始位置。 (15 + text_width_m + 10, 25 + text_height_m + 20) :背景框的右下角位置,留出 10 像素的间距。cv2.rectangle(self.im, (15, 25), (15 + text_width_m + 10, 25 + text_height_m + 20), (255, 255, 255), -1)# 绘制文本。使用 cv2.putText 方法在图像上绘制文本。cv2.putText(# 图像数组。self.im,# 格式化的文本,显示距离(以米为单位),保留两位小数。f"Distance M: {distance_m:.2f}m",# 文本的起始位置。(20, 50),# 字体类型。cv2.FONT_HERSHEY_SIMPLEX,# 字体缩放比例。0.8,# 文本颜色(黑色)。(0, 0, 0),# 字体厚度。2,# 使用抗锯齿线条。cv2.LINE_AA,)# 这段代码的主要功能是在图像上绘制两个质心之间的距离(以米为单位)。计算文本的尺寸:使用 cv2.getTextSize 获取文本的宽度和高度。绘制背景框:根据文本尺寸和间距计算背景框的位置和大小,使用 cv2.rectangle 绘制白色背景框。绘制文本:使用 cv2.putText 绘制黑色文本。# 这段代码是 plot_distance_and_line 方法的一部分,用于在图像上绘制两个质心之间的距离(以毫米为单位)。具体步骤包括计算文本的尺寸、确定文本和背景框的位置,以及绘制背景框和文本。# 绘制距离(毫米)。# 计算文本的尺寸。使用 cv2.getTextSize 方法获取文本的宽度和高度。 (text_width_mm, text_height_mm) :获取文本的宽度和高度。(text_width_mm, text_height_mm), _ = cv2.getTextSize(# f"Distance MM: {distance_mm:.2f}mm" :格式化的文本,显示距离(以毫米为单位),保留两位小数。# cv2.FONT_HERSHEY_SIMPLEX :字体类型。# 0.8 :字体缩放比例。# 2 :字体厚度。f"Distance MM: {distance_mm:.2f}mm", cv2.FONT_HERSHEY_SIMPLEX, 0.8, 2)# 绘制背景框。使用 cv2.rectangle 方法绘制背景框。 (15, 75) :背景框的起始位置。 (15 + text_width_mm + 10, 75 + text_height_mm + 20) :背景框的右下角位置,留出 10 像素的间距。 -1 :表示填充矩形。cv2.rectangle(self.im, (15, 75), (15 + text_width_mm + 10, 75 + text_height_mm + 20), (255, 255, 255), -1)# 绘制文本。使用 cv2.putText 方法在图像上绘制文本。cv2.putText(# 图像数组。self.im,# 格式化的文本,显示距离(以毫米为单位),保留两位小数。f"Distance MM: {distance_mm:.2f}mm",# 文本的起始位置。(20, 100),# 字体类型。cv2.FONT_HERSHEY_SIMPLEX,# 字体缩放比例。0.8,# 文本颜色(黑色)。(0, 0, 0),# 字体厚度。2,# 使用抗锯齿线条。cv2.LINE_AA,)# 这段代码的主要功能是在图像上绘制两个质心之间的距离(以毫米为单位)。计算文本的尺寸:使用 cv2.getTextSize 获取文本的宽度和高度。绘制背景框:根据文本尺寸和间距计算背景框的位置和大小,使用 cv2.rectangle 绘制白色背景框。绘制文本:使用 cv2.putText 绘制黑色文本。# 绘制连接线。使用 cv2.line 方法绘制两个质心之间的连接线。cv2.line(self.im, centroids[0], centroids[1], line_color, 3)# 绘制第一个质心点。使用 cv2.circle 方法绘制第一个质心点。cv2.circle(self.im, centroids[0], 6, centroid_color, -1)# 绘制第二个质心点。使用 cv2.circle 方法绘制第二个质心点。cv2.circle(self.im, centroids[1], 6, centroid_color, -1)# plot_distance_and_line 方法的主要功能是在图像上绘制两个质心之间的距离和连接线。绘制距离(米):计算文本尺寸,绘制背景框和文本。绘制距离(毫米):计算文本尺寸,绘制背景框和文本。绘制连接线:使用 cv2.line 绘制两个质心之间的连接线。绘制质心:使用 cv2.circle 绘制两个质心。# 这段代码定义了 Annotator 类的一个方法 visioneye ,用于在图像上绘制一个视觉焦点(vision eye)的效果。具体来说,这个方法会在图像上绘制一个中心点、一个边界框的中心点,以及连接这两个点的线。# 1.box :边界框的坐标,格式为 [x1, y1, x2, y2] ,其中 (x1, y1) 是左上角坐标, (x2, y2) 是右下角坐标。# 2.center_point :视觉焦点的中心点坐标,格式为 (x, y) 。# 3.color :连接线和边界框中心点的颜色,默认为 (235, 219, 11) 。# 4.pin_color :视觉焦点中心点的颜色,默认为 (255, 0, 255) 。# 5.thickness :连接线的厚度,默认为 2 。# 6.pins_radius :中心点的半径,默认为 10 。def visioneye(self, box, center_point, color=(235, 219, 11), pin_color=(255, 0, 255), thickness=2, pins_radius=10):# 用于精确定位人眼视觉映射和绘图的函数。"""Function for pinpoint human-vision eye mapping and plotting.Args:box (list): Bounding box coordinatescenter_point (tuple): center point for vision eye viewcolor (tuple): object centroid and line color valuepin_color (tuple): visioneye point color valuethickness (int): int value for line thicknesspins_radius (int): visioneye point radius value"""# 计算边界框的中心点。 int((box[0] + box[2]) / 2) :计算 x 坐标的中点。 int((box[1] + box[3]) / 2) :计算 y 坐标的中点。center_bbox = int((box[0] + box[2]) / 2), int((box[1] + box[3]) / 2)# 绘制视觉焦点中心点。使用 cv2.circle 方法绘制视觉焦点中心点。cv2.circle(self.im, center_point, pins_radius, pin_color, -1)# 绘制边界框中心点。使用 cv2.circle 方法绘制边界框中心点。cv2.circle(self.im, center_bbox, pins_radius, color, -1)# 绘制连接线。使用 cv2.line 方法绘制连接线。cv2.line(self.im, center_point, center_bbox, color, thickness)# visioneye 方法的主要功能是在图像上绘制一个视觉焦点的效果,包括一个中心点、一个边界框的中心点,以及连接这两个点的线。计算边界框的中心点:根据边界框的坐标计算中心点。绘制视觉焦点中心点:使用 cv2.circle 绘制视觉焦点中心点。绘制边界框中心点:使用 cv2.circle 绘制边界框中心点。绘制连接线:使用 cv2.line 绘制连接线。

4.def plot_labels(boxes, cls, names=(), save_dir=Path(""), on_plot=None): 

# 这段代码定义了一个名为 plot_labels 的函数,用于绘制训练标签,包括类别直方图和框统计信息。
# @TryExcept() 用于捕获并处理函数执行过程中可能出现的异常,确保程序的健壮性
# class TryExcept(contextlib.ContextDecorator):
# -> 这个类用于捕获在 with 语句块中发生的异常,并根据提供的参数决定是否打印错误信息。
# -> def __init__(self, msg="", verbose=True):
@TryExcept()  # known issue https://github.com/ultralytics/yolov5/issues/5395
# @plt_settings() 用于设置绘图相关的参数或环境,如字体、颜色等,以便在函数中统一使用。
# def plt_settings(rcparams=None, backend="Agg"):
# -> 用于设置Matplotlib的全局配置参数(rcparams)和后端(backend),并在函数执行后恢复原始设置。这个装饰器在绘图函数中非常有用,特别是当需要临时修改绘图配置而不影响全局设置时。返回装饰器工厂。返回装饰器 decorator 。
# -> return decorator
@plt_settings()
# 定义了函数 plot_labels ,它接受以下参数 :
# 1.boxes :一个数组,包含边界框的坐标信息,格式为[x, y, width, height]。
# 2.cls :一个数组,包含每个边界框对应的类别标签。
# 3.names :一个字典,键为类别索引,值为类别名称,默认为空元组。
# 4.save_dir :一个 Path 对象,指定保存绘制结果的目录,默认为当前目录。
# 5.on_plot :一个可调用对象,当绘图完成后会调用它,并传入保存的文件名,默认为 None 。
def plot_labels(boxes, cls, names=(), save_dir=Path(""), on_plot=None):# 绘制包括类别直方图和框统计在内的训练标签。"""Plot training labels including class histograms and box statistics."""# 导入了 pandas 和 seaborn 库, pandas 用于数据处理, seaborn 用于绘制统计图表。import pandas as pdimport seaborn as sn# 这两行代码的作用是过滤掉特定类型的警告信息,以避免在程序运行过程中输出不必要的警告,使输出更加简洁清晰。# warnings.filterwarnings(action, category=None, message='', module='', lineno=0)# warnings.filterwarnings() 是 Python 标准库 warnings 中的一个函数,用于控制警告消息的显示。这个函数允许开发者根据警告的类别、消息内容或其他属性来过滤警告,决定哪些警告应该显示,哪些应该被忽略。# 参数说明 :# action :一个字符串,指定对警告执行的操作。常用的值包括 'ignore' (忽略警告)、 'default' (显示警告,除非被过滤)、 'always' (总是显示警告,即使已经被过滤)、 'module' (仅当警告来自模块顶层时显示)、 'once' (只显示一次警告)。# category :一个警告类别,指定要过滤的警告类型。如果为 None ,则匹配所有类别。# message :一个字符串或正则表达式,用于匹配警告消息的内容。如果为 None 或空字符串,则不基于消息内容过滤。# module :一个字符串或正则表达式,用于匹配引发警告的模块名称。如果为 None ,则不基于模块名称过滤。# lineno :一个整数,指定引发警告的代码行号。如果为 0 ,则不基于行号过滤。# 返回值 :warnings.filterwarnings() 没有返回值,它直接修改 Python 的警告过滤列表。# warnings.filterwarnings() 函数是一个强大的工具,用于控制 Python 程序运行时的警告信息。通过这个函数,开发者可以根据需要过滤掉不希望看到的警告,从而使得程序的输出更加清晰。# Filter matplotlib>=3.7.2 warning and Seaborn use_inf and is_categorical FutureWarnings    过滤 matplotlib>=3.7.2 警告和 Seaborn use_inf 和 is_categorical FutureWarnings 。# 忽略特定的 UserWarning 警告。具体来说,它会忽略那些警告信息中包含字符串 "The figure layout has changed to tight" 的警告。这类警告通常是由 matplotlib 在调整图形布局时触发的,例如当使用 tight_layout=True 参数自动调整子图布局时,可能会出现此类警告。通过这行代码,可以避免这些警告信息在控制台或日志文件中输出,从而减少干扰。warnings.filterwarnings("ignore", category=UserWarning, message="The figure layout has changed to tight")# 忽略所有的 FutureWarning 警告。 FutureWarning 是一种特殊的警告类型,通常由库作者发出,提示用户当前使用的某些功能或参数在未来版本中可能会发生变化或被弃用。虽然这些警告对于开发者来说是有用的,但在某些情况下,用户可能更希望专注于程序的主要输出,而不是这些可能不会立即影响程序运行的警告。因此,通过这行代码可以忽略所有 FutureWarning ,使程序的输出更加简洁。warnings.filterwarnings("ignore", category=FutureWarning)# 这两行代码通过过滤特定的警告类型,帮助用户减少程序运行过程中不必要的干扰,使输出更加专注于程序的主要功能和结果。这对于提高代码的可读性和用户体验非常有帮助,尤其是在进行数据可视化等操作时,可以避免因布局调整或库版本更新带来的警告信息干扰主要的图形输出。# 这段代码的主要目的是绘制数据集标签的相关图表,并将结果保存到指定的文件中。# Plot dataset labels    接下来的代码块用于绘制数据集标签的相关图表。# 通过日志记录器 LOGGER 输出一条信息,告知用户正在将标签绘制到指定路径的 labels.jpg 文件中。这里使用了f-string格式化字符串,动态插入 save_dir 路径。LOGGER.info(f"Plotting labels to {save_dir / 'labels.jpg'}... ")    # 将标签绘制到 {save_dir / 'labels.jpg'}...# 计算数据集中 类别的数量 。 cls.max() 获取类别标签数组 cls 中的最大值,加1后转换为整数,得到总的类别数量 nc 。nc = int(cls.max() + 1)  # number of classes# 将 边界框数组 boxes 限制为最多100万个边界框。这是为了防止数据量过大导致性能问题或内存不足。boxes = boxes[:1000000]  # limit to 1M boxes# pd.DataFrame(data, index=None, columns=None, dtype=None, copy=False)# pd.DataFrame 是 Pandas 库中的一个构造函数,用于创建 DataFrame 对象。DataFrame 是 Pandas 中最核心的数据结构之一,它提供了一个二维标签化数据结构,可以存储不同类型的数据,如数值、字符串、布尔值等。# 参数说明 :# data :这是创建 DataFrame 时必须提供的数据。 data 可以是以下几种类型之一 :# 一个字典,其中键是列名,值是数据列表或数组。# 一个二维 NumPy 数组或类似数组的结构。# 另一个 DataFrame 或相似的表格型数据结构。# 一个列表或列表的列表。# index :(可选)索引标签,可以是索引对象或数组。如果未指定,Pandas 将自动创建一个从 0 开始的整数索引。# columns :(可选)列名,可以是列名列表。如果未指定且 data 是字典,则使用字典的键作为列名。# dtype :(可选)数据类型,用于强制 DataFrame 中的数据类型。如果未指定,Pandas 将自动推断数据类型。# copy :(可选)布尔值,指示是否在创建 DataFrame 时复制数据。默认为 False,即不复制数据。# 返回值 :# 返回一个 DataFrame 对象。# 将边界框数组 boxes 转换为 pandas 的 DataFrame 对象,列名分别为 "x" , "y" , "width" , "height" 。这样可以方便地进行数据处理和绘图操作。x = pd.DataFrame(boxes, columns=["x", "y", "width", "height"])# Seaborn correlogram    接下来的代码块用于绘制Seaborn的散点图矩阵(correlogram)。# 使用 seaborn 的 pairplot 函数绘制边界框坐标的散点图矩阵。# corner=True :只绘制下三角部分,节省空间。# diag_kind="auto" :自动选择对角线上的图表类型,通常为直方图或核密度估计图。# kind="hist" :非对角线上的图表类型为直方图。# diag_kws=dict(bins=50) :设置对角线上直方图的柱数为50。# plot_kws=dict(pmax=0.9) :设置非对角线上散点图的点的最大比例为0.9,避免过度绘制。sn.pairplot(x, corner=True, diag_kind="auto", kind="hist", diag_kws=dict(bins=50), plot_kws=dict(pmax=0.9))# 将绘制的散点图矩阵保存到指定路径的 labels_correlogram.jpg 文件中,分辨率为200dpi。plt.savefig(save_dir / "labels_correlogram.jpg", dpi=200)# 关闭当前的绘图窗口,释放资源,避免在后续绘图操作中出现资源冲突或内存泄漏。plt.close()# 这段代码通过 seaborn 的 pairplot 函数绘制了边界框坐标的散点图矩阵,并将结果保存到指定的文件中。这个散点图矩阵可以帮助用户直观地了解边界框坐标之间的分布关系和相关性,对于数据探索和分析非常有帮助。通过限制数据量和设置合理的绘图参数,确保了绘图过程的高效性和输出结果的可读性。# 这段代码使用 matplotlib 和 seaborn 库绘制了多个图表,用于展示数据集标签的统计信息。# Matplotlib labels    说明接下来的代码块用于绘制与数据集标签相关的图表。# 使用 matplotlib 的 subplots 函数创建一个2x2的子图布局。# figsize=(8, 8) :设置整个图形的大小为8x8英寸。# tight_layout=True :自动调整子图参数,使其填充整个图形区域,避免标签重叠。# [1].ravel() :获取子图的轴对象数组,方便后续操作。ax = plt.subplots(2, 2, figsize=(8, 8), tight_layout=True)[1].ravel()# matplotlib.pyplot.hist(x, bins=None, range=None, density=False, weights=None, cumulative=False, bottom=None, histtype='bar', align='mid', orientation='vertical', rwidth=None, log=False, color=None, label=None, stacked=False, **kwargs)# hist 函数是 Matplotlib 库中用于绘制直方图的函数。# 参数说明 :# x :表示要绘制直方图的数据,可以是一个一维数组或列表。# bins :可选参数,表示直方图的箱数。默认为10。# range :可选参数,表示直方图的值域范围,可以是一个二元组或列表。默认为None,即使用数据中的最小值和最大值。# density :可选参数,表示是否将直方图归一化。默认为False,即直方图的高度为每个箱子内的样本数,而不是频率或概率密度。# weights :可选参数,表示每个数据点的权重。默认为None。# cumulative :可选参数,表示是否绘制累积分布图。默认为False。# bottom :可选参数,表示直方图的起始高度。默认为None。# histtype :可选参数,表示直方图的类型,可以是'bar'、'barstacked'、'step'、'stepfilled'等。默认为'bar'。# align :可选参数,表示直方图箱子的对齐方式,可以是'left'、'mid'、'right'。默认为'mid'。# orientation :可选参数,表示直方图的方向,可以是'vertical'、'horizontal'。默认为'vertical'。# rwidth :可选参数,表示每个箱子的相对宽度。默认为None。# log :可选参数,表示是否在y轴上使用对数刻度。默认为False。# color :可选参数,表示直方图的颜色。# label :可选参数,表示直方图的标签。# stacked :可选参数,表示是否堆叠不同的直方图。默认为False。# **kwargs :可选参数,表示其他绘图参数。# 返回值 :# n :一个数组,包含每个箱子中的计数。# bins :一个数组,包含箱子的边界。# patches :一个由矩形(矩形)组成的容器,代表直方图的各个部分。# 在第一个子图上绘制类别标签的直方图。# cls :类别标签数组。# bins=np.linspace(0, nc, nc + 1) - 0.5 :设置直方图的柱间距,使其正好对应每个类别。# rwidth=0.8 :设置柱的宽度为柱间距的80%。# y :返回值包含直方图的各个部分,其中 y[2].patches 是直方图的柱对象。y = ax[0].hist(cls, bins=np.linspace(0, nc, nc + 1) - 0.5, rwidth=0.8)# 遍历每个类别,将直方图中对应类别的柱的颜色设置为通过 colors 函数获取的颜色值。 colors(i) 返回一个颜色的RGB值,将其转换为 matplotlib 可接受的颜色格式(0-1范围)。for i in range(nc):y[2].patches[i].set_color([x / 255 for x in colors(i)])# 设置第一个子图的y轴标签为“instances”,表示 每个类别的实例数量 。ax[0].set_ylabel("instances")# 如果类别名称的数量在1到30之间。if 0 < len(names) < 30:# Axes.set_xticks(self, ticks, labels=None, **kwargs)# set_xticks() 是 matplotlib 库中 Axes 对象的一个方法,用于设置图表的 x 轴刻度位置。这个方法允许你自定义 x 轴上的刻度,例如,你可以指定刻度的精确位置,或者更改刻度的显示方式。# 参数 :# ticks :必须。这是一个数字列表,指定了刻度线应该放置的位置。 也可以是一个空列表,这将清除所有的刻度。# labels (可选) :这是一个与 ticks 参数同样长度的列表,指定了每个刻度位置的标签。 如果不提供,将使用默认的刻度标签。# **kwargs (可选) : 这是一些额外的关键字参数,可以用来设置刻度的属性,例如颜色、大小等。# 返回值 :无。# 描述 :# set_xticks() 方法允许你控制 x 轴上的刻度位置和标签。这对于创建更精确的图表非常有用,特别是当你想要突出显示特定数据点或者更改默认的刻度间隔时。# 将x轴的刻度设置为类别索引。ax[0].set_xticks(range(len(names)))# Axes.set_xticklabels(self, labels, fontdict=None, fontproperties=None, **kwargs)# set_xticklabels() 是 matplotlib 库中 Axes 对象的一个方法,用于设置 x 轴刻度的标签。这个方法允许你自定义 x 轴上的刻度标签,例如,你可以指定标签的文本、位置、颜色等。# 参数 :# labels :必须。这是一个字符串列表或数组,指定了每个刻度位置的标签文本。# fontdict (可选) :这是一个字典,用于指定标签的字体属性,如大小、重量、风格等。例如, fontdict={'fontsize': 12, 'fontweight': 'bold'} 。# fontproperties (可选) : 这是一个 matplotlib.font_manager.FontProperties 对象,也用于指定标签的字体属性。这可以作为 fontdict 的替代方法。# kwargs (可选) :这是一些额外的关键字参数,可以用来设置刻度标签的属性,如颜色、对齐方式等。# 返回值 :无。# 描述 :# set_xticklabels() 方法允许你控制 x 轴上的刻度标签。这对于创建更具可读性和吸引力的图表非常有用,特别是当你想要改变标签的文本、样式或者格式化标签以适应特定的数据展示需求时。# 将刻度标签设置为类别名称,旋转90度,字体大小为10。否则,仅设置x轴标签为“classes”。ax[0].set_xticklabels(list(names.values()), rotation=90, fontsize=10)# 否则,仅设置x轴标签为“classes”。else:ax[0].set_xlabel("classes")# 在第三个子图上使用 seaborn 的 histplot 函数绘制边界框的x和y坐标的二维直方图。# x : DataFrame 对象,包含边界框的坐标信息。# x="x" 和 y="y" :指定x和y坐标列。# ax=ax[2] :指定绘制在第三个子图上。# bins=50 :设置直方图的柱数。# pmax=0.9 :设置点的最大比例为0.9,避免过度绘制。sn.histplot(x, x="x", y="y", ax=ax[2], bins=50, pmax=0.9)# 在第四个子图上绘制边界框的宽度和高度的二维直方图,参数设置与上一行相同。sn.histplot(x, x="width", y="height", ax=ax[3], bins=50, pmax=0.9)# 这段代码通过 matplotlib 和 seaborn 库绘制了多个图表,用于展示数据集标签的统计信息。第一个子图展示了类别标签的直方图,帮助用户了解每个类别的实例数量。第三个子图展示了边界框的x和y坐标的二维直方图,帮助用户了解边界框在图像中的位置分布。第四个子图展示了边界框的宽度和高度的二维直方图,帮助用户了解边界框的尺寸分布。这些图表为数据探索和分析提供了直观的可视化工具,有助于用户更好地理解数据集的特性。# 这段代码的主要目的是在图像上绘制边界框,并将绘制结果保存到指定的文件中。# Rectangles    说明接下来的代码块用于绘制边界框。# 将边界框数组 boxes 中每个边界框的中心坐标设置为0.5。这里假设边界框的坐标格式为[x, y, width, height],其中(x, y)是中心点坐标。boxes[:, 0:2] = 0.5  # center# 将边界框的格式从中心点坐标和宽高(xywh)转换为两个对角点坐标(xyxy),并将其缩放到1000x1000的范围内。 ops.xywh2xyxy 用于进行坐标格式的转换。boxes = ops.xywh2xyxy(boxes) * 1000# 创建一个1000x1000的白色图像,用于绘制边界框。 np.ones((1000, 1000, 3), dtype=np.uint8) * 255 生成一个全白的三维数组,然后使用 Image.fromarray 将其转换为PIL图像。img = Image.fromarray(np.ones((1000, 1000, 3), dtype=np.uint8) * 255)# 遍历前500个 类别标签 和 对应的边界框 ,在图像上绘制边界框。for cls, box in zip(cls[:500], boxes[:500]):#  ImageDraw.Draw(img) :创建一个可以在图像上绘图的对象。# rectangle(box, width=1, outline=colors(cls)) :绘制边界框, box 是边界框的坐标, width=1 设置线条宽度为1, outline=colors(cls) 设置边界框的颜色,颜色通过 colors 函数获取。ImageDraw.Draw(img).rectangle(box, width=1, outline=colors(cls))  # plot# 在第二个子图上显示绘制好的图像,并关闭坐标轴。ax[1].imshow(img)ax[1].axis("off")# 遍历所有子图,将每个子图的四个边框设置为不可见,使图形更加简洁。for a in [0, 1, 2, 3]:for s in ["top", "right", "left", "bottom"]:ax[a].spines[s].set_visible(False)# 将绘制好的图形保存到指定路径的 labels.jpg 文件中,分辨率为200dpi,然后关闭当前的绘图窗口,释放资源。fname = save_dir / "labels.jpg"plt.savefig(fname, dpi=200)plt.close()# 如果 on_plot 参数不为 None ,则调用 on_plot 函数,并传入保存的文件名。这可以用于在绘图完成后执行一些额外的操作,例如显示图像或进行进一步的处理。if on_plot:on_plot(fname)# 这段代码通过在图像上绘制边界框,直观地展示了数据集中的边界框分布情况。通过将边界框绘制在白色背景的图像上,并将结果保存到文件中,用户可以方便地查看和分析边界框的位置和尺寸。此外,代码还通过设置子图的边框不可见,使最终的图形更加简洁和专业。
# 这段代码通过多个步骤,使用 matplotlib 和 seaborn 库绘制了训练标签的多种统计图表,包括类别直方图、边界框坐标的散点图矩阵、二维直方图以及边界框在图像上的分布情况,并将这些图表保存到指定的文件中。整个过程涉及数据处理、图形绘制和文件保存等多个环节,最终为用户提供了一个直观展示训练标签统计信息的工具。

5.def save_one_box(xyxy, im, file=Path("im.jpg"), gain=1.02, pad=10, square=False, BGR=False, save=True): 

# 这段代码定义了一个名为 save_one_box 的函数,用于从图像中裁剪出一个指定的边界框区域,并将其保存为一个新的图像文件。
# 定义了函数 save_one_box ,它接受以下参数 :
# 1.xyxy :一个包含边界框坐标的数组或张量,格式为[x1, y1, x2, y2],表示边界框的左上角和右下角坐标。
# 2.im :输入图像,通常是一个NumPy数组。
# 3.file :保存裁剪图像的文件路径,默认为 Path("im.jpg") 。
# 4.gain :边界框尺寸的放大倍数,默认为1.02。
# 5.pad :边界框的填充像素数,默认为10。
# 6.square :是否将边界框转换为正方形,默认为 False 。
# 7.BGR :图像是否为BGR格式,默认为 False (即RGB格式)。
# 8.save :是否保存裁剪后的图像,默认为 True 。
def save_one_box(xyxy, im, file=Path("im.jpg"), gain=1.02, pad=10, square=False, BGR=False, save=True):# 将图像裁剪保存为 {file},裁剪大小为 {gain} 和 {pad} 像素的倍数。保存和/或返回裁剪。# 此函数接受边界框和图像,然后根据边界框保存裁剪的图像部分。裁剪可以是可选的平方,并且该函数允许对边界框进行增益和填充调整。"""Save image crop as {file} with crop size multiple {gain} and {pad} pixels. Save and/or return crop.This function takes a bounding box and an image, and then saves a cropped portion of the image accordingto the bounding box. Optionally, the crop can be squared, and the function allows for gain and paddingadjustments to the bounding box.Args:xyxy (torch.Tensor or list): A tensor or list representing the bounding box in xyxy format.im (numpy.ndarray): The input image.file (Path, optional): The path where the cropped image will be saved. Defaults to 'im.jpg'.gain (float, optional): A multiplicative factor to increase the size of the bounding box. Defaults to 1.02.pad (int, optional): The number of pixels to add to the width and height of the bounding box. Defaults to 10.square (bool, optional): If True, the bounding box will be transformed into a square. Defaults to False.BGR (bool, optional): If True, the image will be saved in BGR format, otherwise in RGB. Defaults to False.save (bool, optional): If True, the cropped image will be saved to disk. Defaults to True.Returns:(numpy.ndarray): The cropped image.Example:```pythonfrom ultralytics.utils.plotting import save_one_boxxyxy = [50, 50, 150, 150]im = cv2.imread('image.jpg')cropped_im = save_one_box(xyxy, im, file='cropped.jpg', square=True)```"""# 如果 xyxy 不是 torch.Tensor 类型(可能是列表),则将其转换为 torch.Tensor 。if not isinstance(xyxy, torch.Tensor):  # may be list# torch.stack(tensors, dim=0, *, out=None) -> Tensor# torch.stack 是 PyTorch 中的一个函数,用于将一系列张量(tensors)沿着一个新的维度连接起来,从而创建一个新的张量。这个新张量是由输入张量堆叠而成的,堆叠的维度由参数 dim 指定。# 参数 :# tensors :必须。一个张量序列,可以是列表、元组或任何可迭代的张量对象集合。所有张量必须具有相同的形状。# dim :可选。指定沿着哪个维度堆叠张量,默认为0。这个维度将被添加到结果张量的维度中。# out :可选。用于存储输出结果的张量。它必须具有与要创建的堆叠张量相同的形状和类型。# 返回值 :# 返回一个新的张量,它是输入张量沿着指定维度 dim 堆叠的结果。# 描述 :# torch.stack 函数与 torch.cat 函数类似,但它们的主要区别在于堆叠的维度。 torch.cat 是在已存在的维度上连接张量,而 torch.stack 是创建一个新的维度来堆叠张量。xyxy = torch.stack(xyxy)# 将边界框坐标从[x1, y1, x2, y2]格式转换为[x, y, width, height]格式。b = ops.xyxy2xywh(xyxy.view(-1, 4))  # boxes# 如果 square 为 True ,则将边界框转换为正方形。具体操作是取宽度和高度中的最大值,并将其设置为宽度和高度。if square:# 这行代码的目的是将边界框从矩形转换为正方形。具体操作是取边界框的宽度和高度中的最大值,并将其设置为宽度和高度。# b[:, 2:] : b 是一个张量,包含边界框的坐标,格式为[x, y, width, height]。 b[:, 2:] 表示选取所有边界框的宽度和高度部分,即 width 和 height 。# b[:, 2:].max(1) :  max(1) 是一个  torch  操作,用于沿着指定维度(这里是维度1,即每一行)计算最大值。 max(1) 返回一个包含两个部分的元组:最大值和最大值的索引。 max(1)[0] 表示取最大值部分,即每个边界框的宽度和高度中的最大值。# unsqueeze(1) : unsqueeze(1) 是一个 torch 操作,用于在指定维度(这里是维度1)添加一个大小为1的维度。 这样做的目的是将一维的最大值数组转换为二维张量,以便可以赋值回 b 张量中。# b[:, 2:] = ... :将计算得到的最大值赋值回 b 张量的宽度和高度部分,使每个边界框的宽度和高度都等于它们中的最大值,从而将矩形边界框转换为正方形。# 这行代码通过取边界框的宽度和高度中的最大值,并将其设置为宽度和高度,将矩形边界框转换为正方形。这对于某些图像处理任务(如目标检测中的数据预处理)非常有用,可以确保裁剪后的图像区域是正方形,便于后续处理。b[:, 2:] = b[:, 2:].max(1)[0].unsqueeze(1)  # attempt rectangle to square# 将边界框的宽度和高度放大 gain 倍,并加上 pad 像素的填充。b[:, 2:] = b[:, 2:] * gain + pad  # box wh * gain + pad# 将边界框坐标从[x, y, width, height]格式转换回[x1, y1, x2, y2]格式,并转换为长整型。xyxy = ops.xywh2xyxy(b).long()# 将边界框坐标裁剪到图像的边界内,确保边界框不会超出图像范围。xyxy = ops.clip_boxes(xyxy, im.shape)# 从输入图像 im 中裁剪出指定的边界框区域。如果 BGR 为 True ,则保持BGR格式;否则,转换为RGB格式。crop = im[int(xyxy[0, 1]) : int(xyxy[0, 3]), int(xyxy[0, 0]) : int(xyxy[0, 2]), :: (1 if BGR else -1)]# 如果 save 为 True ,则保存裁剪后的图像。if save:# 创建保存路径的父目录,如果目录已存在则不报错。file.parent.mkdir(parents=True, exist_ok=True)  # make directory# 生成一个唯一的文件路径,确保不会覆盖已存在的文件。# def increment_path(path, exist_ok=False, sep="", mkdir=False): -> 用于处理路径的增量创建,即当路径已存在时,自动创建一个增量的路径版本。返回处理后的路径 path 。 -> return pathf = str(increment_path(file).with_suffix(".jpg"))# cv2.imwrite(f, crop)  # save BGR, https://github.com/ultralytics/yolov5/issues/7007 chroma subsampling issue# 将裁剪后的图像保存为RGB格式的JPEG文件,质量为95,不进行子采样。Image.fromarray(crop[..., ::-1]).save(f, quality=95, subsampling=0)  # save RGB# 返回裁剪后的图像数组。return crop
# 这个函数的主要功能是从输入图像中裁剪出一个指定的边界框区域,并可以将其保存为一个新的图像文件。通过参数设置,可以调整边界框的尺寸、是否转换为正方形、是否添加填充、图像格式等。这个函数在图像处理和目标检测任务中非常有用,可以方便地提取和保存感兴趣的区域。

6.def plot_images(images, batch_idx, cls, bboxes=np.zeros(0, dtype=np.float32), confs=None, masks=np.zeros(0, dtype=np.uint8), kpts=np.zeros((0, 51), dtype=np.float32), paths=None, fname="images.jpg", names=None, on_plot=None, max_subplots=16, save=True, conf_thres=0.25,): 

# 这段代码定义了一个名为 plot_images 的函数,用于绘制图像网格,并在每个图像上标注边界框、类别标签、关键点和掩码。该函数被 @threaded 装饰器修饰,可以根据 threaded 参数决定是否在新线程中运行。
# 这是一个装饰器,用于将函数包装为多线程执行,提高处理速度。
# def threaded(func): -> 用于将给定的函数多线程化。装饰器可以根据传入的 threaded 关键字参数决定是否在新线程中运行函数。返回包装后的 wrapper 函数,这样当 threaded 装饰器应用于某个函数时,调用该函数将实际调用 wrapper 函数。 -> return wrapper
@threaded
# 定义了 plot_images 函数,它接受以下参数 :
# 1.images :图像张量,形状为 (batch_size, channels, height, width) 。
# 2.batch_idx :每个边界框对应的批量索引。
# 3.cls :类别标签数组。
# 4.bboxes :边界框坐标数组,形状为 (n, 4) 或 (n, 5) (如果包含旋转角度)。
# 5.confs :置信度数组。
# 6.masks :掩码数组。
# 7.kpts :关键点数组,形状为 (n, 51) 。
# 8.paths :图像路径列表。
# 9.fname :保存图像的文件名,默认为 "images.jpg" 。
# 10.names :类别名称字典。
# 11.on_plot :绘图完成后调用的函数。
# 12.max_subplots :最大子图数量,默认为16。
# 13.save :是否保存图像,默认为 True 。
# 14.conf_thres :置信度阈值,默认为0.25。
def plot_images(images,batch_idx,cls,bboxes=np.zeros(0, dtype=np.float32),confs=None,masks=np.zeros(0, dtype=np.uint8),kpts=np.zeros((0, 51), dtype=np.float32),paths=None,fname="images.jpg",names=None,on_plot=None,max_subplots=16,save=True,conf_thres=0.25,
):# 这段代码的主要目的是将输入的张量(如果它们是 torch.Tensor 类型)转换为NumPy数组,并进行一些预处理操作,以便后续可以更方便地进行图像处理和绘制。# 用标签绘制图像网格。"""Plot image grid with labels."""# 如果 images 是一个 torch.Tensor ,则将其移动到CPU上,转换为浮点类型,并转换为NumPy数组。这一步确保了 images 可以被NumPy函数处理。if isinstance(images, torch.Tensor):images = images.cpu().float().numpy()# 如果 cls 是一个 torch.Tensor ,则将其移动到CPU上,并转换为NumPy数组。这一步确保了 cls 可以被NumPy函数处理。if isinstance(cls, torch.Tensor):cls = cls.cpu().numpy()# 如果 bboxes 是一个 torch.Tensor ,则将其移动到CPU上,并转换为NumPy数组。这一步确保了 bboxes 可以被NumPy函数处理。if isinstance(bboxes, torch.Tensor):bboxes = bboxes.cpu().numpy()# 如果 masks 是一个 torch.Tensor ,则将其移动到CPU上,转换为NumPy数组,并转换为整数类型。这一步确保了 masks 可以被NumPy函数处理,并且是整数类型,适合用于掩码操作。if isinstance(masks, torch.Tensor):masks = masks.cpu().numpy().astype(int)# 如果 kpts 是一个 torch.Tensor ,则将其移动到CPU上,并转换为NumPy数组。这一步确保了 kpts 可以被NumPy函数处理。if isinstance(kpts, torch.Tensor):kpts = kpts.cpu().numpy()# 如果 batch_idx 是一个 torch.Tensor ,则将其移动到CPU上,并转换为NumPy数组。这一步确保了 batch_idx 可以被NumPy函数处理。if isinstance(batch_idx, torch.Tensor):batch_idx = batch_idx.cpu().numpy()# 设置 最大图像尺寸 为1920像素。这用于后续的图像缩放操作,确保生成的图像不会过大。max_size = 1920  # max image size# 从 images 张量中提取 批量大小 bs 、 高度 h 和 宽度 w 。 _ 是一个占位符,表示忽略 通道数 。bs, _, h, w = images.shape  # batch size, _, height, width# 将 批量大小 bs 限制为 max_subplots ,以确保绘制的图像数量不会超过最大子图数量。bs = min(bs, max_subplots)  # limit plot images# 计算 子图的数量 ,使其尽可能接近正方形。 np.ceil(bs**0.5) 计算批量大小的平方根并向上取整,确保子图数量足够容纳所有图像。ns = np.ceil(bs**0.5)  # number of subplots (square)# 如果第一个图像的最大值小于等于1,说明图像可能被归一化到了0-1范围。if np.max(images[0]) <= 1:# 将其乘以255,将其缩放到0-255范围,以便进行后续的图像处理和绘制。images *= 255  # de-normalise (optional)# 这段代码通过将输入的张量转换为NumPy数组,并进行一些预处理操作,确保了图像数据的格式适合后续的处理和绘制。这些操作包括将张量移动到CPU、转换为NumPy数组、限制批量大小、计算子图数量以及将归一化的图像数据缩放到0-255范围。这些步骤为后续的图像处理和标注提供了便利。# 这段代码的主要目的是构建一个图像网格(mosaic),将多个图像拼接在一起,并根据需要进行缩放。# Build Image# 创建一个全白的三维数组 mosaic ,形状为 (int(ns * h), int(ns * w), 3) ,表示图像网格的 高度 、 宽度 和 颜色通道数 。初始值为255,表示白色背景。mosaic = np.full((int(ns * h), int(ns * w), 3), 255, dtype=np.uint8)  # init# 遍历每个图像,计算其在网格中的位置,并将其放置在相应的位置。for i in range(bs):# 计算当前图像在网格中的起始坐标 (x, y) 。x, y = int(w * (i // ns)), int(h * (i % ns))  # block origin# 将当前图像 images[i] 转置为 (height, width, channels) 格式,并放置在 mosaic 的相应位置。mosaic[y : y + h, x : x + w, :] = images[i].transpose(1, 2, 0)# Resize (optional)# 计算 缩放比例 scale ,确保图像网格的最大尺寸不超过 max_size 。 max_size / ns 计算每个子图的最大尺寸,再除以 max(h, w) 得到最终的缩放比例。scale = max_size / ns / max(h, w)# 如果缩放比例 scale 小于1,说明需要缩放图像。if scale < 1:# 计算缩放后的高度和宽度。h = math.ceil(scale * h)w = math.ceil(scale * w)# 使用 cv2.resize 函数对 mosaic 进行缩放,目标尺寸为 (w * ns, h * ns) 。mosaic = cv2.resize(mosaic, tuple(int(x * ns) for x in (w, h)))# 这段代码通过创建一个全白的图像网格,并将每个图像放置在相应的位置,构建了一个图像拼接图。如果图像尺寸超过最大尺寸限制,则进行缩放,确保最终的图像网格不会过大。这在可视化批量图像时非常有用,例如在机器学习和计算机视觉任务中展示多个图像的处理结果。# 这段代码的主要目的是在构建的图像网格(mosaic)上进行标注,包括绘制边界框、标注文件名和类别标签。# Annotate# 计算 字体大小 fs ,基于图像的高度 h 、宽度 w 和子图数量 ns 。这个公式确保字体大小与图像尺寸成比例。fs = int((h + w) * ns * 0.01)  # font size# 创建一个 Annotator 对象,用于在图像网格上进行标注。 Annotator 类的参数包括 :# mosaic :图像网格。 line_width :线条宽度,设置为字体大小的1/10。 font_size :字体大小。 pil :是否使用PIL库进行绘制,设置为 True 。 example :类别名称字典,用于示例标注。# class Annotator:# -> 用于在图像上添加注释,如绘制边界框、关键点、文本等。# -> def __init__(self, im, line_width=None, font_size=None, font="Arial.ttf", pil=False, example="abc"):annotator = Annotator(mosaic, line_width=round(fs / 10), font_size=fs, pil=True, example=names)# 遍历每个图像,计算其 在网格中的起始坐标 (x, y) 。for i in range(bs):x, y = int(w * (i // ns)), int(h * (i % ns))  # block origin# 在每个图像周围绘制白色边框,边框宽度为2像素。# def rectangle(self, xy, fill=None, outline=None, width=1): -> 用于在图像上绘制矩形。这个方法使用 PIL 库的 ImageDraw 模块来绘制矩形。annotator.rectangle([x, y, x + w, y + h], None, (255, 255, 255), width=2)  # borders# 如果提供了路径列表 paths ,则在每个图像的左上角标注文件名。文件名截取前40个字符,文本颜色为浅灰色。if paths:# def text(self, xy, text, txt_color=(255, 255, 255), anchor="top", box_style=False): -> 用于在图像上绘制文本。这个方法支持PIL库和OpenCV库,可以处理文本的对齐、背景框的绘制以及多行文本的处理。annotator.text((x + 5, y + 5), text=Path(paths[i]).name[:40], txt_color=(220, 220, 220))  # filenames# 如果提供了 类别标签 cls 。if len(cls) > 0:# 找到当前图像对应的类别标签索引。idx = batch_idx == i# 提取当前图像的类别标签,并转换为整数类型。classes = cls[idx].astype("int")# 检查是否提供了置信度 confs ,如果没有提供,则 labels 为 True ,表示标注的是类别标签而不是预测结果。labels = confs is None# 这段代码通过  Annotator  对象在图像网格上进行标注,包括绘制边界框、标注文件名和类别标签。这些标注帮助用户更好地理解每个图像的内容和来源,特别是在可视化批量图像时非常有用。通过动态计算字体大小和线条宽度,确保标注在不同尺寸的图像上都能清晰可见。# 这段代码的主要目的是在图像网格上绘制边界框,并标注类别标签和置信度。# 如果提供了 边界框数组 bboxes ,则进行后续处理。if len(bboxes):# 提取当前图像对应的边界框。boxes = bboxes[idx]# 如果提供了 置信度数组 confs ,则提取当前图像对应的置信度;否则,置信度为 None 。conf = confs[idx] if confs is not None else None  # check for confidence presence (label vs pred)# 检查边界框是否包含旋转角度(即是否为定向边界框,形状为 (n, 5) ),如果是,则 is_obb 为 True 。is_obb = boxes.shape[-1] == 5  # xywhr# 将边界框坐标从 xywhr (中心点坐标、宽度、高度、旋转角度)格式转换为 xyxyxyxy (四个顶点坐标)格式,或者从 xywh 格式转换为 xyxy 格式。boxes = ops.xywhr2xyxyxyxy(boxes) if is_obb else ops.xywh2xyxy(boxes)# 如果存在边界框,则进行后续处理。if len(boxes):# 如果边界框坐标是归一化的(最大值小于等于1.1),则将其缩放到像素坐标。if boxes[:, :4].max() <= 1.1:  # if normalized with tolerance 0.1boxes[..., 0::2] *= w  # scale to pixelsboxes[..., 1::2] *= h# 如果图像进行了缩放( scale < 1 ),则将边界框坐标也进行相应的缩放。elif scale < 1:  # absolute coords need scale if image scalesboxes[..., :4] *= scale# 将边界框的x和y坐标偏移,使其在图像网格中的位置正确。boxes[..., 0::2] += xboxes[..., 1::2] += y# 遍历每个边界框,将其转换为整数列表。for j, box in enumerate(boxes.astype(np.int64).tolist()):# 获取当前边界框的类别标签 c ,并获取对应的颜色。c = classes[j]color = colors(c)# 如果提供了类别名称字典 names ,则使用字典中的名称;否则,使用类别索引。c = names.get(c, c) if names else c# 如果标注的是 类别标签 ( labels 为 True ),或者置信度大于阈值 conf_thres ,则进行标注。if labels or conf[j] > conf_thres:# 生成标注文本,如果标注的是类别标签,则只显示类别名称;否则,显示类别名称和置信度。label = f"{c}" if labels else f"{c} {conf[j]:.1f}"# 使用 Annotator 对象在图像上绘制边界框,并标注文本。如果 is_obb 为 True ,则绘制旋转的边界框。# def box_label(self, box, label="", color=(128, 128, 128), txt_color=(255, 255, 255), rotated=False): -> 用于在图像上绘制带有标签的边界框。这个方法支持两种情况:使用PIL库和使用OpenCV库。annotator.box_label(box, label, color=color, rotated=is_obb)# 这段代码通过在图像网格上绘制边界框,并标注类别标签和置信度,帮助用户更好地理解每个图像中的目标对象。通过动态处理边界框的坐标格式和缩放,确保标注在不同尺寸的图像上都能正确显示。这在可视化目标检测结果时非常有用。# 这段代码的主要目的是在图像网格上标注类别标签,但不绘制边界框。这通常用于没有提供边界框的情况,或者边界框为空的情况。# 如果提供了 类别标签数组 classes ,但没有提供边界框,则进行后续处理。elif len(classes):# 遍历每个 类别标签 c 。for c in classes:# 获取当前类别标签 c 对应的颜色。 colors 函数假设返回一个颜色的RGB值。color = colors(c)# 如果提供了 类别名称字典 names ,则使用字典中的名称;否则,使用类别索引。这一步确保标注的文本是可读的类别名称,而不是类别索引。c = names.get(c, c) if names else c# 使用 Annotator 对象在图像的左上角标注类别标签。# (x, y) :标注的起始坐标,这里选择每个图像块的左上角。 f"{c}" :标注的文本内容,即类别名称。 txt_color=color :文本颜色,使用之前获取的颜色。 box_style=True :是否使用带边框的文本框样式,这里设置为 True ,表示绘制一个带边框的文本框。# # def text(self, xy, text, txt_color=(255, 255, 255), anchor="top", box_style=False): -> 用于在图像上绘制文本。这个方法支持PIL库和OpenCV库,可以处理文本的对齐、背景框的绘制以及多行文本的处理。annotator.text((x, y), f"{c}", txt_color=color, box_style=True)# 这段代码通过在图像网格的每个图像块的左上角标注类别标签,帮助用户了解每个图像的类别信息。这在没有边界框的情况下非常有用,例如在分类任务中可视化结果。通过使用类别名称字典,确保标注的文本是可读的,提高了可视化效果的友好性。# 这段代码的主要目的是在图像网格上绘制关键点。关键点通常用于表示物体的特定部位,例如人体的关键点(头部、手部、脚部等)。# Plot keypoints# 如果提供了 关键点数组 kpts ,则进行后续处理。if len(kpts):# 提取当前图像对应的关键点,并创建一个副本,以避免修改原始数据。kpts_ = kpts[idx].copy()# 如果存在关键点,则进行后续处理。if len(kpts_):# 如果关键点的x或y坐标最大值小于等于1.01,说明关键点坐标是归一化的(在0到1之间),则将其缩放到像素坐标。这里使用图像的宽度 w 和高度 h 进行缩放。if kpts_[..., 0].max() <= 1.01 or kpts_[..., 1].max() <= 1.01:  # if normalized with tolerance .01kpts_[..., 0] *= w  # scale to pixelskpts_[..., 1] *= h# 如果图像进行了缩放( scale < 1 ),则将关键点坐标也进行相应的缩放。elif scale < 1:  # absolute coords need scale if image scaleskpts_ *= scale# 将关键点的x和y坐标偏移,使其在图像网格中的位置正确。这里使用每个图像块的起始坐标 (x, y) 进行偏移。kpts_[..., 0] += xkpts_[..., 1] += y# 遍历每个关键点。for j in range(len(kpts_)):# 如果标注的是 类别标签 ( labels 为 True ),或者 置信度大于阈值 conf_thres ,则进行标注。if labels or conf[j] > conf_thres:# 使用 Annotator 对象在图像上绘制关键点。 kpts_[j] 是一个关键点的坐标数组, annotator.kpts 方法负责在图像上绘制这些关键点。# def kpts(self, kpts, shape=(640, 640), radius=5, kpt_line=True): -> 用于在图像上绘制关键点(keypoints)和关键点之间的连接线。这个方法支持PIL库和OpenCV库,可以处理不同形状的关键点数据,并根据关键点的置信度过滤掉不准确的关键点。annotator.kpts(kpts_[j])# 这段代码通过在图像网格上绘制关键点,帮助用户更好地理解每个图像中的目标对象的特定部位。通过动态处理关键点的坐标格式和缩放,确保关键点在不同尺寸的图像上都能正确显示。这在可视化目标检测和关键点检测结果时非常有用。# 这段代码的主要目的是在图像网格上绘制掩码(masks),通常用于表示物体的区域。掩码可以是二值图像,其中物体区域的像素值为1,背景区域的像素值为0。# Plot masks# 如果提供了 掩码数组 masks ,则进行后续处理。if len(masks):# 根据 masks 的形状和 idx 的长度,决定如何处理掩码。# 如果 idx.shape[0] == masks.shape[0] ,说明每个掩码对应一个边界框,直接提取当前图像对应的掩码。if idx.shape[0] == masks.shape[0]:  # overlap_masks=Falseimage_masks = masks[idx]# 如果 overlap_masks 为 True ,说明多个掩码重叠在一张图像上,需要进行分离处理。else:  # overlap_masks=True# 提取当前图像 对应的掩码 。假设 masks 是一个形状为 (batch_size, height, width) 的数组, masks[[i]] 提取第 i 个图像的掩码,形状为 (1, height, width) 。image_masks = masks[[i]]  # (1, 640, 640)# 计算当前图像中 有效掩码的数量 。 idx 是一个布尔数组,表示哪些掩码属于当前图像。 idx.sum() 计算 True 的数量,即当前图像中的掩码数量。nl = idx.sum()# 创建一个索引数组,形状为 (nl, 1, 1) ,值从1到 nl 。这个索引数组用于 标记每个掩码的唯一标识 。index = np.arange(nl).reshape((nl, 1, 1)) + 1# 将提取的掩码沿第一个维度(批量维度)重复 nl 次,形状变为 (nl, height, width) 。image_masks = np.repeat(image_masks, nl, axis=0)# 使用 np.where 函数将重叠的掩码分离成多个独立的掩码。 image_masks == index :生成一个布尔数组,表示每个位置的掩码是否属于当前索引。 np.where :将符合条件的位置设置为1.0,不符合的位置设置为0.0,从而分离出每个独立的掩码。image_masks = np.where(image_masks == index, 1.0, 0.0)# 创建当前图像的副本,用于绘制掩码。im = np.asarray(annotator.im).copy()# 遍历每个掩码。for j in range(len(image_masks)):# 如果标注的是 类别标签( labels 为 True ),或者 置信度大于阈值 conf_thres ,则进行绘制。if labels or conf[j] > conf_thres:# 获取当前类别 classes[j] 对应的颜色。 colors 函数返回一个颜色值,用于后续绘制掩码时的颜色设置。color = colors(classes[j])# 获取当前掩码 image_masks[j] 的高度 mh 和宽度 mw 。mh, mw = image_masks[j].shape# 检查掩码的尺寸是否与目标图像的尺寸不匹配。 h 和 w 分别是目标图像的高度和宽度。if mh != h or mw != w:# 如果尺寸不匹配,首先将掩码转换为 np.uint8 类型,这是 cv2.resize 函数所需的输入类型。mask = image_masks[j].astype(np.uint8)# 使用 cv2.resize 函数将掩码调整为目标图像的尺寸 (w, h) 。mask = cv2.resize(mask, (w, h))# 将调整尺寸后的掩码转换为布尔类型,以便用于后续的像素操作。mask = mask.astype(bool)# 如果掩码的尺寸已经与目标图像匹配,则直接将掩码转换为布尔类型。else:mask = image_masks[j].astype(bool)# 在图像的相应位置上绘制掩码。使用 contextlib.suppress(Exception) 忽略可能的异常,确保代码的健壮性。绘制时,将掩码区域的像素值与颜色混合,实现半透明效果。with contextlib.suppress(Exception):# 这行代码的主要目的是在图像的特定区域上绘制掩码,并实现半透明效果。具体来说,它将 掩码区域的像素值 与 指定颜色混合 ,使掩码在图像上以半透明的形式显示。# im[y : y + h, x : x + w, :] :这部分代码选择了图像 im 中从 (y, x) 到 (y + h, x + w) 的区域,即当前图像块的区域。# [mask] :进一步选择该区域中掩码 mask 为 True 的部分。 mask 是一个布尔数组,形状与图像块的区域相同,用于指示哪些像素属于当前掩码。# im[y : y + h, x : x + w, :][mask] * 0.4 :将选定区域中掩码为 True 的像素值乘以0.4。这一步是为了实现半透明效果,将原始图像的像素值保留40%。# np.array(color) * 0.6 :将指定颜色 color 转换为NumPy数组,并乘以0.6。这一步是为了将颜色值的60%用于混合,实现半透明效果。# im[y : y + h, x : x + w, :][mask] * 0.4 + np.array(color) * 0.6 :将上述两部分相加,实现原始图像像素值的40%与颜色值的60%的混合,从而得到半透明的掩码效果。# im[y : y + h, x : x + w, :][mask] = ... :将计算得到的混合像素值赋值回图像的相应区域中,完成掩码的绘制。# 这行代码通过将原始图像的像素值与指定颜色混合,实现了半透明的掩码效果。这种效果在可视化目标检测和实例分割任务中非常有用,因为它可以清晰地显示掩码区域,同时保留背景图像的细节。通过调整混合比例(0.4和0.6),可以控制掩码的透明度。im[y : y + h, x : x + w, :][mask] = (im[y : y + h, x : x + w, :][mask] * 0.4 + np.array(color) * 0.6)# 将绘制好的图像传递给 Annotator 对象,以便后续可以继续进行其他标注操作。# def fromarray(self, im): -> 用于将 NumPy 数组转换为 PIL 图像,并更新 Annotator 实例中的图像和绘图对象。annotator.fromarray(im)# 这段代码通过在图像网格上绘制掩码,帮助用户更好地理解每个图像中的目标对象的区域。通过动态处理掩码的尺寸和重叠情况,确保掩码在不同尺寸的图像上都能正确显示。这在可视化目标检测和实例分割结果时非常有用。# 这段代码的主要目的是根据条件保存或返回绘制好的图像,并在需要时调用一个回调函数。# 如果 save 参数为 False ,则不保存图像,而是将 Annotator 对象中的图像转换为NumPy数组并返回。这允许调用者在不保存到文件的情况下获取绘制好的图像,例如用于进一步处理或显示。if not save:return np.asarray(annotator.im)# 如果 save 参数为 True ,则使用 Annotator 对象中的图像调用 save 方法,将图像保存到指定的文件路径 fname 。这一步确保绘制好的图像被持久化到磁盘。annotator.im.save(fname)  # save# 如果提供了 on_plot 回调函数,则调用该函数,并将保存的文件名 fname 作为参数传递。这允许调用者在图像保存后执行一些自定义操作,例如显示图像、记录日志或进行其他处理。if on_plot:on_plot(fname)# 这段代码提供了灵活的图像处理和保存选项。通过 save 参数,用户可以选择是否将图像保存到文件,或者直接获取图像的NumPy数组。此外,通过 on_plot 回调函数,用户可以在图像保存后执行自定义操作,增加了代码的灵活性和可扩展性。
# plot_images 函数是一个功能强大的工具,用于将批量图像绘制到一个网格中,并在每个图像上标注边界框、类别标签、关键点和掩码。该函数支持多线程执行,可以通过  threaded  装饰器在新线程中运行,以提高性能。函数接受多个参数,包括图像张量、类别标签、边界框、置信度、掩码、关键点等,提供了灵活的配置选项。通过动态处理图像尺寸、坐标缩放和标注内容, plot_images 能够生成直观且信息丰富的可视化结果,适用于目标检测、实例分割和关键点检测等任务的可视化。
# def plot_images(images, batch_idx, cls, bboxes=np.zeros(0, dtype=np.float32), confs=None, masks=np.zeros(0, dtype=np.uint8), kpts=np.zeros((0, 51), dtype=np.float32), paths=None, fname="images.jpg", names=None, on_plot=None, max_subplots=16, save=True, conf_thres=0.25,):

7.def plot_results(file="path/to/results.csv", dir="", segment=False, pose=False, classify=False, on_plot=None): 

# 这段代码定义了一个名为 plot_results 的函数,用于从CSV文件中读取训练结果数据,并将其绘制为图表。函数支持多种任务类型(分类、分割、姿态估计)的可视化,并提供了平滑处理选项。
# @plt_settings() 用于设置绘图相关的参数或环境,如字体、颜色等,以便在函数中统一使用。
# def plt_settings(rcparams=None, backend="Agg"):
# -> 用于设置Matplotlib的全局配置参数(rcparams)和后端(backend),并在函数执行后恢复原始设置。这个装饰器在绘图函数中非常有用,特别是当需要临时修改绘图配置而不影响全局设置时。返回装饰器工厂。返回装饰器 decorator 。
# -> return decorator
@plt_settings()
# 定义了 plot_results 函数,它接受以下参数 :
# 1.file :结果文件的路径,默认为 "path/to/results.csv" 。
# 2.dir :结果文件所在的目录,默认为空字符串。
# 3.segment :是否为分割任务,默认为 False 。
# 4.pose :是否为姿态估计任务,默认为 False 。
# 5.classify :是否为分类任务,默认为 False 。
# 6.on_plot :绘图完成后调用的函数,默认为 None 。
def plot_results(file="path/to/results.csv", dir="", segment=False, pose=False, classify=False, on_plot=None):# 从结果 CSV 文件绘制训练结果。该函数支持各种类型的数据,包括分割、姿势估计和分类。绘图将以“results.png”的形式保存在 CSV 所在的目录中。"""Plot training results from a results CSV file. The function supports various types of data including segmentation,pose estimation, and classification. Plots are saved as 'results.png' in the directory where the CSV is located.Args:file (str, optional): Path to the CSV file containing the training results. Defaults to 'path/to/results.csv'.dir (str, optional): Directory where the CSV file is located if 'file' is not provided. Defaults to ''.segment (bool, optional): Flag to indicate if the data is for segmentation. Defaults to False.pose (bool, optional): Flag to indicate if the data is for pose estimation. Defaults to False.classify (bool, optional): Flag to indicate if the data is for classification. Defaults to False.on_plot (callable, optional): Callback function to be executed after plotting. Takes filename as an argument.Defaults to None.Example:```pythonfrom ultralytics.utils.plotting import plot_resultsplot_results('path/to/results.csv', segment=True)```"""# 导入所需的库。# pandas 用于数据处理。import pandas as pd# scipy.ndimage.gaussian_filter1d 用于对数据进行平滑处理。from scipy.ndimage import gaussian_filter1d# 根据 file 参数确定保存目录。如果 file 不为空,则使用 file 的父目录;否则,使用 dir 参数。save_dir = Path(file).parent if file else Path(dir)# 根据任务类型创建不同布局的子图。# 如果任务是分类( classify 为 True ),则创建一个2x2的子图布局,每个子图的大小为6x6英寸,并启用紧凑布局( tight_layout=True )。索引列表 index 设置为 [1, 4, 2, 3] ,这些索引用于 选择CSV文件中的特定列进行绘图 。if classify:fig, ax = plt.subplots(2, 2, figsize=(6, 6), tight_layout=True)index = [1, 4, 2, 3]# 如果任务是分割( segment 为 True ),则创建一个2x8的子图布局,每个子图的大小为18x6英寸,并启用紧凑布局。索引列表 index 设置为 [1, 2, 3, 4, 5, 6, 9, 10, 13, 14, 15, 16, 7, 8, 11, 12] ,这些索引用于选择CSV文件中的特定列进行绘图。elif segment:fig, ax = plt.subplots(2, 8, figsize=(18, 6), tight_layout=True)index = [1, 2, 3, 4, 5, 6, 9, 10, 13, 14, 15, 16, 7, 8, 11, 12]# 如果任务是姿态估计( pose 为 True ),则创建一个2x9的子图布局,每个子图的大小为21x6英寸,并启用紧凑布局。索引列表 index 设置为 [1, 2, 3, 4, 5, 6, 7, 10, 11, 14, 15, 16, 17, 18, 8, 9, 12, 13] ,这些索引用于选择CSV文件中的特定列进行绘图。elif pose:fig, ax = plt.subplots(2, 9, figsize=(21, 6), tight_layout=True)index = [1, 2, 3, 4, 5, 6, 7, 10, 11, 14, 15, 16, 17, 18, 8, 9, 12, 13]# 如果任务类型不是分类、分割或姿态估计,则创建一个2x5的子图布局,每个子图的大小为12x6英寸,并启用紧凑布局。索引列表 index 设置为 [1, 2, 3, 4, 5, 8, 9, 10, 6, 7] ,这些索引用于选择CSV文件中的特定列进行绘图。else:fig, ax = plt.subplots(2, 5, figsize=(12, 6), tight_layout=True)index = [1, 2, 3, 4, 5, 8, 9, 10, 6, 7]# 将子图数组展平,方便后续遍历。ax = ax.ravel()# 查找保存目录下的所有 results*.csv 文件,并确保至少有一个文件,否则抛出异常。files = list(save_dir.glob("results*.csv"))assert len(files), f"No results.csv files found in {save_dir.resolve()}, nothing to plot."    # 在 {save_dir.resolve()} 中未找到 results.csv 文件,没有可绘制的内容。# 这段代码遍历指定目录下的所有CSV文件,读取每个文件中的数据,并将其绘制为图表。每个子图显示一个特定的指标,同时提供原始数据和平滑后的数据曲线。# 遍历 files 列表中的每个文件路径 f 。for f in files:# 使用 try 块来捕获可能的异常,确保即使某个文件处理出错,程序也能继续处理其他文件。try:# 使用 pandas 的 read_csv 函数读取CSV文件 f 中的数据,返回一个 DataFrame 对象 data 。data = pd.read_csv(f)# 获取 DataFrame 的列名,并去除每个列名前后的空白字符,存储在列表 s 中。s = [x.strip() for x in data.columns]# 提取 DataFrame 的第一列数据,通常表示迭代次数或 epoch,存储在数组 x 中。x = data.values[:, 0]# 遍历索引列表 index , i 是当前子图的索引, j 是CSV文件中对应列的索引。for i, j in enumerate(index):# 提取CSV文件中第 j 列的数据,并将其转换为浮点数类型,存储在数组 y 中。y = data.values[:, j].astype("float")# 这行代码被注释掉了,它的作用是将数组 y 中所有等于0的值替换为 np.nan ,这样在绘图时这些值不会显示。这可以用于过滤掉无效或不相关的数据点。# y[y == 0] = np.nan  # don't show zero values# 在第 i 个子图上绘制原始数据曲线,使用点标记( marker="." ),标签为文件名( f.stem ),线宽为2,标记大小为8。ax[i].plot(x, y, marker=".", label=f.stem, linewidth=2, markersize=8)  # actual results# 在第 i 个子图上绘制平滑后的数据曲线,使用虚线( ":" ),标签为"smooth",线宽为2。平滑处理使用 scipy.ndimage.gaussian_filter1d 函数,标准差( sigma )为3。ax[i].plot(x, gaussian_filter1d(y, sigma=3), ":", label="smooth", linewidth=2)  # smoothing line# 设置第 i 个子图的标题为CSV文件中第 j 列的列名,字体大小为12。ax[i].set_title(s[j], fontsize=12)# 这行代码被注释掉了,它的作用是 共享训练和验证损失的y轴 。如果需要共享y轴,可以取消注释并使用 ax[i].get_shared_y_axes().join(ax[i], ax[i - 5]) 。# if j in [8, 9, 10]:  # share train and val loss y axes#     ax[i].get_shared_y_axes().join(ax[i], ax[i - 5])# 如果在处理文件 f 时发生任何异常,捕获异常并记录警告信息,继续处理下一个文件。except Exception as e:LOGGER.warning(f"WARNING: Plotting error for {f}: {e}")    # 警告:{f} 的绘图错误:{e}。# 这段代码通过读取CSV文件中的数据,并将其绘制为图表,提供了直观的训练结果可视化。每个子图显示一个特定的指标,同时提供原始数据和平滑后的数据曲线,帮助用户更好地理解训练过程中的变化趋势。通过异常处理,确保程序的健壮性,即使某些文件处理出错,也不会影响整体的执行。# 为第二个子图添加图例。ax[1].legend()# 保存图表到文件,并关闭绘图窗口。fname = save_dir / "results.png"fig.savefig(fname, dpi=200)plt.close()# 如果提供了 on_plot 回调函数,则调用该函数,并传入保存的文件名。if on_plot:on_plot(fname)
# plot_results 函数通过读取CSV文件中的训练结果数据,并将其绘制为图表,支持多种任务类型(分类、分割、姿态估计)的可视化。函数提供了平滑处理选项,使图表更加清晰。通过灵活的布局和详细的标注,该函数为用户提供了直观的训练结果可视化工具。

8.def plt_color_scatter(v, f, bins=20, cmap="viridis", alpha=0.8, edgecolors="none"): 

# 这段代码定义了一个名为 plt_color_scatter 的函数,用于创建一个彩色散点图,其中每个点的颜色基于其在2D直方图中的密度。这种可视化方法可以帮助用户更好地理解数据点的分布和密度。
# 定义了 plt_color_scatter 函数,它接受以下参数 :
# 1.v :第一个变量的值,通常表示x轴数据。
# 2.f :第二个变量的值,通常表示y轴数据。
# 3.bins :直方图的 bins 数量,默认为20。
# 4.cmap :颜色映射表,默认为"viridis"。
# 5.alpha :点的透明度,默认为0.8。
# 6.edgecolors :点的边缘颜色,默认为"none"。
def plt_color_scatter(v, f, bins=20, cmap="viridis", alpha=0.8, edgecolors="none"):# 绘制基于二维直方图的点着色散点图。"""Plots a scatter plot with points colored based on a 2D histogram.Args:v (array-like): Values for the x-axis.f (array-like): Values for the y-axis.bins (int, optional): Number of bins for the histogram. Defaults to 20.cmap (str, optional): Colormap for the scatter plot. Defaults to 'viridis'.alpha (float, optional): Alpha for the scatter plot. Defaults to 0.8.edgecolors (str, optional): Edge colors for the scatter plot. Defaults to 'none'.Examples:>>> v = np.random.rand(100)>>> f = np.random.rand(100)>>> plt_color_scatter(v, f)"""# Calculate 2D histogram and corresponding colors# np.histogram2d(x, y, bins=10, range=None, density=None, weights=None)# np.histogram2d 是 NumPy 库中的一个函数,用于计算两个数据集的二维直方图。这个函数可以处理两个输入数组,并返回它们在二维空间中的分布情况。# 参数说明 :# x :第一个数据数组。# y :第二个数据数组,与 x 有相同的长度。# bins :一个整数或一对整数。如果是一个整数,则 x 和 y 都将使用相同数量的箱子。如果是一对整数,则分别指定 x 和 y 的箱子数量。# range :一个元组,形式为 ([x_min, x_max], [y_min, y_max]) ,用于指定直方图的值范围。如果没有指定,则使用 x 和 y 的最小值和最大值。# density :布尔值,如果为 True ,则表示返回的直方图是归一化的,即每个箱子的高度表示密度而不是计数。# weights :一个与 x 和 y 相同长度的数组,用于计算加权直方图。# 返回值 :# H :直方图的值,形状为 (bins[0], bins[1]) 。# X : x 轴的箱子边缘值数组。# Y : y 轴的箱子边缘值数组。# 如果指定了 density 参数,返回的 H 数组将被归一化,使得 H 的所有值之和等于 1。如果没有指定 density ,则 H 的所有值之和等于输入数据点的总数。# 使用 numpy 的 histogram2d 函数计算 v 和 f 的2D直方图。 bins 参数指定了直方图的箱数。函数返回直方图的值 hist ,以及x和y边缘的边界 xedges 和 yedges 。hist, xedges, yedges = np.histogram2d(v, f, bins=bins)# 为每个点计算颜色值。使用 np.digitize 函数确定每个点 (v[i], f[i]) 落在哪个箱中,然后从直方图 hist 中获取相应的值作为颜色。 min 函数确保索引不会超出直方图的边界。colors = [hist[# np.digitize(x, bins, right=False)# np.digitize 是 NumPy 库中的一个函数,它用于将一维数组中的元素映射到与之对应的箱子(bin)索引。这个函数常用于确定数据点落在哪个区间或箱子中,是直方图计算中的一个辅助步骤。# 参数说明 :# x :待确定箱子索引的一维数组。# bins :定义箱子边界的一维数组。 x 中的元素将根据这些边界值被分配到不同的箱子中。# right :布尔值,指定箱子的对齐方式。如果为 False ,则 x 中的值小于 bins[0] 时返回 0,大于等于 bins[-1] 时返回 len(bins) 。如果为 True ,则 x 中的值小于 bins[0] 时返回 1,大于等于 bins[-1] 时返回 len(bins) 。# 返回值 :# 返回一个整数数组,其中每个元素是 x 中对应元素的箱子索引。min(np.digitize(v[i], xedges, right=True) - 1, hist.shape[0] - 1),min(np.digitize(f[i], yedges, right=True) - 1, hist.shape[1] - 1),]for i in range(len(v))]# Scatter plot# matplotlib.pyplot.scatter(x, y, s=None, c=None, marker='o', cmap=None, norm=None, vmin=None, vmax=None, alpha=None, linewidths=None, edgecolors=None, **kwargs)# plt.scatter 是 Matplotlib 库中用于绘制散点图的函数。它可以根据提供的 x 和 y 坐标值绘制点,并且可以自定义点的颜色、大小、形状等属性。# 参数说明 :# x , y :分别是散点图的 x 坐标和 y 坐标数据。# s :(可选)点的大小,可以是数字或与 x 和 y 相同长度的数组。# c :(可选)点的颜色,可以是单色、颜色数组或颜色映射对象。# marker :(可选)点的形状,如 'o'(圆圈)、's'(正方形)等,默认为 'o'。# cmap :(可选)颜色映射对象,用于将数值映射到颜色。# norm :(可选)归一化对象,用于调整颜色数据。# vmin , vmax :(可选)颜色数据的最小值和最大值。# alpha :(可选)点的透明度。# linewidths :(可选)点的边框宽度。# edgecolors :(可选)点的边框颜色。# **kwargs :其他关键字参数,用于自定义点的样式。# 返回值 :# 返回一个包含散点图对象的列表。# 使用 matplotlib.pyplot 的 scatter 函数创建散点图。点的颜色 c 设置为之前计算的颜色值,颜色映射表 cmap 设置为指定的映射表,透明度 alpha 和边缘颜色 edgecolors 也根据参数设置。plt.scatter(v, f, c=colors, cmap=cmap, alpha=alpha, edgecolors=edgecolors)
# plt_color_scatter 函数通过计算2D直方图并根据点的密度分配颜色,创建了一个彩色散点图。这种可视化方法特别适用于展示两个变量之间的关系以及数据点的分布密度。通过调整 bins 、 cmap 、 alpha 和 edgecolors 参数,用户可以自定义散点图的外观,以更好地适应不同的数据集和可视化需求。

9.def plot_tune_results(csv_file="tune_results.csv"): 

# 这段代码定义了一个名为 plot_tune_results 的函数,其目的是从一个 CSV 文件中读取数据,并生成两种类型的图表:一种是针对每个超参数的散点图,另一种是适应度随迭代次数变化的折线图。
# 定义一个名为 plot_tune_results 的函数,它接受一个参数。
# 1.csv_file :默认值为 "tune_results.csv" 。这个函数的目的是绘制调参结果的可视化图像。
def plot_tune_results(csv_file="tune_results.csv"):# 绘制存储在“tune_results.csv”文件中的演化结果。该函数为 CSV 中的每个键生成一个散点图,并根据适应度得分进行颜色编码。性能最佳的配置会在图中突出显示。"""Plot the evolution results stored in an 'tune_results.csv' file. The function generates a scatter plot for each keyin the CSV, color-coded based on fitness scores. The best-performing configurations are highlighted on the plots.Args:csv_file (str, optional): Path to the CSV file containing the tuning results. Defaults to 'tune_results.csv'.Examples:>>> plot_tune_results('path/to/tune_results.csv')"""# 导入 pandas 模块,用于数据处理。import pandas as pd# 从 scipy.ndimage 模块导入 gaussian_filter1d 函数,用于平滑数据。from scipy.ndimage import gaussian_filter1d# 这段代码主要负责绘制每个超参数的散点图。# Scatter plots for each hyperparameter# 将传入的 csv_file 参数转换为 Path 对象,以便后续进行路径操作。 Path 是 pathlib 模块中的一个类,提供了一种面向对象的方式来处理文件系统路径。csv_file = Path(csv_file)# 使用 pandas 库的 read_csv 函数读取 CSV 文件的内容,并将其存储在 data 变量中。 data 是一个 DataFrame 对象,它是 pandas 中用于存储表格数据的主要数据结构。data = pd.read_csv(csv_file)# 设置 num_metrics_columns 变量的值为 1,表示数据中有一个指标列(如 fitness)。这意味着在数据的列中,第一列是指标列,其余列是超参数。num_metrics_columns = 1# 使用列表推导式和切片操作提取数据列名。# data.columns 获取 DataFrame 的列名列表。 x.strip() 去除每个列名前后的空格。 [num_metrics_columns:] 切片操作从第 num_metrics_columns 列开始获取列名,即跳过第一列(指标列),获取所有超参数的列名。keys = [x.strip() for x in data.columns][num_metrics_columns:]# 将 DataFrame 对象 data 转换为 NumPy 数组 x 。 data.values 返回 DataFrame 中的数据部分,不包括列名和索引。x = data.values# 从 NumPy 数组 x 中提取第一列数据,即 fitness 值,并将其存储在 fitness 变量中。 x[:, 0] 表示获取 x 的所有行的第一列。fitness = x[:, 0]  # fitness# 使用 numpy 的 argmax 函数找到 fitness 数组中最大值的索引,并将其存储在 j 变量中。这个索引表示 fitness 值最大的超参数组合在数据中的位置。j = np.argmax(fitness)  # max fitness index# 计算绘图的行数和列数。 len(keys) 获取超参数的数量, ** 0.5 计算平方根, math.ceil 向上取整。这样计算出的 n 值使得绘图尽可能接近正方形,以便在网格布局中均匀地展示每个超参数的散点图。n = math.ceil(len(keys) ** 0.5)  # columns and rows in plot# 创建一个新的 Matplotlib 图像窗口,设置窗口大小为 10x10 英寸,并启用紧凑布局模式。 tight_layout=True 会自动调整子图参数,使得子图之间的间距和布局更加紧凑,避免重叠。plt.figure(figsize=(10, 10), tight_layout=True)# 遍历每个超参数的列名 k 和索引 i 。for i, k in enumerate(keys):# 从 x 中提取当前超参数的值。由于 num_metrics_columns 是 1,所以 i + num_metrics_columns 表示当前超参数在 x 中的列索引。v = x[:, i + num_metrics_columns]# 获取当前超参数在 fitness 最大值对应的索引 j 处的值,即最佳值。mu = v[j]  # best single result# 创建一个子图, n 行 n 列,当前子图的位置是 i + 1 。plt.subplot(n, n, i + 1)# 绘制当前超参数的散点图。 v 是超参数值, fitness 是对应的 fitness 值, cmap="viridis" 设置颜色映射为 "viridis", alpha=0.8 设置透明度为 0.8, edgecolors="none" 设置无边缘颜色。plt_color_scatter(v, fitness, cmap="viridis", alpha=0.8, edgecolors="none")# 在散点图上绘制一个黑色的 "+" 标记,表示最佳值点。 mu 是最佳值, fitness.max() 是最大 fitness 值, markersize=15 设置标记大小为 15。plt.plot(mu, fitness.max(), "k+", markersize=15)# plt.title(label, fontdict=None, loc=None, pad=None, **kwargs)# 参数 :# label :标题文本的字符串。# fontdict :一个字典,包含字体属性的键值对,如字体大小、字体类型等。# loc :标题的位置,可以是 'left' 、 'center' 或 'right' 。# pad :标题与绘图区域之间的间距,单位为点(1/72 英寸)。# **kwargs :其他可选参数,如字体颜色等。# 设置子图的标题为当前超参数的名称和最佳值,使用格式化字符串限制标题长度。plt.title(f"{k} = {mu:.3g}", fontdict={"size": 9})  # limit to 40 characters# matplotlib.pyplot.tick_params(axis='both', **kwargs)# plt.tick_params() 函数是 Matplotlib 库中用于设置刻度线、刻度标签和网格线外观的一个函数。# 参数说明 :# axis : {'x', 'y', 'both'}, 默认为 'both' 指定要应用参数的轴。可以是 'x'、'y' 或 'both',表示分别对 X 轴、Y 轴或两者同时进行设置。# which : {'major', 'minor', 'both'}, 默认为 'major' 指定要应用参数的刻度类型。可以是 'major'、'minor' 或 'both',表示分别对主刻度、次刻度或两者同时进行设置。# reset : bool, 默认为 False 如果为 True,则在处理其他关键字参数之前将所有参数设置为默认值。# direction : {'in', 'out', 'inout'} 设置刻度线的方向,可以是 'in'(指向轴内)、'out'(指向轴外)或 'inout'(同时指向轴内和轴外)。# length : float 刻度线的长度,单位为点(points)。# width : float 刻度线的宽度,单位为点(points)。# color : 颜色 刻度线的颜色,可以是任何 Matplotlib 支持的颜色格式,如颜色名称、十六进制颜色代码等。# pad : float 刻度线与刻度标签之间的距离,单位为点(points)。# labelsize : float 或 str 刻度标签的字体大小,可以是具体的数值(以点为单位)或字符串(如 'small'、'medium'、'large' 等)。# labelcolor : 颜色 刻度标签的颜色,可以是任何 Matplotlib 支持的颜色格式。# colors : 颜色 同时设置刻度线和刻度标签的颜色,可以是任何 Matplotlib 支持的颜色格式。# zorder : float 刻度线和刻度标签的 zorder,用于控制它们在图层中的顺序。# bottom, top, left, right : bool 分别表示是否在 X 轴的底部、顶部或 Y 轴的左侧、右侧绘制刻度线,True 表示绘制,False 表示不绘制。# labelbottom, labeltop, labelleft, labelright : bool 分别表示是否在 X 轴的底部、顶部或 Y 轴的左侧、右侧绘制刻度标签,True 表示绘制,False 表示不绘制。# labelrotation : float 刻度标签的旋转角度,单位为度(degrees)。# rid_color : 颜色 网格线的颜色,可以是任何 Matplotlib 支持的颜色格式。# grid_alpha : float 网格线的透明度,取值范围为 0(完全透明)到 1(完全不透明)。# grid_linewidth : float 网格线的宽度,单位为点(points)。# grid_linestyle : str 网格线的样式,可以是任何有效的 Line2D 线型规范,如 '-'(实线)、'--'(虚线)等。# 设置 x 轴和 y 轴的刻度标签大小为 8。plt.tick_params(axis="both", labelsize=8)  # Set axis label size to 8# 如果当前子图不是每行的第一个子图,则隐藏 y 轴的刻度标签,以避免重复显示。if i % n != 0:plt.yticks([])# 使用 Path 对象的 with_name 方法生成一个新的文件名,将原始 CSV 文件名替换为 "tune_scatter_plots.png",作为散点图图像的文件名。file = csv_file.with_name("tune_scatter_plots.png")  # filename# 保存当前图像到文件 file ,设置图像分辨率为 200 DPI(每英寸点数)。plt.savefig(file, dpi=200)# 关闭当前图像窗口,释放资源。plt.close()# 使用 LOGGER 记录一条信息日志,内容为 "Saved {file}",表示散点图图像已成功保存到文件 file 。 LOGGER 是一个日志记录器对象,通常用于记录程序运行过程中的信息、警告和错误等日志消息。LOGGER.info(f"Saved {file}")# Fitness vs iteration# 创建一个范围对象 x ,表示迭代次数。 range(1, len(fitness) + 1) 生成从 1 到 len(fitness) 的整数序列,假设 fitness 数组的长度为 n ,则 x 包含从 1 到 n 的整数。这样, x 和 fitness 数组的元素数量相同,可以一一对应地表示每次迭代的 fitness 值。x = range(1, len(fitness) + 1)# 创建一个新的 Matplotlib 图像窗口,设置窗口大小为 10x6 英寸,并启用紧凑布局模式。 tight_layout=True 会自动调整子图参数,使得图表的布局更加紧凑,避免标题、轴标签等元素与图表内容重叠或被截断。plt.figure(figsize=(10, 6), tight_layout=True)# 绘制原始 fitness 随迭代次数变化的折线图。 x 是迭代次数, fitness 是对应的 fitness 值。 marker="o" 指定使用圆形标记每个数据点, linestyle="none" 表示不绘制线条,只显示标记点。 label="fitness" 设置图例标签为 "fitness",表示这条折线代表原始的 fitness 值。plt.plot(x, fitness, marker="o", linestyle="none", label="fitness")# 绘制经过高斯滤波平滑处理后的 fitness 随迭代次数变化的折线图。 gaussian_filter1d(fitness, sigma=3) 使用高斯滤波函数对 fitness 数组进行平滑处理, sigma=3 是高斯滤波的标准差参数,决定了平滑的程度。 ":" 指定使用点划线样式, label="smoothed" 设置图例标签为 "smoothed",表示这条折线代表平滑后的 fitness 值。 linewidth=2 设置线条宽度为 2,使得平滑线更加明显。plt.plot(x, gaussian_filter1d(fitness, sigma=3), ":", label="smoothed", linewidth=2)  # smoothing line# 设置图表的标题为 "Fitness vs Iteration",表示图表展示的是 fitness 随迭代次数的变化情况。plt.title("Fitness vs Iteration")# 设置 x 轴的标签为 "Iteration",表示 x 轴代表迭代次数。plt.xlabel("Iteration")# 设置 y 轴的标签为 "Fitness",表示 y 轴代表 fitness 值。plt.ylabel("Fitness")# 显示图表的网格线。 True 表示启用网格线,使得图表更容易观察数据点的位置和趋势。plt.grid(True)# 显示图表的图例。图例会根据之前设置的 label 参数自动显示,帮助区分不同的折线(原始 fitness 和平滑后的 fitness)。plt.legend()# Path.with_name(name)# Path.with_name() 方法是 pathlib 模块中 Path 类的一个方法,用于更改路径的文件名(不包括扩展名)。# 参数 :# name :一个新的文件名字符串,这个方法会用这个新的文件名替换原始路径对象的文件名。# 返回值 :# 返回一个新的 Path 对象,其文件名已被更改为指定的 name ,而保持其他部分(如目录路径和扩展名)不变。# 使用 Path 对象的 with_name 方法生成一个新的文件名,将原始 CSV 文件名替换为 "tune_fitness.png",作为 fitness 图像的文件名。file = csv_file.with_name("tune_fitness.png")  # filename# 保存当前图像到文件 file ,设置图像分辨率为 200 DPI(每英寸点数)。这样可以确保保存的图像具有较好的清晰度和质量。plt.savefig(file, dpi=200)# 关闭当前图像窗口,释放资源。这一步是必要的,因为在绘制多个图像时,如果不关闭当前图像,可能会导致内存占用增加或图像显示异常。plt.close()# 使用 LOGGER 记录一条信息日志,内容为 "Saved {file}",表示 fitness 图像已成功保存到文件 file 。 LOGGER 是一个日志记录器对象,通常用于记录程序运行过程中的信息、警告和错误等日志消息。LOGGER.info(f"Saved {file}")
# 该函数的主要功能是绘制调参结果的可视化图像,包括每个超参数的散点图和 fitness 随迭代次数变化的图像。它通过读取 CSV 文件中的数据,提取超参数和 fitness 值,然后使用 Matplotlib 进行绘图。散点图展示了每个超参数与 fitness 的关系,并标记出最佳值点。fitness 图像则展示了 fitness 随迭代次数的变化趋势,并通过高斯滤波平滑处理来更清晰地观察趋势。最后,函数将绘制的图像保存为 PNG 文件,并记录保存信息。

10.def output_to_target(output, max_det=300): 

# 这段代码定义了一个名为 output_to_target 的函数,它将模型的输出转换为一个目标格式,以便于后续处理。
# 定义函数 output_to_target ,它接受两个参数。
# 1.output :模型的输出。
# 2.max_det :每个图像中最多检测到的目标数量,默认值为300。
def output_to_target(output, max_det=300):# 将模型输出转换为目标格式 [batch_id、class_id、x、y、w、h、conf] 以进行绘图。"""Convert model output to target format [batch_id, class_id, x, y, w, h, conf] for plotting."""# 初始化一个空列表 targets ,用于存储转换后的目标数据。targets = []# 遍历模型输出 output 中的每个元素 o ,同时获取索引 i 。这里假设 output 是一个列表或可迭代对象,每个元素 o 对应一个图像的检测结果。for i, o in enumerate(output):# o[:max_det, :6] :从 o 中提取前 max_det 个检测结果的前6个元素,这通常包括边 界框的坐标 、 置信度 和 类别 。# .cpu() :将提取的数据从GPU(如果使用的话)转移到CPU,以便于后续操作。# .split((4, 1, 1), 1) :将提取的数据按列分割为三部分, 边界框坐标 box (4列)、 置信度 conf (1列)和 类别 cls (1列)。box, conf, cls = o[:max_det, :6].cpu().split((4, 1, 1), 1)# 创建一个形状为 (conf.shape[0], 1) 的张量 j ,其中每个元素的值都为 i 。这个张量用于记录每个检测结果对应的图像索引。j = torch.full((conf.shape[0], 1), i)# torch.cat((j, cls, ops.xyxy2xywh(box), conf), 1) :将 j 、 cls 、转换后的边界框坐标 ops.xyxy2xywh(box) 和 conf 按列拼接在一起,形成一个包含图像索引、类别、边界框坐标和置信度的完整目标数据。# targets.append(...) :将拼接好的目标数据添加到列表 targets 中。targets.append(torch.cat((j, cls, ops.xyxy2xywh(box), conf), 1))# torch.cat(targets, 0) :将列表 targets 中的所有目标数据按行拼接在一起,形成一个完整的张量。# .numpy() :将张量转换为NumPy数组,以便于后续处理。targets = torch.cat(targets, 0).numpy()# 返回四个值 : targets[:, 0] 图像索引。 targets[:, 1] 类别。 targets[:, 2:-1] 边界框坐标(转换为XYWH格式)。 targets[:, -1] 置信度。return targets[:, 0], targets[:, 1], targets[:, 2:-1], targets[:, -1]
# 这个函数的主要作用是将模型的输出转换为一个包含图像索引、类别、边界框坐标和置信度的目标格式。它首先提取每个图像的检测结果,然后将边界框坐标从XYXY格式转换为XYWH格式,并将所有目标数据拼接在一起,最后返回这些数据。这个过程有助于后续的目标检测任务,如评估模型性能或进一步的数据处理。

11.def output_to_rotated_target(output, max_det=300): 

# 这段代码定义了一个名为 output_to_rotated_target 的函数,其作用是将模型的输出转换为旋转目标检测的格式。
# 定义了一个名为 output_to_rotated_target 的函数,它接受两个参数。
# 1.output :一个包含多个检测结果的列表或张量。
# 2.max_det :一个可选参数,表示每个检测结果中最多保留的检测框数量,默认为300。
def output_to_rotated_target(output, max_det=300):# 将模型输出转换为目标格式 [batch_id、class_id、x、y、w、h、conf] 以进行绘图。"""Convert model output to target format [batch_id, class_id, x, y, w, h, conf] for plotting."""# 初始化一个空列表 targets ,用于存储处理后的检测结果。targets = []# 遍历 output 中的每个检测结果 o ,同时获取其索引 i 。 enumerate 函数用于同时获取索引和元素值。for i, o in enumerate(output):# 对当前检测结果 o 进行处理 :# o[:max_det] :取检测结果的前 max_det 个检测框,以限制检测框数量。# .cpu() :将检测结果从GPU(如果在GPU上)移动到CPU,以便后续操作。# .split((4, 1, 1, 1), 1) :将检测结果按照指定的维度进行分割。这里将检测结果分割为四个部分: box (检测框坐标,4个值)、 conf (置信度,1个值)、 cls (类别,1个值)和 angle (旋转角度,1个值)。 1 表示沿着第1维(即检测框的维度)进行分割。box, conf, cls, angle = o[:max_det].cpu().split((4, 1, 1, 1), 1)# 创建一个形状为 (conf.shape[0], 1) 的张量 j ,其所有元素值都为当前检测结果的索引 i 。 conf.shape[0] 表示当前检测结果中检测框的数量。这个张量用于记录每个检测框属于哪个检测结果。j = torch.full((conf.shape[0], 1), i)# 将当前检测结果的各个部分拼接成一个新的张量,并将其添加到 targets 列表中。# torch.cat((j, cls, box, angle, conf), 1) :将 j 、 cls 、 box 、 angle 和 conf 沿着第1维(即列)进行拼接。拼接后的张量包含了检测框的 索引 、 类别 、 坐标 、 旋转角度 和 置信度 。targets.append(torch.cat((j, cls, box, angle, conf), 1))# 将 targets 列表中的所有张量拼接成一个大的张量,并将其转换为NumPy数组。# torch.cat(targets, 0) :将 targets 列表中的张量沿着第0维(即行)进行拼接,得到一个包含所有检测结果的张量。# .numpy() :将拼接后的张量转换为NumPy数组,以便后续使用。targets = torch.cat(targets, 0).numpy()# 返回处理后的检测结果的各个部分。 targets[:, 0] 检测框的索引。 targets[:, 1] 检测框的类别。 targets[:, 2:-1] 检测框的坐标(旋转矩形的坐标)。 targets[:, -1] 检测框的置信度。return targets[:, 0], targets[:, 1], targets[:, 2:-1], targets[:, -1]
# 该函数的主要作用是将检测模型的输出结果进行处理和重组,以便后续的使用和分析。它首先对每个检测结果进行限制和分割,提取出检测框的索引、类别、坐标、旋转角度和置信度等信息,然后将这些信息拼接成一个新的张量,并将其转换为NumPy数组。最后,函数返回检测框的索引、类别、坐标和置信度,方便后续的处理和分析。

12.def feature_visualization(x, module_type, stage, n=32, save_dir=Path("runs/detect/exp")): 

# 这段代码定义了一个名为 feature_visualization 的函数,它用于可视化深度学习模型中的特征图。
# 这行定义了 feature_visualization 函数,它接受以下参数 :
# 1.x :模型层的输出特征图。
# 2.module_type :产生特征图的模块类型。
# 3.stage :特征图所在的网络阶段。
# 4.n (默认为 32) :要可视化的特征图数量。
# 5.save_dir (默认为 Path("runs/detect/exp") ) :保存可视化结果的目录路径。
def feature_visualization(x, module_type, stage, n=32, save_dir=Path("runs/detect/exp")):# 在推理过程中可视化给定模型模块的特征图。"""Visualize feature maps of a given model module during inference.Args:x (torch.Tensor): Features to be visualized.module_type (str): Module type.stage (int): Module stage within the model.n (int, optional): Maximum number of feature maps to plot. Defaults to 32.save_dir (Path, optional): Directory to save results. Defaults to Path('runs/detect/exp')."""# 检查 module_type 中是否包含某些特定的模块名称(如 "Detect", "Pose", "Segment"),如果包含,则函数直接返回,不执行可视化操作。for m in ["Detect", "Pose", "Segment"]:if m in module_type:return# 解包输入特征图 x 的形状,获取 批次大小 、 通道数 、 高度 和 宽度 。_, channels, height, width = x.shape  # batch, channels, height, width# 检查特征图的高度和宽度是否大于 1,即特征图是否具有空间维度。if height > 1 and width > 1:# 如果特征图具有空间维度,构造保存特征图的文件名,并将其保存在 save_dir 指定的目录下。f = save_dir / f"stage{stage}_{module_type.split('.')[-1]}_features.png"  # filename# 从特征图 x 中选择第一个批次的元素,并按照通道数将其分割成多个块。blocks = torch.chunk(x[0].cpu(), channels, dim=0)  # select batch index 0, block by channels# 确定要可视化的特征图数量,不超过通道数。n = min(n, channels)  # number of plots# 创建一个子图,其行数为 n/8 的上限值,列数为 8,用于排列特征图。_, ax = plt.subplots(math.ceil(n / 8), 8, tight_layout=True)  # 8 rows x n/8 cols# 将子图的轴( ax )展平,并调整子图之间的间距。ax = ax.ravel()# plt.subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=None, hspace=None, aspect=None)# plt.subplots_adjust() 是 matplotlib 库中的一个函数,用于调整子图之间的间距以及子图与图形边缘之间的间距。这个函数允许用户自定义子图布局的参数,以便在显示多个子图时获得更好的视觉效果。# 参数 :# left :子图左边缘到图形左边缘的距离,以相对位置表示(0-1之间的值)。# bottom :子图底部边缘到图形底部边缘的距离,以相对位置表示。# right :子图右边缘到图形右边缘的距离,以相对位置表示。# top :子图顶部边缘到图形顶部边缘的距离,以相对位置表示。# wspace :子图之间的水平间距。可以是绝对值(如0.05英寸)或相对值(0-1之间的值)。# hspace :子图之间的垂直间距。可以是绝对值或相对值。# aspect :子图宽高比的调整因子。# 功能描述 :# plt.subplots_adjust() 函数的主要功能是微调子图的布局。通过调整上述参数,用户可以控制子图之间的间距以及子图与图形边缘之间的间距,从而优化图形的整体外观。# 使用 plt.subplots_adjust() 可以帮助你创建更加清晰和专业的图形布局,特别是在你的图形包含多个子图时。plt.subplots_adjust(wspace=0.05, hspace=0.05)# 遍历每个特征图块,使用 imshow 函数显示图像,并关闭坐标轴。for i in range(n):# plt.imshow(X, cmap=None, norm=None, aspect=None, interpolation=None, alpha=None, vmin=None, vmax=None, origin=None, extent=None, shape=None, filternorm=True, filterrad=4.0, imlim=None, resample=None, url=None, **kwargs)# imshow() 函数是 Matplotlib 库中的一个函数,用于在 Pyplot 模块中显示图像。这个函数可以将图像数据直接显示在图表中,常用于数据可视化。# imshow() 函数是 Matplotlib 库中的一个函数,用于显示图像。# imshow() 函数常用于绘制二维的灰度图像或彩色图像。# imshow() 函数可用于绘制矩阵、热力图、地图等。# 参数 :# X :要显示的图像数据,可以是二维数组或三维数组(后者的最后一个维度表示颜色通道)。# cmap :颜色映射表,用于指定颜色。例如, 'gray' 、 'viridis' 、 'plasma' 等。# norm :归一化对象,用于调整数据值到颜色映射表。# aspect :设置图像的纵横比,可以是数值或字符串 'auto' 、 'equal' 。# interpolation :插值方法,用于指定图像缩放时的插值算法,如 'spline36' 、 'nearest' 、 'bilinear' 等。# alpha :图像的透明度。# vmin 和 vmax :设置数据值映射到颜色映射表的最小值和最大值。# origin :设置图像原点位置,可以是 'upper' 或 'lower' 。# extent :设置图像的边界框,以坐标对列表形式给出。# shape :用于指定输出图像的形状。# filternorm 和 filterrad :用于图像滤波的对象。可以设置为None、antigrain、freetype等。# imlim :用于指定图像显示范围。# resample :用于指定图像重采样方式。# url :用于指定图像链接。# imshow() 函数非常灵活,可以根据需要调整各种参数来优化图像的显示效果。ax[i].imshow(blocks[i].squeeze())  # cmap='gray'ax[i].axis("off")# 记录一条信息,指示正在保存的特征图文件及其数量。LOGGER.info(f"Saving {f}... ({n}/{channels})")    # 正在保存 {f}... ({n}/{channels}) 。# 码保存可视化的特征图为 PNG 文件,并关闭绘图窗口。plt.savefig(f, dpi=300, bbox_inches="tight")plt.close()# 将第一个批次的特征图数据保存为 NumPy 文件( .npy 格式)。np.save(str(f.with_suffix(".npy")), x[0].cpu().numpy())  # npy save
# feature_visualization 函数用于将模型中的特征图可视化并保存为图像文件和 NumPy 文件。它首先检查是否需要跳过可视化,然后处理特征图数据,并使用 Matplotlib 创建子图来显示特征图。最后,它保存图像和 NumPy 文件,并记录相关信息。这个函数对于理解模型在不同阶段的特征表示非常有用。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/68013.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HTML5实现好看的端午节网页源码

HTML5实现好看的端午节网页源码 前言一、设计来源1.1 网站首页界面1.2 登录注册界面1.3 端午节由来界面1.4 端午节习俗界面1.5 端午节文化界面1.6 端午节美食界面1.7 端午节故事界面1.8 端午节民谣界面1.9 联系我们界面 二、效果和源码2.1 动态效果2.2 源代码 源码下载结束语 H…

net-http-transport 引发的句柄数(协程)泄漏问题

Reference 关于 Golang 中 http.Response.Body 未读取导致连接复用问题的一点研究https://manishrjain.com/must-close-golang-http-responsehttps://www.reddit.com/r/golang/comments/13fphyz/til_go_response_body_must_be_closed_even_if_you/?rdt35002https://medium.co…

关于husky8.0 与 4.0的配置

husky的场景使用很多&#xff0c;一般大多场景是在配置git commit 命令拦截hook, 校验 commit-msg 格式规范。以下环境默认&#xff1a;git > 2.27.0, node >14 1、安装huskey8.0.1 npm install --save-dev husky8.0.1 2、初始化配置文件 在package.json scripts 属性…

CV(9)--迁移学习

前言 仅记录学习过程&#xff0c;有问题欢迎讨论 fine-tuning 在实践中&#xff0c;由于数据集不够大&#xff0c;很少有人从头开始训练网络。常见的做法是使用预训练的网络 &#xff08;例如在ImageNet上训练的分类1000类的网络&#xff09;来重新fine-tuning&#xff08;也…

LS1046+XILINX XDMA PCIE调通

欢迎点赞收藏&#xff0c;欢迎私下讨论技术&#xff0c;分享技术 硬件平台 &#xff1a;NXP LS1046 XILINX FPGA 软件平台&#xff1a;LINUX 4.19.68 buildroot LS1046 PEX3 接 XILINX FPGA&#xff0c;linux使用designware的PCI主控制器。下载XILINX DMA驱动&#xff0c;解…

C语言gdb调试

目录 1.gdb介绍 2.设置断点 2.1.测试代码 2.2.设置函数断点 2.3.设置文件行号断点 2.4.设置条件断点 2.5.多线程调试 3.删除断点 3.1.删除指定断点 3.2.删除全部断点 4.查看变量信息 4.1.p命令 4.2.display命令 4.3.watch命令 5.coredump日志 6.总结 1.gdb介绍…

[程序设计]—代理模式

[程序设计]—代理模式&#x1f473; 本文章记录学习于——52.面向切面&#xff1a;AOP-场景模拟_哔哩哔哩_bilibili 最近闲来无事&#xff0c;在学习Spring的源码&#xff1a; 后面慢慢更新源码系列blog&#xff0c;希望多多关注&#x1f64f;&#x1f64f; 目前已经总结的b…

我的128天创作之路:回顾与展望

大家好呀&#xff01;今天来和你们分享一下我的创作历程&#x1f601;。 一、机缘 最开始创作呢&#xff0c;是因为在学习 C 的 STL 时&#xff0c;像 string、list、vector 这些模板可把我折腾得够呛&#xff0c;但也让我学到了超多东西&#xff01;我就想&#xff0c;要是把我…

RAID储存技术

RAID独立磁盘冗余技术是一种把2个或者多个HDD或SSD合并为一个协调的存储单元或列阵&#xff0c;从而预防数据丢失的技术&#xff0c;其最早由加州大学伯克利分校的计算机科学家David Patterson、Garth Gibson和Randy Katz在1987年提出。他们的研究论文“关于RAID的论证”提出了…

Openstack持久存储-Swift,Cinder,Manila三者之间的区别

总结不易&#xff0c;给个三连吧&#xff01;&#xff01;&#xff01; 补充&#xff1a; 文件共享存储服务Manila 在OpenStack生态系统中&#xff0c;Cinder和Manila分别提供了两种不同类型的存储服务&#xff0c;类似于传统的SAN&#xff08;存储区域网络&#xff09;和NAS&…

软件测试预备知识④—NTFS权限管理、磁盘配额与文件共享

在软件测试的实际环境搭建与管理过程中&#xff0c;了解和掌握NTFS权限管理、磁盘配额以及文件共享等知识至关重要。这些功能不仅影响系统的安全性和稳定性&#xff0c;还对测试数据的存储、访问以及多用户协作测试有着深远的影响。 一、NTFS权限管理 1.1 NTFS简介 NTFS&am…

PyTorch深度学习CNN神经网络ResNet、DenseNet在CIFAR图像数据集分类应用与分析

全文链接&#xff1a;https://tecdat.cn/?p38782 在当今深度学习领域&#xff0c;卷积神经网络&#xff08;CNN&#xff09;架构不断发展与创新&#xff0c;诸多先进的架构被提出并广泛应用。像GoogleNet&#xff08;ILSVRC 2014获胜者&#xff09;、ResNet&#xff08;ILSVRC…

word论文排版常见问题汇总

word论文排版常见问题汇总 常用快捷键&#xff1a; Alt F9 正常模式与域代码模式切换 Ctrl F9 插入域代码 F9 刷新域代码显示&#xff0c;要注意选定后刷新才会有效果 word中在当前列表的基础上修改列表 在使用word时&#xff0c;我们会定义一个列表&#xff0c;并将其链接…

【Rust】数据类型

目录 思维导图 1. 数据类型概述 1.1 标量类型 1.1.1 整数类型 1.1.2 浮点数类型 1.1.3 布尔类型 1.1.4 字符类型 1.2 复合类型 1.2.1 元组类型 1.2.2 数组类型 2. 类型注解与类型推断 3. 整数溢出处理 4. 数字运算 5. 示例 思维导图 1. 数据类型概述 Rust是一种静…

Proteus-8086调试汇编格式的一点心得

这阵子开始做汇编的微机实验&#xff08;微机原理与接口技术题解及实验指导&#xff0c;吴宁版本13章&#xff09;&#xff0c;中间出了挺多问题&#xff0c;解决后记录下。 先上电路图 用子电路来仿真发现仿真的时候子电路这块根本没有高低电平输出&#xff0c;只好把子电路拿…

跨界融合:人工智能与区块链如何重新定义数据安全?

引言&#xff1a;数据安全的挑战与现状 在信息化驱动的数字化时代&#xff0c;数据已成为企业和个人最重要的资产之一。然而&#xff0c;随着网络技术的逐步优化和数据量的爆发式增长&#xff0c;数据安全问题也愈变突出。 数据安全现状&#xff1a;– 数据泄露驱动相关事件驱…

机器人碳钢去毛刺,用大扭去毛刺主轴可轻松去除

在碳钢精密加工的最后阶段&#xff0c;去除毛刺是确保产品质量的关键步骤。面对碳钢这种硬度较高的材料&#xff0c;采用大扭矩的SycoTec去毛刺主轴&#xff0c;成为了行业内的高效解决方案。SycoTec作为精密加工领域的领军品牌&#xff0c;其生产的高速电主轴以其卓越的性能&a…

大疆上云API连接遥控器和无人机

文章目录 1、部署大疆上云API关于如何连接我们自己部署的上云API2、开启无人机和遥控器并连接自己部署的上云API如果遥控器和无人机没有对频的情况下即只有遥控器没有无人机的情况下如果遥控器和无人机已经对频好了的情况下 4、订阅无人机或遥控器的主题信息4.1、订阅无人机实时…

[OPEN SQL] 限定选择行数

本次操作使用的数据库表为SCUSTOM&#xff0c;其字段内容如下所示 航班用户(SCUSTOM) 该数据库表中的部分值如下所示 指定查询多少行数据&#xff0c;我们可以使用语法UP TO n ROWS来实现对数据前n项的查询 语法格式 SELECT * FROM <dbtab> UP TO n ROWS 参数说明 db…

机器视觉3-线性分类器

机器视觉3-线性分类器 前言一、整体流程二、其他相关内容 图像的表示图像类型黑白图像灰度图像彩色图像 图像表示为向量一、基本概念二、表示方法三、优点四、局限性五、应用场景 线性分类器一、神经网络的层级结构形成非线性模型二、支撑向量机的高维映射形成非线性模型 线性分…