ultralytics\engine\results.py
目录
results.py
1.所需的库和模块
2.class BaseTensor(SimpleClass):
3.class Results(SimpleClass):
4.class Boxes(BaseTensor):
5.class Masks(BaseTensor):
6.class Keypoints(BaseTensor):
7.class Probs(BaseTensor):
8.class OBB(BaseTensor):
# Ultralytics AGPL-3.0 License - https://ultralytics.com/license
"""
Ultralytics Results, Boxes and Masks classes for handling inference results.
Usage: See https://docs.ultralytics.com/modes/predict/
"""
from copy import deepcopy
from functools import lru_cache
from pathlib import Path
import numpy as np
import torch
from ultralytics.data.augment import LetterBox
from ultralytics.utils import LOGGER, SimpleClass, ops
from ultralytics.utils.checks import check_requirements
from ultralytics.utils.plotting import Annotator, colors, save_one_box
from ultralytics.utils.torch_utils import smart_inference_mode
# 这段代码定义了 BaseTensor 类,一个用于封装和操作张量( torch.Tensor 或 np.ndarray )的基础类。它提供了对张量的基本操作,如形状查询、设备转换、数据类型转换以及索引访问,同时保留了原始数据的形状信息。
# 定义了一个名为 BaseTensor 的类,继承自 SimpleClass 。 SimpleClass 可能是一个基础类,提供了额外的通用功能。
class BaseTensor(SimpleClass):
# 带有附加方法的基本张量类,便于操作和设备处理。
# 方法:
# cpu:返回存储在 CPU 内存中的张量的副本。
# numpy:返回张量的副本作为 numpy 数组。
# cuda:将张量移动到 GPU 内存,必要时返回新实例。
# to:返回具有指定设备和 dtype 的张量的副本。
"""
Base tensor class with additional methods for easy manipulation and device handling.
Attributes:
data (torch.Tensor | np.ndarray): Prediction data such as bounding boxes, masks, or keypoints.
orig_shape (Tuple[int, int]): Original shape of the image, typically in the format (height, width).
Methods:
cpu: Return a copy of the tensor stored in CPU memory.
numpy: Returns a copy of the tensor as a numpy array.
cuda: Moves the tensor to GPU memory, returning a new instance if necessary.
to: Return a copy of the tensor with the specified device and dtype.
Examples:
>>> import torch
>>> data = torch.tensor([[1, 2, 3], [4, 5, 6]])
>>> orig_shape = (720, 1280)
>>> base_tensor = BaseTensor(data, orig_shape)
>>> cpu_tensor = base_tensor.cpu()
>>> numpy_array = base_tensor.numpy()
>>> gpu_tensor = base_tensor.cuda()
"""
# 定义了类的初始化方法,接收两个参数。
# 1.data :张量数据,可以是 torch.Tensor 或 np.ndarray 。
# 2.orig_shape :原始数据的形状,用于记录数据的初始形状。
def __init__(self, data, orig_shape) -> None:
# 使用预测数据和图像的原始形状初始化 BaseTensor。
"""
Initialize BaseTensor with prediction data and the original shape of the image.
Args:
data (torch.Tensor | np.ndarray): Prediction data such as bounding boxes, masks, or keypoints.
orig_shape (Tuple[int, int]): Original shape of the image in (height, width) format.
Examples:
>>> import torch
>>> data = torch.tensor([[1, 2, 3], [4, 5, 6]])
>>> orig_shape = (720, 1280)
>>> base_tensor = BaseTensor(data, orig_shape)
"""
# 使用 assert 语句确保 data 是 torch.Tensor 或 np.ndarray 类型。 如果类型不匹配,抛出错误 "data must be torch.Tensor or np.ndarray" 。
assert isinstance(data, (torch.Tensor, np.ndarray)), "data must be torch.Tensor or np.ndarray" # 数据必须是 torch.Tensor 或 np.ndarray 。
# 将 传入的张量数据 存储在 self.data 属性中。
self.data = data
# 将 原始数据的形状 存储在 self.orig_shape 属性中。
self.orig_shape = orig_shape
# 定义了一个属性 shape ,用于获取张量的形状。
# 使用 @property 装饰器,使得 shape 可以像属性一样访问,而不需要调用方法。
@property
def shape(self):
# 返回底层数据张量的形状。
"""
Returns the shape of the underlying data tensor.
Returns:
(Tuple[int, ...]): The shape of the data tensor.
Examples:
>>> data = torch.rand(100, 4)
>>> base_tensor = BaseTensor(data, orig_shape=(720, 1280))
>>> print(base_tensor.shape)
(100, 4)
"""
# 返回张量的形状。如果 self.data 是 torch.Tensor 或 np.ndarray , self.data.shape 是一个元组,表示张量的维度。
return self.data.shape
# 定义了一个方法 cpu ,用于将张量移动到 CPU 设备。
def cpu(self):
# 返回存储在 CPU 内存中的张量的副本。
"""
Returns a copy of the tensor stored in CPU memory.
Returns:
(BaseTensor): A new BaseTensor object with the data tensor moved to CPU memory.
Examples:
>>> data = torch.tensor([[1, 2, 3], [4, 5, 6]]).cuda()
>>> base_tensor = BaseTensor(data, orig_shape=(720, 1280))
>>> cpu_tensor = base_tensor.cpu()
>>> isinstance(cpu_tensor, BaseTensor)
True
>>> cpu_tensor.data.device
device(type='cpu')
"""
# __class__
# __class__ 是 Python 中的一个特殊属性,它指向对象所属的类。每个 Python 对象都有一个 __class__ 属性,指向该对象的类。这个属性通常用于动态地引用类的属性或方法,或者在不知道对象确切类型的情况下创建新实例。
# 以下是 __class__ 属性的一些常见用法 :
# 创建新实例 :
# 当你想要基于当前对象的类来创建一个新实例时,可以使用 __class__ 。这在你想要复制对象或者根据对象的类型动态创建新对象时非常有用。
# new_instance = obj.__class__()
# 调用类方法 :
# 如果你需要动态地调用一个类方法,可以使用 __class__ 来引用类,然后调用该方法。
# result = obj.__class__.class_method()
# 检查类型 :
# 虽然 isinstance() 和 issubclass() 是检查类型的首选方法,但 __class__ 也可以用来检查一个对象的类。
# 继承和多态 :
# 在面向对象编程中, __class__ 属性可以用来实现多态行为,即在子类中重写方法,并在运行时动态地调用适当的方法。
# class Base:
# def method(self):
# pass
# class Derived(Base):
# def method(self):
# super().method()
# obj = Derived()
# obj.__class__.method(obj) # Calls Derived's method, which in turn calls Base's method
# 如果 self.data 是 np.ndarray ,直接返回当前实例(因为 NumPy 数组已经在 CPU 上)。 否则,调用 self.data.cpu() 将张量移动到 CPU,并返回一个新的 BaseTensor 实例,保留原始形状。
return self if isinstance(self.data, np.ndarray) else self.__class__(self.data.cpu(), self.orig_shape)
# 定义了一个方法 numpy ,用于将张量转换为 NumPy 数组。
def numpy(self):
# 以 numpy 数组形式返回张量的副本。
"""
Returns a copy of the tensor as a numpy array.
Returns:
(np.ndarray): A numpy array containing the same data as the original tensor.
Examples:
>>> data = torch.tensor([[1, 2, 3], [4, 5, 6]])
>>> orig_shape = (720, 1280)
>>> base_tensor = BaseTensor(data, orig_shape)
>>> numpy_array = base_tensor.numpy()
>>> print(type(numpy_array))
"""
# 如果 self.data 已经是 np.ndarray ,直接返回当前实例。 否则,调用 self.data.numpy() 将张量转换为 NumPy 数组,并返回一个新的 BaseTensor 实例。
return self if isinstance(self.data, np.ndarray) else self.__class__(self.data.numpy(), self.orig_shape)
# 定义了一个方法 cuda ,用于将张量移动到 GPU 设备(CUDA)。
def cuda(self):
# 将张量移至 GPU 内存。
"""
Moves the tensor to GPU memory.
Returns:
(BaseTensor): A new BaseTensor instance with the data moved to GPU memory if it's not already a
numpy array, otherwise returns self.
Examples:
>>> import torch
>>> from ultralytics.engine.results import BaseTensor
>>> data = torch.tensor([[1, 2, 3], [4, 5, 6]])
>>> base_tensor = BaseTensor(data, orig_shape=(720, 1280))
>>> gpu_tensor = base_tensor.cuda()
>>> print(gpu_tensor.data.device)
cuda:0
"""
# 使用 torch.as_tensor 确保数据是 PyTorch 张量,然后调用 .cuda() 将张量移动到 GPU。 返回一个新的 BaseTensor 实例,保留原始形状。
return self.__class__(torch.as_tensor(self.data).cuda(), self.orig_shape)
# 定义了一个方法 to ,用于将张量移动到指定设备或数据类型。
def to(self, *args, **kwargs):
# 返回具有指定设备和 dtype 的张量的副本。
"""
Return a copy of the tensor with the specified device and dtype.
Args:
*args (Any): Variable length argument list to be passed to torch.Tensor.to().
**kwargs (Any): Arbitrary keyword arguments to be passed to torch.Tensor.to().
Returns:
(BaseTensor): A new BaseTensor instance with the data moved to the specified device and/or dtype.
Examples:
>>> base_tensor = BaseTensor(torch.randn(3, 4), orig_shape=(480, 640))
>>> cuda_tensor = base_tensor.to("cuda")
>>> float16_tensor = base_tensor.to(dtype=torch.float16)
"""
# 使用 torch.as_tensor 确保数据是 PyTorch 张量,然后调用 .to(*args, **kwargs) 将张量移动到指定设备或转换为指定数据类型。 返回一个新的 BaseTensor 实例,保留原始形状。
return self.__class__(torch.as_tensor(self.data).to(*args, **kwargs), self.orig_shape)
# 定义了 __len__ 方法,用于返回张量的长度。 这是一个特殊方法,使得 len(instance) 可以返回张量的长度。
def __len__(self): # override len(results)
# 返回底层数据张量的长度。
"""
Returns the length of the underlying data tensor.
Returns:
(int): The number of elements in the first dimension of the data tensor.
Examples:
>>> data = torch.tensor([[1, 2, 3], [4, 5, 6]])
>>> base_tensor = BaseTensor(data, orig_shape=(720, 1280))
>>> len(base_tensor)
2
"""
# 返回张量的长度。对于多维张量, len(self.data) 返回第一个维度的大小。
return len(self.data)
# 定义了 __getitem__ 方法,用于支持索引访问。 这是一个特殊方法,使得 instance[idx] 可以返回张量的子集。
def __getitem__(self, idx):
# 返回一个包含数据张量的指定索引元素的新 BaseTensor 实例。
"""
Returns a new BaseTensor instance containing the specified indexed elements of the data tensor.
Args:
idx (int | List[int] | torch.Tensor): Index or indices to select from the data tensor.
Returns:
(BaseTensor): A new BaseTensor instance containing the indexed data.
Examples:
>>> data = torch.tensor([[1, 2, 3], [4, 5, 6]])
>>> base_tensor = BaseTensor(data, orig_shape=(720, 1280))
>>> result = base_tensor[0] # Select the first row
>>> print(result.data)
tensor([1, 2, 3])
"""
# 使用索引 idx 获取张量的子集 self.data[idx] 。 返回一个新的 BaseTensor 实例,保留原始形状。
return self.__class__(self.data[idx], self.orig_shape)
# BaseTensor 类是一个封装张量操作的基础类,提供了以下功能。数据封装:支持封装 torch.Tensor 或 np.ndarray ,并记录原始数据的形状。设备转换:支持将张量移动到 CPU 或 GPU 设备。数据类型转换:支持将张量转换为 NumPy 数组。形状查询:通过 shape 属性查询张量的形状。索引访问:支持通过索引访问张量的子集。长度查询:支持通过 len() 查询张量的长度。通用转换:支持通过 to() 方法将张量移动到指定设备或转换为指定数据类型。这种设计使得 BaseTensor 类能够灵活地处理张量数据,同时保留原始形状信息,适用于需要对张量进行多种操作的场景。
# 这段代码定义了一个名为 Results 的类,用于存储和操作从 YOLO 模型中得到的推理结果。该类封装了检测、分割、姿态估计和分类任务的结果处理功能。
# 定义了一个名为 Results 的类,继承自 SimpleClass 。 SimpleClass 可能提供了某些通用功能。
class Results(SimpleClass):
# 用于存储和操作推理结果的类。
# 此类封装了处理 YOLO 模型的检测、分割、姿势估计和分类结果的功能。
# 方法:
# update:使用新的检测结果更新对象属性。
# cpu:返回带有 CPU 内存上所有张量的 Results 对象副本。
# numpy:返回带有所有张量的 Results 对象副本作为 numpy 数组。
# cuda:返回带有 GPU 内存上所有张量的 Results 对象副本。
# to:返回带有指定设备和 dtype 上的张量的 Results 对象副本。
# new:返回具有相同图像、路径和名称的新 Results 对象。
# plot:在输入图像上绘制检测结果,返回带注释的图像。
# show:在屏幕上显示带注释的结果。
# save:将带注释的结果保存到文件。
# verbose:返回每个任务的日志字符串,详细说明检测和分类。
# save_txt:将检测结果保存到文本文件。
# save_crop:保存裁剪后的检测图片。
# tojson:将检测结果转换为 JSON 格式。
"""
A class for storing and manipulating inference results.
This class encapsulates the functionality for handling detection, segmentation, pose estimation,
and classification results from YOLO models.
Attributes:
orig_img (numpy.ndarray): Original image as a numpy array.
orig_shape (Tuple[int, int]): Original image shape in (height, width) format.
boxes (Boxes | None): Object containing detection bounding boxes.
masks (Masks | None): Object containing detection masks.
probs (Probs | None): Object containing class probabilities for classification tasks.
keypoints (Keypoints | None): Object containing detected keypoints for each object.
obb (OBB | None): Object containing oriented bounding boxes.
speed (Dict[str, float | None]): Dictionary of preprocess, inference, and postprocess speeds.
names (Dict[int, str]): Dictionary mapping class IDs to class names.
path (str): Path to the image file.
_keys (Tuple[str, ...]): Tuple of attribute names for internal use.
Methods:
update: Updates object attributes with new detection results.
cpu: Returns a copy of the Results object with all tensors on CPU memory.
numpy: Returns a copy of the Results object with all tensors as numpy arrays.
cuda: Returns a copy of the Results object with all tensors on GPU memory.
to: Returns a copy of the Results object with tensors on a specified device and dtype.
new: Returns a new Results object with the same image, path, and names.
plot: Plots detection results on an input image, returning an annotated image.
show: Shows annotated results on screen.
save: Saves annotated results to file.
verbose: Returns a log string for each task, detailing detections and classifications.
save_txt: Saves detection results to a text file.
save_crop: Saves cropped detection images.
tojson: Converts detection results to JSON format.
Examples:
>>> results = model("path/to/image.jpg")
>>> for result in results:
... print(result.boxes) # Print detection boxes
... result.show() # Display the annotated image
... result.save(filename="result.jpg") # Save annotated image
"""
# 这段代码定义了 Results 类的初始化方法 __init__ ,用于创建类的实例并初始化其属性。
# 定义了 Results 类的初始化方法 __init__ ,它接收以下参数 :
# 1.orig_img : 原始图像,以 NumPy 数组的形式传入。
# 2.path : 图像文件的路径,用于记录图像的来源。
# 3.names : 类别名称的字典,将类别 ID 映射到类别名称(例如 {0: 'person', 1: 'car'} )。
# 4.boxes : 检测框的坐标,格式为 (N, 6) 的张量,包含 (x1, y1, x2, y2, confidence, class) ,默认为 None 。
# 5.masks : 分割掩码,格式为 (N, H, W) 的张量,每个掩码是一个二值图像,用于分割任务,默认为 None 。
# 6.probs : 分类任务的概率,格式为 (num_classes,) 的张量,表示每个类别的概率,默认为 None 。
# 7.keypoints : 检测到的关键点,格式为 (N, K, 3) 的张量,其中 K 是关键点的数量,每个关键点包含 (x, y, visibility) ,默认为 None 。
# 8.obb : 定向边界框(Oriented Bounding Box),格式为 (N, 5) 的张量,表示旋转框的坐标,默认为 None 。
# 9.speed : 一个字典,包含预处理、推理和后处理的速度(单位为毫秒/图像),默认为 None 。
def __init__(
self, orig_img, path, names, boxes=None, masks=None, probs=None, keypoints=None, obb=None, speed=None
) -> None:
# 初始化 Results 类以存储和操作推理结果。
# 注意:
# 对于默认姿势模型,人体姿势估计的关键点索引为:
# 0:鼻子,1:左眼,2:右眼,3:左耳,4:右耳
# 5:左肩,6:右肩,7:左肘,8:右肘
# 9:左手腕,10:右手腕,11:左臀部,12:右臀部
# 13:左膝盖,14:右膝盖,15:左脚踝,16:右脚踝
"""
Initialize the Results class for storing and manipulating inference results.
Args:
orig_img (numpy.ndarray): The original image as a numpy array.
path (str): The path to the image file.
names (Dict): A dictionary of class names.
boxes (torch.Tensor | None): A 2D tensor of bounding box coordinates for each detection.
masks (torch.Tensor | None): A 3D tensor of detection masks, where each mask is a binary image.
probs (torch.Tensor | None): A 1D tensor of probabilities of each class for classification task.
keypoints (torch.Tensor | None): A 2D tensor of keypoint coordinates for each detection.
obb (torch.Tensor | None): A 2D tensor of oriented bounding box coordinates for each detection.
speed (Dict | None): A dictionary containing preprocess, inference, and postprocess speeds (ms/image).
Examples:
>>> results = model("path/to/image.jpg")
>>> result = results[0] # Get the first result
>>> boxes = result.boxes # Get the boxes for the first result
>>> masks = result.masks # Get the masks for the first result
Notes:
For the default pose model, keypoint indices for human body pose estimation are:
0: Nose, 1: Left Eye, 2: Right Eye, 3: Left Ear, 4: Right Ear
5: Left Shoulder, 6: Right Shoulder, 7: Left Elbow, 8: Right Elbow
9: Left Wrist, 10: Right Wrist, 11: Left Hip, 12: Right Hip
13: Left Knee, 14: Right Knee, 15: Left Ankle, 16: Right Ankle
"""
# 将 传入的原始图像 orig_img 存储到实例属性 self.orig_img 中。
self.orig_img = orig_img
# 提取 原始图像的形状 (高度和宽度),并将其存储到 self.orig_shape 中。 orig_img.shape[:2] 表示取图像数组的前两个维度(即高度和宽度)。
self.orig_shape = orig_img.shape[:2]
# 如果 boxes 参数不为 None ,则使用 Boxes 类创建一个对象,并将其存储到 self.boxes 中。 Boxes 类负责处理 检测框 的逻辑,例如调整框的大小以匹配原始图像的尺寸。如果 boxes 为 None ,则 self.boxes 也为 None 。
self.boxes = Boxes(boxes, self.orig_shape) if boxes is not None else None # native size boxes
# 如果 masks 参数不为 None ,则使用 Masks 类创建一个对象,并将其存储到 self.masks 中。 Masks 类用于处理 分割掩码 ,例如调整掩码的大小以匹配原始图像的尺寸。如果 masks 为 None ,则 self.masks 也为 None 。
self.masks = Masks(masks, self.orig_shape) if masks is not None else None # native size or imgsz masks
# 如果 probs 参数不为 None ,则使用 Probs 类创建一个对象,并将其存储到 self.probs 中。 Probs 类用于处理 分类任务 的概率。如果 probs 为 None ,则 self.probs 也为 None 。
self.probs = Probs(probs) if probs is not None else None
# 如果 keypoints 参数不为 None ,则使用 Keypoints 类创建一个对象,并将其存储到 self.keypoints 中。 Keypoints 类用于处理 关键点 数据,例如调整关键点的坐标以匹配原始图像的尺寸。如果 keypoints 为 None ,则 self.keypoints 也为 None 。
self.keypoints = Keypoints(keypoints, self.orig_shape) if keypoints is not None else None
# 如果 obb 参数不为 None ,则使用 OBB 类创建一个对象,并将其存储到 self.obb 中。 OBB 类用于处理 定向边界框 的逻辑。如果 obb 为 None ,则 self.obb 也为 None 。
self.obb = OBB(obb, self.orig_shape) if obb is not None else None
# 如果 speed 参数不为 None ,则直接将其存储到 self.speed 中。否则,初始化为一个默认字典,包含三个键 : "preprocess" 、 "inference" 和 "postprocess" ,其值均为 None 。
self.speed = speed if speed is not None else {"preprocess": None, "inference": None, "postprocess": None}
# 将 类别名称字典 names 存储到 self.names 中。
self.names = names
# 将 图像路径 path 存储到 self.path 中。
self.path = path
# 初始化 self.save_dir 为 None ,用于 后续保存结果时指定保存路径 。
self.save_dir = None
# 定义一个元组 self._keys ,包含 类中用于内部处理的主要属性名称 。这些属性名称在类的其他方法中可能会被引用,例如在 _apply 方法中。
self._keys = "boxes", "masks", "probs", "keypoints", "obb"
# 这段代码初始化了 Results 类的实例,接收多种类型的推理结果(如检测框、分割掩码、分类概率、关键点和定向边界框),并将它们封装为类的属性。同时,它还存储了原始图像、图像路径和类别名称字典等信息。通过这种方式, Results 类能够灵活地处理不同任务类型的推理结果,并为后续的操作(如可视化、保存和数据转换)提供统一的接口。
# 这段代码定义了 Results 类的 __getitem__ 方法,用于实现对类实例的索引操作(例如通过 results[0] 获取第一个结果)。
# 定义了 Results 类的 __getitem__ 方法。这个方法允许用户通过索引(例如 results[0] 或 results[1:3] )访问类实例中的数据。
# 1.idx :参数可以是一个整数(用于单个索引)或一个切片对象(用于获取多个结果)。
def __getitem__(self, idx):
# 返回特定索引的推理结果的 Results 对象。
"""
Return a Results object for a specific index of inference results.
Args:
idx (int | slice): Index or slice to retrieve from the Results object.
Returns:
(Results): A new Results object containing the specified subset of inference results.
Examples:
>>> results = model("path/to/image.jpg") # Perform inference
>>> single_result = results[0] # Get the first result
>>> subset_results = results[1:4] # Get a slice of results
"""
# 调用了类内部的 _apply 方法,并将 __getitem__ 和 idx 作为参数传递给它。 _apply 方法的作用是将一个指定的操作(在这里是 __getitem__ )应用到类的所有非空属性上(例如 boxes 、 masks 、 probs 等),并返回一个新的 Results 对象,该对象包含应用操作后的属性值。
return self._apply("__getitem__", idx)
# 功能说明 :
# __getitem__ 的作用 :这个方法允许用户像操作列表或数组一样操作 Results 类的实例。例如 :
# results = model("path/to/image.jpg") # 假设 model 返回一个 Results 对象
# first_result = results[0] # 获取第一个结果
# subset_results = results[1:3] # 获取索引为 1 和 2 的结果
# 通过这种方式,用户可以方便地访问和处理单个或多个推理结果。
# _apply 方法的作用 :
# _apply 是一个内部辅助方法,用于将某个操作(如 __getitem__ )应用到类的所有非空属性上。它会检查哪些属性是非空的(例如 boxes 、 masks 等),并对这些属性执行相同的操作。
# 在这里, _apply 方法会调用每个非空属性的 __getitem__ 方法,从而返回一个新的 Results 对象,该对象包含索引后的数据。
# 这段代码通过实现 __getitem__ 方法,使得 Results 类的实例支持索引操作。它利用内部的 _apply 方法,将索引操作应用到类的所有非空属性上,并返回一个新的 Results 对象。这种方法不仅提高了类的灵活性,还使得用户能够以直观的方式访问和处理推理结果。例如,用户可以通过索引获取单个结果或结果的子集,而无需手动处理每个属性。
# 这段代码定义了 Results 类的 __len__ 方法,用于实现对类实例的长度计算(例如通过 len(results) 获取结果的数量)。
# 定义了 Results 类的 __len__ 方法。这个方法允许用户通过内置函数 len() 获取类实例的长度,即结果的数量。
def __len__(self):
# 返回 Results 对象中的检测数量。
"""
Return the number of detections in the Results object.
Returns:
(int): The number of detections, determined by the length of the first non-empty attribute
(boxes, masks, probs, keypoints, or obb).
Examples:
>>> results = Results(orig_img, path, names, boxes=torch.rand(5, 4))
>>> len(results)
5
"""
# 遍历类内部定义的 _keys 属性。 _keys 是一个元组,包含类中用于存储主要推理结果的属性名称,例如 ("boxes", "masks", "probs", "keypoints", "obb") 。
for k in self._keys:
# 使用 getattr 函数动态获取当前属性 k 的值。例如,如果 k 是 "boxes" ,则 v 将获取 self.boxes 的值。
v = getattr(self, k)
# 检查当前属性 v 是否为 None 。如果 v 不为 None ,说明该属性有实际的数据。
if v is not None:
# 如果当前属性 v 不为 None ,则返回其长度 len(v) 。这表示返回该属性中存储的结果数量。
return len(v)
# 这段代码通过实现 __len__ 方法,使得 Results 类的实例支持长度计算。它通过遍历 _keys 中定义的属性,动态地找到第一个非空属性,并返回其长度。这种方法不仅提供了对结果数量的直观访问,还确保了类的灵活性和通用性,能够适应多种任务类型的结果。
# 这段代码定义了 Results 类的 update 方法,用于更新类实例中的检测结果,包括检测框、分割掩码、分类概率、定向边界框和关键点。
# 定义了 update 方法,允许用户传入新的检测结果数据。参数包括 :
# 1.boxes : 新的检测框数据(可选)。
# 2.masks : 新的分割掩码数据(可选)。
# 3.probs : 新的分类概率数据(可选)。
# 4.obb : 新的定向边界框数据(可选)。
# 5.keypoints : 新的关键点数据(可选)。
def update(self, boxes=None, masks=None, probs=None, obb=None, keypoints=None):
# 使用新的检测数据更新 Results 对象。
# 此方法允许更新 Results 对象的框、掩码、概率和定向边界框 (OBB)。它确保将框剪裁为原始图像形状。
# 参数:
# boxes (torch.Tensor | None):包含边界框坐标和置信度分数的形状为 (N, 6) 的张量。格式为 (x1, y1, x2, y2, conf, class)。
# mask (torch.Tensor | None):包含分割掩码的形状为 (N, H, W) 的张量。
# probs (torch.Tensor | None):包含类概率的形状为 (num_classes,) 的张量。
# obb (torch.Tensor | None):包含定向边界框坐标的形状为 (N, 5) 的张量。
# keypoints (torch.Tensor | None):包含关键点的形状为 (N, 17, 3) 的张量。
"""
Updates the Results object with new detection data.
This method allows updating the boxes, masks, probabilities, and oriented bounding boxes (OBB) of the
Results object. It ensures that boxes are clipped to the original image shape.
Args:
boxes (torch.Tensor | None): A tensor of shape (N, 6) containing bounding box coordinates and
confidence scores. The format is (x1, y1, x2, y2, conf, class).
masks (torch.Tensor | None): A tensor of shape (N, H, W) containing segmentation masks.
probs (torch.Tensor | None): A tensor of shape (num_classes,) containing class probabilities.
obb (torch.Tensor | None): A tensor of shape (N, 5) containing oriented bounding box coordinates.
keypoints (torch.Tensor | None): A tensor of shape (N, 17, 3) containing keypoints.
Examples:
>>> results = model("image.jpg")
>>> new_boxes = torch.tensor([[100, 100, 200, 200, 0.9, 0]])
>>> results[0].update(boxes=new_boxes)
"""
# 如果传入了 新的检测框数据 boxes 。
if boxes is not None:
# 使用 ops.clip_boxes 方法将检测框的坐标裁剪到原始图像的边界内(确保框不会超出图像范围)。 创建一个新的 Boxes 对象,并将其存储到 self.boxes 属性中。 Boxes 类负责处理检测框的逻辑,例如调整框的大小以匹配原始图像的尺寸。
self.boxes = Boxes(ops.clip_boxes(boxes, self.orig_shape), self.orig_shape)
# 如果传入了 新的分割掩码数据 masks 。
if masks is not None:
# 创建一个新的 Masks 对象,并将其存储到 self.masks 属性中。 Masks 类用于处理分割掩码,例如调整掩码的大小以匹配原始图像的尺寸。
self.masks = Masks(masks, self.orig_shape)
# 如果传入了 新的分类概率数据 probs 。
if probs is not None:
# 直接将传入的概率数据存储到 self.probs 属性中。这里没有进行额外处理,假设传入的数据已经是正确的格式。
self.probs = probs
# 如果传入了 新的定向边界框数据 obb 。
if obb is not None:
# 创建一个新的 OBB 对象,并将其存储到 self.obb 属性中。 OBB 类用于处理定向边界框的逻辑。
self.obb = OBB(obb, self.orig_shape)
# 如果传入了 新的关键点数据 keypoints 。
if keypoints is not None:
# 创建一个新的 Keypoints 对象,并将其存储到 self.keypoints 属性中。 Keypoints 类用于处理关键点数据,例如调整关键点的坐标以匹配原始图像的尺寸。
self.keypoints = Keypoints(keypoints, self.orig_shape)
# 这段代码通过 update 方法实现了对 Results 类实例中检测结果的动态更新。它支持传入新的检测框、分割掩码、分类概率、定向边界框和关键点数据,并通过相应的类(如 Boxes 、 Masks 、 OBB 和 Keypoints )对数据进行处理和封装。这种方法使得 Results 类能够灵活地适应不同的任务需求,例如在推理过程中更新检测结果或在多阶段处理中逐步完善结果。同时,通过裁剪检测框到图像边界内( ops.clip_boxes ),确保了数据的合理性,避免了潜在的错误。
# 这段代码定义了 Results 类的 _apply 方法,用于将一个指定的操作(函数)应用到类的所有非空属性上,并返回一个新的 Results 对象,该对象包含操作后的属性值。
# 定义了 _apply 方法,它接收以下参数 :
# 1.fn : 一个字符串,表示要应用到每个属性上的方法名(例如 "cpu" 或 "numpy" )。
# 2.*args : 可变参数列表,用于传递给目标方法 fn 的位置参数。
# 3.**kwargs : 关键字参数,用于传递给目标方法 fn 的命名参数。
def _apply(self, fn, *args, **kwargs):
# 将函数应用于所有非空属性,并返回具有修改后的属性的新 Results 对象。
# 此方法由 .to()、.cuda()、.cpu() 等方法内部调用。
"""
Applies a function to all non-empty attributes and returns a new Results object with modified attributes.
This method is internally called by methods like .to(), .cuda(), .cpu(), etc.
Args:
fn (str): The name of the function to apply.
*args (Any): Variable length argument list to pass to the function.
**kwargs (Any): Arbitrary keyword arguments to pass to the function.
Returns:
(Results): A new Results object with attributes modified by the applied function.
Examples:
>>> results = model("path/to/image.jpg")
>>> for result in results:
... result_cuda = result.cuda()
... result_cpu = result.cpu()
"""
# 调用 self.new() 方法创建一个新的 Results 对象 r 。 new() 方法会返回一个与当前实例具有相同图像、路径和类别名称的新实例,但不包含任何检测结果。这个新对象将用于存储操作后的属性值。
r = self.new()
# 遍历类内部定义的 _keys 属性,它是一个元组,包含类中用于存储主要推理结果的属性名称(例如 ("boxes", "masks", "probs", "keypoints", "obb") )。
for k in self._keys:
# 使用 getattr 函数动态获取当前属性 k 的值。例如,如果 k 是 "boxes" ,则 v 将获取 self.boxes 的值。
v = getattr(self, k)
# 检查当前属性 v 是否为 None 。如果属性值不为 None ,说明该属性包含有效的数据,需要对其进行操作。
if v is not None:
# getattr(v, fn) :动态调用属性 v 上的方法 fn 。例如,如果 fn 是 "cpu" ,则调用 v.cpu() 。
# (*args, **kwargs) :将传入的可变参数和关键字参数传递给方法 fn 。
# setattr(r, k, ...) :将操作后的结果存储到新对象 r 的对应属性 k 中。
setattr(r, k, getattr(v, fn)(*args, **kwargs))
# 返回新创建的 Results 对象 r ,它包含了所有操作后的属性值。
return r
# 这段代码通过 _apply 方法实现了一个通用的属性操作机制。它允许用户将任意方法(如 .cpu() 、 .numpy() 或 .to() )应用到 Results 类的所有非空属性上,并返回一个新的 Results 对象,该对象包含操作后的数据。这种方法的设计具有以下优点:通用性:通过动态调用方法和处理属性, _apply 方法能够适应多种操作需求,而无需为每种操作单独实现逻辑。灵活性:用户可以通过传入不同的方法名和参数,轻松地对属性进行操作(例如将数据移动到 CPU 或 GPU,或转换为 NumPy 数组)。封装性:操作后的结果被封装到一个新的 Results 对象中,保持了类的结构和接口的一致性,同时也避免了对原始数据的直接修改。这种设计使得 Results 类能够高效地支持多种数据处理和转换任务,例如在推理结果的后处理阶段,用户可以方便地将数据移动到不同的设备或格式,而无需手动处理每个属性。
# 这段代码定义了 Results 类的 cpu 方法,用于将类实例中的所有张量(Tensor)数据移动到 CPU 内存中。
# 定义了 Results 类的 cpu 方法。该方法的作用是将类实例中所有与推理结果相关的张量数据从 GPU(如果有的话)移动到 CPU 内存中。
def cpu(self):
# 返回 Results 对象的副本,其所有张量都已移动到 CPU 内存。
# 此方法创建一个新的 Results 对象,其中所有张量属性(框、掩码、概率、关键点、obb)都已传输到 CPU 内存。它对于将数据从 GPU 移动到 CPU 以进行进一步处理或保存非常有用。
"""
Returns a copy of the Results object with all its tensors moved to CPU memory.
This method creates a new Results object with all tensor attributes (boxes, masks, probs, keypoints, obb)
transferred to CPU memory. It's useful for moving data from GPU to CPU for further processing or saving.
Returns:
(Results): A new Results object with all tensor attributes on CPU memory.
Examples:
>>> results = model("path/to/image.jpg") # Perform inference
>>> cpu_result = results[0].cpu() # Move the first result to CPU
>>> print(cpu_result.boxes.device) # Output: cpu
"""
# 调用了类内部的 _apply 方法,并将字符串 "cpu" 作为参数传递给它。 _apply 方法的作用是 :
# 遍历类中定义的 _keys 属性(如 ("boxes", "masks", "probs", "keypoints", "obb") )。
# 对每个非空属性(如 self.boxes 、 self.masks 等),动态调用其 cpu 方法。
# 创建一个新的 Results 对象,并将每个属性调用 cpu 方法后的结果存储到新对象中。
# 返回这个新的 Results 对象,其中所有张量数据都已移动到 CPU 内存中。
return self._apply("cpu")
# 这段代码通过实现 cpu 方法,使得 Results 类的实例能够将所有张量数据移动到 CPU 内存中。它利用内部的 _apply 方法动态地对所有非空属性执行 cpu 操作,并返回一个新的 Results 对象。这种方法不仅提高了代码的复用性,还确保了类的接口简洁明了,方便用户在需要时将数据移动到 CPU。
# cpu 方法的作用 :
# 在深度学习中,张量数据可以在 CPU 和 GPU 之间移动。 cpu 方法确保所有与推理结果相关的张量数据都被移动到 CPU 内存中。
# 这对于后续的处理非常有用,例如 :当需要将数据保存到磁盘时,通常需要将张量移动到 CPU。
# 当需要与其他仅支持 CPU 的库(如 NumPy)进行交互时,也需要将数据移动到 CPU。
# 这段代码定义了 Results 类的 numpy 方法,用于将类实例中的所有张量(Tensor)数据转换为 NumPy 数组。
# 定义了 Results 类的 numpy 方法。该方法的作用是将类实例中所有与推理结果相关的张量数据从 PyTorch 张量格式转换为 NumPy 数组格式。
def numpy(self):
# 将 Results 对象中的所有张量转换为 numpy 数组。
# 注释:
# 此方法创建一个新的 Results 对象,保持原始对象不变。它对于与基于 numpy 的库的互操作性或需要基于 CPU 的操作时很有用。
"""
Converts all tensors in the Results object to numpy arrays.
Returns:
(Results): A new Results object with all tensors converted to numpy arrays.
Examples:
>>> results = model("path/to/image.jpg")
>>> numpy_result = results[0].numpy()
>>> type(numpy_result.boxes.data)
Notes:
This method creates a new Results object, leaving the original unchanged. It's useful for
interoperability with numpy-based libraries or when CPU-based operations are required.
"""
# 调用了类内部的 _apply 方法,并将字符串 "numpy" 作为参数传递给它。 _apply 方法的作用是 :
# 遍历类中定义的 _keys 属性(如 ("boxes", "masks", "probs", "keypoints", "obb") )。
# 对每个非空属性(如 self.boxes 、 self.masks 等),动态调用其 numpy 方法。
# 创建一个新的 Results 对象,并将每个属性调用 numpy 方法后的结果存储到新对象中。
# 返回这个新的 Results 对象,其中所有张量数据都已转换为 NumPy 数组。
return self._apply("numpy")
# 这段代码通过实现 numpy 方法,使得 Results 类的实例能够将所有张量数据转换为 NumPy 数组。它利用内部的 _apply 方法动态地对所有非空属性执行 numpy 操作,并返回一个新的 Results 对象。这种方法不仅提高了代码的复用性,还确保了类的接口简洁明了,方便用户在需要时将数据转换为 NumPy 格式,从而与更多工具和库进行无缝对接。
# numpy 方法的作用 :
# 在深度学习中,数据通常以 PyTorch 张量的形式存储。然而,许多传统计算机视觉库(如 OpenCV、SciPy)和数据分析工具(如 Pandas、Matplotlib)更倾向于使用 NumPy 数组。
# numpy 方法将张量数据转换为 NumPy 数组,从而使得 Results 类的实例能够与这些工具无缝对接。
# 这段代码定义了 Results 类的 cuda 方法,用于将类实例中的所有张量(Tensor)数据移动到 GPU(CUDA)内存中。
# 定义了 Results 类的 cuda 方法。该方法的作用是将类实例中所有与推理结果相关的张量数据从 CPU 或其他设备移动到 GPU(CUDA)内存中。
def cuda(self):
# 将 Results 对象中的所有张量移动到 GPU 内存。
"""
Moves all tensors in the Results object to GPU memory.
Returns:
(Results): A new Results object with all tensors moved to CUDA device.
Examples:
>>> results = model("path/to/image.jpg")
>>> cuda_results = results[0].cuda() # Move first result to GPU
>>> for result in results:
... result_cuda = result.cuda() # Move each result to GPU
"""
# 调用了类内部的 _apply 方法,并将字符串 "cuda" 作为参数传递给它。 _apply 方法的作用是 :
# 遍历类中定义的 _keys 属性(如 ("boxes", "masks", "probs", "keypoints", "obb") )。
# 对每个非空属性(如 self.boxes 、 self.masks 等),动态调用其 cuda 方法。
# 创建一个新的 Results 对象,并将每个属性调用 cuda 方法后的结果存储到新对象中。
# 返回这个新的 Results 对象,其中所有张量数据都已移动到 GPU 内存中。
return self._apply("cuda")
# 这段代码通过实现 cuda 方法,使得 Results 类的实例能够将所有张量数据移动到 GPU 内存中。它利用内部的 _apply 方法动态地对所有非空属性执行 cuda 操作,并返回一个新的 Results 对象。这种方法不仅提高了代码的复用性,还确保了类的接口简洁明了,方便用户在需要时将数据移动到 GPU,从而加速后续的计算任务。
# cuda 方法的作用 :
# 在深度学习中,将数据移动到 GPU 内存中可以显著加速计算,因为 GPU 通常比 CPU 更擅长处理并行计算任务。
# cuda 方法允许用户将推理结果中的所有张量数据移动到 GPU 上,从而为后续的 GPU 加速操作(如进一步处理或可视化)做好准备。
# 这段代码定义了 Results 类的 to 方法,用于将类实例中的所有张量(Tensor)数据移动到指定的设备(如 CPU 或 GPU)或转换为指定的数据类型。
# 定义了 Results 类的 to 方法。该方法的作用是将类实例中所有与推理结果相关的张量数据移动到指定的设备(如 CPU 或 GPU),或者将数据类型转换为指定的格式(如 float16 或 float32 )。
# 1.*args : 可变参数列表,用于传递给 PyTorch 的 to 方法。例如,可以传递设备名称(如 "cuda" 或 "cpu" )。
# 2.**kwargs : 关键字参数,用于传递给 PyTorch 的 to 方法。例如,可以指定数据类型(如 dtype=torch.float16 )。
def to(self, *args, **kwargs):
# 将 Results 对象中的所有张量移动到指定的设备和 dtype。
"""
Moves all tensors in the Results object to the specified device and dtype.
Args:
*args (Any): Variable length argument list to be passed to torch.Tensor.to().
**kwargs (Any): Arbitrary keyword arguments to be passed to torch.Tensor.to().
Returns:
(Results): A new Results object with all tensors moved to the specified device and dtype.
Examples:
>>> results = model("path/to/image.jpg")
>>> result_cuda = results[0].to("cuda") # Move first result to GPU
>>> result_cpu = results[0].to("cpu") # Move first result to CPU
>>> result_half = results[0].to(dtype=torch.float16) # Convert first result to half precision
"""
# 调用了类内部的 _apply 方法,并将字符串 "to" 以及传入的 *args 和 **kwargs 参数传递给它。 _apply 方法的作用是 :
# 遍历类中定义的 _keys 属性(如 ("boxes", "masks", "probs", "keypoints", "obb") )。
# 对每个非空属性(如 self.boxes 、 self.masks 等),动态调用其 to 方法,并将 *args 和 **kwargs 参数传递给它。
# 创建一个新的 Results 对象,并将每个属性调用 to 方法后的结果存储到新对象中。
# 返回这个新的 Results 对象,其中所有张量数据都已移动到指定的设备或转换为指定的数据类型。
return self._apply("to", *args, **kwargs)
# 这段代码通过实现 to 方法,使得 Results 类的实例能够将所有张量数据移动到指定的设备或转换为指定的数据类型。它利用内部的 _apply 方法动态地对所有非空属性执行 to 操作,并返回一个新的 Results 对象。这种方法不仅提高了代码的复用性和灵活性,还确保了类的接口简洁明了,方便用户在需要时对数据进行设备迁移或类型转换。
# to 方法的作用 :
# to 方法是 PyTorch 中用于设备迁移和数据类型转换的通用方法。通过在 Results 类中实现 to 方法,可以方便地将所有张量数据移动到指定的设备(如 GPU 或 CPU),或者将数据类型转换为指定的格式(如 float16 或 float32 )。
# 这种方法提供了比单独的 cpu 、 cuda 或 numpy 方法更灵活的功能,因为它允许用户指定任意设备或数据类型。
# 这段代码定义了 Results 类的 new 方法,用于创建一个新的 Results 对象,该对象与当前实例共享相同的图像、路径、类别名称和速度信息,但不包含任何检测结果数据。
# 定义了 Results 类的 new 方法。该方法的作用是创建一个新的 Results 对象,用于初始化一个空白的结果实例,同时保留当前实例的上下文信息(如图像、路径和类别名称)。
def new(self):
# 创建具有相同图像、路径、名称和速度属性的新 Results 对象。
"""
Creates a new Results object with the same image, path, names, and speed attributes.
Returns:
(Results): A new Results object with copied attributes from the original instance.
Examples:
>>> results = model("path/to/image.jpg")
>>> new_result = results[0].new()
"""
# 调用 Results 类的构造函数来创建一个新的实例。新实例的参数如下 :
# orig_img : 使用当前实例的原始图像数据 self.orig_img 。
# path : 使用当前实例的图像路径 self.path 。
# names : 使用当前实例的类别名称字典 self.names 。
# speed : 使用当前实例的速度信息字典 self.speed 。
# 这些参数被传递给新的 Results 对象,但检测结果相关的属性(如 boxes 、 masks 、 probs 、 keypoints 和 obb )默认为 None ,因为这些参数在调用 new 方法时没有被显式传递。
return Results(orig_img=self.orig_img, path=self.path, names=self.names, speed=self.speed)
# 这段代码通过实现 new 方法,使得 Results 类的实例能够快速创建一个新的空白实例,该实例与当前实例共享相同的上下文信息(如图像、路径和类别名称)。这种方法不仅提高了代码的复用性,还为多阶段处理和任务复用提供了便利。通过这种方式,用户可以在需要时逐步填充检测结果,而无需重复初始化上下文信息。
# 使用示例 :
# 假设 results 是一个 Results 类的实例 :
# results = model("path/to/image.jpg") # 假设返回一个 Results 对象
# new_results = results.new() # 创建一个新的空白 Results 对象
# 此时, new_results 将包含与 results 相同的 orig_img 、 path 、 names 和 speed ,但其 boxes 、 masks 、 probs 、 keypoints 和 obb 属性均为 None 。
# 这段代码定义了 Results 类的 plot 方法,用于在原始图像上绘制检测、分割、分类和姿态估计的结果,并支持显示和保存这些标注后的图像。
# 定义了 plot 方法,用于在图像上绘制推理结果。该方法接收多个参数,用于控制绘制的细节,例如是否显示置信度、线条宽度、字体大小、是否使用 PIL 图像等。
# 1.conf (bool) :是否在图像上显示检测框的置信度分数。默认为 True 。
# 2.line_width (float | None) :检测框的线条宽度。如果为 None ,则根据图像大小自动调整。
# 3.font_size (float | None) :标签的字体大小。如果为 None ,则根据图像大小自动调整。
# 4.font (str) :标签的字体文件路径。默认为 "Arial.ttf" 。
# 5.pil (bool) :是否返回 PIL 图像对象。默认为 False 。
# 6.img (np.ndarray | None) :用于绘制的图像。如果为 None ,则使用 self.orig_img 。
# 7.im_gpu (torch.Tensor | None) :用于加速掩码绘制的 GPU 图像张量。如果为 None ,则自动创建。
# 8.kpt_radius (int) :关键点的绘制半径。默认为 5 。
# 9.kpt_line (bool) :是否绘制关键点之间的连线。默认为 True 。
# 10.labels (bool) :是否在检测框上显示标签(类别名称)。默认为 True 。
# 11.boxes (bool) :是否绘制检测框。默认为 True 。
# 12.masks (bool) :是否绘制分割掩码。默认为 True 。
# 13.probs (bool) :是否显示分类概率。默认为 True 。
# 14.show (bool) :是否直接显示标注后的图像。默认为 False 。
# 15.save (bool) :是否保存标注后的图像。默认为 False 。
# 16.filename (str | None) :保存图像的文件名。如果为 None ,则根据 self.path 自动生成。
# 17.color_mode (str) :颜色模式,支持 "class" (按类别着色)和 "instance" (按实例着色)。默认为 "class" 。
def plot(
self,
conf=True,
line_width=None,
font_size=None,
font="Arial.ttf",
pil=False,
img=None,
im_gpu=None,
kpt_radius=5,
kpt_line=True,
labels=True,
boxes=True,
masks=True,
probs=True,
show=False,
save=False,
filename=None,
color_mode="class",
):
# 在输入的 RGB 图像上绘制检测结果。
"""
Plots detection results on an input RGB image.
Args:
conf (bool): Whether to plot detection confidence scores.
line_width (float | None): Line width of bounding boxes. If None, scaled to image size.
font_size (float | None): Font size for text. If None, scaled to image size.
font (str): Font to use for text.
pil (bool): Whether to return the image as a PIL Image.
img (np.ndarray | None): Image to plot on. If None, uses original image.
im_gpu (torch.Tensor | None): Normalized image on GPU for faster mask plotting.
kpt_radius (int): Radius of drawn keypoints.
kpt_line (bool): Whether to draw lines connecting keypoints.
labels (bool): Whether to plot labels of bounding boxes.
boxes (bool): Whether to plot bounding boxes.
masks (bool): Whether to plot masks.
probs (bool): Whether to plot classification probabilities.
show (bool): Whether to display the annotated image.
save (bool): Whether to save the annotated image.
filename (str | None): Filename to save image if save is True.
color_mode (bool): Specify the color mode, e.g., 'instance' or 'class'. Default to 'class'.
Returns:
(np.ndarray): Annotated image as a numpy array.
Examples:
>>> results = model("image.jpg")
>>> for result in results:
... im = result.plot()
... im.show()
"""
# 断言 color_mode 参数必须是 "instance" 或 "class" ,否则抛出错误。 color_mode 决定了绘制时的颜色模式。
# "class" : 根据类别 ID 选择颜色。
# "instance" : 根据实例 ID 选择颜色。
assert color_mode in {"instance", "class"}, f"Expected color_mode='instance' or 'class', not {color_mode}." # 预期 color_mode='instance' 或 'class',而不是 {color_mode}。
# 如果未提供 img 参数,并且 self.orig_img 是一个 PyTorch 张量,则将其转换为 NumPy 数组。具体步骤如下 :
# detach() : 从计算图中分离张量,避免影响梯度计算。
# permute(1, 2, 0) : 将通道维度从第一个位置移到最后一个位置(从 CHW 到 HWC)。
# contiguous() : 确保张量在内存中是连续的。
# * 255 : 将像素值从 [0, 1] 转换为 [0, 255] 。
# to(torch.uint8) : 将数据类型转换为 uint8 。
# cpu().numpy() : 将张量移动到 CPU 并转换为 NumPy 数组。
if img is None and isinstance(self.orig_img, torch.Tensor):
img = (self.orig_img[0].detach().permute(1, 2, 0).contiguous() * 255).to(torch.uint8).cpu().numpy()
# 这段代码是 plot 方法的核心部分,用于初始化绘图所需的变量和 Annotator 对象。
# 将 self.names ( 类别名称字典 )赋值给变量 names 。这个字典将类别 ID 映射到对应的类别名称,例如 {0: 'person', 1: 'car'} 。它将在后续的标注中用于显示类别名称。
names = self.names
# 检查 self.obb 是否为 None ,并将其结果赋值给变量 is_obb 。 is_obb 是一个布尔值,表示 是否使用定向边界框 (Oriented Bounding Box, OBB)。
is_obb = self.obb is not None
# 如果 is_obb 为 True (即存在 OBB 数据),则将 self.obb 赋值给 pred_boxes ,并将 boxes 参数赋值给 show_boxes 。
# 如果 is_obb 为 False ,则将 self.boxes 赋值给 pred_boxes ,并将 boxes 参数赋值给 show_boxes 。
# 这里的逻辑是为了根据是否存在 OBB 数据,动态选择使用 OBB 或普通边界框( self.boxes )。
pred_boxes, show_boxes = self.obb if is_obb else self.boxes, boxes
# 将 self.masks 赋值给 pred_masks ,并将 masks 参数赋值给 show_masks 。 pred_masks 表示 分割掩码数据 ,而 show_masks 表示 是否需要绘制这些掩码 。
pred_masks, show_masks = self.masks, masks
# 将 self.probs 赋值给 pred_probs ,并将 probs 参数赋值给 show_probs 。 pred_probs 表示 分类概率数据 ,而 show_probs 表示 是否需要显示这些概率 。
pred_probs, show_probs = self.probs, probs
# 创建一个 Annotator 对象,用于在图像上绘制标注。 Annotator 的参数包括 :
# class Annotator:
# -> 用于在图像上进行标注和绘制各种图形、文本和关键点等。
# -> def __init__(self, im, line_width=None, font_size=None, font="Arial.ttf", pil=False, example="abc"):
annotator = Annotator(
# 如果 img 参数为 None ,则使用 self.orig_img ;否则使用传入的 img 。
# 使用 deepcopy 确保原始图像不会被修改。
deepcopy(self.orig_img if img is None else img),
# 绘制边界框的线条宽度。
line_width,
# 绘制标签的字体大小。
font_size,
# 绘制标签的字体文件路径,默认为 "Arial.ttf" 。
font,
# 是否使用 PIL 图像格式。如果 pred_probs 存在且 show_probs=True ,则默认使用 PIL 格式(因为 PIL 更适合绘制文本)。
pil or (pred_probs is not None and show_probs), # Classify tasks default to pil=True
# 提供类别名称字典,用于示例和标注。
example=names,
)
# 这段代码通过初始化必要的变量和 Annotator 对象,为后续的绘图逻辑做好了准备。它根据是否存在 OBB 数据动态选择边界框类型,并根据方法参数决定是否绘制边界框、掩码和分类概率。通过这种方式, plot 方法能够灵活地支持多种任务类型(如检测、分割、分类)的可视化需求。
# 这段代码是 plot 方法中用于绘制分割掩码(Segmentation Masks)的部分。它的主要功能是根据提供的掩码数据 pred_masks 和显示标志 show_masks ,在图像上绘制分割掩码,并根据颜色模式( color_mode )为每个掩码分配颜色。
# Plot Segment results
# 只有当 pred_masks (分割掩码数据)存在且 show_masks=True 时,才会执行掩码绘制逻辑。
if pred_masks and show_masks:
# 检查 GPU 图像是否存在。 如果 im_gpu (用于加速掩码绘制的 GPU 图像张量)未提供,则需要创建一个。
if im_gpu is None:
# 使用 LetterBox 方法将图像调整为与掩码相同的大小。 LetterBox 是一种图像缩放方法,它会在不改变图像宽高比的情况下,将图像填充到目标尺寸。
# pred_masks.shape[1:] :获取掩码的高度和宽度。
# annotator.result() :获取当前 Annotator 对象中的图像(是已经绘制了部分内容的图像)。
# class LetterBox:
# -> 用于对图像进行缩放和填充操作,以适应指定的目标尺寸。这种操作通常用于深度学习中的图像预处理,尤其是在目标检测和分割任务中。 LetterBox 的核心功能是将图像缩放到指定大小,同时保持原始图像的宽高比,并通过填充(通常是灰色)来补充剩余部分。
# -> def __init__(self, new_shape=(640, 640), auto=False, scaleFill=False, scaleup=True, center=True, stride=32):
img = LetterBox(pred_masks.shape[1:])(image=annotator.result())
# 创建 GPU 图像张量。
im_gpu = (
# 将调整大小后的图像 img 转换为 PyTorch 张量,并移动到与 pred_masks 相同的设备(通常是 GPU)。
# torch.float16 :使用半精度浮点数( float16 )以节省内存并加速计算。
torch.as_tensor(img, dtype=torch.float16, device=pred_masks.data.device)
# 将图像的通道维度从 (H, W, C) 转换为 (C, H, W) ,以符合 PyTorch 的张量格式。
.permute(2, 0, 1)
# 翻转通道维度(例如从 RGB 到 BGR)。
.flip(0)
# 确保张量在内存中是连续的。
.contiguous()
# 将像素值从 [0, 255] 转换为 [0, 1] 。
/ 255
)
# 确定颜色索引。
idx = (
# 如果 color_mode="instance" 且 pred_boxes.id 存在,则使用实例 ID 作为颜色索引。
pred_boxes.id
if pred_boxes.id is not None and color_mode == "instance"
# 如果 color_mode="class" ,则使用类别 ID ( pred_boxes.cls ) 作为颜色索引。
else pred_boxes.cls
if pred_boxes and color_mode == "class"
# 如果两者都不是,则使用默认的颜色索引(从 len(pred_masks) 开始递减)。
else reversed(range(len(pred_masks)))
)
# 绘制分割掩码。
# 调用 annotator.masks 方法绘制分割掩码。
# pred_masks.data :提供掩码数据。
# colors=[colors(x, True) for x in idx] :根据颜色索引 idx 生成颜色列表。 colors(x, True) 是一个函数,根据索引生成颜色。
# im_gpu=im_gpu :提供 GPU 图像张量以加速绘制。
annotator.masks(pred_masks.data, colors=[colors(x, True) for x in idx], im_gpu=im_gpu)
# 这段代码实现了分割掩码的绘制功能,支持根据类别或实例分配颜色,并利用 GPU 加速绘制过程。它通过 Annotator 类的 masks 方法将掩码绘制到图像上,同时根据颜色模式动态选择颜色索引。这种方法不仅灵活地支持多种任务需求,还通过 GPU 加速提高了绘制效率。
# 这段代码是 plot 方法中用于绘制检测框(Detection Boxes)的部分。它的主要功能是根据提供的检测框数据 pred_boxes 和显示标志 show_boxes ,在图像上绘制检测框,并根据颜色模式( color_mode )为每个框分配颜色,同时显示类别名称和置信度(如果需要)。
# Plot Detect results
# 只有当 pred_boxes (检测框数据)存在且 show_boxes=True 时,才会执行检测框绘制逻辑。
if pred_boxes is not None and show_boxes:
# 对检 测框进行遍历 。 reversed(pred_boxes) 表示 从后向前遍历检测框 ,这通常是为了在绘制时将置信度较高的框绘制在前面(假设检测框按置信度排序)。
for i, d in enumerate(reversed(pred_boxes)):
# 提取 检测框信息 。
# c : 类别 ID ( int(d.cls) )。
# d_conf : 置信度分数。如果 conf=True ,则提取 float(d.conf) ;否则为 None 。
# id : 实例 ID。如果 d.id 存在,则提取 int(d.id.item()) ;否则为 None 。
c, d_conf, id = int(d.cls), float(d.conf) if conf else None, None if d.id is None else int(d.id.item())
# 生成 标签名称 。
# 如果 id 存在,则在类别名称前加上实例 ID(例如 "id:1 person" )。
# 如果 id 不存在,则直接使用类别名称(例如 "person" )。
name = ("" if id is None else f"id:{id} ") + names[c]
# 生成 最终标签 。
# 如果 conf=True ,则在标签中显示置信度(例如 "person 0.95" )。
# 如果 conf=False ,则仅显示类别名称(例如 "person" )。
# 如果 labels=False ,则不显示任何标签( label=None )。
label = (f"{name} {d_conf:.2f}" if conf else name) if labels else None
# 提取 检测框坐标 。
# 如果使用 定向边界框 ( is_obb=True ),则将 OBB 坐标从 (x1, y1, x2, y2, x3, y3, x4, y4) 转换为多边形格式( (4, 2) )。
# 如果使用 普通边界框 ( is_obb=False ),则直接提取 xyxy 格式的坐标( (x1, y1, x2, y2) )。
box = d.xyxyxyxy.reshape(-1, 4, 2).squeeze() if is_obb else d.xyxy.squeeze()
# 绘制检测框和标签。
# 调用 annotator.box_label 方法绘制检测框和标签。
annotator.box_label(
# 检测框的坐标。
box,
# 标签内容。
label,
# 根据颜色模式动态选择颜色。
color=colors(
# 如果 color_mode="class" ,则根据类别 ID ( c ) 选择颜色。
c
if color_mode == "class"
# 如果 color_mode="instance" 且实例 ID ( id ) 存在,则根据实例 ID 选择颜色。
else id
if id is not None
# 如果两者都不是,则根据 检测框的索引 ( i ) 选择颜色。
else i
if color_mode == "instance"
else None,
True,
),
# 是否绘制旋转框( is_obb )。
rotated=is_obb,
)
# 这段代码实现了检测框的绘制功能,支持普通边界框和定向边界框,并根据颜色模式动态选择颜色。它通过 Annotator 类的 box_label 方法将检测框和标签绘制到图像上,同时支持显示类别名称和置信度。这种方法不仅灵活地支持多种任务需求,还通过动态颜色选择和旋转框绘制功能,提供了丰富的可视化选项。
# 这段代码是 plot 方法中用于绘制分类结果(Classification Results)的部分。它的主要功能是根据提供的分类概率 pred_probs 和显示标志 show_probs ,在图像上显示分类结果的前 5 个最高概率类别及其对应的概率值。
# Plot Classify results
# 只有当 pred_probs (分类概率数据)存在且 show_probs=True 时,才会执行分类结果的绘制逻辑。
if pred_probs is not None and show_probs:
# 生成 分类结果文本 。
# pred_probs.top5 :获取分类概率最高的前 5 个类别的索引。
# names[j] :根据类别索引 j ,从 names 字典中获取对应的类别名称。如果 names 为空,则直接使用类别索引 j 。
# pred_probs.data[j] :获取类别索引 j 对应的概率值。
# f"{...:.2f}" :将概率值格式化为两位小数。
# ",\n".join(...) :将生成的类别名称和概率值以逗号和换行符连接成一个字符串。
text = ",\n".join(f"{names[j] if names else j} {pred_probs.data[j]:.2f}" for j in pred_probs.top5)
# 计算文本起始位置的 x 坐标。 使用图像的高度 self.orig_shape[0] 的 3% 作为起始 x 坐标。 通过 round 函数确保 x 坐标为整数。
x = round(self.orig_shape[0] * 0.03)
# 绘制文本。
# 调用 annotator.text 方法在图像上绘制文本。
# [x, x] :指定文本的起始位置(左上角坐标)。
# text :要绘制的文本内容(分类结果)。
# txt_color=(255, 255, 255) :设置文本颜色为白色(RGB:255, 255, 255)。
# # TODO: allow setting colors :这是一个待办事项注释,表示未来可能允许用户自定义文本颜色。
annotator.text([x, x], text, txt_color=(255, 255, 255)) # TODO: allow setting colors
# 这段代码实现了分类结果的绘制功能,支持在图像上显示前 5 个最高概率的类别及其概率值。它通过 Annotator 类的 text 方法将文本绘制到图像上,并根据图像大小动态调整文本位置。这种方法不仅灵活地支持分类任务的可视化需求,还通过动态生成文本内容和位置,提供了丰富的可视化选项。
# 这段代码是 plot 方法中用于绘制姿态估计结果(Pose Estimation Results)的部分。它的主要功能是根据提供的关键点数据 self.keypoints ,在图像上绘制关键点及其连线(如果需要)。
# Plot Pose results
# 只有当 self.keypoints (关键点数据)存在时,才会执行姿态估计结果的绘制逻辑。
if self.keypoints is not None:
# 对关键点数据进行遍历。 reversed(self.keypoints.data) 表示从后向前遍历关键点,这通常是为了在绘制时将置信度较高的关键点绘制在前面(假设关键点按置信度排序)。
# i :关键点的索引。
# k :每个关键点的数据,通常是一个包含关键点坐标的数组。
for i, k in enumerate(reversed(self.keypoints.data)):
# 绘制关键点。
# 调用 Annotator 类的 kpts 方法绘制关键点。
annotator.kpts(
# 当前关键点的数据。
k,
# 原始图像的尺寸(高度和宽度),用于调整关键点的绘制比例。
self.orig_shape,
# 关键点的绘制半径,默认值为 5 。
radius=kpt_radius,
# 是否绘制关键点之间的连线,默认值为 True 。
kpt_line=kpt_line,
# 关键点的颜色。
# 如果 color_mode="instance" ,则根据关键点的索引 i 选择颜色( colors(i, True) )。
# 如果 color_mode 不是 "instance" ,则不指定颜色( None ),默认使用 Annotator 的默认颜色。
kpt_color=colors(i, True) if color_mode == "instance" else None,
)
# 这段代码实现了姿态估计结果的绘制功能,支持在图像上绘制关键点及其连线。它通过 Annotator 类的 kpts 方法将关键点绘制到图像上,并根据颜色模式动态选择颜色。这种方法不仅灵活地支持姿态估计任务的可视化需求,还通过动态生成关键点的颜色和连线,提供了丰富的可视化选项。
# 这段代码是 plot 方法的最后部分,用于显示和保存标注后的图像,并返回最终的图像结果。
# Show results
# 如果 show=True ,则调用 annotator.show 方法显示标注后的图像。 self.path 是原始图像的路径,用于在显示窗口中提供上下文信息(例如窗口标题)。
if show:
annotator.show(self.path)
# Save results
# 如果 save=True ,则调用 annotator.save 方法将标注后的图像保存到指定的文件路径。 filename 是用户指定的保存路径。如果未提供 filename ,则会根据 self.path 自动生成一个默认的文件名。
if save:
annotator.save(filename)
# 返回标注后的图像。
# 调用 annotator.result() 方法获取最终的标注图像。 返回的图像通常是 NumPy 数组格式,但也可以根据 pil 参数返回 PIL 图像。
return annotator.result()
# 这段代码实现了 plot 方法的最后两个功能:显示和保存标注后的图像。它通过 annotator.show 方法直接显示图像,并通过 annotator.save 方法将图像保存到指定路径。最后,方法返回标注后的图像,用户可以进一步处理或使用该图像。这种设计使得 plot 方法不仅能够用于可视化,还可以将结果保存为文件,方便后续分析和记录。
# plot 方法是一个功能强大的可视化工具,用于在原始图像上绘制检测、分割、分类和姿态估计的结果。它支持多种自定义选项,包括是否显示置信度、绘制分割掩码、显示分类概率以及绘制姿态关键点等。通过灵活的颜色模式选择、动态调整的线条和字体大小,以及对 GPU 加速的支持, plot 方法能够高效地生成标注后的图像,并支持实时显示或保存到文件。最终,它返回标注后的图像,方便用户进行进一步处理或分析。
# 这段代码定义了 Results 类的 show 方法,用于直接显示标注后的图像。
# 定义了 show 方法,该方法的作用是直接显示标注后的图像。它接收可变参数 *args 和关键字参数 **kwargs ,这些参数将被传递给内部的 plot 方法,用于自定义显示的细节。
def show(self, *args, **kwargs):
# 显示带有注释的推理结果的图像。
# 此方法在原始图像上绘制检测结果并显示。这是一种直接可视化模型预测的便捷方法。
"""
Display the image with annotated inference results.
This method plots the detection results on the original image and displays it. It's a convenient way to
visualize the model's predictions directly.
Args:
*args (Any): Variable length argument list to be passed to the `plot()` method.
**kwargs (Any): Arbitrary keyword arguments to be passed to the `plot()` method.
Examples:
>>> results = model("path/to/image.jpg")
>>> results[0].show() # Display the first result
>>> for result in results:
... result.show() # Display all results
"""
# 调用 Results 类的 plot 方法,并设置 show=True ,以确保标注后的图像会被直接显示。其他参数( *args 和 **kwargs )被传递给 plot 方法,允许用户自定义显示的细节,例如是否显示置信度、是否绘制分割掩码等。
self.plot(show=True, *args, **kwargs)
# 这段代码通过实现 show 方法,提供了一个简洁的接口来直接显示标注后的图像。它利用了 plot 方法的核心功能,并通过设置 show=True 确保图像会被显示在屏幕上。通过接收可变参数和关键字参数, show 方法允许用户灵活地自定义显示的细节,而无需直接调用 plot 方法。这种设计使得 show 方法既简单又灵活,方便用户快速查看推理结果。
# 这段代码定义了 Results 类的 save 方法,用于保存标注后的图像到文件。
# 定义了 save 方法,用于保存标注后的图像。该方法接收以下参数 :
# 1.filename : 保存图像的文件名。如果未提供,则会自动生成一个默认文件名。
# 2.*args 和 3.**kwargs : 可变参数和关键字参数,这些参数将被传递给内部的 plot 方法,用于自定义保存的细节。
def save(self, filename=None, *args, **kwargs):
# 将带注释的推理结果图像保存到文件。
# 此方法在原始图像上绘制检测结果并将带注释的图像保存到文件。它利用 `plot` 方法生成带注释的图像,然后将其保存到指定的文件名。
"""
Saves annotated inference results image to file.
This method plots the detection results on the original image and saves the annotated image to a file. It
utilizes the `plot` method to generate the annotated image and then saves it to the specified filename.
Args:
filename (str | Path | None): The filename to save the annotated image. If None, a default filename
is generated based on the original image path.
*args (Any): Variable length argument list to be passed to the `plot` method.
**kwargs (Any): Arbitrary keyword arguments to be passed to the `plot` method.
Examples:
>>> results = model("path/to/image.jpg")
>>> for result in results:
... result.save("annotated_image.jpg")
>>> # Or with custom plot arguments
>>> for result in results:
... result.save("annotated_image.jpg", conf=False, line_width=2)
"""
# 检查文件名是否提供。
# 如果用户没有提供 filename 参数,则自动生成一个默认文件名。
# 生成默认文件名。 默认文件名的格式为 "results_" ,其中 是原始图像的文件名(从 self.path 提取)。
# Path(self.path).name : 使用 pathlib.Path 提取原始图像路径的文件名部分。
if not filename:
filename = f"results_{Path(self.path).name}"
# 通过调用 plot 方法并设置 save=True ,将标注后的图像保存到指定的文件路径。 将 filename 以及用户提供的其他参数( *args 和 **kwargs )传递给 plot 方法,以自定义保存的细节(例如是否显示置信度、是否绘制分割掩码等)。
self.plot(save=True, filename=filename, *args, **kwargs)
# 方法返回 保存的文件名 ,方便用户知道图像被保存到了哪里。
return filename
# 这段代码通过实现 save 方法,提供了一个简单而灵活的接口来保存标注后的图像。它允许用户指定保存路径,或者自动生成默认文件名。通过将参数传递给内部的 plot 方法, save 方法支持自定义保存的细节,同时返回保存的文件名,方便用户进一步处理或记录。这种设计使得 save 方法既简洁又功能强大,满足了用户保存标注结果的需求。
# 这段代码定义了 Results 类的 verbose 方法,用于生成一个包含检测和分类结果的详细日志字符串。
# 定义了 verbose 方法,该方法的作用是生成一个描述检测和分类结果的详细日志字符串。
def verbose(self):
# 返回结果中每个任务的日志字符串,详细说明检测和分类结果。
# 此方法生成一个人类可读的字符串,总结检测和分类结果。它包括每个类的检测次数和分类任务的最高概率。
# 注释:
# - 如果没有检测,该方法将为检测任务返回“(no detections)”。
# - 对于分类任务,它返回前 5 个类概率及其对应的类名。
# - 返回的字符串以逗号分隔,并以逗号和空格结尾。
"""
Returns a log string for each task in the results, detailing detection and classification outcomes.
This method generates a human-readable string summarizing the detection and classification results. It includes
the number of detections for each class and the top probabilities for classification tasks.
Returns:
(str): A formatted string containing a summary of the results. For detection tasks, it includes the
number of detections per class. For classification tasks, it includes the top 5 class probabilities.
Examples:
>>> results = model("path/to/image.jpg")
>>> for result in results:
... print(result.verbose())
2 persons, 1 car, 3 traffic lights,
dog 0.92, cat 0.78, horse 0.64,
Notes:
- If there are no detections, the method returns "(no detections), " for detection tasks.
- For classification tasks, it returns the top 5 class probabilities and their corresponding class names.
- The returned string is comma-separated and ends with a comma and a space.
"""
# 初始化一个空字符串 log_string ,用于 存储最终的日志信息 。
log_string = ""
# 获取 self.probs 属性,它包含了 分类任务的概率信息 。如果 self.probs 为 None ,则表示当前结果不包含分类任务的概率。
probs = self.probs
# 通过 len(self) 检查是否有检测结果。如果结果数量为 0 。
if len(self) == 0:
# 如果存在 分类概率 ( probs is not None ),则直接返回当前的 log_string (此时 log_string 是空的)。
# 如果没有分类概率,则返回一个字符串,表示没有检测到任何目标( (no detections), )。
return log_string if probs is not None else f"{log_string}(no detections), " # {log_string}(无检测),
# 如果 probs 不为 None ,则提取分类概率最高的前 5 个类别( probs.top5 )。
if probs is not None:
# 对于每个类别索引 j ,从 self.names 中获取 类别名称 ,并从 probs.data 中获取 对应的概率值 (保留两位小数)。 将这些信息格式化为字符串(例如 "dog 0.95, cat 0.88, ..." ),并追加到 log_string 。
log_string += f"{', '.join(f'{self.names[j]} {probs.data[j]:.2f}' for j in probs.top5)}, "
# 使用 Python 3.8+ 的赋值表达式( := ),将 self.boxes 赋值给变量 boxes ,并同时进行条件判断。 如果 self.boxes 不为 None ,则表示存在检测框。
if boxes := self.boxes:
# 统计 每个类别的检测数量 。
# 遍历 检测框的类别 ( boxes.cls ),并提取 唯一的类别索引 c 。
for c in boxes.cls.unique():
# 对于每个类别 c ,统计 该类别下的检测数量 n (通过 (boxes.cls == c).sum() )。
n = (boxes.cls == c).sum() # detections per class
# 根据类别索引 c ,从 self.names 中获取类别名称,并根据检测数量生成日志字符串(例如 "2 persons, 1 car, " )。如果检测数量大于 1,则在类别名称后追加 's' (复数形式)。
log_string += f"{n} {self.names[int(c)]}{'s' * (n > 1)}, "
# 返回最终生成的 日志字符串 。
return log_string
# 这段代码通过实现 verbose 方法,生成了一个描述检测和分类结果的详细日志字符串。它支持以下功能。分类概率:显示分类任务中概率最高的前 5 个类别及其概率值。检测结果:统计每个类别的检测数量,并生成相应的日志信息。空结果处理:如果没有任何检测结果,方法会返回一个明确的提示信息。灵活性:方法根据是否存在分类概率或检测框动态调整日志内容。这种设计使得 verbose 方法能够灵活地处理多种任务类型(分类、检测等),并提供清晰、详细的日志信息,方便用户快速了解模型的推理结果。
# 这段代码定义了 Results 类的 save_txt 方法,用于将检测、分割、分类和姿态估计的结果保存到一个文本文件中。
# 定义了 save_txt 方法,用于将推理结果保存到文本文件中。该方法接收以下参数 :
# 1.txt_file : 保存结果的文本文件路径。
# 2.save_conf : 是否在结果中保存置信度分数,默认为 False 。
def save_txt(self, txt_file, save_conf=False):
# 将检测结果保存到文本文件。
# 注意事项:
# - 该文件将包含每行检测或分类,结构如下:
# - 对于检测:`class confidence x_center y_center width height`
# - 对于分类:`confidence class_name`
# - 对于掩码和关键点,具体格式将相应不同。
# - 如果输出目录不存在,该函数将创建输出目录。
# - 如果 save_conf 为 False,则置信度分数将从输出中排除。
# - 文件的现有内容不会被覆盖;新的结果将被附加。
"""
Save detection results to a text file.
Args:
txt_file (str | Path): Path to the output text file.
save_conf (bool): Whether to include confidence scores in the output.
Returns:
(str): Path to the saved text file.
Examples:
>>> from ultralytics import YOLO
>>> model = YOLO("yolo11n.pt")
>>> results = model("path/to/image.jpg")
>>> for result in results:
... result.save_txt("output.txt")
Notes:
- The file will contain one line per detection or classification with the following structure:
- For detections: `class confidence x_center y_center width height`
- For classifications: `confidence class_name`
- For masks and keypoints, the specific formats will vary accordingly.
- The function will create the output directory if it does not exist.
- If save_conf is False, the confidence scores will be excluded from the output.
- Existing contents of the file will not be overwritten; new results will be appended.
"""
# 检查是否存在定向边界框(OBB)数据。如果 self.obb 不为 None ,则将 is_obb 设置为 True 。
is_obb = self.obb is not None
# 根据是否存在 OBB 数据,选择使用 self.obb 或 self.boxes 作为 检测框数据 。
boxes = self.obb if is_obb else self.boxes
# 分别获取 分割掩码 、 分类概率 和 姿态关键点数据 。
masks = self.masks
probs = self.probs
kpts = self.keypoints
# 初始化一个空列表 texts ,用于 存储每行结果的字符串表示 。
texts = []
# 如果存在 分类概率数据 。
if probs is not None:
# Classify
# 遍历概率最高的前 5 个类别( probs.top5 )。 对于每个类别索引 j ,从 probs.data 中获取概率值,并从 self.names 中获取类别名称。 将格式化后的字符串(例如 "0.95 dog" )追加到 texts 列表中。
[texts.append(f"{probs.data[j]:.2f} {self.names[j]}") for j in probs.top5]
# 如果存在 检测框数据 ( boxes ),则遍历每个检测框。
elif boxes:
# Detect/segment/pose
for j, d in enumerate(boxes):
# 对于每个检测框 d 。 提取 类别 ID ( c ) 和 置信度 ( conf )。 如果检测框有 实例 ID ( d.id ),则提取其值;否则设置为 None 。
c, conf, id = int(d.cls), float(d.conf), None if d.id is None else int(d.id.item())
# 根据检测框的类型(OBB 或普通边界框),提取坐标信息。 如果是 OBB,提取 (x1, y1, x2, y2, x3, y3, x4, y4) 并归一化。 如果是普通边界框,提取 (x_center, y_center, width, height) 并归一化。
line = (c, *(d.xyxyxyxyn.view(-1) if is_obb else d.xywhn.view(-1)))
# 如果存在分割掩码。
if masks:
# 提取当前检测框对应的 掩码轮廓点 ,并将其从 (n, 2) 格式转换为 (n*2) 格式。
seg = masks[j].xyn[0].copy().reshape(-1) # reversed mask.xyn, (n,2) to (n*2)
# 将 类别 ID 和 掩码轮廓点 追加到 line 中。
line = (c, *seg)
# 如果存在姿态关键点。
if kpts is not None:
# 提取当前检测框对应的关键点数据。 如果关键点有置信度信息,则将其与坐标合并;否则仅使用坐标。
kpt = torch.cat((kpts[j].xyn, kpts[j].conf[..., None]), 2) if kpts[j].has_visible else kpts[j].xyn
# 将关键点数据追加到 line 中。
line += (*kpt.reshape(-1).tolist(),)
# 根据 save_conf 参数决定是否保存置信度。 如果 save_conf=True ,则将置信度追加到 line 中。 如果存在实例 ID,则将其追加到 line 中。
line += (conf,) * save_conf + (() if id is None else (id,))
# 将 line 中的所有值格式化为字符串,并追加到 texts 列表中。
texts.append(("%g " * len(line)).rstrip() % line)
# 如果 texts 列表不为空。
if texts:
# 确保目标文件的父目录存在(如果不存在则创建)。
Path(txt_file).parent.mkdir(parents=True, exist_ok=True) # make directory
# 打开目标文件( txt_file ),并以追加模式写入每行结果。
with open(txt_file, "a") as f:
f.writelines(text + "\n" for text in texts)
# 这段代码通过实现 save_txt 方法,将检测、分割、分类和姿态估计的结果保存到一个文本文件中。它支持多种任务类型,并根据任务类型动态调整保存的内容格式。方法还支持保存置信度分数(通过 save_conf 参数控制),并确保目标文件的父目录存在。这种设计使得 save_txt 方法能够灵活地处理多种推理结果,并以文本格式保存,方便后续分析和记录。
# 在计算机视觉和目标检测任务中,“实例 ID”(Instance ID)是一个用于唯一标识每个检测到的目标实例的标识符。它通常用于区分同一类别中的不同目标,尤其是在处理多目标检测和跟踪任务时非常有用。
# 区分同一类别中的不同目标 :在目标检测任务中,同一类别(如“人”或“汽车”)可能有多个实例。实例 ID 可以帮助区分这些实例,避免混淆。例如,在一个场景中可能有多个“人”,每个“人”都有一个唯一的实例 ID。
# 支持目标跟踪 :在视频分析或目标跟踪任务中,实例 ID 可以帮助跟踪同一目标在不同帧中的位置和状态。例如,在视频中跟踪一个特定的行人或车辆时,实例 ID 可以确保跟踪的是同一个目标,而不是同一类别中的其他目标。
# 数据关联和分析 :实例 ID 可以用于将检测到的目标与数据库中的记录或其他数据源进行关联。例如,在监控系统中,可以使用实例 ID 将检测到的目标与历史记录或警报系统关联起来。
# 示例 :
# 假设在一个场景中有多个“人”和“汽车”,检测结果可能如下 :
# 人 :
# 实例 ID :1,类别 :人,位置 : (x1, y1, x2, y2),置信度 : 0.95
# 实例 ID :2,类别 :人,位置 : (x3, y3, x4, y4),置信度 : 0.88
# 汽车 :
# 实例 ID :3,类别 :汽车,位置 : (x5, y5, x6, y6),置信度 : 0.92
# 实例 ID :4,类别 :汽车,位置 : (x7, y7, x8, y8),置信度 : 0.85
# 在这个例子中,实例 ID 1 和 2 表示两个不同的人,而实例 ID 3 和 4 表示两辆不同的汽车。即使它们属于同一类别,实例 ID 也能确保每个目标被唯一标识。
# 总结 :实例 ID 是一个重要的标识符,用于区分同一类别中的不同目标实例。它在目标检测、目标跟踪和数据分析中起着关键作用,帮助确保每个目标都能被准确地识别和处理。
# 这段代码定义了 Results 类的 save_crop 方法,用于将检测到的目标从原始图像中裁剪出来,并保存为单独的图像文件。
# 定义了 save_crop 方法,用于裁剪并保存检测到的目标。该方法接收以下参数 :
# 1.save_dir : 保存裁剪图像的目录路径。
# 2.file_name : 裁剪图像的文件名,默认为 "im.jpg" 。如果未指定,将使用默认值。
def save_crop(self, save_dir, file_name=Path("im.jpg")):
# 将裁剪后的检测图像保存到指定目录。
# 此方法将检测到的对象的裁剪图像保存到指定目录。每个裁剪图像都保存在以对象类命名的子目录中,文件名基于输入的 file_name。
# 注意事项:
# - 此方法不支持分类或定向边界框 (OBB) 任务。
# - 裁剪图像保存为“save_dir/class_name/file_name.jpg”。
# - 如果不存在必要的子目录,该方法将创建它们。
# - 裁剪前复制原始图像以避免修改原始图像。
"""
Saves cropped detection images to specified directory.
This method saves cropped images of detected objects to a specified directory. Each crop is saved in a
subdirectory named after the object's class, with the filename based on the input file_name.
Args:
save_dir (str | Path): Directory path where cropped images will be saved.
file_name (str | Path): Base filename for the saved cropped images. Default is Path("im.jpg").
Notes:
- This method does not support Classify or Oriented Bounding Box (OBB) tasks.
- Crops are saved as 'save_dir/class_name/file_name.jpg'.
- The method will create necessary subdirectories if they don't exist.
- Original image is copied before cropping to avoid modifying the original.
Examples:
>>> results = model("path/to/image.jpg")
>>> for result in results:
... result.save_crop(save_dir="path/to/crops", file_name="detection")
"""
# 检查是否为分类任务。 如果 self.probs 不为 None ,说明当前结果是分类任务的结果,而不是检测任务的结果。
if self.probs is not None:
# 分类任务不支持裁剪功能,因此发出警告并直接返回,不执行后续操作。
LOGGER.warning("WARNING ⚠️ Classify task do not support `save_crop`.") # 警告⚠️分类任务不支持“save_crop”。
return
# 检查是否为定向边界框(OBB)任务。 如果 self.obb 不为 None ,说明当前结果是 OBB 任务的结果。
if self.obb is not None:
# OBB 任务不支持裁剪功能,因此发出警告并直接返回,不执行后续操作。
LOGGER.warning("WARNING ⚠️ OBB task do not support `save_crop`.") # 警告⚠️OBB 任务不支持“save_crop”。
return
# 如果当前结果是 检测任务的结果 ( self.boxes 存在),则 遍历每个检测框 d 。
for d in self.boxes:
# 裁剪并保存检测框。
# 调用 save_one_box 函数,将检测框从原始图像中裁剪出来并保存到指定路径。
# def save_one_box(xyxy, im, file=Path("im.jpg"), gain=1.02, pad=10, square=False, BGR=False, save=True): -> 用于从图像中裁剪出一个指定的边界框区域,并将其保存为一个新的图像文件。返回裁剪的图像。 -> return crop
save_one_box(
# 提取 检测框的坐标 ( xyxy 格式,即 [x1, y1, x2, y2] )。
d.xyxy,
# 使用原始图像的副本,避免修改原始图像。
self.orig_img.copy(),
# 构造保存路径。
# Path(save_dir) : 指定保存目录。
# self.names[int(d.cls)] : 获取检测框对应的类别名称。
# Path(file_name).with_suffix(".jpg") : 确保文件名以 .jpg 结尾。
file=Path(save_dir) / self.names[int(d.cls)] / Path(file_name).with_suffix(".jpg"),
# 指定图像格式为 BGR(OpenCV 默认格式)。
BGR=True,
)
# 这段代码通过实现 save_crop 方法,提供了一个功能,用于将检测到的目标从原始图像中裁剪出来,并保存为单独的图像文件。它支持以下功能。分类任务和 OBB 任务的检查:如果当前结果是分类任务或 OBB 任务的结果,会发出警告并终止操作,因为这些任务不支持裁剪功能。裁剪并保存检测框:遍历每个检测框,从原始图像中裁剪出目标区域,并将其保存为单独的图像文件。保存路径根据类别名称和指定的文件名动态生成,确保每个裁剪的图像都有唯一的路径。灵活性:用户可以通过 save_dir 和 file_name 参数自定义保存路径和文件名。这种设计使得 save_crop 方法能够灵活地处理检测任务的结果,并提供一种简单的方式来保存裁剪后的图像,方便后续分析或使用。
# 这段代码定义了 Results 类的 summary 方法,用于将检测、分割、分类和姿态估计的结果总结为一个结构化的列表,其中每个元素是一个包含详细信息的字典。
# 定义了 summary 方法,用于生成推理结果的结构化总结。该方法接收以下参数 :
# 1.normalize : 是否将坐标值归一化到 [0, 1] 范围内,默认为 False 。
# 2.decimals : 保留的小数位数,默认为 5 。
def summary(self, normalize=False, decimals=5):
# 将推理结果转换为带有可选框坐标归一化的汇总字典。
# 此方法创建一个检测字典列表,每个字典包含有关单个检测或分类结果的信息。对于分类任务,它返回顶级类及其置信度。对于检测任务,它包括类信息、边界框坐标以及可选的掩码段和关键点。
"""
Converts inference results to a summarized dictionary with optional normalization for box coordinates.
This method creates a list of detection dictionaries, each containing information about a single
detection or classification result. For classification tasks, it returns the top class and its
confidence. For detection tasks, it includes class information, bounding box coordinates, and
optionally mask segments and keypoints.
Args:
normalize (bool): Whether to normalize bounding box coordinates by image dimensions. Defaults to False.
decimals (int): Number of decimal places to round the output values to. Defaults to 5.
Returns:
(List[Dict]): A list of dictionaries, each containing summarized information for a single
detection or classification result. The structure of each dictionary varies based on the
task type (classification or detection) and available information (boxes, masks, keypoints).
Examples:
>>> results = model("image.jpg")
>>> summary = results[0].summary()
>>> print(summary)
"""
# Create list of detection dictionaries
# 初始化一个空列表 results ,用于 存储每个检测结果的字典 。
results = []
# 检查是否存在 分类概率数据 。如果存在,说明当前结果是分类任务的结果。
if self.probs is not None:
# 获取概率最高的类别 ID。
class_id = self.probs.top1
# 将分类结果以字典形式添加到 results 列表中。
results.append(
{
# 类别名称。
"name": self.names[class_id],
# 类别 ID。
"class": class_id,
# 置信度,保留指定的小数位数。
"confidence": round(self.probs.top1conf.item(), decimals),
}
)
# 如果当前结果是分类任务的结果,则直接返回包含分类信息的 results 列表。
return results
# 检查是否存在定向边界框(OBB)数据。
is_obb = self.obb is not None
# 根据是否存在 OBB 数据,选择使用 self.obb 或 self.boxes 作为 检测框数据 。
data = self.obb if is_obb else self.boxes
# 据是否需要归一化,选择高度和宽度的值。 如果 normalize=True ,则使用 原始图像的尺寸 self.orig_shape 。 如果 normalize=False ,则将高度和宽度设置为 1 ,以便坐标值保持原样。
h, w = self.orig_shape if normalize else (1, 1)
# 这段代码是 summary 方法的核心部分,用于遍历检测结果并生成每个检测框的详细信息。它将检测框的类别、置信度、坐标、分割掩码和关键点等信息整理为一个结构化的字典,并将这些字典存储在一个列表中。
# 遍历检测结果。 data 是一个 包含检测框信息的张量 ,每个检测框包含 类别 ID、 置信度 、 坐标 等信息。
# i : 当前检测框的索引。
# row : 当前行的数据,表示一个检测框。
for i, row in enumerate(data): # xyxy, track_id if tracking, conf, class_id
# 提取类别 ID 和置信度。
# class_id : 将类别 ID ( row.cls ) 转换为整数。
# conf : 提取置信度 ( row.conf ) 并保留指定的小数位数。
class_id, conf = int(row.cls), round(row.conf.item(), decimals)
# 提取 检测框坐标 。
# 如果是定向边界框(OBB),提取 (x1, y1, x2, y2, x3, y3, x4, y4) 并转换为多边形格式。
# 如果是普通边界框,提取 (x1, y1, x2, y2) 。
# 使用 .squeeze() 和 .reshape(-1, 2) 将坐标转换为 (n, 2) 格式(每行表示一个点的 (x, y) 坐标)。 最后,将 坐标 转换为 Python 列表。
box = (row.xyxyxyxy if is_obb else row.xyxy).squeeze().reshape(-1, 2).tolist()
# 归一化坐标并存储。
# 初始化一个空字典 xy ,用于 存储归一化后的坐标 。
xy = {}
# 遍历每个点的坐标 (x, y) ,并根据是否归一化( normalize 参数)调整坐标值。
for j, b in enumerate(box):
# 如果 normalize=True ,则使用原始图像的宽度和高度 ( w 和 h ) 进行归一化。
# 如果 normalize=False ,则 w 和 h 均为 1 ,坐标值保持原样。
# 将归一化后的坐标存储到字典 xy 中,键为 x1 , y1 , x2 , y2 等。
xy[f"x{j + 1}"] = round(b[0] / w, decimals)
xy[f"y{j + 1}"] = round(b[1] / h, decimals)
# 创建 检测框的字典 。
# name : 类别名称,从 self.names 中获取。
# class : 类别 ID。
# confidence : 置信度。
# box : 检测框的坐标(归一化后的 xy 字典)。
result = {"name": self.names[class_id], "class": class_id, "confidence": conf, "box": xy}
# 添加跟踪 ID 。
# 如果检测框包含跟踪 ID( data.is_track 为 True ),则提取并添加到字典中。
if data.is_track:
result["track_id"] = int(row.id.item()) # track ID
# 添加分割掩码的轮廓点。
# 如果存在分割掩码( self.masks ),则提取当前检测框对应的掩码轮廓点,并归一化后存储到字典中。
if self.masks:
result["segments"] = {
"x": (self.masks.xy[i][:, 0] / w).round(decimals).tolist(),
"y": (self.masks.xy[i][:, 1] / h).round(decimals).tolist(),
}
# 添加姿态关键点。
# 如果存在姿态关键点( self.keypoints ),则提取当前检测框对应的 关键点坐标 和 可见性信息 。
if self.keypoints is not None:
x, y, visible = self.keypoints[i].data[0].cpu().unbind(dim=1) # torch Tensor
result["keypoints"] = {
# 归一化关键点坐标,并将结果存储到字典中。
"x": (x / w).numpy().round(decimals).tolist(), # decimals named argument required
"y": (y / h).numpy().round(decimals).tolist(),
"visible": visible.numpy().round(decimals).tolist(),
}
# 将当前检测框的字典添加到结果列表。
results.append(result)
# 这段代码通过遍历检测结果,生成每个检测框的详细信息,并将其整理为一个结构化的字典。它支持以下功能。提取检测框的类别、置信度和坐标。归一化坐标(根据 normalize 参数)。附加跟踪 ID(如果存在)。提取分割掩码的轮廓点(如果存在)。提取姿态关键点的坐标和可见性信息(如果存在)。最终,这些字典被存储在一个列表中,方便后续分析和使用。这种设计使得 summary 方法能够灵活地处理多种任务类型,并以结构化的形式提供详细的推理结果。
# 返回包含 所有检测结果的 results 列表。
return results
# 这段代码通过实现 summary 方法,将检测、分割、分类和姿态估计的结果总结为一个结构化的列表,其中每个元素是一个包含详细信息的字典。它支持以下功能。分类任务:返回概率最高的类别及其置信度。检测任务:返回每个检测框的类别名称、类别 ID、置信度和坐标信息。归一化选项:支持将坐标值归一化到 [0, 1] 范围内。附加信息:如果存在跟踪 ID,则将其包含在结果中。如果存在分割掩码,则提取并归一化掩码的轮廓点。如果存在姿态关键点,则提取并归一化关键点的坐标和可见性信息。这种设计使得 summary 方法能够灵活地处理多种任务类型,并以结构化的形式提供详细的推理结果,方便后续分析和使用。
# 这段代码定义了 Results 类的 to_df 方法,用于将检测结果转换为 Pandas DataFrame 格式。
# 定义了 to_df 方法,用于将检测结果转换为 Pandas DataFrame 格式。该方法接收以下参数 :
# 1.normalize : 是否将坐标值归一化到 [0, 1] 范围内,默认为 False 。
# 2.decimals : 保留的小数位数,默认为 5 。
def to_df(self, normalize=False, decimals=5):
# 将检测结果转换为 Pandas Dataframe。
# 此方法将检测结果转换为 Pandas Dataframe 格式。它包括有关检测到的对象的信息,例如边界框、类名、置信度分数以及可选的分割掩码和关键点。
"""
Converts detection results to a Pandas Dataframe.
This method converts the detection results into Pandas Dataframe format. It includes information
about detected objects such as bounding boxes, class names, confidence scores, and optionally
segmentation masks and keypoints.
Args:
normalize (bool): Whether to normalize the bounding box coordinates by the image dimensions.
If True, coordinates will be returned as float values between 0 and 1. Defaults to False.
decimals (int): Number of decimal places to round the output values to. Defaults to 5.
Returns:
(DataFrame): A Pandas Dataframe containing all the information in results in an organized way.
Examples:
>>> results = model("path/to/image.jpg")
>>> df_result = results[0].to_df()
>>> print(df_result)
"""
# 在方法内部导入 Pandas 库。这种导入方式可以减少全局作用域的依赖,加快模块加载速度。注释中提到的 'import ultralytics' 是指该代码属于某个特定的库或框架。
import pandas as pd # scope for faster 'import ultralytics'
# 调用 self.summary 方法,将检测结果总结为一个结构化的列表(每个元素是一个字典)。 使用 Pandas 的 DataFrame 构造函数将该列表转换为 DataFrame 格式。 返回生成的 DataFrame。
return pd.DataFrame(self.summary(normalize=normalize, decimals=decimals))
# 这段代码通过实现 to_df 方法,将检测结果转换为 Pandas DataFrame 格式。它支持以下功能。归一化选项:可以选择是否将坐标值归一化到 [0, 1] 范围内。小数位数控制:可以指定保留的小数位数。结构化数据:将检测结果的详细信息(如类别名称、置信度、坐标、分割掩码和关键点等)转换为表格形式,方便后续分析和处理。这种设计使得 to_df 方法能够灵活地处理多种任务类型,并以 Pandas DataFrame 的形式提供详细的推理结果,方便用户进行进一步的数据分析和可视化。
# 这段代码定义了 Results 类的 to_csv 方法,用于将检测结果保存为 CSV 格式的文件。
# 定义了 to_csv 方法,用于将检测结果保存为 CSV 文件。该方法接收以下参数 :
# 1.normalize : 是否将坐标值归一化到 [0, 1] 范围内,默认为 False 。
# 2.decimals : 保留的小数位数,默认为 5 。
# 3.*args 和 4.**kwargs : 可变参数和关键字参数,这些参数将被传递给 Pandas 的 to_csv 方法,用于自定义 CSV 文件的保存细节(例如文件路径、分隔符等)。
def to_csv(self, normalize=False, decimals=5, *args, **kwargs):
# 将检测结果转换为 CSV 格式。
# 此方法将检测结果序列化为 CSV 格式。它包括有关检测到的对象的信息,例如边界框、类名、置信度分数以及可选的分割掩码和关键点。
"""
Converts detection results to a CSV format.
This method serializes the detection results into a CSV format. It includes information
about detected objects such as bounding boxes, class names, confidence scores, and optionally
segmentation masks and keypoints.
Args:
normalize (bool): Whether to normalize the bounding box coordinates by the image dimensions.
If True, coordinates will be returned as float values between 0 and 1. Defaults to False.
decimals (int): Number of decimal places to round the output values to. Defaults to 5.
*args (Any): Variable length argument list to be passed to pandas.DataFrame.to_csv().
**kwargs (Any): Arbitrary keyword arguments to be passed to pandas.DataFrame.to_csv().
Returns:
(str): CSV containing all the information in results in an organized way.
Examples:
>>> results = model("path/to/image.jpg")
>>> csv_result = results[0].to_csv()
>>> print(csv_result)
"""
# 调用 to_df 方法。 将检测结果转换为 Pandas DataFrame 格式,同时根据 normalize 和 decimals 参数处理坐标值和小数位数。
# 调用 to_csv 方法。 将生成的 DataFrame 保存为 CSV 文件。 *args 和 **kwargs 参数被传递给 Pandas 的 to_csv 方法,允许用户自定义保存行为。
# 返回 Pandas 的 to_csv 方法的返回值,通常是保存的 CSV 文件路径或内容。
return self.to_df(normalize=normalize, decimals=decimals).to_csv(*args, **kwargs)
# 这段代码通过实现 to_csv 方法,将检测结果保存为 CSV 格式的文件。它支持以下功能。归一化选项:可以选择是否将坐标值归一化到 [0, 1] 范围内。小数位数控制:可以指定保留的小数位数。灵活的保存选项:通过传递参数给 Pandas 的 to_csv 方法,用户可以自定义 CSV 文件的保存路径、分隔符、编码方式等细节。基于 DataFrame 的实现:通过先将结果转换为 Pandas DataFrame,再调用 to_csv 方法,确保了代码的简洁性和灵活性。这种设计使得 to_csv 方法能够灵活地处理多种任务类型,并以 CSV 文件的形式保存详细的推理结果,方便用户进行后续的数据分析和处理。
# 这段代码定义了 Results 类的 to_xml 方法,用于将检测结果转换为 XML 格式的字符串。
# 定义了 to_xml 方法,用于将检测结果转换为 XML 格式的字符串。该方法接收以下参数 :
# 1.normalize : 是否将坐标值归一化到 [0, 1] 范围内,默认为 False 。
# 2.decimals : 保留的小数位数,默认为 5 。
# 3.*args 和 4.**kwargs : 可变参数和关键字参数,这些参数将被传递给 Pandas 的 to_xml 方法,用于自定义 XML 文件的生成细节。
def to_xml(self, normalize=False, decimals=5, *args, **kwargs):
# 将检测结果转换为 XML 格式。
# 此方法将检测结果序列化为 XML 格式。它包括有关检测到的对象的信息,例如边界框、类名、置信度分数以及可选的分割掩码和关键点。
"""
Converts detection results to XML format.
This method serializes the detection results into an XML format. It includes information
about detected objects such as bounding boxes, class names, confidence scores, and optionally
segmentation masks and keypoints.
Args:
normalize (bool): Whether to normalize the bounding box coordinates by the image dimensions.
If True, coordinates will be returned as float values between 0 and 1. Defaults to False.
decimals (int): Number of decimal places to round the output values to. Defaults to 5.
*args (Any): Variable length argument list to be passed to pandas.DataFrame.to_xml().
**kwargs (Any): Arbitrary keyword arguments to be passed to pandas.DataFrame.to_xml().
Returns:
(str): An XML string containing all the information in results in an organized way.
Examples:
>>> results = model("path/to/image.jpg")
>>> xml_result = results[0].to_xml()
>>> print(xml_result)
"""
# 调用 check_requirements 函数,确保安装了 lxml 库。 lxml 是一个用于处理 XML 和 HTML 的 Python 库,Pandas 的 to_xml 方法依赖于它。
check_requirements("lxml")
# 调用 self.to_df 方法,将检测结果转换为 Pandas DataFrame 格式。 normalize 和 decimals 参数 控制坐标值的归一化 和 小数位数 。
df = self.to_df(normalize=normalize, decimals=decimals)
# 检查 DataFrame 是否为空。
# 如果 DataFrame 为空( df.empty 为 True ),返回一个空的 XML 格式字符串,包含 XML 声明和一个空的根节点 。
# 如果 DataFrame 不为空,调用 Pandas 的 to_xml 方法将 DataFrame 转换为 XML 格式的字符串。
# 传递参数。 将 *args 和 **kwargs 参数传递给 Pandas 的 to_xml 方法,允许用户自定义 XML 文件的生成细节。
return '\n ' if df.empty else df.to_xml(*args, **kwargs)
# 这段代码通过实现 to_xml 方法,将检测结果转换为 XML 格式的字符串。它支持以下功能。归一化选项:可以选择是否将坐标值归一化到 [0, 1] 范围内。小数位数控制:可以指定保留的小数位数。灵活的 XML 生成选项:通过传递参数给 Pandas 的 to_xml 方法,用户可以自定义 XML 文件的生成细节(例如根节点名称、行标签等)。基于 DataFrame 的实现:通过先将结果转换为 Pandas DataFrame,再调用 to_xml 方法,确保了代码的简洁性和灵活性。空数据处理:如果 DataFrame 为空,返回一个空的 XML 格式字符串,避免生成无效的 XML 文件。这种设计使得 to_xml 方法能够灵活地处理多种任务类型,并以 XML 文件的形式保存详细的推理结果,方便用户进行后续的数据分析和处理。
# 这段代码定义了 Results 类的 tojson 方法,用于将检测结果转换为 JSON 格式的字符串。同时,它提醒用户该方法已被弃用,并建议使用新的方法 to_json 。
# 定义了 tojson 方法,用于将检测结果转换为 JSON 格式的字符串。该方法接收以下参数 :
# 1.normalize : 是否将坐标值归一化到 [0, 1] 范围内,默认为 False 。
# 2.decimals : 保留的小数位数,默认为 5 。
def tojson(self, normalize=False, decimals=5):
# to_json() 的弃用版本。
"""Deprecated version of to_json()."""
# 使用 LOGGER.warning 发出一个警告信息,提示用户该方法已被弃用,并建议使用新的方法 to_json 。 提醒用户更新代码,以避免在未来的版本中遇到问题。
LOGGER.warning("WARNING ⚠️ 'result.tojson()' is deprecated, replace with 'result.to_json()'.") # 警告⚠️'result.tojson()' 已弃用,请用'result.to_json()' 替换。
# 调用 self.to_json 方法,将检测结果转换为 JSON 格式的字符串。
# 将 normalize 和 decimals 参数传递给 to_json 方法。
# 返回 to_json 方法的返回值,即 JSON 格式的字符串。
return self.to_json(normalize, decimals)
# 这段代码通过实现 tojson 方法,将检测结果转换为 JSON 格式的字符串。它支持以下功能。归一化选项:可以选择是否将坐标值归一化到 [0, 1] 范围内。小数位数控制:可以指定保留的小数位数。兼容性警告:发出警告信息,提示用户该方法已被弃用,并建议使用新的方法 to_json 。调用新的方法:通过调用 to_json 方法,确保代码的兼容性和一致性。这种设计使得 tojson 方法能够兼容旧版本代码,同时引导用户逐步迁移到新的方法,确保代码的维护性和未来的兼容性。
# 这段代码定义了 Results 类的 to_json 方法,用于将检测结果转换为 JSON 格式的字符串。
# 定义了 to_json 方法,用于将检测结果转换为 JSON 格式的字符串。该方法接收以下参数 :
# 1.normalize : 是否将坐标值归一化到 [0, 1] 范围内,默认为 False 。
# 2.decimals : 保留的小数位数,默认为 5 。
def to_json(self, normalize=False, decimals=5):
# 将检测结果转换为 JSON 格式。
# 此方法将检测结果序列化为 JSON 兼容格式。它包括有关检测到的对象的信息,例如边界框、类名、置信度分数以及可选的分割掩码和关键点。
# 注意事项:
# - 对于分类任务,JSON 将包含类概率而不是边界框。
# - 对于对象检测任务,JSON 将包括边界框坐标、类名称和置信度分数。
# - 如果可用,分割掩码和关键点也将包含在 JSON 输出中。
# - 该方法在内部使用 `summary` 方法生成数据结构,然后再将其转换为 JSON。
"""
Converts detection results to JSON format.
This method serializes the detection results into a JSON-compatible format. It includes information
about detected objects such as bounding boxes, class names, confidence scores, and optionally
segmentation masks and keypoints.
Args:
normalize (bool): Whether to normalize the bounding box coordinates by the image dimensions.
If True, coordinates will be returned as float values between 0 and 1. Defaults to False.
decimals (int): Number of decimal places to round the output values to. Defaults to 5.
Returns:
(str): A JSON string containing the serialized detection results.
Examples:
>>> results = model("path/to/image.jpg")
>>> json_result = results[0].to_json()
>>> print(json_result)
Notes:
- For classification tasks, the JSON will contain class probabilities instead of bounding boxes.
- For object detection tasks, the JSON will include bounding box coordinates, class names, and
confidence scores.
- If available, segmentation masks and keypoints will also be included in the JSON output.
- The method uses the `summary` method internally to generate the data structure before
converting it to JSON.
"""
# 导入 Python 标准库中的 json 模块,用于将 Python 对象序列化为 JSON 格式的字符串。
import json
# 调用 self.summary 方法。 将检测结果总结为一个结构化的列表(每个元素是一个字典),其中包含每个检测框的详细信息(如类别名称、置信度、坐标等)。
# 调用 json.dumps 方法。 将总结后的结果(Python 列表或字典)序列化为 JSON 格式的字符串。
# indent=2 : 设置 JSON 字符串的缩进为 2 个空格,使生成的 JSON 字符串更易于阅读。
# 返回生成的 JSON 格式的字符串。
return json.dumps(self.summary(normalize=normalize, decimals=decimals), indent=2)
# 这段代码通过实现 to_json 方法,将检测结果转换为 JSON 格式的字符串。它支持以下功能。归一化选项:可以选择是否将坐标值归一化到 [0, 1] 范围内。小数位数控制:可以指定保留的小数位数。结构化数据:通过调用 self.summary 方法,将检测结果整理为一个结构化的列表(每个元素是一个字典)。JSON 序列化:使用 Python 的 json.dumps 方法将结构化数据转换为 JSON 格式的字符串,并设置缩进以提高可读性。这种设计使得 to_json 方法能够灵活地处理多种任务类型,并以 JSON 文件的形式保存详细的推理结果,方便用户进行后续的数据分析和处理。
# Results 类是一个功能强大的工具,用于封装和操作从 YOLO 模型中得到的推理结果。它支持多种任务类型,包括目标检测、分割、姿态估计和分类,并提供了丰富的属性和方法来处理和可视化这些结果。通过灵活的接口,用户可以轻松地访问检测框、分割掩码、关键点和分类概率等信息,同时支持将结果保存为图像、文本文件、CSV、XML 和 JSON 等格式。此外, Results 类还提供了设备管理功能,允许用户在 CPU 和 GPU 之间移动数据,确保了高效的数据处理和兼容性。
# 这段代码定义了一个名为 Boxes 的类,用于管理和操作目标检测框(bounding boxes)的数据。它提供了对检测框坐标、置信度分数、类别标签以及可选的跟踪ID的处理功能,并支持多种坐标格式和坐标系之间的转换。
# 定义了一个名为 Boxes 的类,继承自 BaseTensor 。 BaseTensor 是一个基类,提供了与张量操作相关的通用功能。
class Boxes(BaseTensor):
# 用于管理和操作检测框的类。
# 此类提供处理检测框的功能,包括其坐标、置信度分数、类标签和可选的跟踪 ID。它支持各种框格式,并提供方便操作和在不同坐标系之间转换的方法。
# 方法:
# cpu():返回 CPU 内存上包含所有张量的对象副本。
# numpy():以 numpy 数组形式返回包含所有张量的对象副本。
# cuda():返回 GPU 内存上包含所有张量的对象副本。
# to(*args, **kwargs):返回指定设备和 dtype 上包含张量的对象副本。
"""
A class for managing and manipulating detection boxes.
This class provides functionality for handling detection boxes, including their coordinates, confidence scores,
class labels, and optional tracking IDs. It supports various box formats and offers methods for easy manipulation
and conversion between different coordinate systems.
Attributes:
data (torch.Tensor | numpy.ndarray): The raw tensor containing detection boxes and associated data.
orig_shape (Tuple[int, int]): The original image dimensions (height, width).
is_track (bool): Indicates whether tracking IDs are included in the box data.
xyxy (torch.Tensor | numpy.ndarray): Boxes in [x1, y1, x2, y2] format.
conf (torch.Tensor | numpy.ndarray): Confidence scores for each box.
cls (torch.Tensor | numpy.ndarray): Class labels for each box.
id (torch.Tensor | numpy.ndarray): Tracking IDs for each box (if available).
xywh (torch.Tensor | numpy.ndarray): Boxes in [x, y, width, height] format.
xyxyn (torch.Tensor | numpy.ndarray): Normalized [x1, y1, x2, y2] boxes relative to orig_shape.
xywhn (torch.Tensor | numpy.ndarray): Normalized [x, y, width, height] boxes relative to orig_shape.
Methods:
cpu(): Returns a copy of the object with all tensors on CPU memory.
numpy(): Returns a copy of the object with all tensors as numpy arrays.
cuda(): Returns a copy of the object with all tensors on GPU memory.
to(*args, **kwargs): Returns a copy of the object with tensors on specified device and dtype.
Examples:
>>> import torch
>>> boxes_data = torch.tensor([[100, 50, 150, 100, 0.9, 0], [200, 150, 300, 250, 0.8, 1]])
>>> orig_shape = (480, 640) # height, width
>>> boxes = Boxes(boxes_data, orig_shape)
>>> print(boxes.xyxy)
>>> print(boxes.conf)
>>> print(boxes.cls)
>>> print(boxes.xywhn)
"""
# 这段代码是 Boxes 类的初始化方法 __init__ 的实现,用于初始化检测框(bounding boxes)的数据结构,并设置一些关键属性。
# 定义了 Boxes 类的初始化方法 __init__ ,它接受两个参数 :
# 1.boxes :一个张量( torch.Tensor )或 NumPy 数组,包含检测框的数据。
# 2.orig_shape :一个元组,表示原始图像的尺寸(高度和宽度)。
def __init__(self, boxes, orig_shape) -> None:
# 使用检测框数据和原始图像形状初始化 Boxes 类。
# 此类管理检测框,提供对框坐标、置信度分数、类标识符和可选跟踪 ID 的轻松访问和操作。它支持框坐标的多种格式,包括绝对格式和规范化格式。
# 参数:
# boxes (torch.Tensor | np.ndarray):带有形状为 (num_boxes, 6) 或 (num_boxes, 7) 的检测框的张量或 numpy 数组。列应包含 [x1, y1, x2, y2, confidence, class, (optional) track_id]。
# orig_shape (Tuple[int, int]):原始图像形状为 (height, width)。用于规范化。
"""
Initialize the Boxes class with detection box data and the original image shape.
This class manages detection boxes, providing easy access and manipulation of box coordinates,
confidence scores, class identifiers, and optional tracking IDs. It supports multiple formats
for box coordinates, including both absolute and normalized forms.
Args:
boxes (torch.Tensor | np.ndarray): A tensor or numpy array with detection boxes of shape
(num_boxes, 6) or (num_boxes, 7). Columns should contain
[x1, y1, x2, y2, confidence, class, (optional) track_id].
orig_shape (Tuple[int, int]): The original image shape as (height, width). Used for normalization.
Attributes:
data (torch.Tensor): The raw tensor containing detection boxes and their associated data.
orig_shape (Tuple[int, int]): The original image size, used for normalization.
is_track (bool): Indicates whether tracking IDs are included in the box data.
Examples:
>>> import torch
>>> boxes = torch.tensor([[100, 50, 150, 100, 0.9, 0]])
>>> orig_shape = (480, 640)
>>> detection_boxes = Boxes(boxes, orig_shape)
>>> print(detection_boxes.xyxy)
tensor([[100., 50., 150., 100.]])
"""
# 如果输入的 boxes 是一维的(即只有一个检测框),则将其扩展为二维张量。具体操作是通过在第 0 维添加一个新的维度( None 或 np.newaxis ),从而将形状从 (n,) 转换为 (1, n) 。这一步是为了确保后续操作的一致性,因为类的设计假设 boxes 是二维的。
if boxes.ndim == 1:
boxes = boxes[None, :]
# 获取 boxes 的最后一维大小,即 每个检测框数据的列数 。例如, boxes 的形状可能是 (num_boxes, 6) 或 (num_boxes, 7) 。
n = boxes.shape[-1]
# 断言 boxes 的最后一维大小必须为 6 或 7。这是因为 :
# 如果大小为 6,表示每个检测框包含 [x1, y1, x2, y2, confidence, class] 。
# 如果大小为 7,表示每个检测框包含 [x1, y1, x2, y2, confidence, class, track_id] 。
# 如果不符合这个条件,会抛出异常,提示用户输入的格式不正确。
assert n in {6, 7}, f"expected 6 or 7 values but got {n}" # xyxy, track_id, conf, cls 预期为 6 或 7 个值,但得到的结果为 {n} 。
# 调用基类 BaseTensor 的初始化方法,将 boxes 和 orig_shape 传递给基类。这一步是为了继承基类的通用功能,例如张量管理或设备转换。
super().__init__(boxes, orig_shape)
# 根据 boxes 的最后一维大小,判断是否包含跟踪ID。
# 如果大小为 7,则表示包含跟踪ID, is_track 设置为 True 。
# 如果大小为 6,则表示不包含跟踪ID, is_track 设置为 False 。
self.is_track = n == 7
# 将 原始图像的尺寸 ( orig_shape )存储为类的属性。这个属性 用于后续的归一化操作 ,例如将检测框坐标从绝对值转换为相对于图像尺寸的比例值。
self.orig_shape = orig_shape
# 这段初始化代码的主要功能是。检查输入的 boxes 数据格式是否符合要求(必须是二维的,且最后一维大小为 6 或 7)。根据输入数据的格式,设置类的属性 is_track ,以标识是否包含跟踪ID。将原始图像的尺寸存储为属性 orig_shape ,以便后续进行归一化操作。调用基类的初始化方法,完成对底层数据结构的初始化。通过这些步骤, Boxes 类能够正确地初始化检测框数据,并为后续的操作(如坐标转换、归一化等)提供必要的信息。
# 这段代码定义了 Boxes 类的一个属性 xyxy ,用于获取检测框的坐标,格式为 [x1, y1, x2, y2] 。
# @property 是一个装饰器,用于将类中的方法定义为属性。这意味着可以通过 obj.xyxy 的方式访问这个方法的返回值,而不需要像普通方法那样调用 obj.xyxy() 。这使得代码更加简洁,同时隐藏了内部实现细节。
@property
# 定义了一个名为 xyxy 的方法,它属于 Boxes 类的实例。这个方法的作用是提取检测框的坐标部分。
def xyxy(self):
# 返回 [x1, y1, x2, y2] 格式的边界框。
# 返回:
# (torch.Tensor | numpy.ndarray):一个张量或 numpy 数组,形状为 (n, 4),包含 [x1, y1, x2, y2] 格式的边界框坐标,其中 n 是框的数量。
"""
Returns bounding boxes in [x1, y1, x2, y2] format.
Returns:
(torch.Tensor | numpy.ndarray): A tensor or numpy array of shape (n, 4) containing bounding box
coordinates in [x1, y1, x2, y2] format, where n is the number of boxes.
Examples:
>>> results = model("image.jpg")
>>> boxes = results[0].boxes
>>> xyxy = boxes.xyxy
>>> print(xyxy)
"""
# 返回 self.data 的前 4 列。 self.data 是一个二维张量或 NumPy 数组,存储了检测框的所有信息。根据类的定义, self.data 的每一行表示一个检测框,其列的含义如下 :
# 第 0 列到第 3 列 :检测框的坐标 [x1, y1, x2, y2] 。
# 第 4 列 :置信度分数(confidence)。
# 第 5 列 :类别标签(class)。
# 第 6 列(可选) :跟踪ID(track ID,如果存在)。
# 通过 self.data[:, :4] ,代码提取了每一行的前 4 列,即检测框的 [x1, y1, x2, y2] 坐标部分。
return self.data[:, :4]
# 这段代码定义了一个属性 xyxy ,用于快速获取检测框的坐标信息 [x1, y1, x2, y2] 。它通过简单的切片操作从底层数据结构 self.data 中提取所需的列,方便用户在不关心其他信息(如置信度、类别标签等)的情况下直接访问检测框的坐标。这种设计使得代码更加模块化,同时也提高了代码的可读性和易用性。
# 这段代码定义了 Boxes 类的一个属性 conf ,用于获取检测框的置信度分数(confidence scores)。
# @property 是一个装饰器,用于将类中的方法定义为属性。这意味着可以通过 obj.conf 的方式访问这个方法的返回值,而不需要像普通方法那样调用 obj.conf() 。这种设计使得代码更加简洁,同时隐藏了内部实现细节。
@property
# 定义了一个名为 conf 的方法,它属于 Boxes 类的实例。这个方法的作用是提取检测框的置信度分数。
def conf(self):
# 返回每个检测框的置信度分数。
# 返回:
# (torch.Tensor | numpy.ndarray):包含每个检测的置信度分数的一维张量或数组,形状为 (N,),其中 N 是检测数。
"""
Returns the confidence scores for each detection box.
Returns:
(torch.Tensor | numpy.ndarray): A 1D tensor or array containing confidence scores for each detection,
with shape (N,) where N is the number of detections.
Examples:
>>> boxes = Boxes(torch.tensor([[10, 20, 30, 40, 0.9, 0]]), orig_shape=(100, 100))
>>> conf_scores = boxes.conf
>>> print(conf_scores)
tensor([0.9000])
"""
# 返回 self.data 的倒数第二列。
# self.data 是一个二维张量或 NumPy 数组,存储了检测框的所有信息。根据类的定义, self.data 的每一行表示一个检测框,其列的含义如下 :
# 第 0 列到第 3 列 :检测框的坐标 [x1, y1, x2, y2] 。
# 第 4 列 :置信度分数(confidence)。
# 第 5 列 :类别标签(class)。
# 第 6 列(可选) :跟踪ID(track ID,如果存在)。
# 通过 self.data[:, -2] ,代码提取了每一行的倒数第二列,即置信度分数。
return self.data[:, -2]
# 这段代码定义了一个属性 conf ,用于快速获取检测框的置信度分数。它通过简单的索引操作从底层数据结构 self.data 中提取所需的列,方便用户在不关心其他信息(如坐标、类别标签等)的情况下直接访问置信度分数。这种设计使得代码更加模块化,同时也提高了代码的可读性和易用性。此外,使用 @property 装饰器将方法定义为属性,使得用户可以像访问普通属性一样访问置信度分数,而无需显式调用方法,进一步提升了代码的简洁性和用户体验。
# 这段代码定义了 Boxes 类的一个属性 cls ,用于获取检测框的类别标签(class labels)。
# @property 是一个装饰器,用于将类中的方法定义为属性。这意味着可以通过 obj.cls 的方式访问这个方法的返回值,而不需要像普通方法那样调用 obj.cls() 。这种设计使得代码更加简洁,同时隐藏了内部实现细节。
@property
# 定义了一个名为 cls 的方法,它属于 Boxes 类的实例。这个方法的作用是提取检测框的类别标签。
def cls(self):
# 返回表示每个边界框类别预测的类别 ID 张量。
# 返回:
# (torch.Tensor | numpy.ndarray):包含每个检测框的类别 ID 的张量或 numpy 数组。形状为 (N,),其中 N 是框的数量。
"""
Returns the class ID tensor representing category predictions for each bounding box.
Returns:
(torch.Tensor | numpy.ndarray): A tensor or numpy array containing the class IDs for each detection box.
The shape is (N,), where N is the number of boxes.
Examples:
>>> results = model("image.jpg")
>>> boxes = results[0].boxes
>>> class_ids = boxes.cls
>>> print(class_ids) # tensor([0., 2., 1.])
"""
# 返回 self.data 的最后一列。
# self.data 是一个二维张量或 NumPy 数组,存储了检测框的所有信息。根据类的定义, self.data 的每一行表示一个检测框,其列的含义如下 :
# 第 0 列到第 3 列 :检测框的坐标 [x1, y1, x2, y2] 。
# 第 4 列 :置信度分数(confidence)。
# 第 5 列 :类别标签(class)。
# 第 6 列(可选) :跟踪ID(track ID,如果存在)。
# 通过 self.data[:, -1] ,代码提取了每一行的最后一列,即类别标签。
return self.data[:, -1]
# 这段代码定义了一个属性 cls ,用于快速获取检测框的类别标签。它通过简单的索引操作从底层数据结构 self.data 中提取所需的列,方便用户在不关心其他信息(如坐标、置信度分数等)的情况下直接访问类别标签。这种设计使得代码更加模块化,同时也提高了代码的可读性和易用性。此外,使用 @property 装饰器将方法定义为属性,使得用户可以像访问普通属性一样访问类别标签,而无需显式调用方法,进一步提升了代码的简洁性和用户体验。
# 这段代码定义了 Boxes 类的一个属性 id ,用于获取检测框的跟踪ID(如果存在)。
# @property 是一个装饰器,用于将类中的方法定义为属性。这意味着可以通过 obj.id 的方式访问这个方法的返回值,而不需要像普通方法那样调用 obj.id() 。这种设计使得代码更加简洁,同时隐藏了内部实现细节。
@property
# 定义了一个名为 id 的方法,它属于 Boxes 类的实例。这个方法的作用是提取检测框的跟踪ID(如果存在)。
def id(self):
# 如果可用,返回每个检测框的跟踪 ID。
# 返回:
# (torch.Tensor | None):如果启用了跟踪,则返回包含每个框的跟踪 ID 的张量,否则返回 None。形状为 (N,),其中 N 是框的数量。
# 注意事项:
# - 此属性仅在启用跟踪时可用(即当 `is_track` 为 True 时)。
# - 跟踪 ID 通常用于在视频分析中关联跨多帧的检测。
"""
Returns the tracking IDs for each detection box if available.
Returns:
(torch.Tensor | None): A tensor containing tracking IDs for each box if tracking is enabled,
otherwise None. Shape is (N,) where N is the number of boxes.
Examples:
>>> results = model.track("path/to/video.mp4")
>>> for result in results:
... boxes = result.boxes
... if boxes.is_track:
... track_ids = boxes.id
... print(f"Tracking IDs: {track_ids}")
... else:
... print("Tracking is not enabled for these boxes.")
Notes:
- This property is only available when tracking is enabled (i.e., when `is_track` is True).
- The tracking IDs are typically used to associate detections across multiple frames in video analysis.
"""
# 根据 is_track 的值来决定是否返回跟踪ID。
# 如果 is_track 为 True ,表示检测框数据中包含跟踪ID,此时返回 self.data 的倒数第三列,即跟踪ID。
# 如果 is_track 为 False ,表示检测框数据中不包含跟踪ID,此时返回 None 。
# 这种实现方式确保了代码的灵活性和健壮性,能够根据数据的实际情况返回正确的跟踪ID或 None 。
return self.data[:, -3] if self.is_track else None
# 这段代码定义了 Boxes 类的一个属性 xywh ,用于将检测框的坐标从 [x1, y1, x2, y2] 格式转换为 [x_center, y_center, width, height] 格式。
# @property 是一个装饰器,用于将类中的方法定义为属性。这意味着可以通过 obj.xywh 的方式访问这个方法的返回值,而不需要像普通方法那样调用 obj.xywh() 。这种设计使得代码更加简洁,同时隐藏了内部实现细节。
@property
# @lru_cache 是一个 缓存装饰器 ,用于缓存方法的返回值,以提高性能。它会存储最近调用的结果,并在后续调用时直接返回缓存值,避免重复计算。这里设置 maxsize=2 ,表示 最多缓存两次调用的结果 。注释中提到 maxsize=1 也足够,因为通常情况下, xywh 的计算结果在短时间内不会频繁变化。
@lru_cache(maxsize=2) # maxsize 1 should suffice
# 定义了一个名为 xywh 的方法,它属于 Boxes 类的实例。这个方法的作用是将检测框的坐标从 [x1, y1, x2, y2] 格式转换为 [x_center, y_center, width, height] 格式。
def xywh(self):
# 将边界框从 [x1, y1, x2, y2] 格式转换为 [x, y, width, height] 格式。
"""
Convert bounding boxes from [x1, y1, x2, y2] format to [x, y, width, height] format.
Returns:
(torch.Tensor | numpy.ndarray): Boxes in [x_center, y_center, width, height] format, where x_center, y_center are the coordinates of
the center point of the bounding box, width, height are the dimensions of the bounding box and the
shape of the returned tensor is (N, 4), where N is the number of boxes.
Examples:
>>> boxes = Boxes(torch.tensor([[100, 50, 150, 100], [200, 150, 300, 250]]), orig_shape=(480, 640))
>>> xywh = boxes.xywh
>>> print(xywh)
tensor([[100.0000, 50.0000, 50.0000, 50.0000],
[200.0000, 150.0000, 100.0000, 100.0000]])
"""
# 调用 ops.xyxy2xywh 函数,将检测框的 [x1, y1, x2, y2] 坐标转换为 [x_center, y_center, width, height] 格式。
return ops.xyxy2xywh(self.xyxy)
# 这段代码定义了一个属性 xywh ,用于将检测框的坐标从 [x1, y1, x2, y2] 格式转换为 [x_center, y_center, width, height] 格式。它通过调用 ops.xyxy2xywh 函数完成转换,并使用 @lru_cache 装饰器缓存结果,以提高性能。这种设计使得代码更加模块化,同时也提高了代码的可读性和易用性。
# 性能优化 : @lru_cache 装饰器用于缓存计算结果,避免重复计算,尤其在多次调用 xywh 属性时,可以显著提高性能。
# 这段代码定义了 Boxes 类的一个属性 xyxyn ,用于将检测框的坐标从绝对值 [x1, y1, x2, y2] 转换为相对于原始图像尺寸的归一化坐标 [x1_norm, y1_norm, x2_norm, y2_norm] 。
# @property 是一个装饰器,用于将类中的方法定义为属性。这意味着可以通过 obj.xyxyn 的方式访问这个方法的返回值,而不需要像普通方法那样调用 obj.xyxyn() 。这种设计使得代码更加简洁,同时隐藏了内部实现细节。
@property
# @lru_cache 是一个 缓存装饰器 ,用于缓存方法的返回值,以提高性能。它会存储最近调用的结果,并在后续调用时直接返回缓存值,避免重复计算。这里设置 maxsize=2 ,表示 最多缓存两次调用的结果 。这在多次访问 xyxyn 属性时可以显著提高性能,尤其是在处理大量检测框时。
@lru_cache(maxsize=2)
# 定义了一个名为 xyxyn 的方法,它属于 Boxes 类的实例。这个方法的作用是将检测框的坐标归一化到 [0, 1] 范围内。
def xyxyn(self):
# 返回相对于原始图像大小的标准化边界框坐标。
# 此属性计算并返回 [x1, y1, x2, y2] 格式的边界框坐标,根据原始图像尺寸标准化为 [0, 1] 范围。
# 返回:
# (torch.Tensor | numpy.ndarray):标准化边界框坐标,形状为 (N, 4),其中 N 是框的数量。每行包含标准化为 [0, 1] 的 [x1, y1, x2, y2] 值。
"""
Returns normalized bounding box coordinates relative to the original image size.
This property calculates and returns the bounding box coordinates in [x1, y1, x2, y2] format,
normalized to the range [0, 1] based on the original image dimensions.
Returns:
(torch.Tensor | numpy.ndarray): Normalized bounding box coordinates with shape (N, 4), where N is
the number of boxes. Each row contains [x1, y1, x2, y2] values normalized to [0, 1].
Examples:
>>> boxes = Boxes(torch.tensor([[100, 50, 300, 400, 0.9, 0]]), orig_shape=(480, 640))
>>> normalized = boxes.xyxyn
>>> print(normalized)
tensor([[0.1562, 0.1042, 0.4688, 0.8333]])
"""
# 获取检测框的 [x1, y1, x2, y2] 坐标(通过 self.xyxy 属性)。
# 如果 self.xyxy 是一个 torch.Tensor ,则调用 .clone() 方法创建一个新的张量副本,以避免修改原始数据。
# 如果 self.xyxy 是一个 NumPy 数组,则调用 np.copy() 创建一个新的数组副本。这种设计确保了代码的通用性,同时避免了对原始数据的直接修改。
xyxy = self.xyxy.clone() if isinstance(self.xyxy, torch.Tensor) else np.copy(self.xyxy)
# 将检测框的 x 坐标( x1 和 x2 )归一化到 [0, 1] 范围内。具体操作是 :将 x1 和 x2 坐标除以原始图像的宽度( self.orig_shape[1] )。
xyxy[..., [0, 2]] /= self.orig_shape[1]
# 将检测框的 y 坐标( y1 和 y2 )归一化到 [0, 1] 范围内。具体操作是 :将 y1 和 y2 坐标除以原始图像的高度( self.orig_shape[0] )。
xyxy[..., [1, 3]] /= self.orig_shape[0]
# 返回 归一化后的检测框坐标 [x1_norm, y1_norm, x2_norm, y2_norm] 。
return xyxy
# 这段代码定义了一个属性 xyxyn ,用于将检测框的绝对坐标 [x1, y1, x2, y2] 转换为相对于原始图像尺寸的归一化坐标 [x1_norm, y1_norm, x2_norm, y2_norm] 。归一化操作通过将坐标除以图像的宽度和高度完成,确保坐标值在 [0, 1] 范围内。 归一化操作:将检测框坐标归一化到 [0, 1] 范围内,便于后续处理(如模型输入或跨图像尺寸的比较)。数据安全性:通过 .clone() 或 np.copy() 创建副本,避免直接修改原始数据。性能优化:使用 @lru_cache 装饰器缓存结果,避免重复计算,提高性能。通用性:支持 torch.Tensor 和 NumPy 数组两种数据类型,增强了代码的适用性。
# 这段代码定义了 Boxes 类的一个属性 xywhn ,用于将检测框的坐标从 [x1, y1, x2, y2] 格式转换为归一化的 [x_center, y_center, width, height] 格式。归一化是相对于原始图像的宽度和高度进行的。
# @property 是一个装饰器,用于将类中的方法定义为属性。这意味着可以通过 obj.xywhn 的方式访问这个方法的返回值,而不需要像普通方法那样调用 obj.xywhn() 。这种设计使得代码更加简洁,同时隐藏了内部实现细节。
@property
# @lru_cache 是一个缓存装饰器,用于缓存方法的返回值,以提高性能。它会存储最近调用的结果,并在后续调用时直接返回缓存值,避免重复计算。这里设置 maxsize=2 ,表示最多缓存两次调用的结果。这在多次访问 xywhn 属性时可以显著提高性能,尤其是在处理大量检测框时。
@lru_cache(maxsize=2)
# 定义了一个名为 xywhn 的方法,它属于 Boxes 类的实例。这个方法的作用是将检测框的坐标从 [x1, y1, x2, y2] 格式转换为归一化的 [x_center, y_center, width, height] 格式。
def xywhn(self):
# 返回 [x, y, width, height] 格式的标准化边界框。
# 此属性计算并返回 [x_center, y_center, width, height] 格式的标准化边界框坐标,其中所有值均相对于原始图像尺寸。
# 返回:
# (torch.Tensor | numpy.ndarray):形状为 (N, 4) 的标准化边界框,其中 N 是框的数量。每行包含基于原始图像尺寸标准化为 [0, 1] 的 [x_center, y_center, width, height] 值。
"""
Returns normalized bounding boxes in [x, y, width, height] format.
This property calculates and returns the normalized bounding box coordinates in the format
[x_center, y_center, width, height], where all values are relative to the original image dimensions.
Returns:
(torch.Tensor | numpy.ndarray): Normalized bounding boxes with shape (N, 4), where N is the
number of boxes. Each row contains [x_center, y_center, width, height] values normalized
to [0, 1] based on the original image dimensions.
Examples:
>>> boxes = Boxes(torch.tensor([[100, 50, 150, 100, 0.9, 0]]), orig_shape=(480, 640))
>>> normalized = boxes.xywhn
>>> print(normalized)
tensor([[0.1953, 0.1562, 0.0781, 0.1042]])
"""
# 调用了 ops.xyxy2xywh 函数,将检测框的 [x1, y1, x2, y2] 坐标转换为 [x_center, y_center, width, height] 格式。
xywh = ops.xyxy2xywh(self.xyxy)
# 将检测框的 x_center 和 width 坐标归一化到 [0, 1] 范围内。 将 x_center 和 width 坐标除以原始图像的宽度( self.orig_shape[1] )。
xywh[..., [0, 2]] /= self.orig_shape[1]
# 将检测框的 y_center 和 height 坐标归一化到 [0, 1] 范围内。 将 y_center 和 height 坐标除以原始图像的高度( self.orig_shape[0] )。
xywh[..., [1, 3]] /= self.orig_shape[0]
# 返回归一化后的检测框坐标 [x_center_norm, y_center_norm, width_norm, height_norm] 。
return xywh
# 这段代码定义了一个属性 xywhn ,用于将检测框的坐标从 [x1, y1, x2, y2] 格式转换为归一化的 [x_center, y_center, width, height] 格式。归一化操作通过将坐标除以图像的宽度和高度完成,确保坐标值在 [0, 1] 范围内。这种归一化格式在某些算法(如目标检测模型的输入或非极大值抑制)中非常有用。 坐标转换:从 [x1, y1, x2, y2] 格式转换为 [x_center, y_center, width, height] 格式。 归一化:将坐标值归一化到 [0, 1] 范围内,便于后续处理。性能优化:使用 @lru_cache 装饰器缓存结果,避免重复计算,提高性能。代码简洁性:通过 @property 装饰器,用户可以直接访问 xywhn 属性,而无需显式调用方法,进一步提升了代码的简洁性和用户体验。
# Boxes 类是一个用于管理和操作目标检测框的工具类,它提供了一种高效且灵活的方式来处理检测框的坐标、置信度分数、类别标签以及可选的跟踪ID。通过支持多种坐标格式(如 [x1, y1, x2, y2] 和 [x_center, y_center, width, height] )及其归一化形式,该类能够方便地在不同表示方式之间进行转换,从而满足不同场景下的需求。此外, Boxes 类利用属性装饰器( @property )和缓存机制( @lru_cache )优化了代码的可读性和性能,使得用户可以像访问普通属性一样轻松获取检测框的各种信息,而无需关心底层实现细节。这种设计不仅提高了代码的模块化程度,还增强了其在目标检测、目标跟踪以及相关任务中的通用性和实用性。
# 这段代码定义了一个名为 Masks 的类,用于存储和操作目标检测中的分割掩码(segmentation masks)。它继承自 BaseTensor ,并提供了处理分割掩码的功能,包括将掩码从像素坐标转换为归一化坐标。
# 定义了一个名为 Masks 的类,继承自 BaseTensor 。 BaseTensor 是一个基类,提供了与张量操作相关的通用功能,例如设备管理和数据类型转换。
class Masks(BaseTensor):
# 用于存储和操作检测掩码的类。
# 此类扩展了 BaseTensor 并提供处理分割掩码的功能,包括在像素和归一化坐标之间转换的方法。
# 方法:
# cpu():返回带有掩码张量的 Masks 对象在 CPU 内存上的副本。
# numpy():以 numpy 数组的形式返回带有掩码张量的 Masks 对象副本。
# cuda():返回带有掩码张量的 Masks 对象在 GPU 内存上的副本。
# to(*args, **kwargs):返回带有掩码的 Masks 对象副本指定设备和 dtype 上的张量。
"""
A class for storing and manipulating detection masks.
This class extends BaseTensor and provides functionality for handling segmentation masks,
including methods for converting between pixel and normalized coordinates.
Attributes:
data (torch.Tensor | numpy.ndarray): The raw tensor or array containing mask data.
orig_shape (tuple): Original image shape in (height, width) format.
xy (List[numpy.ndarray]): A list of segments in pixel coordinates.
xyn (List[numpy.ndarray]): A list of normalized segments.
Methods:
cpu(): Returns a copy of the Masks object with the mask tensor on CPU memory.
numpy(): Returns a copy of the Masks object with the mask tensor as a numpy array.
cuda(): Returns a copy of the Masks object with the mask tensor on GPU memory.
to(*args, **kwargs): Returns a copy of the Masks object with the mask tensor on specified device and dtype.
Examples:
>>> masks_data = torch.rand(1, 160, 160)
>>> orig_shape = (720, 1280)
>>> masks = Masks(masks_data, orig_shape)
>>> pixel_coords = masks.xy
>>> normalized_coords = masks.xyn
"""
# 这段代码是 Masks 类的初始化方法 __init__ 的实现,用于初始化分割掩码(segmentation masks)的数据结构,并设置一些关键属性。
# 定义了 Masks 类的初始化方法 __init__ ,它接受两个参数。
# 1.masks :一个张量( torch.Tensor )或 NumPy 数组,表示分割掩码的数据。其形状通常为 (num_masks, height, width) ,其中 num_masks 是掩码的数量, height 和 width 是掩码的分辨率。
# 2.orig_shape :一个元组,表示原始图像的尺寸(高度和宽度),格式为 (height, width) 。这个参数用于后续的坐标转换和归一化操作。
def __init__(self, masks, orig_shape) -> None:
# 使用检测掩码数据和原始图像形状初始化 Masks 类。
"""
Initialize the Masks class with detection mask data and the original image shape.
Args:
masks (torch.Tensor | np.ndarray): Detection masks with shape (num_masks, height, width).
orig_shape (tuple): The original image shape as (height, width). Used for normalization.
Examples:
>>> import torch
>>> from ultralytics.engine.results import Masks
>>> masks = torch.rand(10, 160, 160) # 10 masks of 160x160 resolution
>>> orig_shape = (720, 1280) # Original image shape
>>> mask_obj = Masks(masks, orig_shape)
"""
# 检查输入的 masks 是否是二维的(即只有一个掩码且没有批次维度)。
if masks.ndim == 2:
# 如果是二维的(形状为 (height, width) ),则通过在第 0 维添加一个新的维度( None 或 np.newaxis ),将其扩展为三维张量(形状为 (1, height, width) )。这一步是为了确保后续操作的一致性,因为类的设计假设 masks 是三维的。
masks = masks[None, :]
# 调用基类 BaseTensor 的初始化方法,将处理后的 masks 和 orig_shape 传递给基类。这一步是为了继承基类的通用功能,例如张量管理、设备转换(如 CPU/GPU)或数据类型转换。通过这种方式, Masks 类可以复用基类的实现,减少重复代码。
super().__init__(masks, orig_shape)
# 这段初始化代码的主要功能是。检查并调整输入数据的维度:如果输入的掩码数据是二维的(没有批次维度),则通过添加一个新的维度将其扩展为三维张量。这一步确保了掩码数据的形状符合类的预期,便于后续处理。初始化基类:通过调用基类的初始化方法,将掩码数据和原始图像尺寸传递给基类,以便利用基类提供的通用功能(如设备管理和数据类型转换)。通过这种设计, Masks 类能够灵活地处理不同形状的掩码数据,并为后续的分割掩码操作(如坐标转换、归一化等)提供支持。
# 这段代码定义了 Masks 类的一个属性 xyn ,用于将分割掩码(segmentation masks)转换为归一化的多边形轮廓坐标(normalized polygon coordinates)。
# @property 是一个装饰器,用于将类中的方法定义为属性。这意味着可以通过 obj.xyn 的方式访问这个方法的返回值,而不需要像普通方法那样调用 obj.xyn() 。这种设计使得代码更加简洁,同时隐藏了内部实现细节。
@property
# @lru_cache 是一个缓存装饰器,用于缓存方法的返回值,以提高性能。这里设置 maxsize=1 ,表示最多缓存一次调用的结果。由于分割掩码的归一化坐标通常只需要计算一次,因此缓存一次结果即可满足需求。
@lru_cache(maxsize=1)
# 定义了一个名为 xyn 的方法,它属于 Masks 类的实例。这个方法的作用是将分割掩码转换为归一化的多边形轮廓坐标。
def xyn(self):
# 返回分割掩码的标准化 xy 坐标。
# 此属性计算并缓存分掩码的标准化 xy 坐标。坐标相对于原始图像形状进行标准化。
"""
Returns normalized xy-coordinates of the segmentation masks.
This property calculates and caches the normalized xy-coordinates of the segmentation masks. The coordinates
are normalized relative to the original image shape.
Returns:
(List[numpy.ndarray]): A list of numpy arrays, where each array contains the normalized xy-coordinates
of a single segmentation mask. Each array has shape (N, 2), where N is the number of points in the
mask contour.
Examples:
>>> results = model("image.jpg")
>>> masks = results[0].masks
>>> normalized_coords = masks.xyn
>>> print(normalized_coords[0]) # Normalized coordinates of the first mask
"""
# 提取分割轮廓 :调用 ops.masks2segments(self.data) ,将分割掩码(二维张量)转换为多边形轮廓的坐标列表。每个轮廓是一个 NumPy 数组,表示掩码的边界点。
# 归一化坐标 :对于每个轮廓坐标 x ,调用 ops.scale_coords 函数,将其归一化到 [0, 1] 范围内。归一化是相对于原始图像尺寸( self.orig_shape )进行的。 self.data.shape[1:] 表示掩码的分辨率( height, width ),用于确定掩码的原始尺寸。
# 返回归一化坐标列表 :返回一个包含归一化多边形轮廓坐标的列表,每个轮廓是一个 NumPy 数组,形状为 (N, 2) ,表示轮廓的 (x, y) 坐标。
# def scale_coords(img1_shape, coords, img0_shape, ratio_pad=None, normalize=False, padding=True): -> 用于将坐标从一个图像尺寸( img1_shape )缩放到另一个图像尺寸( img0_shape ),并考虑了可能的填充和归一化。返回缩放后的坐标数组。 -> return coords
# # def masks2segments(masks, strategy="all"): -> 用于将二值掩码转换为多边形顶点(segments)。它支持两种策略: all 和 largest ,分别用于处理多个连通区域的情况。返回 转换后的多边形顶点列表 。 -> return segments
return [
ops.scale_coords(self.data.shape[1:], x, self.orig_shape, normalize=True)
for x in ops.masks2segments(self.data)
]
# 这段代码定义了一个属性 xyn ,用于将分割掩码转换为归一化的多边形轮廓坐标。通过调用 ops.masks2segments 提取掩码的轮廓,并使用 ops.scale_coords 将轮廓坐标归一化到 [0, 1] 范围内。这种设计使得分割掩码可以方便地用于下游任务(如可视化、计算IoU等),并且通过 @lru_cache 装饰器缓存结果,避免重复计算,提高了性能。归一化:将轮廓坐标归一化到 [0, 1] 范围内,便于跨不同图像尺寸进行比较和处理。轮廓提取:通过 ops.masks2segments 将掩码转换为多边形轮廓,适用于目标分割任务。性能优化:使用 @lru_cache 缓存归一化结果,避免重复计算。简洁性:通过 @property 装饰器,用户可以直接访问 xyn 属性,而无需显式调用方法,提升了代码的易用性。
# 这段代码定义了 Masks 类的一个属性 xy ,用于将分割掩码(segmentation masks)转换为像素坐标(pixel coordinates)的多边形轮廓。
# @property 是一个装饰器,用于将类中的方法定义为属性。这意味着可以通过 obj.xy 的方式访问这个方法的返回值,而不需要像普通方法那样调用 obj.xy() 。这种设计使得代码更加简洁,同时隐藏了内部实现细节。
@property
# @lru_cache 是一个缓存装饰器,用于缓存方法的返回值,以提高性能。这里设置 maxsize=1 ,表示最多缓存一次调用的结果。由于分割掩码的像素坐标通常只需要计算一次,因此缓存一次结果即可满足需求。
@lru_cache(maxsize=1)
# 定义了一个名为 xy 的方法,它属于 Masks 类的实例。这个方法的作用是将分割掩码转换为像素坐标(pixel coordinates)的多边形轮廓。
def xy(self):
# 返回掩码张量中每个片段的 [x, y] 像素坐标。
# 此属性计算并返回 Masks 对象中每个分割掩码的像素坐标列表。坐标已缩放以匹配原始图像尺寸。
"""
Returns the [x, y] pixel coordinates for each segment in the mask tensor.
This property calculates and returns a list of pixel coordinates for each segmentation mask in the
Masks object. The coordinates are scaled to match the original image dimensions.
Returns:
(List[numpy.ndarray]): A list of numpy arrays, where each array contains the [x, y] pixel
coordinates for a single segmentation mask. Each array has shape (N, 2), where N is the
number of points in the segment.
Examples:
>>> results = model("image.jpg")
>>> masks = results[0].masks
>>> xy_coords = masks.xy
>>> print(len(xy_coords)) # Number of masks
>>> print(xy_coords[0].shape) # Shape of first mask's coordinates
"""
# 提取分割轮廓 :调用 ops.masks2segments(self.data) ,将分割掩码(二维张量)转换为多边形轮廓的坐标列表。每个轮廓是一个 NumPy 数组,表示掩码的边界点。
# 缩放坐标 :对于每个轮廓坐标 x ,调用 ops.scale_coords 函数,将其缩放到原始图像的像素坐标。 self.data.shape[1:] 表示掩码的分辨率( height, width ),用于确定掩码的原始尺寸。 normalize=False 表示不进行归一化,而是直接将坐标缩放到像素级别。
# 返回像素坐标列表 :返回一个包含像素坐标多边形轮廓的列表,每个轮廓是一个 NumPy 数组,形状为 (N, 2) ,表示轮廓的 (x, y) 坐标。
return [
ops.scale_coords(self.data.shape[1:], x, self.orig_shape, normalize=False)
for x in ops.masks2segments(self.data)
]
# 这段代码定义了一个属性 xy ,用于将分割掩码转换为像素坐标(pixel coordinates)的多边形轮廓。通过调用 ops.masks2segments 提取掩码的轮廓,并使用 ops.scale_coords 将轮廓坐标缩放到原始图像的像素级别。这种设计使得分割掩码可以方便地用于下游任务(如可视化、计算IoU等),并且通过 @lru_cache 装饰器缓存结果,避免重复计算,提高了性能。 像素坐标:将轮廓坐标缩放到原始图像的像素级别,便于直接在图像上绘制或处理。轮廓提取:通过 ops.masks2segments 将掩码转换为多边形轮廓,适用于目标分割任务。性能优化:使用 @lru_cache 缓存像素坐标结果,避免重复计算。简洁性:通过 @property 装饰器,用户可以直接访问 xy 属性,而无需显式调用方法,提升了代码的易用性。
# Masks 类是一个用于存储和操作目标检测分割掩码的工具类。它继承自 BaseTensor ,并提供了以下功能。分割掩码的存储:支持以张量或 NumPy 数组的形式存储掩码数据。坐标转换:通过 xy 和 xyn 属性,分别提供像素坐标和归一化坐标,便于在不同场景下使用。性能优化:使用 @lru_cache 装饰器缓存计算结果,避免重复计算。灵活性:支持多种数据格式(张量或数组)和设备(CPU/GPU),并通过 cpu() 、 numpy() 和 cuda() 方法提供数据转换功能。通过这些设计, Masks 类能够高效地处理分割掩码数据,并为后续的目标检测和分割任务提供支持。
# 这段代码定义了一个名为 Keypoints 的类,用于存储和操作目标检测中的关键点(keypoints)数据。它封装了关键点的坐标操作、归一化以及置信度处理等功能,并支持将数据在不同设备(如 CPU 和 GPU)之间转换。
# 定义了一个名为 Keypoints 的类,继承自 BaseTensor 。 BaseTensor 是一个基类,供了与张量操作相关的通用功能,例如设备管理和数据类型转换。
class Keypoints(BaseTensor):
# 用于存储和操作检测关键点的类。
# 此类封装了处理关键点数据的功能,包括坐标操作、规范化和置信度值。
# 方法:
# cpu():返回 CPU 内存上的关键点张量的副本。
# numpy():返回关键点张量的副本作为 numpy 数组。
# cuda():返回GPU 内存上关键点张量的副本。
# to(*args, **kwargs): 返回具有指定设备和 dtype 的关键点张量的副本。
"""
A class for storing and manipulating detection keypoints.
This class encapsulates functionality for handling keypoint data, including coordinate manipulation,
normalization, and confidence values.
Attributes:
data (torch.Tensor): The raw tensor containing keypoint data.
orig_shape (Tuple[int, int]): The original image dimensions (height, width).
has_visible (bool): Indicates whether visibility information is available for keypoints.
xy (torch.Tensor): Keypoint coordinates in [x, y] format.
xyn (torch.Tensor): Normalized keypoint coordinates in [x, y] format, relative to orig_shape.
conf (torch.Tensor): Confidence values for each keypoint, if available.
Methods:
cpu(): Returns a copy of the keypoints tensor on CPU memory.
numpy(): Returns a copy of the keypoints tensor as a numpy array.
cuda(): Returns a copy of the keypoints tensor on GPU memory.
to(*args, **kwargs): Returns a copy of the keypoints tensor with specified device and dtype.
Examples:
>>> import torch
>>> from ultralytics.engine.results import Keypoints
>>> keypoints_data = torch.rand(1, 17, 3) # 1 detection, 17 keypoints, (x, y, conf)
>>> orig_shape = (480, 640) # Original image shape (height, width)
>>> keypoints = Keypoints(keypoints_data, orig_shape)
>>> print(keypoints.xy.shape) # Access xy coordinates
>>> print(keypoints.conf) # Access confidence values
>>> keypoints_cpu = keypoints.cpu() # Move keypoints to CPU
"""
# 这段代码是 Keypoints 类的初始化方法 __init__ 的实现,用于初始化关键点数据,并处理关键点的可见性(通过置信度过滤)。
# @smart_inference_mode() 是一个装饰器,用于确保在推理模式下执行初始化操作。这可以避免某些操作(如原地操作)导致的错误,例如在关键点置信度处理时可能出现的原地修改错误。
# def smart_inference_mode():
# -> 用于根据 PyTorch 的版本动态选择合适的装饰器,以优化模型推理模式。根据 TORCH_1_9 的值选择合适的装饰器。 如果 TORCH_1_9 为 True ,使用 torch.inference_mode() 装饰器。 否则,使用 torch.no_grad() 装饰器。 无论选择哪种装饰器,都会将其应用到输入函数 fn 上,并返回装饰后的函数。
# -> 最后, smart_inference_mode 函数返回嵌套的 decorate 函数。这样, smart_inference_mode 本身可以作为一个装饰器工厂使用。
# -> return decorate
@smart_inference_mode() # avoid keypoints < conf in-place error
# __init__ 是类的初始化方法,接受两个参数。
# 1.keypoints :一个张量( torch.Tensor ),包含关键点数据。其形状可以是 (num_objects, num_keypoints, 2) (仅 x, y 坐标)或 (num_objects, num_keypoints, 3) (包含 x, y 坐标和置信度)。
# 2.orig_shape :一个元组,表示原始图像的尺寸(高度和宽度),格式为 (height, width) 。
def __init__(self, keypoints, orig_shape) -> None:
# 使用检测关键点和原始图像尺寸初始化 Keypoints 对象。
# 此方法处理输入关键点张量,处理 2D 和 3D 格式。对于 3D 张量 (x、y、confidence),它通过将低置信度关键点的坐标设置为零来屏蔽它们。
# 参数:
# keypoints (torch.Tensor):包含关键点数据的张量。形状可以是:
# - (num_objects、num_keypoints、2) 仅适用于 x、y 坐标
# - (num_objects、num_keypoints、3) 适用于 x、y 坐标和置信度分数
# orig_shape (Tuple[int, int]):原始图像尺寸(高度、宽度)。
"""
Initializes the Keypoints object with detection keypoints and original image dimensions.
This method processes the input keypoints tensor, handling both 2D and 3D formats. For 3D tensors
(x, y, confidence), it masks out low-confidence keypoints by setting their coordinates to zero.
Args:
keypoints (torch.Tensor): A tensor containing keypoint data. Shape can be either:
- (num_objects, num_keypoints, 2) for x, y coordinates only
- (num_objects, num_keypoints, 3) for x, y coordinates and confidence scores
orig_shape (Tuple[int, int]): The original image dimensions (height, width).
Examples:
>>> kpts = torch.rand(1, 17, 3) # 1 object, 17 keypoints (COCO format), x,y,conf
>>> orig_shape = (720, 1280) # Original image height, width
>>> keypoints = Keypoints(kpts, orig_shape)
"""
# 检查输入的 keypoints 是否是二维的(即只有一个检测对象,形状为 (num_keypoints, 2) 或 (num_keypoints, 3) )。
if keypoints.ndim == 2:
# 如果是二维的,则通过在第 0 维添加一个新的维度,将其扩展为三维张量(形状变为 (1, num_keypoints, 2) 或 (1, num_keypoints, 3) )。这一步是为了确保后续操作的一致性,因为类的设计假设 keypoints 是三维的。
keypoints = keypoints[None, :]
# 检查 keypoints 的最后一维大小是否为 3,即是否包含置信度信息(x, y, conf)。
if keypoints.shape[2] == 3: # x, y, conf
# 创建一个掩码 mask ,标记置信度小于 0.5 的关键点。这些关键点被认为是不可见的。 keypoints[..., 2] 表示提取所有关键点的置信度值。
mask = keypoints[..., 2] < 0.5 # points with conf < 0.5 (not visible)
# 将置信度小于 0.5 的关键点的坐标(x, y)设置为 0。这一步是为了在后续处理中忽略这些不可见的关键点。 keypoints[..., :2] 表示提取所有关键点的 x 和 y 坐标。 [mask] 是一个布尔索引,用于 选择置信度小于 0.5 的关键点 。
keypoints[..., :2][mask] = 0
# 调用基类 BaseTensor 的初始化方法,将处理后的 keypoints 和 orig_shape 传递给基类。这一步是为了继承基类的通用功能,例如张量管理或设备转换。
super().__init__(keypoints, orig_shape)
# 设置属性 has_visible ,用于指示关键点数据 是否包含置信度信息 。 如果 self.data 的最后一维大小为 3,则表示包含置信度;否则,表示仅包含坐标信息。
self.has_visible = self.data.shape[-1] == 3
# 这段代码的主要功能是。数据预处理:将输入的关键点数据调整为三维张量(如果输入是二维的),以确保后续操作的一致性。置信度过滤:如果关键点数据包含置信度(最后一维大小为 3),则将置信度小于 0.5 的关键点的坐标设置为 0,从而忽略这些不可见的关键点。初始化基类:通过调用基类的初始化方法,将处理后的关键点数据和原始图像尺寸传递给基类,以便利用基类提供的通用功能。标记可见性:通过 self.has_visible 属性,记录关键点数据是否包含置信度信息,以便后续方法可以根据此信息决定是否处理置信度。通过这些设计, Keypoints 类能够灵活地处理不同格式的关键点数据,并为后续的关键点操作(如坐标转换、归一化等)提供支持。
# 这段代码定义了 Keypoints 类的一个属性 xy ,用于获取关键点的像素坐标 [x, y] 。
# @property 是一个装饰器,用于将类中的方法定义为属性。这意味着可以通过 obj.xy 的方式访问这个方法的返回值,而不需要像普通方法那样调用 obj.xy() 。这种设计使得代码更加简洁,同时隐藏了内部实现细节。
@property
# @lru_cache 是一个缓存装饰器,用于缓存方法的返回值,以提高性能。这里设置 maxsize=1 ,表示最多缓存一次调用的结果。由于关键点的坐标通常只需要计算一次,因此缓存一次结果即可满足需求。这在多次访问 xy 属性时可以显著提高性能。
@lru_cache(maxsize=1)
# 定义了一个名为 xy 的方法,它属于 Keypoints 类的实例。这个方法的作用是返回关键点的像素坐标 [x, y] 。
def xy(self):
# 返回关键点的 x、y 坐标。
# 注意事项:
# - 返回的坐标以像素为单位,相对于原始图像尺寸。
# - 如果使用置信度值初始化关键点,则仅返回置信度 >= 0.5 的关键点。
# - 此属性使用 LRU 缓存来提高重复访问的性能。
"""
Returns x, y coordinates of keypoints.
Returns:
(torch.Tensor): A tensor containing the x, y coordinates of keypoints with shape (N, K, 2), where N is
the number of detections and K is the number of keypoints per detection.
Examples:
>>> results = model("image.jpg")
>>> keypoints = results[0].keypoints
>>> xy = keypoints.xy
>>> print(xy.shape) # (N, K, 2)
>>> print(xy[0]) # x, y coordinates of keypoints for first detection
Notes:
- The returned coordinates are in pixel units relative to the original image dimensions.
- If keypoints were initialized with confidence values, only keypoints with confidence >= 0.5 are returned.
- This property uses LRU caching to improve performance on repeated access.
"""
# 返回 self.data 的前两列,即关键点的 [x, y] 坐标。 self.data 是一个三维张量,形状为 (num_objects, num_keypoints, 2) 或 (num_objects, num_keypoints, 3) ,其中 :
# num_objects 是检测对象的数量。
# num_keypoints 是每个对象的关键点数量。
# 最后一维表示关键点的坐标和(可选的)置信度。
# 通过 self.data[..., :2] ,代码提取了每个关键点的 [x, y] 坐标部分,忽略了置信度信息(如果存在)。
return self.data[..., :2]
# 这段代码定义了一个属性 xy ,用于快速获取关键点的像素坐标 [x, y] 。它通过简单的切片操作从底层数据结构 self.data 中提取所需的列,方便用户在不关心其他信息(如置信度)的情况下直接访问关键点的坐标。这种设计使得代码更加模块化,同时也提高了代码的可读性和易用性。 坐标提取:通过 self.data[..., :2] 提取关键点的 [x, y] 坐标,忽略置信度信息。性能优化:使用 @lru_cache 装饰器缓存结果,避免重复计算,提高性能。简洁性:通过 @property 装饰器,用户可以直接访问 xy 属性,而无需显式调用方法,进一步提升了代码的简洁性和用户体验。
# 这段代码定义了 Keypoints 类的一个属性 xyn ,用于获取归一化后的关键点坐标 [x, y] 。归一化是将关键点的像素坐标转换为相对于原始图像尺寸的比例值,范围在 [0, 1] 之间。
# @property 是一个装饰器,用于将类中的方法定义为属性。这意味着可以通过 obj.xyn 的方式访问这个方法的返回值,而不需要像普通方法那样调用 obj.xyn() 。这种设计使得代码更加简洁,同时隐藏了内部实现细节。
@property
# @lru_cache 是一个缓存装饰器,用于缓存方法的返回值,以提高性能。这里设置 maxsize=1 ,表示最多缓存一次调用的结果。由于归一化坐标通常只需要计算一次,因此缓存一次结果即可满足需求。这在多次访问 xyn 属性时可以显著提高性能。
@lru_cache(maxsize=1)
# 定义了一个名为 xyn 的方法,它属于 Keypoints 类的实例。这个方法的作用是返回归一化后的关键点坐标 [x, y] 。
def xyn(self):
# 返回相对于原始图像大小的关键点的归一化坐标 (x, y)。
"""
Returns normalized coordinates (x, y) of keypoints relative to the original image size.
Returns:
(torch.Tensor | numpy.ndarray): A tensor or array of shape (N, K, 2) containing normalized keypoint
coordinates, where N is the number of instances, K is the number of keypoints, and the last
dimension contains [x, y] values in the range [0, 1].
Examples:
>>> keypoints = Keypoints(torch.rand(1, 17, 2), orig_shape=(480, 640))
>>> normalized_kpts = keypoints.xyn
>>> print(normalized_kpts.shape)
torch.Size([1, 17, 2])
"""
# 获取关键点的像素坐标 [x, y] (通过 self.xy 属性)。
# 如果 self.xy 是一个 torch.Tensor ,则调用 .clone() 方法创建一个新的张量副本,以避免修改原始数据。
# 如果 self.xy 是一个 NumPy 数组,则调用 np.copy() 创建一个新的数组副本。
# 这种设计确保了代码的通用性,同时避免了对原始数据的直接修改。
xy = self.xy.clone() if isinstance(self.xy, torch.Tensor) else np.copy(self.xy)
# 将关键点的 x 坐标归一化到 [0, 1] 范围内。具体操作是 :将 x 坐标除以原始图像的宽度( self.orig_shape[1] )。
xy[..., 0] /= self.orig_shape[1]
# 将关键点的 y 坐标归一化到 [0, 1] 范围内。具体操作是 :将 y 坐标除以原始图像的高度( self.orig_shape[0] )。
xy[..., 1] /= self.orig_shape[0]
# 返回归一化后的关键点坐标 [x, y] 。
return xy
# 这段代码定义了一个属性 xyn ,用于将关键点的像素坐标 [x, y] 转换为归一化坐标。归一化是通过将坐标值除以原始图像的宽度和高度完成的,确保坐标值在 [0, 1] 范围内。这种设计使得关键点坐标可以方便地用于跨不同图像尺寸的比较和处理。 归一化操作:将关键点的 x 坐标除以图像宽度,y 坐标除以图像高度,确保坐标值在 [0, 1] 范围内。数据安全性:通过 .clone() 或 np.copy() 创建副本,避免直接修改原始数据。性能优化:使用 @lru_cache 装饰器缓存结果,避免重复计算,提高性能。简洁性:通过 @property 装饰器,用户可以直接访问 xyn 属性,而无需显式调用方法,进一步提升了代码的简洁性和用户体验。
# 这段代码定义了 Keypoints 类的一个属性 conf ,用于获取关键点的置信度(confidence)值。
# @property 是一个装饰器,用于将类中的方法定义为属性。这意味着可以通过 obj.conf 的方式访问这个方法的返回值,而不需要像普通方法那样调用 obj.conf() 。这种设计使得代码更加简洁,同时隐藏了内部实现细节。
@property
# @lru_cache 是一个缓存装饰器,用于缓存方法的返回值,以提高性能。这里设置 maxsize=1 ,表示最多缓存一次调用的结果。由于置信度值通常只需要计算一次,因此缓存一次结果即可满足需求。这在多次访问 conf 属性时可以显著提高性能。
@lru_cache(maxsize=1)
# 定义了一个名为 conf 的方法,它属于 Keypoints 类的实例。这个方法的作用是返回关键点的置信度值(如果存在)。
def conf(self):
# 返回每个关键点的置信度值。
"""
Returns confidence values for each keypoint.
Returns:
(torch.Tensor | None): A tensor containing confidence scores for each keypoint if available,
otherwise None. Shape is (num_detections, num_keypoints) for batched data or (num_keypoints,)
for single detection.
Examples:
>>> keypoints = Keypoints(torch.rand(1, 17, 3), orig_shape=(640, 640)) # 1 detection, 17 keypoints
>>> conf = keypoints.conf
>>> print(conf.shape) # torch.Size([1, 17])
"""
# 检查 self.has_visible 的值。
# 如果 self.has_visible 为 True ,表示关键点数据包含置信度信息(即 self.data 的最后一维大小为 3)。此时,返回 self.data 的最后一列(索引为 2),即置信度值。
# 如果 self.has_visible 为 False ,表示关键点数据不包含置信度信息,返回 None 。
# 这种设计确保了代码的灵活性,能够根据数据的实际结构返回正确的置信度值或 None 。
return self.data[..., 2] if self.has_visible else None
# 这段代码定义了一个属性 conf ,用于获取关键点的置信度值。它通过检查 self.has_visible 的值来判断是否包含置信度信息,并根据情况返回置信度值或 None 。这种设计使得代码能够灵活处理不同格式的关键点数据,并为后续的关键点操作(如过滤低置信度关键点)提供支持。 条件返回:根据 self.has_visible 的值决定是否返回置信度值,确保代码的灵活性。性能优化:使用 @lru_cache 装饰器缓存结果,避免重复计算,提高性能。简洁性:通过 @property 装饰器,用户可以直接访问 conf 属性,而无需显式调用方法,进一步提升了代码的简洁性和用户体验。
# Keypoints 类是一个用于存储和操作目标检测中关键点数据的工具类。它提供了以下功能。关键点数据的存储:支持存储关键点的像素坐标和置信度信息。坐标操作:通过 xy 和 xyn 属性,分别提供像素坐标和归一化坐标,便于在不同场景下使用。置信度处理:支持提取关键点的置信度值,并在初始化时过滤低置信度的关键点。性能优化:使用 @lru_cache 装饰器缓存计算结果,避免重复计算。灵活性:支持多种数据格式(张量或数组)和设备(CPU/GPU),并通过 cpu() 、 numpy() 和 cuda() 方法提供数据转换功能。通过这些设计, Keypoints 类能够高效地处理关键点数据,并为后续的目标检测和关键点任务提供支持。
# 这段代码定义了一个名为 Probs 的类,用于存储和操作分类任务中的概率值。它继承自 BaseTensor ,并提供了访问和操作分类概率的功能,包括获取最高概率(top-1)和最高五个概率(top-5)及其对应的置信度。
# 定义了一个名为 Probs 的类,继承自 BaseTensor 。 BaseTensor 是一个基类,提供了与张量操作相关的通用功能,例如设备管理和数据类型转换。
class Probs(BaseTensor):
# 用于存储和操作分类概率的类。
# 此类扩展了 BaseTensor,并提供访问和操作分类概率的方法,包括前 1 名和前 5 名预测。
# 方法:
# cpu():返回 CPU 内存上概率张量的副本。
# numpy():返回概率张量作为 numpy 数组。
# cuda():返回 GPU 内存上概率张量的副本。
# to(*args, **kwargs):返回具有指定设备和 dtype 的概率张量的副本。
"""
A class for storing and manipulating classification probabilities.
This class extends BaseTensor and provides methods for accessing and manipulating
classification probabilities, including top-1 and top-5 predictions.
Attributes:
data (torch.Tensor | numpy.ndarray): The raw tensor or array containing classification probabilities.
orig_shape (tuple | None): The original image shape as (height, width). Not used in this class.
top1 (int): Index of the class with the highest probability.
top5 (List[int]): Indices of the top 5 classes by probability.
top1conf (torch.Tensor | numpy.ndarray): Confidence score of the top 1 class.
top5conf (torch.Tensor | numpy.ndarray): Confidence scores of the top 5 classes.
Methods:
cpu(): Returns a copy of the probabilities tensor on CPU memory.
numpy(): Returns a copy of the probabilities tensor as a numpy array.
cuda(): Returns a copy of the probabilities tensor on GPU memory.
to(*args, **kwargs): Returns a copy of the probabilities tensor with specified device and dtype.
Examples:
>>> probs = torch.tensor([0.1, 0.3, 0.6])
>>> p = Probs(probs)
>>> print(p.top1)
2
>>> print(p.top5)
[2, 1, 0]
>>> print(p.top1conf)
tensor(0.6000)
>>> print(p.top5conf)
tensor([0.6000, 0.3000, 0.1000])
"""
# 这段代码是 Probs 类的初始化方法 __init__ 的实现,用于初始化分类概率数据并设置相关属性。
# 定义了 Probs 类的初始化方法 __init__ ,它接受两个参数。
# 1.probs :一个张量( torch.Tensor )或 NumPy 数组,表示分类概率。通常是一个一维数组,包含每个类别的概率值。
# 2.orig_shape :一个元组,表示原始图像的尺寸(高度和宽度)。虽然在这个类中未使用,但保留此参数是为了与其他结果类保持一致性。
def __init__(self, probs, orig_shape=None) -> None:
# 使用分类概率初始化 Probs 类。
# 此类存储和管理分类概率,可轻松访问最佳预测及其置信度。
"""
Initialize the Probs class with classification probabilities.
This class stores and manages classification probabilities, providing easy access to top predictions and their
confidences.
Args:
probs (torch.Tensor | np.ndarray): A 1D tensor or array of classification probabilities.
orig_shape (tuple | None): The original image shape as (height, width). Not used in this class but kept for
consistency with other result classes.
Attributes:
data (torch.Tensor | np.ndarray): The raw tensor or array containing classification probabilities.
top1 (int): Index of the top 1 class.
top5 (List[int]): Indices of the top 5 classes.
top1conf (torch.Tensor | np.ndarray): Confidence of the top 1 class.
top5conf (torch.Tensor | np.ndarray): Confidences of the top 5 classes.
Examples:
>>> import torch
>>> probs = torch.tensor([0.1, 0.3, 0.2, 0.4])
>>> p = Probs(probs)
>>> print(p.top1)
3
>>> print(p.top1conf)
tensor(0.4000)
>>> print(p.top5)
[3, 1, 2, 0]
"""
# 调用基类 BaseTensor 的初始化方法,将 probs 和 orig_shape 传递给基类。这一步是为了继承基类的通用功能,例如张量管理或设备转换。
super().__init__(probs, orig_shape)
# 这段代码的主要功能是。初始化分类概率数据:将输入的分类概率 probs 传递给基类 BaseTensor ,以便利用基类提供的通用功能,例如设备管理和数据类型转换。保留一致性:虽然 orig_shape 参数在 Probs 类中未直接使用,但通过将其传递给基类,保持了与其他结果类(如 Boxes 或 Masks )的一致性,便于代码的扩展和维护。通过这种设计, Probs 类能够灵活地处理分类概率数据,并为后续的分类任务提供支持。
# 这段代码定义了 Probs 类的一个属性 top1 ,用于获取分类概率中最高概率(top-1)对应的类别索引。
# @property 是一个装饰器,用于将类中的方法定义为属性。这意味着可以通过 obj.top1 的方式访问这个方法的返回值,而不需要像普通方法那样调用 obj.top1() 。这种设计使得代码更加简洁,同时隐藏了内部实现细节。
@property
# @lru_cache 是一个缓存装饰器,用于缓存方法的返回值,以提高性能。这里设置 maxsize=1 ,表示最多缓存一次调用的结果。由于 top1 的计算结果通常不会改变(除非输入数据改变),缓存一次结果即可满足需求。这在多次访问 top1 属性时可以显著提高性能。
@lru_cache(maxsize=1)
# 定义了一个名为 top1 的方法,它属于 Probs 类的实例。这个方法的作用是返回分类概率中最高概率对应的类别索引。
def top1(self):
# 返回概率最高的类的索引。
"""
Returns the index of the class with the highest probability.
Returns:
(int): Index of the class with the highest probability.
Examples:
>>> probs = Probs(torch.tensor([0.1, 0.3, 0.6]))
>>> probs.top1
2
"""
# 调用 self.data.argmax() ,找到概率值最高的索引。 argmax() 方法返回张量或数组中最大值的索引。
# 使用 int() 将返回值转换为整数类型,确保返回值是一个整数索引。
return int(self.data.argmax())
# 这段代码定义了一个属性 top1 ,用于快速获取分类概率中最高概率对应的类别索引。它通过调用 argmax() 方法找到最大概率值的索引,并使用 @lru_cache 装饰器缓存结果,避免重复计算,提高性能。 功能: top1 属性返回分类概率中最高概率对应的类别索引。性能优化:使用 @lru_cache 装饰器缓存结果,避免重复计算,提高性能。简洁性:通过 @property 装饰器,用户可以直接访问 top1 属性,而无需显式调用方法,进一步提升了代码的简洁性和用户体验。
# 这段代码定义了 Probs 类的一个属性 top5 ,用于获取分类概率中概率最高的前五个类别索引(top-5)。
# @property 是一个装饰器,用于将类中的方法定义为属性。这意味着可以通过 obj.top5 的方式访问这个方法的返回值,而不需要像普通方法那样调用 obj.top5() 。这种设计使得代码更加简洁,同时隐藏了内部实现细节。
@property
# @lru_cache 是一个缓存装饰器,用于缓存方法的返回值,以提高性能。这里设置 maxsize=1 ,表示最多缓存一次调用的结果。由于 top-5 的计算结果通常不会改变(除非输入数据改变),缓存一次结果即可满足需求。这在多次访问 top5 属性时可以显著提高性能。
@lru_cache(maxsize=1)
# 定义了一个名为 top5 的方法,它属于 Probs 类的实例。这个方法的作用是返回分类概率中概率最高的前五个类别索引。
def top5(self):
# 返回前 5 个类别概率的索引。
"""
Returns the indices of the top 5 class probabilities.
Returns:
(List[int]): A list containing the indices of the top 5 class probabilities, sorted in descending order.
Examples:
>>> probs = Probs(torch.tensor([0.1, 0.2, 0.3, 0.4, 0.5]))
>>> print(probs.top5)
[4, 3, 2, 1, 0]
"""
# 取负值 : -self.data 将概率值取负。这是因为 argsort() 默认按升序排序 , 取负后 可以实现 降序排序 的效果。
# 排序 : .argsort(0) 对取负后的概率值进行排序,返回按概率值降序排列的索引。 0 表示沿着第 0 维排序(对于一维数组,这表示整个数组)。
# 取前五个索引 : [:5] 获取排序后的前五个索引。
# 转换为列表 : .tolist() 将结果转换为 Python 列表。
# 这种方法兼容 torch.Tensor 和 numpy.ndarray ,确保代码的通用性。
return (-self.data).argsort(0)[:5].tolist() # this way works with both torch and numpy.
# 这段代码定义了一个属性 top5 ,用于快速获取分类概率中概率最高的前五个类别索引。它通过取负值和排序的方式实现降序排列,并使用 @lru_cache 装饰器缓存结果,避免重复计算,提高性能。 功能: top5 属性返回分类概率中概率最高的前五个类别索引。兼容性:代码兼容 torch.Tensor 和 numpy.ndarray ,确保在不同数据类型下都能正常工作。性能优化:使用 @lru_cache 装饰器缓存结果,避免重复计算,提高性能。简洁性:通过 @property 装饰器,用户可以直接访问 top5 属性,而无需显式调用方法,进一步提升了代码的简洁性和用户体验。
# 这段代码定义了 Probs 类的一个属性 top1conf ,用于获取分类概率中最高概率(top-1)对应的置信度值。
# @property 是一个装饰器,用于将类中的方法定义为属性。这意味着可以通过 obj.top1conf 的方式访问这个方法的返回值,而不需要像普通方法那样调用 obj.top1conf() 。这种设计使得代码更加简洁,同时隐藏了内部实现细节。
@property
# @lru_cache 是一个缓存装饰器,用于缓存方法的返回值,以提高性能。这里设置 maxsize=1 ,表示最多缓存一次调用的结果。由于 top-1 置信度值通常不会改变(除非输入数据改变),缓存一次结果即可满足需求。这在多次访问 top1conf 属性时可以显著提高性能。
@lru_cache(maxsize=1)
# 定义了一个名为 top1conf 的方法,它属于 Probs 类的实例。这个方法的作用是返回分类概率中最高概率对应的置信度值。
def top1conf(self):
# 返回最高概率类的置信度得分。
# 此属性从分类结果中检索具有最高预测概率的类的置信度得分(概率)。
"""
Returns the confidence score of the highest probability class.
This property retrieves the confidence score (probability) of the class with the highest predicted probability
from the classification results.
Returns:
(torch.Tensor | numpy.ndarray): A tensor containing the confidence score of the top 1 class.
Examples:
>>> results = model("image.jpg") # classify an image
>>> probs = results[0].probs # get classification probabilities
>>> top1_confidence = probs.top1conf # get confidence of top 1 class
>>> print(f"Top 1 class confidence: {top1_confidence.item():.4f}")
"""
# 通过 self.top1 获取最高概率对应的类别索引。 使用该索引从 self.data 中提取对应的概率值,即最高概率的置信度值。
return self.data[self.top1]
# 这段代码定义了一个属性 top1conf ,用于快速获取分类概率中最高概率(top-1)对应的置信度值。它通过访问 self.data 中的对应索引实现这一点,并使用 @lru_cache 装饰器缓存结果,避免重复计算,提高性能。
# 这段代码定义了 Probs 类的一个属性 top5conf ,用于获取分类概率中概率最高的前五个类别(top-5)对应的置信度值。
# @property 是一个装饰器,用于将类中的方法定义为属性。这意味着可以通过 obj.top5conf 的方式访问这个方法的返回值,而不需要像普通方法那样调用 obj.top5conf() 。这种设计使得代码更加简洁,同时隐藏了内部实现细节。
@property
# @lru_cache 是一个缓存装饰器,用于缓存方法的返回值,以提高性能。这里设置 maxsize=1 ,表示最多缓存一次调用的结果。由于 top-5 置信度值通常不会改变(除非输入数据改变),缓存一次结果即可满足需求。这在多次访问 top5conf 属性时可以显著提高性能。
@lru_cache(maxsize=1)
# 定义了一个名为 top5conf 的方法,它属于 Probs 类的实例。这个方法的作用是返回分类概率中概率最高的前五个类别(top-5)对应的置信度值。
def top5conf(self):
# 回前 5 个分类预测的置信度分数。
# 此属性检索与模型预测的前 5 个类别概率相对应的置信度分数。它提供了一种快速方法来访问最可能的类别预测及其相关的置信度水平。
"""
Returns confidence scores for the top 5 classification predictions.
This property retrieves the confidence scores corresponding to the top 5 class probabilities
predicted by the model. It provides a quick way to access the most likely class predictions
along with their associated confidence levels.
Returns:
(torch.Tensor | numpy.ndarray): A tensor or array containing the confidence scores for the
top 5 predicted classes, sorted in descending order of probability.
Examples:
>>> results = model("image.jpg")
>>> probs = results[0].probs
>>> top5_conf = probs.top5conf
>>> print(top5_conf) # Prints confidence scores for top 5 classes
"""
# 通过 self.top5 获取概率最高的前五个类别的索引(这些索引是通过 top5 属性计算得到的)。 使用这些索引从 self.data 中提取对应的概率值,即 top-5 类别的置信度值。
return self.data[self.top5]
# 这段代码定义了一个属性 top5conf ,用于快速获取分类概率中概率最高的前五个类别(top-5)对应的置信度值。它通过访问 self.data 中的对应索引实现这一点,并使用 @lru_cache 装饰器缓存结果,避免重复计算,提高性能。
# Probs 类是一个用于存储和操作分类概率的工具类。它提供了以下功能。概率存储:支持以张量或数组的形式存储分类概率。Top-1 和 Top-5 预测:通过 top1 和 top5 属性,分别获取最高概率和最高五个概率对应的类别索引。置信度提取:通过 top1conf 和 top5conf 属性,分别获取最高概率和最高五个概率对应的置信度值。性能优化:使用 @lru_cache 装饰器缓存计算结果,避免重复计算,提高性能。灵活性:支持多种数据格式(张量或数组)和设备(CPU/GPU),并通过 cpu() 、 numpy() 和 cuda() 方法提供数据转换功能。通过这些设计, Probs 类能够高效地处理分类概率数据,并为后续的分类任务提供支持。
# 这段代码定义了一个名为 OBB 的类,用于存储和操作定向边界框(Oriented Bounding Boxes,简称 OBB)。它提供了对 OBB 数据的多种操作,包括格式转换、归一化以及访问置信度、类别标签和跟踪 ID 等属性。
# 定义了一个名为 OBB 的类,继承自 BaseTensor 。 BaseTensor 是一个基类,提供了与张量操作相关的通用功能,例如设备管理和数据类型转换。
class OBB(BaseTensor):
# 用于存储和操作定向边界框 (OBB) 的类。
# 此类提供处理定向边界框的功能,包括不同格式之间的转换、规范化以及对框的各种属性的访问。
# 方法:
# cpu():返回 CPU 内存上包含所有张量的 OBB 对象的副本。
# numpy():以 numpy 数组形式返回包含所有张量的 OBB 对象的副本。
# cuda():返回 GPU 内存上包含所有张量的 OBB 对象的副本。
# to(*args, **kwargs): 返回指定设备和 dtype 上带有张量的 OBB 对象副本。
"""
A class for storing and manipulating Oriented Bounding Boxes (OBB).
This class provides functionality to handle oriented bounding boxes, including conversion between
different formats, normalization, and access to various properties of the boxes.
Attributes:
data (torch.Tensor): The raw OBB tensor containing box coordinates and associated data.
orig_shape (tuple): Original image size as (height, width).
is_track (bool): Indicates whether tracking IDs are included in the box data.
xywhr (torch.Tensor | numpy.ndarray): Boxes in [x_center, y_center, width, height, rotation] format.
conf (torch.Tensor | numpy.ndarray): Confidence scores for each box.
cls (torch.Tensor | numpy.ndarray): Class labels for each box.
id (torch.Tensor | numpy.ndarray): Tracking IDs for each box, if available.
xyxyxyxy (torch.Tensor | numpy.ndarray): Boxes in 8-point [x1, y1, x2, y2, x3, y3, x4, y4] format.
xyxyxyxyn (torch.Tensor | numpy.ndarray): Normalized 8-point coordinates relative to orig_shape.
xyxy (torch.Tensor | numpy.ndarray): Axis-aligned bounding boxes in [x1, y1, x2, y2] format.
Methods:
cpu(): Returns a copy of the OBB object with all tensors on CPU memory.
numpy(): Returns a copy of the OBB object with all tensors as numpy arrays.
cuda(): Returns a copy of the OBB object with all tensors on GPU memory.
to(*args, **kwargs): Returns a copy of the OBB object with tensors on specified device and dtype.
Examples:
>>> boxes = torch.tensor([[100, 50, 150, 100, 30, 0.9, 0]]) # xywhr, conf, cls
>>> obb = OBB(boxes, orig_shape=(480, 640))
>>> print(obb.xyxyxyxy)
>>> print(obb.conf)
>>> print(obb.cls)
"""
# 这段代码是 OBB 类的初始化方法 __init__ 的实现,用于初始化定向边界框(Oriented Bounding Boxes,OBB)的数据结构,并设置一些关键属性。
# 定义了 OBB 类的初始化方法 __init__ ,它接受两个参数。
# 1.boxes :一个张量( torch.Tensor )或 NumPy 数组,包含定向边界框的数据。其形状可以是 (num_boxes, 7) 或 (num_boxes, 8) ,具体格式如下 :
# [x_center, y_center, width, height, rotation, confidence, class] (7 列)
# [x_center, y_center, width, height, rotation, confidence, class, track_id] (8 列,包含跟踪 ID)
# 2.orig_shape :一个元组,表示原始图像的尺寸(高度和宽度),格式为 (height, width) 。
def __init__(self, boxes, orig_shape) -> None:
# 使用定向边界框数据和原始图像形状初始化 OBB(定向边界框)实例。
# 此类存储和操作用于对象检测任务的定向边界框 (OBB)。它提供各种属性和方法来访问和转换 OBB 数据。
# 参数:
# boxes (torch.Tensor | numpy.ndarray):包含检测框的张量或 numpy 数组,形状为 (num_boxes, 7) 或 (num_boxes, 8)。最后两列包含置信度和类值。如果存在,倒数第三列包含轨迹 ID,第五列包含旋转。
# orig_shape (Tuple[int, int]):原始图像大小,格式为 (height, width)。
"""
Initialize an OBB (Oriented Bounding Box) instance with oriented bounding box data and original image shape.
This class stores and manipulates Oriented Bounding Boxes (OBB) for object detection tasks. It provides
various properties and methods to access and transform the OBB data.
Args:
boxes (torch.Tensor | numpy.ndarray): A tensor or numpy array containing the detection boxes,
with shape (num_boxes, 7) or (num_boxes, 8). The last two columns contain confidence and class values.
If present, the third last column contains track IDs, and the fifth column contains rotation.
orig_shape (Tuple[int, int]): Original image size, in the format (height, width).
Attributes:
data (torch.Tensor | numpy.ndarray): The raw OBB tensor.
orig_shape (Tuple[int, int]): The original image shape.
is_track (bool): Whether the boxes include tracking IDs.
Raises:
AssertionError: If the number of values per box is not 7 or 8.
Examples:
>>> import torch
>>> boxes = torch.rand(3, 7) # 3 boxes with 7 values each
>>> orig_shape = (640, 480)
>>> obb = OBB(boxes, orig_shape)
>>> print(obb.xywhr) # Access the boxes in xywhr format
"""
# 如果输入的 boxes 是一维的(即只有一个边界框),则通过在第 0 维添加一个新的维度,将其扩展为二维张量。这一步是为了确保后续操作的一致性,因为类的设计假设 boxes 是二维的。
if boxes.ndim == 1:
boxes = boxes[None, :]
# 获取 boxes 的最后一维大小,即 每个边界框的数据列数 。
n = boxes.shape[-1]
# 断言 boxes 的最后一维大小必须为 7 或 8 。
# 如果大小为 7,表示每个边界框包含 [x_center, y_center, width, height, rotation, confidence, class] 。
# 如果大小为 8,表示每个边界框包含 [x_center, y_center, width, height, rotation, confidence, class, track_id] 。
# 如果不符合这些条件,会抛出异常,提示用户输入的格式不正确。
assert n in {7, 8}, f"expected 7 or 8 values but got {n}" # xywh, rotation, track_id, conf, cls 预期为 7 或 8 个值,但得到的结果为 {n} 。
# 调用基类 BaseTensor 的初始化方法,将 boxes 和 orig_shape 传递给基类。这一步是为了继承基类的通用功能,例如张量管理或设备转换。
super().__init__(boxes, orig_shape)
# 根据 boxes 的最后一维大小,判断是否包含跟踪 ID 。如果大小为 8,则表示包含跟踪 ID, is_track 设置为 True 。 否则, is_track 设置为 False 。
self.is_track = n == 8
# 将原始图像的尺寸存储为类的属性 orig_shape ,用于后续的归一化操作。
self.orig_shape = orig_shape
# 这段代码的主要功能是。检查并调整输入数据的维度:如果输入的边界框数据是一维的(没有批次维度),则通过添加一个新的维度将其扩展为二维张量。验证输入数据的格式:确保每个边界框的数据列数为 7 或 8,分别对应不包含或包含跟踪 ID 的情况。初始化基类:通过调用基类的初始化方法,将边界框数据和原始图像尺寸传递给基类,以便利用基类提供的通用功能。设置关键属性:通过 self.is_track 标记是否包含跟踪 ID,并将原始图像尺寸存储为属性 self.orig_shape 。通过这种设计, OBB 类能够灵活地处理不同格式的定向边界框数据,并为后续的操作(如格式转换、归一化等)提供支持。
# 这段代码定义了 OBB 类中的一组属性,用于访问和操作定向边界框(Oriented Bounding Boxes,OBB)的不同数据字段。每个属性都通过 @property 装饰器实现,使得它们可以像普通属性一样被访问,而无需显式调用方法。
# 返回定向边界框的 [x_center, y_center, width, height, rotation] 格式。
# 通过切片操作 self.data[:, :5] 提取 data 的前 5 列,这 5 列分别表示边界框的中心点坐标、宽度、高度和旋转角度。
# 这种格式常用于表示旋转的边界框,适用于需要考虑方向的目标检测任务。
@property
def xywhr(self):
# 返回 [x_center, y_center, width, height, rotation] 格式的框。
# 返回:
# (torch.Tensor | numpy.ndarray):包含方向边界框的张量或 numpy 数组,格式为 [x_center, y_center, width, height, rotation]。形状为 (N, 5),其中 N 是框的数量。
"""
Returns boxes in [x_center, y_center, width, height, rotation] format.
Returns:
(torch.Tensor | numpy.ndarray): A tensor or numpy array containing the oriented bounding boxes with format
[x_center, y_center, width, height, rotation]. The shape is (N, 5) where N is the number of boxes.
Examples:
>>> results = model("image.jpg")
>>> obb = results[0].obb
>>> xywhr = obb.xywhr
>>> print(xywhr.shape)
torch.Size([3, 5])
"""
return self.data[:, :5]
# 返回每个定向边界框的置信度分数。
# 通过切片操作 self.data[:, -2] 提取 data 的倒数第二列,这列表示每个边界框的置信度(confidence)。
# 置信度分数用于表示模型对检测结果的可信度,通常用于过滤低置信度的检测结果。
@property
def conf(self):
# 返回定向边界框 (OBB) 的置信度分数。
# 此属性检索与每个 OBB 检测相关的置信度值。置信度分数表示模型在检测中的确定性。
# 返回:
# (torch.Tensor | numpy.ndarray):形状为 (N,) 的张量或 numpy 数组,包含 N 个检测的置信度分数,其中每个分数在 [0, 1] 范围内。
"""
Returns the confidence scores for Oriented Bounding Boxes (OBBs).
This property retrieves the confidence values associated with each OBB detection. The confidence score
represents the model's certainty in the detection.
Returns:
(torch.Tensor | numpy.ndarray): A tensor or numpy array of shape (N,) containing confidence scores
for N detections, where each score is in the range [0, 1].
Examples:
>>> results = model("image.jpg")
>>> obb_result = results[0].obb
>>> confidence_scores = obb_result.conf
>>> print(confidence_scores)
"""
return self.data[:, -2]
# 返回每个定向边界框的类别标签。
# 通过切片操作 self.data[:, -1] 提取 data 的最后一列,这列表示每个边界框的类别标签(class)。
# 类别标签用于区分不同类型的检测目标,例如“人”、“车”等。
@property
def cls(self):
# 返回定向边界框的类值。
# 返回:
# (torch.Tensor | numpy.ndarray):包含每个定向边界框的类值的张量或 numpy 数组。形状为 (N,),其中 N 是框的数量。
"""
Returns the class values of the oriented bounding boxes.
Returns:
(torch.Tensor | numpy.ndarray): A tensor or numpy array containing the class values for each oriented
bounding box. The shape is (N,), where N is the number of boxes.
Examples:
>>> results = model("image.jpg")
>>> result = results[0]
>>> obb = result.obb
>>> class_values = obb.cls
>>> print(class_values)
"""
return self.data[:, -1]
# 返回每个定向边界框的跟踪 ID(如果存在)。
# 如果 self.is_track 为 True ,表示数据中包含跟踪 ID,则通过切片操作 self.data[:, -3] 提取倒数第三列。
# 如果 self.is_track 为 False ,表示数据中不包含跟踪 ID,则返回 None 。
# 跟踪 ID 用于在视频或序列图像中跟踪同一目标,便于分析目标的运动轨迹。
@property
def id(self):
# 返回定向边界框的跟踪 ID(如果可用)。
"""
Returns the tracking IDs of the oriented bounding boxes (if available).
Returns:
(torch.Tensor | numpy.ndarray | None): A tensor or numpy array containing the tracking IDs for each
oriented bounding box. Returns None if tracking IDs are not available.
Examples:
>>> results = model("image.jpg", tracker=True) # Run inference with tracking
>>> for result in results:
... if result.obb is not None:
... track_ids = result.obb.id
... if track_ids is not None:
... print(f"Tracking IDs: {track_ids}")
"""
return self.data[:, -3] if self.is_track else None
# 将定向边界框从 [x_center, y_center, width, height, rotation] 格式转换为 8 点格式 [x1, y1, x2, y2, x3, y3, x4, y4] 。
# 调用 ops.xywhr2xyxyxyxy 函数,将 xywhr 格式的边界框转换为 8 点格式。
# 使用 @lru_cache(maxsize=2) 装饰器缓存结果,避免重复计算,提高性能。
# 8 点格式表示边界框的四个角点坐标,适用于需要绘制旋转边界框或进行几何计算的场景。
@property
@lru_cache(maxsize=2)
def xyxyxyxy(self):
# 将 OBB 格式转换为旋转边界框的 8 点 (xyxyxyxy) 坐标格式。
# 返回:
# (torch.Tensor | numpy.ndarray):xyxyxyxy 格式的旋转边界框,形状为 (N, 4, 2),其中 N 是框数。每个框由 4 个点 (x, y) 表示,从左上角开始顺时针移动。
"""
Converts OBB format to 8-point (xyxyxyxy) coordinate format for rotated bounding boxes.
Returns:
(torch.Tensor | numpy.ndarray): Rotated bounding boxes in xyxyxyxy format with shape (N, 4, 2), where N is
the number of boxes. Each box is represented by 4 points (x, y), starting from the top-left corner and
moving clockwise.
Examples:
>>> obb = OBB(torch.tensor([[100, 100, 50, 30, 0.5, 0.9, 0]]), orig_shape=(640, 640))
>>> xyxyxyxy = obb.xyxyxyxy
>>> print(xyxyxyxy.shape)
torch.Size([1, 4, 2])
"""
return ops.xywhr2xyxyxyxy(self.xywhr)
# 这段代码通过定义多个属性,提供了对定向边界框(OBB)数据的灵活访问和操作。 xywhr :返回边界框的 [x_center, y_center, width, height, rotation] 格式。 conf :返回每个边界框的置信度分数。 cls :返回每个边界框的类别标签。 id :返回每个边界框的跟踪 ID(如果存在)。 xyxyxyxy :将边界框转换为 8 点格式 [x1, y1, x2, y2, x3, y3, x4, y4] ,并使用缓存机制优化性能。这些属性的设计使得 OBB 类能够高效地处理定向边界框数据,并为后续的目标检测、跟踪和可视化任务提供支持。
# 这段代码定义了 OBB 类的一个属性 xyxyxyxyn ,用于将定向边界框(Oriented Bounding Boxes,OBB)的 8 点坐标归一化到 [0, 1] 范围内。归一化是相对于原始图像的宽度和高度进行的。
# @property 将方法定义为属性,可以通过 obj.xyxyxyxyn 的方式访问,而无需显式调用方法。
@property
# @lru_cache(maxsize=2) 缓存最近两次的计算结果,避免重复计算,提高性能。
@lru_cache(maxsize=2)
# 定义了一个属性 xyxyxyxyn ,用于获取归一化的 8 点坐标。
def xyxyxyxyn(self):
# 将旋转的边界框转换为标准化的 xyxyxyxy 格式。
# 返回:
# (torch.Tensor | numpy.ndarray):标准化的旋转边界框,采用 xyxyxyxy 格式,形状为 (N, 4, 2),其中 N 是框的数量。每个框由 4 个点 (x, y) 表示,相对于原始图像尺寸进行标准化。
"""
Converts rotated bounding boxes to normalized xyxyxyxy format.
Returns:
(torch.Tensor | numpy.ndarray): Normalized rotated bounding boxes in xyxyxyxy format with shape (N, 4, 2),
where N is the number of boxes. Each box is represented by 4 points (x, y), normalized relative to
the original image dimensions.
Examples:
>>> obb = OBB(torch.rand(10, 7), orig_shape=(640, 480)) # 10 random OBBs
>>> normalized_boxes = obb.xyxyxyxyn
>>> print(normalized_boxes.shape)
torch.Size([10, 4, 2])
"""
# 创建 self.xyxyxyxy 的副本,确保不修改原始数据。
# 如果 self.xyxyxyxy 是 torch.Tensor ,使用 .clone() 创建副本。
# 如果是 numpy.ndarray ,使用 np.copy() 创建副本。
xyxyxyxyn = self.xyxyxyxy.clone() if isinstance(self.xyxyxyxy, torch.Tensor) else np.copy(self.xyxyxyxy)
# 将 8 点坐标的 x 和 y 分别归一化到 [0, 1] 范围内。
# xyxyxyxyn[..., 0] 表示所有点的 x 坐标,将其除以图像宽度( self.orig_shape[1] )。
xyxyxyxyn[..., 0] /= self.orig_shape[1]
# xyxyxyxyn[..., 1] 表示所有点的 y 坐标,将其除以图像高度( self.orig_shape[0] )。
xyxyxyxyn[..., 1] /= self.orig_shape[0]
# 返回归一化后的 8 点坐标。
# 形状为 (N, 4, 2) 的张量或数组,其中 N 是边界框的数量,每个边界框由 4 个点( (x, y) )表示,坐标值在 [0, 1] 范围内。
return xyxyxyxyn
# 这段代码定义了一个属性 xyxyxyxyn ,用于将定向边界框的 8 点坐标归一化到 [0, 1] 范围内。归一化是通过将每个点的 x 和 y 坐标分别除以原始图像的宽度和高度完成的。这种设计使得归一化后的坐标可以方便地用于跨不同图像尺寸的比较和处理。 归一化操作:将 8 点坐标的 x 和 y 分别除以图像宽度和高度,确保坐标值在 [0, 1] 范围内。数据安全性:通过 .clone() 或 np.copy() 创建副本,避免直接修改原始数据。性能优化:使用 @lru_cache 装饰器缓存结果,避免重复计算,提高性能。简洁性:通过 @property 装饰器,用户可以直接访问 xyxyxyxyn 属性,而无需显式调用方法,进一步提升了代码的简洁性和用户体验。
# 这段代码定义了 OBB 类的一个属性 xyxy ,用于将定向边界框(Oriented Bounding Boxes,OBB)转换为轴对齐边界框(Axis-Aligned Bounding Boxes,AABB)的 [x1, y1, x2, y2] 格式。
# @property 将方法定义为属性,可以通过 obj.xyxy 的方式访问,而无需显式调用方法。
@property
# @lru_cache(maxsize=2) 缓存最近两次的计算结果,避免重复计算,提高性能。
@lru_cache(maxsize=2)
# 定义了一个属性 xyxy ,用于获取轴对齐边界框的 [x1, y1, x2, y2] 格式。
def xyxy(self):
# 将定向边界框 (OBB) 转换为 xyxy 格式的轴对齐边界框。
# 此属性计算每个定向边界框的最小外接矩形,并以 xyxy 格式 (x1, y1, x2, y2) 返回。这对于需要轴对齐边界框的操作很有用,例如使用非旋转框进行 IoU 计算。
# 返回:
# (torch.Tensor | numpy.ndarray):xyxy 格式的轴对齐边界框,形状为 (N, 4),其中 N 是框的数量。每行包含 [x1, y1, x2, y2] 坐标。
# 注释:
# - 此方法通过其最小外接矩形近似 OBB。
# - 返回的格式与标准对象检测指标和可视化工具兼容。
# - 该属性使用缓存来提高重复访问的性能。
"""
Converts oriented bounding boxes (OBB) to axis-aligned bounding boxes in xyxy format.
This property calculates the minimal enclosing rectangle for each oriented bounding box and returns it in
xyxy format (x1, y1, x2, y2). This is useful for operations that require axis-aligned bounding boxes, such
as IoU calculation with non-rotated boxes.
Returns:
(torch.Tensor | numpy.ndarray): Axis-aligned bounding boxes in xyxy format with shape (N, 4), where N
is the number of boxes. Each row contains [x1, y1, x2, y2] coordinates.
Examples:
>>> import torch
>>> from ultralytics import YOLO
>>> model = YOLO("yolo11n-obb.pt")
>>> results = model("path/to/image.jpg")
>>> for result in results:
... obb = result.obb
... if obb is not None:
... xyxy_boxes = obb.xyxy
... print(xyxy_boxes.shape) # (N, 4)
Notes:
- This method approximates the OBB by its minimal enclosing rectangle.
- The returned format is compatible with standard object detection metrics and visualization tools.
- The property uses caching to improve performance for repeated access.
"""
# 从 self.xyxyxyxy 属性中提取所有点的 x 坐标和 y 坐标。
# self.xyxyxyxy 返回每个定向边界框的 8 点坐标,形状为 (N, 4, 2) ,其中 N 是边界框的数量,每个边界框由 4 个点表示。 [..., 0] 提取所有点的 x 坐标, [..., 1] 提取所有点的 y 坐标。
x = self.xyxyxyxy[..., 0]
y = self.xyxyxyxy[..., 1]
# 计算每个定向边界框的最小外接矩形(轴对齐边界框)。
# 对于每个定向边界框,计算其 4 个点的最小 x、最小 y、最大 x 和最大 y,得到轴对齐边界框的 [x1, y1, x2, y2] 格式。
# 如果 x 是 torch.Tensor ,使用 torch.stack 和 amin / amax 进行计算。
# 如果 x 是 numpy.ndarray ,使用 np.stack 和 min / max 进行计算。
# 返回形状为 (N, 4) 的张量或数组,其中 N 是边界框的数量,每行表示一个轴对齐边界框的 [x1, y1, x2, y2] 坐标。
return (
torch.stack([x.amin(1), y.amin(1), x.amax(1), y.amax(1)], -1)
if isinstance(x, torch.Tensor)
else np.stack([x.min(1), y.min(1), x.max(1), y.max(1)], -1)
)
# 这段代码定义了一个属性 xyxy ,用于将定向边界框(OBB)转换为轴对齐边界框(AABB)的 [x1, y1, x2, y2] 格式。它通过计算每个定向边界框的 4 个点的最小和最大 x、y 坐标,得到最小外接矩形。这种设计使得定向边界框可以方便地用于需要轴对齐边界框的场景,例如 IoU 计算或可视化。 功能:将定向边界框转换为轴对齐边界框的 [x1, y1, x2, y2] 格式。兼容性:支持 torch.Tensor 和 numpy.ndarray 两种数据类型。性能优化:使用 @lru_cache 装饰器缓存结果,避免重复计算,提高性能。简洁性:通过 @property 装饰器,用户可以直接访问 xyxy 属性,而无需显式调用方法,进一步提升了代码的简洁性和用户体验。
# OBB 类是一个用于存储和操作定向边界框(Oriented Bounding Boxes)的工具类。它继承自 BaseTensor ,并提供了多种功能,包括对定向边界框数据的存储、格式转换、归一化以及访问置信度、类别标签和跟踪 ID 等属性。通过支持多种格式(如 [x_center, y_center, width, height, rotation] 和 8 点格式 [x1, y1, x2, y2, x3, y3, x4, y4] ),并提供轴对齐边界框(AABB)的 [x1, y1, x2, y2] 格式转换, OBB 类能够灵活地满足不同场景下的需求。此外,它还通过 @lru_cache 装饰器优化了性能,避免重复计算,并利用 @property 装饰器增强了代码的简洁性和易用性。这种设计使得 OBB 类能够高效地处理定向边界框数据,并为后续的目标检测、跟踪和可视化任务提供强大的支持。