欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 科技 > 名人名企 > YOLOv11-ultralytics-8.3.67部分代码阅读笔记-augment.py

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

2025/2/21 3:32:45 来源:https://blog.csdn.net/m0_58169876/article/details/145717589  浏览:    关键词:YOLOv11-ultralytics-8.3.67部分代码阅读笔记-augment.py

augment.py

ultralytics\data\augment.py

目录

augment.py

1.所需的库和模块

2.class BaseTransform: 

3.class Compose: 

4.class BaseMixTransform: 

5.class Mosaic(BaseMixTransform): 

6.class MixUp(BaseMixTransform): 

7.class RandomPerspective: 

8.class RandomHSV: 

9.class RandomFlip: 

10.class LetterBox: 

11.class CopyPaste(BaseMixTransform): 

12.class Albumentations: 

13.class Format: 

14.class RandomLoadText: 

15.def v8_transforms(dataset, imgsz, hyp, stretch=False): 

16.def classify_transforms(size=224, mean=DEFAULT_MEAN, std=DEFAULT_STD, interpolation="BILINEAR", crop_fraction: float = DEFAULT_CROP_FRACTION,): 

17.def classify_augmentations(size=224, mean=DEFAULT_MEAN, std=DEFAULT_STD, scale=None, ratio=None, hflip=0.5, vflip=0.0, auto_augment=None, hsv_h=0.015, hsv_s=0.4, hsv_v=0.4, force_color_jitter=False, erasing=0.0, interpolation="BILINEAR",): 

18.class ClassifyLetterBox: 

19.class CenterCrop: 

20.class ToTensor: 


1.所需的库和模块

# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/licenseimport math
import random
from copy import deepcopy
from typing import Tuple, Unionimport cv2
import numpy as np
import torch
from PIL import Imagefrom ultralytics.data.utils import polygons2masks, polygons2masks_overlap
from ultralytics.utils import LOGGER, colorstr
from ultralytics.utils.checks import check_version
from ultralytics.utils.instance import Instances
from ultralytics.utils.metrics import bbox_ioa
from ultralytics.utils.ops import segment2box, xyxyxyxy2xywhr
from ultralytics.utils.torch_utils import TORCHVISION_0_10, TORCHVISION_0_11, TORCHVISION_0_13# 这段代码定义了三个默认的常量,通常用于图像预处理或数据标准化的上下文中。
# 定义了一个名为 DEFAULT_MEAN 的元组,其值为 (0.0, 0.0, 0.0) 。这通常表示图像归一化时使用的均值(mean),用于每个通道(通常是 RGB 通道)的归一化操作。
# 在图像处理中,归一化是将像素值调整到一个特定范围(如 [0, 1] 或 [-1, 1] )的过程,以提高模型的训练效率和稳定性。
# 这里的均值为 (0.0, 0.0, 0.0) 表示在归一化时,每个通道的均值为 0。
DEFAULT_MEAN = (0.0, 0.0, 0.0)
# 定义了一个名为 DEFAULT_STD 的元组,其值为 (1.0, 1.0, 1.0) 。这通常表示图像归一化时使用的标准差(standard deviation)。
# 标准差用于归一化操作中,将像素值除以标准差,使归一化后的数据具有单位方差。
# 这里的标准差为 (1.0, 1.0, 1.0) 表示在归一化时,每个通道的标准差为 1,即归一化后的像素值范围为 [0, 1] 。
DEFAULT_STD = (1.0, 1.0, 1.0)
# 定义了一个名为 DEFAULT_CROP_FRACTION 的浮点数,其值为 1.0 。这通常表示裁剪比例(crop fraction),用于图像裁剪操作。
# 在数据增强或预处理中,裁剪比例用于控制裁剪区域的大小。值为 1.0 表示不进行裁剪,即保留图像的完整区域。
# 如果值小于 1.0 ,则表示裁剪图像的一部分,例如 0.5 表示裁剪图像中心的 50% 区域。
DEFAULT_CROP_FRACTION = 1.0
# 这三行代码定义了图像预处理中常用的默认参数。 DEFAULT_MEAN :归一化时使用的均值,表示每个通道的均值为 0。 DEFAULT_STD :归一化时使用的标准差,表示每个通道的标准差为 1。 DEFAULT_CROP_FRACTION :裁剪比例,值为 1.0 表示不进行裁剪。这些参数通常用于深度学习模型的输入预处理,尤其是在处理图像数据时。它们可以根据具体任务进行调整,以适应不同的数据集或模型需求。# 在机器学习和深度学习中,将数据归一化为具有单位方差(即方差为1)是一种常见的预处理步骤,尤其是在处理图像数据时。这种归一化方式通常被称为 Z-score normalization 或 标准归一化。其作用主要体现在以下几个方面 :
# 加速模型收敛 :
# 在训练神经网络时,输入数据的分布对梯度下降的效率有很大影响。如果输入特征的分布差异较大(例如,某些特征的方差远大于其他特征),梯度下降过程可能会变得不稳定,导致训练速度变慢甚至无法收敛。
# 通过将数据归一化为单位方差,可以使不同特征的分布更加一致,从而减少梯度下降过程中的尺度差异。这有助于加速模型的收敛速度,提高训练效率。
# 提高模型的数值稳定性 :
# 在深度学习中,神经网络的权重更新依赖于梯度计算。如果输入数据的方差过大,可能会导致梯度的数值范围过大或过小,从而引发数值不稳定问题,例如梯度爆炸(gradient explosion)或梯度消失(gradient vanishing)。
# 将数据归一化为单位方差可以有效缓解这些问题,使梯度的数值范围更加稳定,从而提高模型训练的数值稳定性。
# 增强模型的泛化能力 :
# 归一化后的数据具有统一的分布特性(均值为0,方差为1),这使得模型在训练过程中更容易学习到数据的通用特征,而不是依赖于特定的数据尺度。这种统一的分布有助于模型在不同数据集上表现得更加稳定,从而增强模型的泛化能力。
# 简化模型的超参数调整 :
# 在某些优化算法(如Adam、SGD等)中,学习率等超参数的选择对模型的训练效果有很大影响。如果输入数据的分布差异较大,可能需要针对每个特征单独调整学习率,这增加了超参数调整的复杂性。
# 通过将数据归一化为单位方差,可以简化超参数的选择过程,使模型在默认的学习率下更容易收敛。
# 减少特征之间的相关性 :
# 在多特征输入的情况下,不同特征之间可能存在相关性。归一化为单位方差可以减少这种相关性,使每个特征对模型的贡献更加独立。这有助于提高模型的解释性和可调试性。
# 便于模型的扩展和迁移 :
# 当模型需要迁移到新的数据集时,归一化为单位方差可以确保新数据的分布与训练数据保持一致。这使得模型在新数据上更容易适应,减少了因数据分布差异导致的性能下降。
# 总结 :将数据归一化为单位方差是一种重要的预处理步骤,它通过统一数据的分布特性,可以加速模型收敛、提高数值稳定性、增强泛化能力、简化超参数调整,并减少特征之间的相关性。这些作用使得归一化成为深度学习中不可或缺的一部分,尤其是在处理图像、语音等高维数据时。

2.class BaseTransform: 

# 这段代码定义了一个名为 BaseTransform 的类,它是一个基础的变换类,用于对不同类型的数据(如图像、实例标注、语义标注)进行统一的处理。
# 定义了一个名为 BaseTransform 的类。这个类的作用是作为所有变换操作的基础类,提供了一个通用的框架,用于对不同类型的数据进行处理。
class BaseTransform:# Ultralytics 库中图像转换的基类。# 此类是实现各种图像处理操作的基础,旨在兼容分类和语义分割任务。# 方法:# apply_image:将图像转换应用于标签。# apply_instances:将转换应用于标签中的对象实例。# apply_semantic:将语义分割应用于图像。# __call__:将所有标签转换应用于图像、实例和语义掩码。"""Base class for image transformations in the Ultralytics library.This class serves as a foundation for implementing various image processing operations, designed to becompatible with both classification and semantic segmentation tasks.Methods:apply_image: Applies image transformations to labels.apply_instances: Applies transformations to object instances in labels.apply_semantic: Applies semantic segmentation to an image.__call__: Applies all label transformations to an image, instances, and semantic masks.Examples:>>> transform = BaseTransform()>>> labels = {"image": np.array(...), "instances": [...], "semantic": np.array(...)}>>> transformed_labels = transform(labels)"""# 定义了类的初始化方法 __init__ ,它在创建类的实例时被调用。# -> None 表示该方法没有返回值。def __init__(self) -> None:# 初始化 BaseTransform 对象。# 此构造函数设置基本转换对象,该对象可以针对特定图像处理任务进行扩展。它旨在兼容分类和语义分割。"""Initializes the BaseTransform object.This constructor sets up the base transformation object, which can be extended for specific imageprocessing tasks. It is designed to be compatible with both classification and semantic segmentation.Examples:>>> transform = BaseTransform()"""# 这里没有实现具体的初始化逻辑,只是通过 pass 占位。子类可以在继承时根据需要重写这个方法,添加自己的初始化逻辑。pass# 定义了一个名为 apply_image 的方法,用于对图像数据进行处理。 参数 :# 1.labels :可能是图像数据的标签或其他相关信息。def apply_image(self, labels):# 将图像转换应用于标签。# 此方法旨在由子类重写以实现特定的图像转换逻辑。 在其基本形式中,它返回未更改的输入标签。"""Applies image transformations to labels.This method is intended to be overridden by subclasses to implement specific image transformationlogic. In its base form, it returns the input labels unchanged.Args:labels (Any): The input labels to be transformed. The exact type and structure of labels mayvary depending on the specific implementation.Returns:(Any): The transformed labels. In the base implementation, this is identical to the input.Examples:>>> transform = BaseTransform()>>> original_labels = [1, 2, 3]>>> transformed_labels = transform.apply_image(original_labels)>>> print(transformed_labels)[1, 2, 3]"""# 这个方法目前没有实现具体的逻辑,只是通过 pass 占位。子类需要根据具体需求实现图像处理的逻辑。pass# 定义了一个名为 apply_instances 的方法,用于对实例标注数据(如目标检测中的边界框或实例分割的掩码)进行处理。 参数 :# 1.labels :实例标注的数据。def apply_instances(self, labels):# 将转换应用于标签中的对象实例。# 此方法负责将各种转换应用于给定标签内的对象实例。它旨在由子类重写以实现特定的实例转换逻辑。"""Applies transformations to object instances in labels.This method is responsible for applying various transformations to object instances within the givenlabels. It is designed to be overridden by subclasses to implement specific instance transformationlogic.Args:labels (Dict): A dictionary containing label information, including object instances.Returns:(Dict): The modified labels dictionary with transformed object instances.Examples:>>> transform = BaseTransform()>>> labels = {"instances": Instances(xyxy=torch.rand(5, 4), cls=torch.randint(0, 80, (5,)))}>>> transformed_labels = transform.apply_instances(labels)"""# 同样,这个方法目前没有实现具体逻辑,只是通过 pass 占位。子类需要根据需求实现实例标注的处理逻辑。pass# 定义了一个名为 apply_semantic 的方法,用于对语义标注数据(如语义分割的掩码)进行处理。 参数 :# 1.labels :语义标注的数据。def apply_semantic(self, labels):# 将语义分割转换应用于图像。# 此方法旨在由子类重写以实现特定的语义分割转换。 在其基本形式中,它不执行任何操作。"""Applies semantic segmentation transformations to an image.This method is intended to be overridden by subclasses to implement specific semantic segmentationtransformations. In its base form, it does not perform any operations.Args:labels (Any): The input labels or semantic segmentation mask to be transformed.Returns:(Any): The transformed semantic segmentation mask or labels.Examples:>>> transform = BaseTransform()>>> semantic_mask = np.zeros((100, 100), dtype=np.uint8)>>> transformed_mask = transform.apply_semantic(semantic_mask)"""# 这个方法也没有实现具体逻辑,只是通过 pass 占位。子类需要根据需求实现语义标注的处理逻辑。pass# 定义了一个名为 __call__ 的特殊方法,使得类的实例可以像函数一样被调用。 参数 :# 1.labels :需要处理的数据。def __call__(self, labels):# 将所有标签转换应用于图像、实例和语义掩码。# 此方法协调将 BaseTransform 类中定义的各种转换应用于输入标签。它依次调用 apply_image 和 apply_instances 方法分别处理图像和对象实例。"""Applies all label transformations to an image, instances, and semantic masks.This method orchestrates the application of various transformations defined in the BaseTransform classto the input labels. It sequentially calls the apply_image and apply_instances methods to process theimage and object instances, respectively.Args:labels (Dict): A dictionary containing image data and annotations. Expected keys include 'img' forthe image data, and 'instances' for object instances.Returns:(Dict): The input labels dictionary with transformed image and instances.Examples:>>> transform = BaseTransform()>>> labels = {"img": np.random.rand(640, 640, 3), "instances": []}>>> transformed_labels = transform(labels)"""# 在这个方法中,依次调用了 apply_image 、 apply_instances 和 apply_semantic 方法,对输入数据进行处理。self.apply_image(labels)self.apply_instances(labels)self.apply_semantic(labels)
# BaseTransform 类是一个基础的变换类,用于对不同类型的数据(图像、实例标注、语义标注)进行统一的处理。它提供了一个通用的框架,子类可以通过继承并实现具体的 apply_image 、 apply_instances 和 apply_semantic 方法,来定义不同的变换逻辑。这种设计的好处包括。模块化:将不同类型的数据处理逻辑分离到不同的方法中,便于维护和扩展。灵活性:子类可以根据需要重写任意方法,实现自定义的变换逻辑。统一接口:通过 __call__ 方法,所有子类的实例都可以通过统一的接口被调用,简化了代码的使用。这种设计模式在计算机视觉任务中非常常见,例如在数据预处理、数据增强或模型推理阶段,需要对不同类型的数据进行统一的处理。

3.class Compose: 

# 这段代码定义了一个名为 Compose 的类,用于将多个图像变换(或数据处理)操作组合在一起,并按顺序应用到输入数据上。这种设计模式在数据预处理、数据增强以及机器学习任务中非常常见。
# Compose 类用于组合多个变换操作,这些操作以列表的形式存储在 transforms 属性中。 该类提供了多种方法,用于动态管理变换操作(如添加、插入、获取、设置变换)以及应用这些变换。
class Compose:# 用于组合多个图像变换的类。# 方法:# __call__:将一系列变换应用于输入数据。# append:将新变换附加到现有变换列表。# insert:在变换列表中的指定索引处插入新变换。# __getitem__:使用索引检索特定变换或一组变换。# __setitem__:使用索引设置特定变换或一组变换。# tolist:将变换列表转换为标准 Python 列表。"""A class for composing multiple image transformations.Attributes:transforms (List[Callable]): A list of transformation functions to be applied sequentially.Methods:__call__: Applies a series of transformations to input data.append: Appends a new transform to the existing list of transforms.insert: Inserts a new transform at a specified index in the list of transforms.__getitem__: Retrieves a specific transform or a set of transforms using indexing.__setitem__: Sets a specific transform or a set of transforms using indexing.tolist: Converts the list of transforms to a standard Python list.Examples:>>> transforms = [RandomFlip(), RandomPerspective(30)]>>> compose = Compose(transforms)>>> transformed_data = compose(data)>>> compose.append(CenterCrop((224, 224)))>>> compose.insert(0, RandomFlip())"""# 这段代码是 Compose 类的初始化方法 __init__ ,用于接收一个变换操作列表或单个变换操作,并将其存储为类的属性。# 这行代码定义了 Compose 类的初始化方法 __init__ ,它接受一个参数。# 1.transforms :一个变换操作列表,或者是一个单独的变换操作对象。# 这个方法的作用是将传入的变换操作存储到类的内部属性中,以便后续可以按顺序应用这些操作。def __init__(self, transforms):# 使用转换列表初始化 Compose 对象。"""Initializes the Compose object with a list of transforms.Args:transforms (List[Callable]): A list of callable transform objects to be applied sequentially.Examples:>>> from ultralytics.data.augment import Compose, RandomHSV, RandomFlip>>> transforms = [RandomHSV(), RandomFlip()]>>> compose = Compose(transforms)"""# 对传入的 transforms 参数进行处理,并将其存储到类的属性 self.transforms 中。# isinstance(transforms, list) :检查 transforms 是否是一个列表。# 如果是列表,直接将其赋值给 self.transforms 。# 如果不是列表(例如是一个单独的变换操作对象),则将其包装成一个列表 [transforms] ,然后赋值给 self.transforms 。# 这种设计确保 self.transforms 总是一个列表,即使用户传入的是单个变换操作,也可以统一处理。self.transforms = transforms if isinstance(transforms, list) else [transforms]# 这段代码的作用是初始化 Compose 类的 transforms 属性,确保它始终是一个列表。通过这种方式, Compose 类可以灵活地接受单个变换操作或多个变换操作的列表,并在后续的 __call__ 方法中按顺序应用这些变换。这种设计提高了代码的灵活性和通用性,使得用户在使用 Compose 时可以更方便地组合变换操作,而无需担心输入的类型问题。# 这段代码定义了 Compose 类的 __call__ 方法,用于对输入数据依次应用一系列变换操作。# 这行代码定义了 __call__ 方法,它使得 Compose 类的实例可以像函数一样被调用。 参数 :# 1.data :需要被变换的输入数据,可以是图像、标注或其他任意类型的数据,具体取决于变换操作的实现。def __call__(self, data):# 对输入数据应用一系列转换。此方法按顺序将 Compose 对象的转换列表中的每个转换应用于输入数据。"""Applies a series of transformations to input data. This method sequentially applies each transformation in theCompose object's list of transforms to the input data.Args:data (Any): The input data to be transformed. This can be of any type, depending on thetransformations in the list.Returns:(Any): The transformed data after applying all transformations in sequence.Examples:>>> transforms = [Transform1(), Transform2(), Transform3()]>>> compose = Compose(transforms)>>> transformed_data = compose(input_data)"""# 开始一个循环,遍历 self.transforms 列表中的每一个变换操作 t 。 self.transforms 是在类的初始化方法中定义的属性,存储了所有需要应用的变换操作。 这个循环会依次取出每个变换操作,并对输入数据 data 进行处理。for t in self.transforms:# 将当前的变换操作 t 应用到输入数据 data 上,并将变换后的结果重新赋值给 data 。 每次循环中, data 都会被更新为经过当前变换操作处理后的结果。 这种方式确保了变换操作是按顺序依次应用的,每个变换操作的输出都会作为下一个变换操作的输入。data = t(data)# 返回最终经过所有变换操作处理后的数据。 经过循环中所有变换操作的处理后, data 包含了最终的变换结果。 这个结果可以是变换后的图像、标注或其他数据,具体取决于输入和变换操作的定义。return data# 这段代码的作用是实现 Compose 类的核心功能:对输入数据依次应用一系列变换操作。通过循环遍历 self.transforms 列表中的每个变换操作,并将每个操作的结果传递给下一个操作,最终返回经过所有变换后的数据。这种设计使得 Compose 类能够灵活地组合和应用多个变换操作,广泛用于数据预处理和数据增强任务中。# 这段代码定义了 Compose 类的 append 方法,用于将一个新的变换操作添加到现有的变换列表中。# 这行代码定义了 append 方法,它接受一个参数。# 1.transform :一个可调用的变换操作对象(通常是一个函数或类实例),它将被添加到 Compose 的变换列表中。def append(self, transform):# 将新变换附加到现有变换列表。"""Appends a new transform to the existing list of transforms.Args:transform (BaseTransform): The transformation to be added to the composition.Examples:>>> compose = Compose([RandomFlip(), RandomPerspective()])>>> compose.append(RandomHSV())"""# 调用了 Python 列表的 append 方法,将传入的 transform 添加到 self.transforms 列表的末尾。# self.transforms 是在类的初始化方法中定义的属性,存储了所有需要应用的变换操作。# 通过 append 方法,新的变换操作被动态地添加到列表中,而不会影响已有的变换操作。self.transforms.append(transform)# 这段代码的作用是为 Compose 类提供了一种动态扩展变换操作的功能。通过调用 append 方法,用户可以随时将新的变换操作添加到变换序列的末尾。这种设计使得 Compose 类更加灵活,能够根据需要动态调整变换操作的组合,而无需重新初始化整个对象。# 这段代码定义了 Compose 类的 insert 方法,用于在指定的索引位置插入一个新的变换操作。# 这行代码定义了 insert 方法,它接受两个参数。# 1.index :一个整数,表示要插入变换操作的位置。# 2.transform :一个可调用的变换操作对象,它将被插入到指定的位置。def insert(self, index, transform):# 在现有变换列表中的指定索引处插入新变换。"""Inserts a new transform at a specified index in the existing list of transforms.Args:index (int): The index at which to insert the new transform.transform (BaseTransform): The transform object to be inserted.Examples:>>> compose = Compose([Transform1(), Transform2()])>>> compose.insert(1, Transform3())>>> len(compose.transforms)3"""# 调用了 Python 列表的 insert 方法,将传入的 transform 插入到 self.transforms 列表的指定索引位置 index 。 self.transforms 是存储变换操作的列表。 insert 方法会将新的变换操作插入到指定位置,而不会覆盖原有的变换操作。插入操作会将索引位置之后的所有元素向后移动一位。self.transforms.insert(index, transform)# 这段代码的作用是为 Compose 类提供了一种灵活的扩展功能,允许用户在指定的位置插入新的变换操作。通过 insert 方法,用户可以动态调整变换操作的顺序,而无需重新定义整个变换序列。这种设计使得 Compose 类在使用过程中更加灵活,能够根据具体需求动态调整变换操作的组合和顺序。# 这段代码定义了 Compose 类的 __getitem__ 方法,用于通过索引访问和提取变换操作,同时支持单个索引和索引列表。# 这行代码定义了 __getitem__ 方法,它接受一个参数。# 1.index :该参数可以是单个整数或整数列表。 Union[list, int] 表示 index 的类型可以是 list 或 int 。# 方法的返回值是一个新的 Compose 对象。def __getitem__(self, index: Union[list, int]) -> "Compose":# 使用索引检索特定变换或一组变换。"""Retrieves a specific transform or a set of transforms using indexing.Args:index (int | List[int]): Index or list of indices of the transforms to retrieve.Returns:(Compose): A new Compose object containing the selected transform(s).Raises:AssertionError: If the index is not of type int or list.Examples:>>> transforms = [RandomFlip(), RandomPerspective(10), RandomHSV(0.5, 0.5, 0.5)]>>> compose = Compose(transforms)>>> single_transform = compose[1]  # Returns a Compose object with only RandomPerspective>>> multiple_transforms = compose[0:2]  # Returns a Compose object with RandomFlip and RandomPerspective"""# 通过 assert 语句检查 index 的类型是否为 int 或 list 。 如果 index 不是这两种类型之一,会抛出一个断言错误,并提示用户传入的索引类型不正确。 这种类型检查确保了方法的输入符合预期,避免后续操作出现错误。assert isinstance(index, (int, list)), f"The indices should be either list or int type but got {type(index)}"    # 索引应该是列表或 int 类型,但得到的是 {type(index)} 。# 将 index 统一处理为列表形式。 如果 index 是一个整数( int ),则将其包装为一个列表 [index] 。 如果 index 已经是一个列表( list ),则保持不变。 这种处理方式使得后续的逻辑可以统一处理单个索引和多个索引的情况。index = [index] if isinstance(index, int) else index# 通过列表推导式从 self.transforms 中提取指定索引位置的变换操作,并将这些操作传递给新的 Compose 对象。# [self.transforms[i] for i in index] :根据 index 列表中的每个索引值,从 self.transforms 中提取对应的变换操作。# Compose(...) :使用提取的变换操作创建一个新的 Compose 对象并返回。# 返回的新 Compose 对象包含用户请求的变换操作,可以用于进一步的组合或调用。return Compose([self.transforms[i] for i in index])# 这段代码的作用是为 Compose 类提供了一种灵活的索引访问机制,允许用户通过单个索引或索引列表提取特定的变换操作。通过返回一个新的 Compose 对象,用户可以方便地对提取的变换操作进行进一步的组合或应用。这种设计使得 Compose 类在使用过程中更加灵活,能够满足多样化的数据处理需求。# 这段代码定义了 Compose 类的 __setitem__ 方法,用于通过索引设置(或替换)变换操作。它支持单个索引和索引列表,并允许用户动态修改 Compose 中的变换操作。# 这行代码定义了 __setitem__ 方法,它接受两个参数。# 1.index :可以是单个整数( int )或整数列表( list ),表示要设置的索引位置。# 2.value :可以是单个变换操作或变换操作列表,表示要设置的新值。# 方法的返回值为 None ,因为它的作用是修改内部状态,而不是返回新值。def __setitem__(self, index: Union[list, int], value: Union[list, int]) -> None:# 使用索引设置组合中的一个或多个变换。"""Sets one or more transforms in the composition using indexing.Args:index (int | List[int]): Index or list of indices to set transforms at.value (Any | List[Any]): Transform or list of transforms to set at the specified index(es).Raises:AssertionError: If index type is invalid, value type doesn't match index type, or index is out of range.Examples:>>> compose = Compose([Transform1(), Transform2(), Transform3()])>>> compose[1] = NewTransform()  # Replace second transform>>> compose[0:2] = [NewTransform1(), NewTransform2()]  # Replace first two transforms"""# 通过 assert 语句检查 index 的类型是否为 int 或 list 。如果 index 的类型不是这两种之一,会抛出断言错误,并提示用户传入的索引类型不正确。 这种类型检查确保了方法的输入符合预期,避免后续操作出现错误。assert isinstance(index, (int, list)), f"The indices should be either list or int type but got {type(index)}"    # 索引应该是列表或 int 类型,但得到的是 {type(index)} 。# 检查 index 是否为列表。 如果 index 是列表,表示用户希望同时设置多个索引位置的变换操作。if isinstance(index, list):# 如果 index 是列表,会进一步检查 value 是否也是列表。 如果 value 不是列表,会抛出断言错误,提示用户索引类型和值类型不匹配。 这种检查确保了索引列表和值列表的类型一致,避免后续操作出现错误。assert isinstance(value, list), (f"The indices should be the same type as values, but got {type(index)} and {type(value)}"    # 索引应与值的类型相同,但得到的是 {type(index)} 和 {type(value)} 。)# 检查 index 是否为整数。 如果 index 是整数,表示用户希望设置单个索引位置的变换操作。if isinstance(index, int):# 如果 index 是整数,会将 index 和 value 分别包装为列表 [index] 和 [value] 。这样可以统一处理单个索引和多个索引的情况,后续的逻辑不需要分别处理这两种情况。index, value = [index], [value]# 使用 zip 函数将 index 和 value 配对,然后遍历每一对索引和值。 i 是索引位置, v 是对应的变换操作。for i, v in zip(index, value):# 检查索引 i 是否在 self.transforms 的有效范围内。 如果索引超出范围,会抛出断言错误,提示用户索引超出范围。 这种检查避免了对不存在的索引位置进行赋值操作。assert i < len(self.transforms), f"list index {i} out of range {len(self.transforms)}."    # 列表索引 {i} 超出范围 {len(self.transforms)}。# 将索引位置 i 的变换操作设置为新的值 v 。这是 __setitem__ 方法的核心逻辑,用于替换指定位置的变换操作。self.transforms[i] = v# 这段代码的作用是为 Compose 类提供了一种灵活的索引设置机制,允许用户通过单个索引或索引列表动态修改 Compose 中的变换操作。它通过以下方式实现了这一功能。类型检查:确保索引和值的类型一致,避免类型不匹配的错误。统一处理:将单个索引和值包装为列表,统一处理单个和多个索引的情况。范围检查:确保索引在有效范围内,避免越界错误。动态修改:通过索引直接修改 self.transforms 中的变换操作。这种设计使得 Compose 类在使用过程中更加灵活,用户可以根据需要动态调整变换操作的组合,而无需重新初始化整个对象。# 这段代码定义了 Compose 类的 tolist 方法,用于将内部存储的变换操作列表转换为一个普通的 Python 列表并返回。# 这行代码定义了 tolist 方法,它是一个实例方法,没有额外的参数。# 方法的作用是将 Compose 类内部的 self.transforms 属性(即存储变换操作的列表)转换为一个普通的 Python 列表。def tolist(self):# 将变换列表转换为标准 Python 列表。"""Converts the list of transforms to a standard Python list.Returns:(List): A list containing all the transform objects in the Compose instance.Examples:>>> transforms = [RandomFlip(), RandomPerspective(10), CenterCrop()]>>> compose = Compose(transforms)>>> transform_list = compose.tolist()>>> print(len(transform_list))3"""# 直接返回 self.transforms 。 self.transforms 是在类的初始化方法中定义的属性,存储了所有变换操作的列表。 由于 self.transforms 已经是一个列表,这里直接返回它即可。return self.transforms# 这段代码的作用是为 Compose 类提供了一个简单的方法,用于将内部的变换操作列表转换为一个普通的 Python 列表。这种设计使得用户可以方便地获取和操作变换操作的列表,例如:直接访问:用户可以直接获取变换操作的列表,而不必通过 Compose 的其他方法。进一步处理:返回的列表可以被用户进一步处理,例如排序、过滤或修改。兼容性:返回一个普通的 Python 列表,使得 Compose 的内部数据结构更加透明,方便与其他 Python 工具或函数配合使用。总之, tolist 方法为用户提供了一种简单、直接的方式来访问和操作 Compose 内部的变换操作列表。# 这段代码定义了 Compose 类的 __repr__ 方法,用于返回类的字符串表示。这种表示通常用于调试和记录,帮助用户直观地了解对象的内部状态。# 这行代码定义了 __repr__ 方法,它是一个特殊方法,用于返回对象的“官方”字符串表示。# 在 Python 中, __repr__ 方法通常返回一个可以用来重新创建对象的字符串,或者至少能够清晰地描述对象的状态。# 这个方法会在打印对象、使用 repr() 函数或在交互式环境中直接显示对象时被调用。def __repr__(self):# 返回 Compose 对象的字符串表示形式。"""Returns a string representation of the Compose object.Returns:(str): A string representation of the Compose object, including the list of transforms.Examples:>>> transforms = [RandomFlip(), RandomPerspective(degrees=10, translate=0.1, scale=0.1)]>>> compose = Compose(transforms)>>> print(compose)Compose([RandomFlip(),RandomPerspective(degrees=10, translate=0.1, scale=0.1)])"""# 生成并返回 Compose 对象的字符串表示。# self.__class__.__name__ : self.__class__ 表示当前对象的类(即 Compose )。 __name__ 是类的名称属性,返回字符串 "Compose" 。 这部分的作用是将类名作为字符串表示的开头。# [f'{t}' for t in self.transforms] :这是一个列表推导式,用于生成 self.transforms 中每个变换操作的字符串表示。 f'{t}' 调用了每个变换操作的 __repr__ 方法(如果定义了),或者默认的字符串表示。 假设每个变换操作也有自己的 __repr__ 方法,这里会生成一个包含所有变换操作描述的列表。# ', '.join(...) :使用 join 方法将上述列表中的字符串用逗号和空格连接起来,形成一个完整的字符串。 例如,如果有两个变换操作 RandomFlip() 和 RandomCrop(224, 224) ,这部分会生成字符串 "RandomFlip(), RandomCrop(224, 224)" 。# f"{...}" :使用 f-string 格式化字符串,将类名和变换操作的字符串拼接起来,形成最终的输出。 最终返回的字符串类似于 "Compose(RandomFlip(), RandomCrop(224, 224))" 。return f"{self.__class__.__name__}({', '.join([f'{t}' for t in self.transforms])})"# 这段代码的作用是为 Compose 类提供了一个清晰的字符串表示,方便用户在调试或记录时快速了解对象的状态。通过将类名和内部变换操作的描述组合在一起,用户可以直观地看到 Compose 对象包含哪些变换操作,以及它们的顺序。这种设计的优点包括。可读性:生成的字符串清晰地展示了 Compose 对象的内部状态。调试友好:在打印对象或使用调试工具时,用户可以快速了解对象的结构。灵活性:如果变换操作本身也有 __repr__ 方法,它们的描述会自动包含在最终的字符串中,使得整个表示更加详细。
# Compose 类是一个功能强大的工具,用于组合和管理多个数据变换操作。它提供了以下主要功能。动态管理变换操作:通过 append 、 insert 、 __getitem__ 和 __setitem__ 方法,可以灵活地添加、插入、获取和替换变换操作。按顺序应用变换:通过 __call__ 方法,可以按顺序对输入数据应用所有变换操作。方便的调试和记录:通过 __repr__ 方法,可以直观地查看 Compose 对象中包含的所有变换操作。灵活的索引支持:支持通过单个索引或索引列表操作变换操作。这种设计模式在数据预处理和增强任务中非常常见,例如在计算机视觉任务中,通常需要将多个变换(如随机翻转、随机裁剪、颜色调整等)组合在一起,以生成多样化的训练数据。

4.class BaseMixTransform: 

# 这段代码定义了一个名为 BaseMixTransform 的类,它是一个基础类,用于实现图像混合变换(如 Mosaic 或 MixUp)的通用逻辑。
# 定义了一个名为 BaseMixTransform 的类,它是一个基础类,用于实现混合变换(如 Mosaic 或 MixUp)的通用逻辑。
class BaseMixTransform:# MixUp 和 Mosaic 等混合转换的基类。# 此类为在数据集上实现混合转换提供了基础。它处理基于概率的转换应用并管理多个图像和标签的混合。# 方法:# __call__:将混合转换应用于输入标签。# _mix_transform:由子类为特定混合操作实现的抽象方法。# get_indexes:获取要混合的图像索引的抽象方法。# _update_label_text:更新混合图像的标签文本。"""Base class for mix transformations like MixUp and Mosaic.This class provides a foundation for implementing mix transformations on datasets. It handles theprobability-based application of transforms and manages the mixing of multiple images and labels.Attributes:dataset (Any): The dataset object containing images and labels.pre_transform (Callable | None): Optional transform to apply before mixing.p (float): Probability of applying the mix transformation.Methods:__call__: Applies the mix transformation to the input labels._mix_transform: Abstract method to be implemented by subclasses for specific mix operations.get_indexes: Abstract method to get indexes of images to be mixed._update_label_text: Updates label text for mixed images.Examples:>>> class CustomMixTransform(BaseMixTransform):...     def _mix_transform(self, labels):...         # Implement custom mix logic here...         return labels......     def get_indexes(self):...         return [random.randint(0, len(self.dataset) - 1) for _ in range(3)]>>> dataset = YourDataset()>>> transform = CustomMixTransform(dataset, p=0.5)>>> mixed_labels = transform(original_labels)"""# 初始化方法,用于设置混合变换的基本参数。参数 :# 1.dataset :数据集对象,用于获取图像和标签信息。# 2.pre_transform :可选的预处理变换,用于在混合变换之前对图像和标签进行处理。# 3.p :混合变换的概率,默认为 0.0,表示不应用混合变换。def __init__(self, dataset, pre_transform=None, p=0.0) -> None:# 初始化 BaseMixTransform 对象,用于 MixUp 和 Mosaic 等混合转换。# 此类用作在图像处理管道中实现混合转换的基础。"""Initializes the BaseMixTransform object for mix transformations like MixUp and Mosaic.This class serves as a base for implementing mix transformations in image processing pipelines.Args:dataset (Any): The dataset object containing images and labels for mixing.pre_transform (Callable | None): Optional transform to apply before mixing.p (float): Probability of applying the mix transformation. Should be in the range [0.0, 1.0].Examples:>>> dataset = YOLODataset("path/to/data")>>> pre_transform = Compose([RandomFlip(), RandomPerspective()])>>> mix_transform = BaseMixTransform(dataset, pre_transform, p=0.5)"""# 存储传入的数据集对象。self.dataset = dataset# 存储传入的预处理变换。self.pre_transform = pre_transform# 存储混合变换的概率。self.p = p# 这段代码是 BaseMixTransform 类的 __call__ 方法,用于对输入的标签( labels )执行混合变换(如 Mosaic 或 MixUp)。# 定义了 __call__ 方法,使得 BaseMixTransform 类的实例可以像函数一样被调用。 参数 :# 1.labels :输入的标签信息,通常包含图像及其对应的标注(如类别、边界框等)。def __call__(self, labels):# 将预处理转换和混合/马赛克转换应用于标签数据。# 此方法根据概率因子确定是否应用混合转换。 如果应用,它将选择其他图像,应用预转换(如果指定),然后执行混合转换。"""Applies pre-processing transforms and mixup/mosaic transforms to labels data.This method determines whether to apply the mix transform based on a probability factor. If applied, itselects additional images, applies pre-transforms if specified, and then performs the mix transform.Args:labels (Dict): A dictionary containing label data for an image.Returns:(Dict): The transformed labels dictionary, which may include mixed data from other images.Examples:>>> transform = BaseMixTransform(dataset, pre_transform=None, p=0.5)>>> result = transform({"image": img, "bboxes": boxes, "cls": classes})"""# 生成一个 [0, 1) 范围内的随机数,并与 self.p (混合变换的概率)进行比较。 如果随机数大于 self.p ,则直接返回原始 labels ,不进行混合变换。 这种设计允许以一定的概率跳过混合变换,增加了变换的随机性。if random.uniform(0, 1) > self.p:return labels# Get index of one or three other images# 调用 self.get_indexes() 方法,获取一个或多个其他图像的索引。 这个方法是抽象的,需要在子类中实现。它返回的索引用于选择其他图像进行混合变换。 返回值可以是一个整数(单个索引)或一个列表(多个索引)。indexes = self.get_indexes()# 检查 indexes 是否为单个整数(即只选择了一个图像进行混合)。if isinstance(indexes, int):# 如果是单个整数,则将其转换为列表 [indexes] ,以便后续逻辑可以统一处理单个索引和多个索引的情况。indexes = [indexes]# Get images information will be used for Mosaic or MixUp# 使用列表推导式,从数据集中获取与 indexes 对应的 图像 及其 标签信息 。 self.dataset.get_image_and_label(i) 是一个假设的方法,用于根据索引 i 获取图像及其标签。 返回的 mix_labels 是一个列表,包含 所有用于混合的图像 和 标签信息 。mix_labels = [self.dataset.get_image_and_label(i) for i in indexes]# 检查是否定义了预处理变换 self.pre_transform 。 如果定义了预处理变换,则对每个混合图像及其标签应用预处理操作。if self.pre_transform is not None:# 遍历 mix_labels 列表,对每个混合图像及其标签进行处理。for i, data in enumerate(mix_labels):# 对当前的 混合图像及其标签 应用预处理变换 self.pre_transform ,并将结果存储回 mix_labels 列表。mix_labels[i] = self.pre_transform(data)# 将混合图像的标签信息存储到 labels 字典中,键为 "mix_labels" 。 这为后续的混合变换逻辑提供了输入数据。labels["mix_labels"] = mix_labels# Update cls and texts# 调用 _update_label_text 方法,更新标签中的类别和文本信息。 这个方法的作用是确保混合后的标签信息(如类别索引和文本描述)是正确的。labels = self._update_label_text(labels)# Mosaic or MixUp# 调用 _mix_transform 方法,实现具体的混合变换逻辑(如 Mosaic 或 MixUp)。 这个方法是抽象的,需要在子类中实现具体的混合变换逻辑。labels = self._mix_transform(labels)# 从 labels 字典中移除临时的 "mix_labels" 键,以清理不再需要的中间数据。labels.pop("mix_labels", None)# 返回经过混合变换后的标签信息。return labels# 这段代码实现了混合变换(如 Mosaic 或 MixUp)的核心逻辑,主要包括以下步骤。随机决定是否应用混合变换:根据概率 self.p 决定是否跳过混合变换。获取其他图像的索引:通过 self.get_indexes() 获取用于混合的图像索引。获取混合图像的信息:从数据集中获取其他图像及其标签。应用预处理变换:对混合图像及其标签应用预处理操作(如果定义了)。更新标签信息:通过 _update_label_text 方法更新类别和文本信息。实现具体的混合变换逻辑:通过 _mix_transform 方法实现 Mosaic 或 MixUp 等混合变换。清理中间数据:移除临时的 "mix_labels" 键,返回最终的标签信息。这种设计使得 BaseMixTransform 类可以作为混合变换的基础框架,用户可以通过继承该类并实现具体的混合逻辑来扩展功能。# 这个方法用于实现具体的混合变换逻辑(如 Mosaic 或 MixUp)。 它是一个抽象方法,必须在子类中实现。def _mix_transform(self, labels):# 将 MixUp 或 Mosaic 增强应用于标签字典。# 此方法应由子类实现,以执行特定的混合转换,如 MixUp 或 Mosaic。它使用增强数据就地修改输入标签字典。"""Applies MixUp or Mosaic augmentation to the label dictionary.This method should be implemented by subclasses to perform specific mix transformations like MixUp orMosaic. It modifies the input label dictionary in-place with the augmented data.Args:labels (Dict): A dictionary containing image and label data. Expected to have a 'mix_labels' keywith a list of additional image and label data for mixing.Returns:(Dict): The modified labels dictionary with augmented data after applying the mix transform.Examples:>>> transform = BaseMixTransform(dataset)>>> labels = {"image": img, "bboxes": boxes, "mix_labels": [{"image": img2, "bboxes": boxes2}]}>>> augmented_labels = transform._mix_transform(labels)"""# 抛出 NotImplementedError 异常,提示用户需要在子类中实现具体逻辑。raise NotImplementedError# 这个方法用于获取其他图像的索引。 它是一个抽象方法,必须在子类中实现。def get_indexes(self):# 获取用于马赛克增强的混洗索引列表。"""Gets a list of shuffled indexes for mosaic augmentation.Returns:(List[int]): A list of shuffled indexes from the dataset.Examples:>>> transform = BaseMixTransform(dataset)>>> indexes = transform.get_indexes()>>> print(indexes)  # [3, 18, 7, 2]"""# 抛出 NotImplementedError 异常,提示用户需要在子类中实现具体逻辑。raise NotImplementedError# 这段代码定义了 BaseMixTransform 类中的一个静态方法 _update_label_text ,用于更新混合后的标签信息,特别是类别( cls )和文本描述( texts )。# 将 _update_label_text 方法定义为静态方法。静态方法不需要访问类或实例的属性,因此不绑定到类或实例。它可以直接通过类名调用,也可以在类外部独立使用。@staticmethod# 定义了 _update_label_text 方法,它接受一个参数。# 1.labels :表示包含标签信息的字典。 labels 通常是一个字典,包含图像的类别、边界框、文本描述等信息。def _update_label_text(labels):# 更新图像增强中混合标签的标签文本和类别 ID。# 此方法处理输入标签字典和任何混合标签的“texts”和“cls”字段,创建一组统一的文本标签并相应地更新类别 ID。"""Updates label text and class IDs for mixed labels in image augmentation.This method processes the 'texts' and 'cls' fields of the input labels dictionary and any mixed labels,creating a unified set of text labels and updating class IDs accordingly.Args:labels (Dict): A dictionary containing label information, including 'texts' and 'cls' fields,and optionally a 'mix_labels' field with additional label dictionaries.Returns:(Dict): The updated labels dictionary with unified text labels and updated class IDs.Examples:>>> labels = {...     "texts": [["cat"], ["dog"]],...     "cls": torch.tensor([[0], [1]]),...     "mix_labels": [{"texts": [["bird"], ["fish"]], "cls": torch.tensor([[0], [1]])}],... }>>> updated_labels = self._update_label_text(labels)>>> print(updated_labels["texts"])[['cat'], ['dog'], ['bird'], ['fish']]>>> print(updated_labels["cls"])tensor([[0],[1]])>>> print(updated_labels["mix_labels"][0]["cls"])tensor([[2],[3]])"""# 检查 labels 字典中是否包含键 "texts" 。if "texts" not in labels:# 如果没有 "texts" 键,说明不需要更新文本信息,直接返回原始 labels 。return labels# 合并 原始标签 和 混合标签 中的 文本描述 。# [labels["texts"]] :获取原始标签中的文本描述。# [x["texts"] for x in labels["mix_labels"]] :从每个混合标签中提取文本描述。# sum(..., []) :将这些文本描述列表合并成一个大列表。mix_texts = sum([labels["texts"]] + [x["texts"] for x in labels["mix_labels"]], [])# 去重并确保文本描述是唯一的。# {tuple(x) for x in mix_texts} :将每个文本描述转换为元组(因为列表不可哈希,不能直接去重),并使用集合去重。# list(...) :将去重后的集合转换回列表。mix_texts = list({tuple(x) for x in mix_texts})# 创建一个 从文本描述 到 唯一类别索引的映射字典 。# enumerate(mix_texts) :为每个唯一的文本描述分配一个唯一的索引。# text2id :一个字典,键是文本描述(元组形式),值是对应的类别索引。text2id = {text: i for i, text in enumerate(mix_texts)}# 遍历原始标签和所有混合标签。# [labels] :原始标签。# labels["mix_labels"] :混合标签列表。# 这个循环的作用是对所有标签(原始和混合)进行统一处理。for label in [labels] + labels["mix_labels"]:# 遍历 当前标签中的类别索引 。# label["cls"].squeeze(-1).tolist() :将类别张量压缩并转换为列表。# enumerate(...) :为每个类别索引分配一个索引 i 。for i, cls in enumerate(label["cls"].squeeze(-1).tolist()):# 根据 类别索引 cls 获取对应的文本描述。# int(cls) :将类别索引转换为整数。# label["texts"][...] :从文本描述列表中获取对应的文本。text = label["texts"][int(cls)]# 将类别索引更新为新的唯一索引。# tuple(text) :将文本描述转换为元组形式。# text2id[...] :根据文本描述获取对应的唯一类别索引。# label["cls"][i] = ... :更新类别索引。label["cls"][i] = text2id[tuple(text)]# 将所有标签的文本描述更新为去重后的唯一文本描述列表。# mix_texts :去重后的文本描述列表。label["texts"] = mix_texts# 返回更新后的标签信息。return labels# 这段代码的作用是更新混合后的标签信息,确保类别索引和文本描述一致且唯一。具体步骤包括。合并文本描述:将原始标签和混合标签中的文本描述合并。去重:确保文本描述是唯一的。创建映射:创建从文本描述到唯一类别索引的映射。更新类别索引:根据映射更新所有标签中的类别索引。更新文本描述:将所有标签的文本描述更新为去重后的列表。这种设计使得混合变换后的标签信息能够正确反映类别和文本描述的关系,适用于需要对混合数据进行分类或识别的任务。
# BaseMixTransform 类是一个基础类,用于实现图像混合变换(如 Mosaic 或 MixUp)的通用逻辑。它的主要功能包括。随机决定是否应用混合变换:根据概率 p 决定是否进行混合变换。获取其他图像的索引:通过抽象方法 get_indexes 获取其他图像的索引。获取混合图像的信息:从数据集中获取其他图像及其标签信息。应用预处理变换:对混合图像及其标签应用预处理变换(如果定义了)。更新标签信息:通过 _update_label_text 方法更新类别和文本信息。实现具体的混合变换逻辑:通过抽象方法 _mix_transform 实现具体的混合变换逻辑(如 Mosaic 或 MixUp)。这种设计使得 BaseMixTransform 类可以作为 Mosaic、MixUp 等混合变换算法的基础框架,用户可以通过继承该类并实现具体的混合逻辑来扩展功能。

5.class Mosaic(BaseMixTransform): 

# 这段代码定义了一个名为 Mosaic 的类,继承自 BaseMixTransform ,用于实现 Mosaic 数据增强方法。Mosaic 是一种常用于目标检测和分割任务的数据增强技术,通过将多张图像拼接在一起,形成一张更大的图像,从而增加模型对多目标场景的学习能力。
# 定义了 Mosaic 类,继承自 BaseMixTransform ,继承了混合变换的基础逻辑和方法。
class Mosaic(BaseMixTransform):# 图像数据集的马赛克增强。# 此类通过将多张(4 张或 9 张)图像组合成一张马赛克图像来执行马赛克增强。增强以给定的概率应用于数据集。# 方法:# get_indexes:返回来自数据集的随机索引列表。# _mix_transform:将混合转换应用于输入图像和标签。# _mosaic3:创建 1x3 图像马赛克。# _mosaic4:创建 2x2 图像马赛克。# _mosaic9:创建 3x3 图像马赛克。# _update_labels:使用填充更新标签。# _cat_labels:连接标签并剪辑马赛克边框实例。"""Mosaic augmentation for image datasets.This class performs mosaic augmentation by combining multiple (4 or 9) images into a single mosaic image.The augmentation is applied to a dataset with a given probability.Attributes:dataset: The dataset on which the mosaic augmentation is applied.imgsz (int): Image size (height and width) after mosaic pipeline of a single image.p (float): Probability of applying the mosaic augmentation. Must be in the range 0-1.n (int): The grid size, either 4 (for 2x2) or 9 (for 3x3).border (Tuple[int, int]): Border size for width and height.Methods:get_indexes: Returns a list of random indexes from the dataset._mix_transform: Applies mixup transformation to the input image and labels._mosaic3: Creates a 1x3 image mosaic._mosaic4: Creates a 2x2 image mosaic._mosaic9: Creates a 3x3 image mosaic._update_labels: Updates labels with padding._cat_labels: Concatenates labels and clips mosaic border instances.Examples:>>> from ultralytics.data.augment import Mosaic>>> dataset = YourDataset(...)  # Your image dataset>>> mosaic_aug = Mosaic(dataset, imgsz=640, p=0.5, n=4)>>> augmented_labels = mosaic_aug(original_labels)"""# 这段代码定义了 Mosaic 类的初始化方法 __init__ ,用于设置 Mosaic 数据增强的核心参数和属性。# 定义了 Mosaic 类的初始化方法,接受以下参数 :# 1.dataset :数据集对象,用于获取图像和标签信息。# 2.imgsz :目标图像的尺寸,默认值为 640。这是 Mosaic 拼接后图像的大小。# 3.p :应用 Mosaic 数据增强的概率,默认值为 1.0(总是应用)。# 4.n :拼接的图像数量,默认值为 4。支持 3、4 或 9 张图像拼接。def __init__(self, dataset, imgsz=640, p=1.0, n=4):# 初始化马赛克增强对象。# 此类通过将多张(4 张或 9 张)图像组合成一张马赛克图像来执行马赛克增强。# 增强以给定的概率应用于数据集。"""Initializes the Mosaic augmentation object.This class performs mosaic augmentation by combining multiple (4 or 9) images into a single mosaic image.The augmentation is applied to a dataset with a given probability.Args:dataset (Any): The dataset on which the mosaic augmentation is applied.imgsz (int): Image size (height and width) after mosaic pipeline of a single image.p (float): Probability of applying the mosaic augmentation. Must be in the range 0-1.n (int): The grid size, either 4 (for 2x2) or 9 (for 3x3).Examples:>>> from ultralytics.data.augment import Mosaic>>> dataset = YourDataset(...)>>> mosaic_aug = Mosaic(dataset, imgsz=640, p=0.5, n=4)"""# 验证参数 p 是否在 [0, 1] 范围内。 p 表示应用 Mosaic 数据增强的概率。 如果 p 不在 [0, 1] 范围内,抛出断言错误,并提示用户传入的概率值无效。assert 0 <= p <= 1.0, f"The probability should be in range [0, 1], but got {p}."    # 概率应该在 [0, 1] 范围内,但得到的是 {p}。# 验证参数 n 是否为 4 或 9 。 n 表示 Mosaic 拼接的图像数量。 Mosaic 支持 3×3 或 2×2 的网格拼接,因此 n 必须是 4 或 9。 如果 n 不是 4 或 9,抛出断言错误。assert n in {4, 9}, "grid must be equal to 4 or 9."    # 网格必须等于 4 或 9。# 调用父类 BaseMixTransform 的初始化方法,将 dataset 和 p 传递给父类 。# dataset :数据集对象。# p :应用混合变换的概率。# 这一步继承了父类的基本功能,如随机决定是否应用变换。super().__init__(dataset=dataset, p=p)# 将目标图像尺寸 imgsz 赋值给类的属性 self.imgsz 。 imgsz 是 Mosaic 拼接后图像的大小。 这个属性将用于后续的图像拼接和裁剪逻辑。self.imgsz = imgsz# 设置拼接图像的 边界偏移量 self.border 。 (-imgsz // 2, -imgsz // 2) 表示在宽度和高度方向上的偏移量。 这个偏移量用于裁剪拼接后的图像,以确保最终输出的图像大小为 imgsz 。 例如,如果 imgsz=640 ,则偏移量为 (-320, -320) 。self.border = (-imgsz // 2, -imgsz // 2)  # width, height# 将 拼接的图像数量 n 赋值给类的属性 self.n 。 n 表示拼接的图像数量,支持 3、4 或 9 张图像拼接。 这个属性将用于后续的拼接逻辑,决定是调用 _mosaic3 、 _mosaic4 还是 _mosaic9 方法。self.n = n# 这段代码的作用是初始化 Mosaic 类的核心参数和属性,为后续的 Mosaic 数据增强逻辑做准备。主要功能包括。参数验证:确保概率 p 在 [0, 1] 范围内,拼接数量 n 为 4 或 9。继承父类功能:通过调用父类的初始化方法,继承了随机决定是否应用变换的逻辑。设置关键属性: self.imgsz :目标图像尺寸。 self.border :拼接图像的边界偏移量。 self.n :拼接的图像数量。这些设置为 Mosaic 数据增强的实现提供了基础,确保了拼接逻辑的正确性和灵活性。# 这段代码定义了 Mosaic 类中的 get_indexes 方法,用于获取用于 Mosaic 数据增强的其他图像的索引。# 定义了 get_indexes 方法,它接受一个布尔参数。# 1.buffer :用于决定从何处选择图像索引。 如果 buffer=True ,则从数据集的缓冲区中选择图像。 如果 buffer=False ,则从整个数据集中随机选择图像。def get_indexes(self, buffer=True):# 返回用于马赛克增强的数据集随机索引列表。# 此方法根据“buffer”参数从缓冲区或整个数据集中选择随机图像索引。它用于选择用于创建马赛克增强的图像。"""Returns a list of random indexes from the dataset for mosaic augmentation.This method selects random image indexes either from a buffer or from the entire dataset, depending onthe 'buffer' parameter. It is used to choose images for creating mosaic augmentations.Args:buffer (bool): If True, selects images from the dataset buffer. If False, selects from the entiredataset.Returns:(List[int]): A list of random image indexes. The length of the list is n-1, where n is the numberof images used in the mosaic (either 3 or 8, depending on whether n is 4 or 9).Examples:>>> mosaic = Mosaic(dataset, imgsz=640, p=1.0, n=4)>>> indexes = mosaic.get_indexes()>>> print(len(indexes))  # Output: 3"""# 判断是否从缓冲区中选择图像。 如果 buffer=True ,执行缓冲区选择逻辑。if buffer:  # select images from buffer# 从缓冲区中选择图像索引。 self.dataset.buffer 是一个可迭代对象,包含缓冲区中的图像索引。 random.choices(..., k=self.n - 1) 从缓冲区中随机选择 self.n - 1 个图像索引。 返回一个包含 self.n - 1 个索引的列表。# self.n 是 Mosaic 拼接的图像数量(如 3、4 或 9)。Mosaic 数据增强通常需要 self.n - 1 张额外的图像来拼接。# 缓冲区选择逻辑允许在训练过程中重复使用某些图像,这有助于提高数据增强的效率。return random.choices(list(self.dataset.buffer), k=self.n - 1)# 如果 buffer=False ,则从整个数据集中随机选择图像。else:  # select any images# 从整个数据集中随机选择图像索引。 使用列表推导式生成 self.n - 1 个随机索引。 random.randint(0, len(self.dataset) - 1) 生成一个范围在 [0, len(self.dataset) - 1] 内的随机整数。 返回一个包含 self.n - 1 个随机索引的列表。# 这种方式确保了从整个数据集中均匀地选择图像,避免了缓冲区选择可能带来的偏差。return [random.randint(0, len(self.dataset) - 1) for _ in range(self.n - 1)]# get_indexes 方法的作用是为 Mosaic 数据增强选择额外的图像索引。它支持两种选择方式。从缓冲区中选择:通过 buffer=True ,从数据集的缓冲区中随机选择 self.n - 1 个图像索引。这种方式有助于提高数据增强的效率,尤其是在大规模数据集上。从整个数据集中选择:通过 buffer=False ,从整个数据集中随机选择 self.n - 1 个图像索引。这种方式确保了图像选择的随机性,避免了缓冲区选择可能带来的偏差。这种设计使得 Mosaic 类可以根据具体需求灵活选择图像索引,从而实现高效且随机的数据增强。# 这段代码定义了 Mosaic 类中的 _mix_transform 方法,它是 Mosaic 数据增强的核心逻辑入口。这个方法根据拼接的图像数量( self.n )选择具体的 Mosaic 实现方式(3张、4张或9张图像拼接)。# 定义了 _mix_transform 方法,它接受一个参数。# 1.labels :表示包含图像和标签信息的字典。 labels 包含 当前图像的标签信息 以及通过 get_indexes 方法获取的 其他图像的标签信息 (存储在 labels["mix_labels"] 中)。def _mix_transform(self, labels):# 将马赛克增强应用于输入图像和标签。# 此方法根据“n”属性将多幅图像(3、4 或 9)组合成一幅马赛克图像。# 它确保不存在矩形注释,并且有其他图像可用于马赛克增强。"""Applies mosaic augmentation to the input image and labels.This method combines multiple images (3, 4, or 9) into a single mosaic image based on the 'n' attribute.It ensures that rectangular annotations are not present and that there are other images available formosaic augmentation.Args:labels (Dict): A dictionary containing image data and annotations. Expected keys include:- 'rect_shape': Should be None as rect and mosaic are mutually exclusive.- 'mix_labels': A list of dictionaries containing data for other images to be used in the mosaic.Returns:(Dict): A dictionary containing the mosaic-augmented image and updated annotations.Raises:AssertionError: If 'rect_shape' is not None or if 'mix_labels' is empty.Examples:>>> mosaic = Mosaic(dataset, imgsz=640, p=1.0, n=4)>>> augmented_data = mosaic._mix_transform(labels)"""# 验证 labels 中是否包含 "rect_shape" 键。 如果存在 "rect_shape" ,说明当前正在使用 Rect 数据增强,而 Rect 和 Mosaic 是互斥的。 如果存在 "rect_shape" ,抛出断言错误,提示用户 Rect 和 Mosaic 不能同时使用。assert labels.get("rect_shape", None) is None, "rect and mosaic are mutually exclusive."    # rect 和 mosaic 是互斥的。# 验证 labels["mix_labels"] 是否包含其他图像的标签信息。 labels["mix_labels"] 是通过 get_indexes 方法获取的其他图像的标签列表。 如果 labels["mix_labels"] 为空,说明没有其他图像用于 Mosaic 数据增强,抛出断言错误。assert len(labels.get("mix_labels", [])), "There are no other images for mosaic augment."    # 没有其他可用于马赛克增强的图像。# 根据 self.n 的值选择具体的 Mosaic 实现方式。# 如果 self.n == 3 ,调用 _mosaic3 方法,实现 3 张图像的 Mosaic 拼接。# 如果 self.n == 4 ,调用 _mosaic4 方法,实现 4 张图像的 Mosaic 拼接。# 如果 self.n == 9 ,调用 _mosaic9 方法,实现 9 张图像的 Mosaic 拼接。# self.n 是在类初始化时设置的参数,表示 Mosaic 拼接的图像数量。# 这里使用了 Python 的三元运算符,简洁地实现了条件分支逻辑。return (self._mosaic3(labels) if self.n == 3 else self._mosaic4(labels) if self.n == 4 else self._mosaic9(labels))  # This code is modified for mosaic3 method.# _mix_transform 方法是 Mosaic 数据增强的核心逻辑入口,它的主要功能包括。验证输入条件:确保 Mosaic 和 Rect 数据增强不会同时使用。确保有其他图像用于 Mosaic 数据增强。选择具体的 Mosaic 实现方式:根据 self.n 的值,选择 _mosaic3 、 _mosaic4 或 _mosaic9 方法实现具体的拼接逻辑。这种设计使得 Mosaic 类能够灵活支持不同数量的图像拼接。通过 _mix_transform 方法, Mosaic 类能够根据用户指定的拼接数量动态选择合适的拼接逻辑,从而实现高效且灵活的数据增强。# 这段代码定义了 Mosaic 类中的 _mosaic3 方法,用于实现 3 张图像的 Mosaic 数据增强。# 定义了 _mosaic3 方法,它接受一个参数。# 1.labels :表示包含当前图像及其标签信息的字典。 labels 包含 当前图像的标签信息 以及通过 get_indexes 方法获取的 其他图像的标签信息 (存储在 labels["mix_labels"] 中)。def _mosaic3(self, labels):# 通过组合三幅图像创建 1x3 图像马赛克。# 此方法将三幅图像以水平布局排列,主图像位于中心,两幅附加图像位于两侧。它是对象检测中使用的马赛克增强技术的一部分。"""Creates a 1x3 image mosaic by combining three images.This method arranges three images in a horizontal layout, with the main image in the center and twoadditional images on either side. It's part of the Mosaic augmentation technique used in object detection.Args:labels (Dict): A dictionary containing image and label information for the main (center) image.Must include 'img' key with the image array, and 'mix_labels' key with a list of twodictionaries containing information for the side images.Returns:(Dict): A dictionary with the mosaic image and updated labels. Keys include:- 'img' (np.ndarray): The mosaic image array with shape (H, W, C).- Other keys from the input labels, updated to reflect the new image dimensions.Examples:>>> mosaic = Mosaic(dataset, imgsz=640, p=1.0, n=3)>>> labels = {...     "img": np.random.rand(480, 640, 3),...     "mix_labels": [{"img": np.random.rand(480, 640, 3)} for _ in range(2)],... }>>> result = mosaic._mosaic3(labels)>>> print(result["img"].shape)(640, 640, 3)"""# 初始化一个空列表 mosaic_labels ,用于 存储每张图像的更新后的标签信息 。mosaic_labels = []# 将 目标图像尺寸 self.imgsz 赋值给变量 s ,用于 后续的图像拼接和裁剪逻辑 。s = self.imgsz# 开始一个循环,遍历 3 张图像(当前图像和 2 张其他图像)。for i in range(3):# 在 Mosaic 数据增强中,我们需要将多张图像拼接在一起。具体来说 :# 当前图像 :这是主图像,通常是触发 Mosaic 数据增强的原始图像。# 其他图像 :这些是从数据集中随机选择的图像,用于与当前图像拼接。# 在 labels 字典中 :# labels 包含当前图像的标签信息。# labels["mix_labels"] 是一个列表,包含其他图像的标签信息。# 索引偏移当 :# i == 0 时, labels_patch 直接使用当前图像的标签信息( labels )。当 i > 0 时, labels_patch 需要从 labels["mix_labels"] 中获取其他图像的标签信息。# 为什么使用 [i - 1] ?# 这里的核心问题是 索引的对齐 :# i 是一个从 0 开始的循环索引,表示当前处理的是第几张图像(包括当前图像和其他图像)。# 当 i == 0 时,处理的是当前图像,不需要从 labels["mix_labels"] 中获取。# 当 i > 0 时,处理的是其他图像。由于 labels["mix_labels"] 是一个列表,存储的是 其他图像的标签信息,因此需要从这个列表中获取对应的标签。# 具体解释 :# 假设 self.n = 3 ,表示需要拼接 3 张图像(1 张当前图像 + 2 张其他图像) :# i == 0 :处理当前图像,直接使用 labels 。# i == 1 :处理第一张其他图像,从 labels["mix_labels"][0] 获取标签。# i == 2 :处理第二张其他图像,从 labels["mix_labels"][1] 获取标签。# 因此,当 i > 0 时, i - 1 的作用是将循环索引 i 转换为 labels["mix_labels"] 列表中的索引。这样可以确保 :# i == 1 时,访问 labels["mix_labels"][0] 。# i == 2 时,访问 labels["mix_labels"][1] 。# 总结 : labels["mix_labels"][i - 1] 的作用是 :# 对齐索引 :将循环索引 i 转换为 labels["mix_labels"] 列表中的索引。# 访问其他图像的标签 :确保能够正确地从 labels["mix_labels"] 中获取其他图像的标签信息。# 这种设计使得代码能够简洁地处理当前图像和其他图像的标签信息,而不需要额外的逻辑来区分它们。# 根据当前索引 i ,选择当前图像或其他图像的标签信息。 如果 i == 0 , labels_patch 是当前图像的标签信息。 如果 i > 0 , labels_patch 是其他图像的标签信息,从 labels["mix_labels"] 中获取。labels_patch = labels if i == 0 else labels["mix_labels"][i - 1]# Load image# 从 labels_patch 中获取当前图像的数据。img = labels_patch["img"]# 从 labels_patch 中获取 当前图像的大小 ( 高度 h 和 宽度 w ),并从字典中移除该键。h, w = labels_patch.pop("resized_shape")# Place img in img3# 判断当前图像是否为第一张图像(中心图像)。if i == 0:  # center# 如果当前图像为第一张图像。 初始化一个大小为 3s × 3s 的空白图像 img3 ,用于放置 3 张图像。img3 = np.full((s * 3, s * 3, img.shape[2]), 114, dtype=np.uint8)  # base image with 3 tiles# 将 当前图像的大小 存储为 h0 和 w0 ,用于后续计算。h0, w0 = h, w# 定义当前图像的放置位置 c ,即 (xmin, ymin, xmax, ymax) 。c = s, s, s + w, s + h  # xmin, ymin, xmax, ymax (base) coordinates# 判断当前图像是否为第二张图像(右侧图像)。elif i == 1:  # right# 定义第二张图像的放置位置 c ,即 (xmin, ymin, xmax, ymax) 。c = s + w0, s, s + w0 + w, s + h# 判断当前图像是否为第三张图像(左侧图像)。elif i == 2:  # left# 定义第三张图像的放置位置 c ,即 (xmin, ymin, xmax, ymax) 。c = s - w, s + h0 - h, s, s + h0# 从 c 中提取 水平 和 垂直 方向的偏移量 padw 和 padh 。padw, padh = c[:2]# 计算 当前图像在拼接图像中的实际放置位置 ,确保坐标不小于 0。x1, y1, x2, y2 = (max(x, 0) for x in c)  # allocate coordinates# 将当前图像的对应部分放置到拼接图像 img3 的指定位置。img3[y1:y2, x1:x2] = img[y1 - padh :, x1 - padw :]  # img3[ymin:ymax, xmin:xmax]# hp, wp = h, w  # height, width previous for next iteration# Labels assuming imgsz*2 mosaic size# 更新当前图像的标签信息 ,调整边界框坐标以适应拼接后的图像。labels_patch = self._update_labels(labels_patch, padw + self.border[0], padh + self.border[1])# 并将更新后的标签信息添加到 mosaic_labels 列表中。mosaic_labels.append(labels_patch)# 调用 _cat_labels 方法,将所有图像的标签信息合并为一个最终的标签字典。final_labels = self._cat_labels(mosaic_labels)# 裁剪拼接后的图像 img3 , 去除边界偏移量 ,得到最终的 Mosaic 图像。final_labels["img"] = img3[-self.border[0] : self.border[0], -self.border[1] : self.border[1]]# 返回最终的标签信息,包括 拼接后的图像 和 更新后的标签 。return final_labels# _mosaic3 方法实现了 3 张图像的 Mosaic 数据增强,具体步骤包括。初始化一个大小为 3s × 3s 的空白图像 img3 。遍历 3 张图像,分别放置在拼接图像的不同位置(中心、右侧、左侧)。更新每张图像的标签信息,调整边界框坐标以适应拼接后的图像。合并所有图像的标签信息,裁剪拼接后的图像以去除边界偏移量。返回最终的标签信息,包括拼接后的图像和更新后的标签。这种设计使得 Mosaic 类能够灵活支持 3 张图像的拼接逻辑,适用于目标检测和分割任务中的数据增强。# 这段代码定义了 Mosaic 类中的 _mosaic4 方法,用于实现 4 张图像的 Mosaic 数据增强。4 张图像被拼接成一个 2×2 的网格,每张图像占据网格的一个象限。# 定义了 _mosaic4 方法,它接受一个参数。# 1.labels :表示包含当前图像及其标签信息的字典。 labels 包含 当前图像的标签信息 以及通过 get_indexes 方法获取的 其他图像的标签信息 (存储在 labels["mix_labels"] 中)。def _mosaic4(self, labels):# 从四幅输入图像创建 2x2 图像马赛克。# 此方法将四幅图像放在 2x2 网格中,将它们组合成一幅马赛克图像。它还会更新马赛克中每幅图像的相应标签。"""Creates a 2x2 image mosaic from four input images.This method combines four images into a single mosaic image by placing them in a 2x2 grid. It alsoupdates the corresponding labels for each image in the mosaic.Args:labels (Dict): A dictionary containing image data and labels for the base image (index 0) and threeadditional images (indices 1-3) in the 'mix_labels' key.Returns:(Dict): A dictionary containing the mosaic image and updated labels. The 'img' key contains the mosaicimage as a numpy array, and other keys contain the combined and adjusted labels for all four images.Examples:>>> mosaic = Mosaic(dataset, imgsz=640, p=1.0, n=4)>>> labels = {...     "img": np.random.rand(480, 640, 3),...     "mix_labels": [{"img": np.random.rand(480, 640, 3)} for _ in range(3)],... }>>> result = mosaic._mosaic4(labels)>>> assert result["img"].shape == (1280, 1280, 3)"""# 初始化一个空列表 mosaic_labels ,用于 存储每张图像的更新后的标签信息 。mosaic_labels = []# 将 目标图像尺寸 self.imgsz 赋值给变量 s ,用于 后续的图像拼接和裁剪逻辑 。s = self.imgsz# 随机生成 Mosaic 图像的中心点坐标 (xc, yc) 。 self.border 是一个元组,表示 Mosaic 图像的边界偏移量。 random.uniform(-x, 2 * s + x) 生成一个随机数,范围从 -x 到 2 * s + x ,确保中心点可以在整个拼接图像范围内随机选择。yc, xc = (int(random.uniform(-x, 2 * s + x)) for x in self.border)  # mosaic center x, y# 开始一个循环,遍历 4 张图像(当前图像和 3 张其他图像)。for i in range(4):# 根据当前索引 i ,选择当前图像或其他图像的标签信息。 如果 i == 0 , labels_patch 是 当前图像的标签信息 。 如果 i > 0 , labels_patch 是 其他图像的标签信息 ,从 labels["mix_labels"] 中获取。labels_patch = labels if i == 0 else labels["mix_labels"][i - 1]# Load image# 从 labels_patch 中获取 当前图像的数据 。img = labels_patch["img"]# 从 labels_patch 中获取 当前图像的大小 ( 高度 h 和 宽度 w ),并从字典中移除该键。h, w = labels_patch.pop("resized_shape")# 这段代码是 _mosaic4 方法的核心部分,用于将 4 张图像拼接到一个 2×2 的网格中,并更新每张图像的标签信息。# Place img in img4# 判断当前图像是否为第一张图像(左上角)。if i == 0:  # top left# 初始化一个大小为 2s × 2s 的空白图像 img4 ,用于放置 4 张图像 。 s 是单张图像的目标尺寸。 img.shape[2] 是图像的通道数(通常是 3,表示 RGB 图像)。 使用值 114 填充空白图像,这是一个常见的填充值,通常用于 YOLO 等目标检测算法。img4 = np.full((s * 2, s * 2, img.shape[2]), 114, dtype=np.uint8)  # base image with 4 tiles# 定义 第一张图像在拼接图像中的放置位置 (x1a, y1a, x2a, y2a) 。 xc 和 yc 是拼接中心点的坐标。 w 和 h 是当前图像的宽度和高度。 max(..., 0) 确保坐标不会超出拼接图像的边界。x1a, y1a, x2a, y2a = max(xc - w, 0), max(yc - h, 0), xc, yc  # xmin, ymin, xmax, ymax (large image)# 定义 第一张图像的裁剪区域 (x1b, y1b, x2b, y2b) 。这些坐标用于从原始图像中裁剪出与拼接位置匹配的区域。x1b, y1b, x2b, y2b = w - (x2a - x1a), h - (y2a - y1a), w, h  # xmin, ymin, xmax, ymax (small image)# 判断当前图像是否为第二张图像(右上角)。elif i == 1:  # top right# 定义第二张图像 在拼接图像中的放置位置 和 裁剪区域 。x1a, y1a, x2a, y2a = xc, max(yc - h, 0), min(xc + w, s * 2), ycx1b, y1b, x2b, y2b = 0, h - (y2a - y1a), min(w, x2a - x1a), # 判断当前图像是否为第三张图像(左下角)。elif i == 2:  # bottom left# 定义第三张图像 在拼接图像中的放置位置 和 裁剪区域 。x1a, y1a, x2a, y2a = max(xc - w, 0), yc, xc, min(s * 2, yc + h)x1b, y1b, x2b, y2b = w - (x2a - x1a), 0, w, min(y2a - y1a, h)# 判断当前图像是否为第四张图像(右下角)。elif i == 3:  # bottom right# 定义第四张图像 在拼接图像中的放置位置 和 裁剪区域 。x1a, y1a, x2a, y2a = xc, yc, min(xc + w, s * 2), min(s * 2, yc + h)x1b, y1b, x2b, y2b = 0, 0, min(w, x2a - x1a), min(y2a - y1a, h)# 将 当前图像的裁剪区域 放置到拼接图像 img4 的指定位置。 img[y1b:y2b, x1b:x2b] 是从原始图像中裁剪出的区域。 img4[y1a:y2a, x1a:x2a] 是拼接图像中的目标位置。img4[y1a:y2a, x1a:x2a] = img[y1b:y2b, x1b:x2b]  # img4[ymin:ymax, xmin:xmax]# 计算当前图像在拼接图像中的水平和垂直偏移量。# padw 和 padh 用于后续更新标签信息。padw = x1a - x1bpadh = y1a - y1b# 更新当前图像的标签信息,调整边界框坐标以适应拼接后的图像。labels_patch = self._update_labels(labels_patch, padw, padh)# 并将更新后的标签信息添加到 mosaic_labels 列表中。mosaic_labels.append(labels_patch)# 这段代码的核心功能是将 4 张图像拼接到一个 2×2 的网格中,并更新每张图像的标签信息。具体步骤包括。初始化拼接图像:创建一个大小为 2s × 2s 的空白图像 img4 。计算放置位置和裁剪区域:根据当前图像的位置(左上、右上、左下、右下),计算其在拼接图像中的放置位置和裁剪区域。放置图像:将裁剪后的图像区域放置到拼接图像的指定位置。更新标签信息:调用 _update_labels 方法,调整边界框坐标以适应拼接后的图像。存储标签信息:将更新后的标签信息存储到 mosaic_labels 列表中。这种设计确保了 Mosaic 数据增强后的图像和标签信息是完整且准确的,适用于目标检测和分割任务中的数据增强。# 调用 _cat_labels 方法,将所有图像的标签信息合并为一个最终的标签字典。final_labels = self._cat_labels(mosaic_labels)# 将拼接后的图像 img4 存储到最终的标签字典中。final_labels["img"] = img4# 返回最终的标签信息,包括拼接后的图像和更新后的标签。return final_labels# _mosaic4 方法实现了 4 张图像的 Mosaic 数据增强,具体步骤包括。初始化拼接图像:创建一个大小为 2s × 2s 的空白图像 img4 。随机选择拼接中心点:通过 random.uniform 随机选择拼接中心点 (xc, yc) 。遍历 4 张图像:分别处理当前图像和其他 3 张图像,计算每张图像在拼接图像中的放置位置和裁剪区域。更新标签信息:调用 _update_labels 方法,调整每张图像的边界框坐标以适应拼接后的图像。合并标签信息:调用 _cat_labels 方法,将所有图像的标签信息合并为一个完整的标签字典。返回最终结果:返回包含拼接后图像和更新后标签的字典。这种设计使得 Mosaic 类能够灵活支持 4 张图像的拼接逻辑,适用于目标检测和分割任务中的数据增强。# 这段代码定义了 Mosaic 类中的 _mosaic9 方法,用于实现 9 张图像的 Mosaic 数据增强。9 张图像被拼接成一个 3×3 的网格,每张图像占据网格的一个单元格。# 定义了 _mosaic9 方法,它接受一个参数。# 1.labels :表示包含当前图像及其标签信息的字典。 labels 包含 当前图像的标签信息 以及通过 get_indexes 方法获取的 其他图像的标签信息 (存储在 labels["mix_labels"] 中)。def _mosaic9(self, labels):# 从输入图像和另外八幅图像创建 3x3 图像马赛克。# 此方法将九幅图像组合成一幅马赛克图像。输入图像位于中心,来自数据集的另外八幅图像以 3x3 网格模式放置在其周围。"""Creates a 3x3 image mosaic from the input image and eight additional images.This method combines nine images into a single mosaic image. The input image is placed at the center,and eight additional images from the dataset are placed around it in a 3x3 grid pattern.Args:labels (Dict): A dictionary containing the input image and its associated labels. It should havethe following keys:- 'img' (numpy.ndarray): The input image.- 'resized_shape' (Tuple[int, int]): The shape of the resized image (height, width).- 'mix_labels' (List[Dict]): A list of dictionaries containing information for the additionaleight images, each with the same structure as the input labels.Returns:(Dict): A dictionary containing the mosaic image and updated labels. It includes the following keys:- 'img' (numpy.ndarray): The final mosaic image.- Other keys from the input labels, updated to reflect the new mosaic arrangement.Examples:>>> mosaic = Mosaic(dataset, imgsz=640, p=1.0, n=9)>>> input_labels = dataset[0]>>> mosaic_result = mosaic._mosaic9(input_labels)>>> mosaic_image = mosaic_result["img"]"""# 初始化一个空列表 mosaic_labels ,用于 存储每张图像的更新后的标签信息 。mosaic_labels = []# 将 目标图像尺寸 self.imgsz 赋值给变量 s ,用于 后续的图像拼接和裁剪逻辑 。s = self.imgsz# 初始化变量 hp 和 wp ,分别用于 存储前一张图像的高度和宽度 。初始值为 -1 ,表示尚未处理任何图像。hp, wp = -1, -1  # height, width previous# 开始一个循环,遍历 9 张图像(当前图像和 8 张其他图像)。for i in range(9):# 根据当前索引 i ,选择当前图像或其他图像的标签信息。 如果 i == 0 , labels_patch 是当前图像的标签信息。 如果 i > 0 , labels_patch 是其他图像的标签信息,从 labels["mix_labels"] 中获取。labels_patch = labels if i == 0 else labels["mix_labels"][i - 1]# Load image# 从 labels_patch 中获取 当前图像的数据 。img = labels_patch["img"]# 从 labels_patch 中获取 当前图像的大小 ( 高度 h 和 宽度 w ),并从字典中移除该键。h, w = labels_patch.pop("resized_shape")# Place img in img9# 判断当前图像是否为第一张图像(中心图像)。if i == 0:  # center# 如果当前图像为第一张图像。# 初始化一个大小为 3s × 3s 的空白图像 img9 ,用于放置 9 张图像。img9 = np.full((s * 3, s * 3, img.shape[2]), 114, dtype=np.uint8)  # base image with 4 tiles# 将当前图像的大小存储为 h0 和 w0 ,用于后续计算。h0, w0 = h, w# 定义当前图像 在拼接图像中的放置位置 c ,即 (xmin, ymin, xmax, ymax) 。c = s, s, s + w, s + h  # xmin, ymin, xmax, ymax (base) coordinates# 判断当前图像是否为第二张图像(顶部中间)。elif i == 1:  # top# 定义第二张图像在拼接图像中的放置位置。c = s, s - h, s + w, s# 判断当前图像是否为第三张图像(右上角)。elif i == 2:  # top right# 定义第三张图像在拼接图像中的放置位置, wp 是前一张图像的宽度。c = s + wp, s - h, s + wp + w, s# 判断当前图像是否为第四张图像(右侧中间)。elif i == 3:  # right# 定义第四张图像在拼接图像中的放置位置, w0 是中心图像的宽度。c = s + w0, s, s + w0 + w, s + h# 判断当前图像是否为第五张图像(右下角)。elif i == 4:  # bottom right# 定义第五张图像在拼接图像中的放置位置, hp 是前一张图像的高度。c = s + w0, s + hp, s + w0 + w, s + hp + h# 判断当前图像是否为第六张图像(底部中间)。elif i == 5:  # bottom# 定义第六张图像在拼接图像中的放置位置, h0 是中心图像的高度。c = s + w0 - w, s + h0, s + w0, s + h0 + h# 判断当前图像是否为第七张图像(左下角)。elif i == 6:  # bottom left# 定义第七张图像在拼接图像中的放置位置。c = s + w0 - wp - w, s + h0, s + w0 - wp, s + h0 + h# 判断当前图像是否为第八张图像(左侧中间)。elif i == 7:  # left# 定义第八张图像在拼接图像中的放置位置。c = s - w, s + h0 - h, s, s + h0# 判断当前图像是否为第九张图像(左上角)。elif i == 8:  # top left# 定义第九张图像在拼接图像中的放置位置。c = s - w, s + h0 - hp - h, s, s + h0 - hp# c 是一个包含 (xmin, ymin, xmax, ymax) 的元组,表示 当前图像在拼接图像中的目标位置 。 padw 和 padh 分别是 水平 和 垂直 方向的 偏移量 ,用于 后续调整边界框坐标 。padw, padh = c[:2]# 确保裁剪区域的坐标不会小于 0(防止越界)。 x1, y1 是裁剪区域的左上角坐标, x2, y2 是裁剪区域的右下角坐标。x1, y1, x2, y2 = (max(x, 0) for x in c)  # allocate coordinates# Image# 将裁剪后的图像放置到拼接图像的指定位置。# img9[y1:y2, x1:x2] :指定在拼接图像 img9 中的目标区域。# img[y1 - padh :, x1 - padw :] :从原始图像 img 中裁剪出与目标区域匹配的部分。img9[y1:y2, x1:x2] = img[y1 - padh :, x1 - padw :]  # img9[ymin:ymax, xmin:xmax]# 更新前一张图像的尺寸。 hp 和 wp 分别存储当前图像的高度和宽度,用于下一次迭代时计算其他图像的放置位置。hp, wp = h, w  # height, width previous for next iteration# Labels assuming imgsz*2 mosaic size# 更新标签信息。# self._update_labels :调用 _update_labels 方法,更新当前图像的标签信息。# padw + self.border[0] 和 padh + self.border[1] :计算边界框的偏移量,确保边界框坐标正确地映射到拼接图像中。# _update_labels 方法会调整边界框的坐标,使其适应 Mosaic 数据增强后的图像。labels_patch = self._update_labels(labels_patch, padw + self.border[0], padh + self.border[1])# 将 更新后的标签信息 添加到 mosaic_labels 列表中,用于后续的标签合并。mosaic_labels.append(labels_patch)# 调用 _cat_labels 方法,将所有图像的标签信息合并为一个最终的标签字典。final_labels = self._cat_labels(mosaic_labels)# 裁剪拼接后的图像 img9 ,去除边界偏移量,得到最终的 Mosaic 图像。final_labels["img"] = img9[-self.border[0] : self.border[0], -self.border[1] : self.border[1]]# 返回最终的标签信息,包括拼接后的图像和更新后的标签。return final_labels# _mosaic9 方法实现了 9 张图像的 Mosaic 数据增强,具体步骤包括。初始化拼接图像:创建一个大小为 3s × 3s 的空白图像 img9 。遍历 9 张图像:分别处理当前图像和其他 8 张图像,计算每张图像在拼接图像中的放置位置和裁剪区域。更新标签信息:调用 _update_labels 方法,调整每张图像的边界框坐标以适应拼接后的图像。合并标签信息:调用 _cat_labels 方法,将所有图像的标签信息合并为一个完整的标签字典。裁剪拼接图像:去除边界偏移量,得到最终的 Mosaic 图像。返回最终结果:返回包含拼接后图像和更新后标签的字典。这种设计使得 Mosaic 类能够灵活支持 9 张图像的拼接逻辑,适用于目标检测和分割任务中的数据增强。# 这段代码定义了 Mosaic 类中的一个静态方法 _update_labels ,用于更新标签信息,特别是边界框的坐标。这个方法的作用是调整边界框的坐标以适应 Mosaic 数据增强后的图像。# 将 _update_labels 方法定义为静态方法。静态方法不需要访问类或实例的属性,因此可以独立于类或实例调用。@staticmethod# 定义了 _update_labels 方法,它接受以下参数 :# 1.labels :包含图像和标签信息的字典。# 2.padw :水平方向的偏移量(宽度方向的填充)。# 3.padh :垂直方向的偏移量(高度方向的填充)。def _update_labels(labels, padw, padh):# 使用填充值更新标签坐标。# 此方法通过添加填充值来调整标签中对象实例的边界框坐标。如果坐标之前已标准化,它还会对其进行反标准化。"""Updates label coordinates with padding values.This method adjusts the bounding box coordinates of object instances in the labels by adding paddingvalues. It also denormalizes the coordinates if they were previously normalized.Args:labels (Dict): A dictionary containing image and instance information.padw (int): Padding width to be added to the x-coordinates.padh (int): Padding height to be added to the y-coordinates.Returns:(Dict): Updated labels dictionary with adjusted instance coordinates.Examples:>>> labels = {"img": np.zeros((100, 100, 3)), "instances": Instances(...)}>>> padw, padh = 50, 50>>> updated_labels = Mosaic._update_labels(labels, padw, padh)"""# 从 labels["img"] 中获取图像的高度和宽度。 labels["img"] 是当前图像的数据。 .shape[:2] 提取图像的高度和宽度(假设图像是一个 NumPy 数组)。 nh 是图像的高度, nw 是图像的宽度。nh, nw = labels["img"].shape[:2]# 调用 labels["instances"] 的 convert_bbox 方法,将边界框的格式转换为 "xyxy" 。 "xyxy" 表示边界框的坐标格式为 [xmin, ymin, xmax, ymax] 。 这一步确保边界框的格式统一,便于后续处理。labels["instances"].convert_bbox(format="xyxy")# 调用 labels["instances"] 的 denormalize 方法,将边界框的坐标从归一化格式转换为 绝对坐标 。 nw 和 nh 分别是图像的宽度和高度。 这一步将边界框的坐标从 [0, 1] 范围转换为 实际的像素坐标 。labels["instances"].denormalize(nw, nh)# 调用 labels["instances"] 的 add_padding 方法,为边界框添加偏移量。 padw 和 padh 分别是水平和垂直方向的偏移量。 这一步调整边界框的坐标,使其适应 Mosaic 数据增强后的图像。# def add_padding(self, padw, padh): -> 用于在边界框、分割掩码和关键点的坐标上添加填充偏移量。labels["instances"].add_padding(padw, padh)# 返回更新后的 labels 字典,其中 包含调整后的边界框坐标 。return labels# _update_labels 方法的作用是更新标签信息,特别是边界框的坐标,以适应 Mosaic 数据增强后的图像。具体步骤包括。获取图像尺寸:从 labels["img"] 中提取图像的高度和宽度。转换边界框格式:将边界框的格式统一为 "xyxy" 。去归一化:将边界框的坐标从归一化格式转换为绝对坐标。添加偏移量:为边界框添加水平和垂直方向的偏移量,使其适应拼接后的图像。这种设计确保了边界框的坐标在 Mosaic 数据增强后仍然准确地表示目标的位置,从而保证了数据增强的有效性和正确性。# 这段代码定义了 Mosaic 类中的 _cat_labels 方法,用于将多个图像的标签信息合并为一个完整的标签字典。这个方法是 Mosaic 数据增强流程中的关键步骤,确保所有拼接图像的标签信息能够正确地整合在一起。# 定义了 _cat_labels 方法,它接受一个参数。# 1.mosaic_labels :这是一个列表,包含每张图像的标签信息。def _cat_labels(self, mosaic_labels):# 连接并处理马赛克增强的标签。# 此方法组合马赛克增强中使用的多幅图像的标签,将实例剪辑到马赛克边框,并删除零面积框。"""Concatenates and processes labels for mosaic augmentation.This method combines labels from multiple images used in mosaic augmentation, clips instances to themosaic border, and removes zero-area boxes.Args:mosaic_labels (List[Dict]): A list of label dictionaries for each image in the mosaic.Returns:(Dict): A dictionary containing concatenated and processed labels for the mosaic image, including:- im_file (str): File path of the first image in the mosaic.- ori_shape (Tuple[int, int]): Original shape of the first image.- resized_shape (Tuple[int, int]): Shape of the mosaic image (imgsz * 2, imgsz * 2).- cls (np.ndarray): Concatenated class labels.- instances (Instances): Concatenated instance annotations.- mosaic_border (Tuple[int, int]): Mosaic border size.- texts (List[str], optional): Text labels if present in the original labels.Examples:>>> mosaic = Mosaic(dataset, imgsz=640)>>> mosaic_labels = [{"cls": np.array([0, 1]), "instances": Instances(...)} for _ in range(4)]>>> result = mosaic._cat_labels(mosaic_labels)>>> print(result.keys())dict_keys(['im_file', 'ori_shape', 'resized_shape', 'cls', 'instances', 'mosaic_border'])"""# 检查 mosaic_labels 是否为空。if len(mosaic_labels) == 0:# 如果为空,说明 没有标签信息需要合并 ,直接返回一个空字典 {} 。return {}# 初始化一个空列表 cls ,用于 存储所有图像的类别信息 。cls = []# 初始化一个空列表 instances ,用于 存储所有图像的实例信息 (如 边界框 )。instances = []# 计算 Mosaic 图像的大小。 self.imgsz 是单张图像的目标尺寸。 Mosaic 图像的大小通常是单张图像的两倍(如 2 * imgsz ),因为 Mosaic 通常是将多张图像拼接在一起。imgsz = self.imgsz * 2  # mosaic imgsz# 遍历 mosaic_labels 列表,处理 每张图像的标签信息 。for labels in mosaic_labels:# 从 当前图像的标签信息 中提取 类别信息 ,并将其添加到 cls 列表中。cls.append(labels["cls"])# 从 当前图像的标签信息 中提取 实例信息 (如 边界框 ),并将其添加到 instances 列表中。instances.append(labels["instances"])# Final labels# 创建一个字典 final_labels ,用于 存储最终的合并标签信息 。final_labels = {# 取自第一张图像的文件路径。"im_file": mosaic_labels[0]["im_file"],# 取自第一张图像的原始尺寸。"ori_shape": mosaic_labels[0]["ori_shape"],# 设置为 (imgsz, imgsz) ,即 Mosaic 图像的目标尺寸。"resized_shape": (imgsz, imgsz),# 将 所有图像的类别信息 合并为一个 NumPy 数组。"cls": np.concatenate(cls, 0),# 将所有图像的实例信息合并为一个 Instances 对象。# def concatenate(cls, instances_list: List["Instances"], axis=0) -> "Instances":# -> 用于将多个 Instances 对象拼接成一个新的 Instances 对象。如果 instances_list 中只有一个 Instances 对象,则直接返回该对象,无需拼接。使用 拼接后的 边界框 、分割掩码 和 关键点数据 ,创建一个新的 Instances 对象并返回。# -> return instances_list[0] / return cls(cat_boxes, cat_segments, cat_keypoints, bbox_format, normalized)"instances": Instances.concatenate(instances, axis=0),# 存储 Mosaic 图像的边界偏移量。"mosaic_border": self.border,}# 调用 Instances 对象的 clip 方法,裁剪边界框,确保它们不会超出 Mosaic 图像的边界。final_labels["instances"].clip(imgsz, imgsz)# 调用 Instances 对象的 remove_zero_area_boxes 方法,移除面积为零的边界框。 返回一个布尔数组 good ,表示 哪些边界框是有效的 。good = final_labels["instances"].remove_zero_area_boxes()# 根据 good 数组过滤类别信息, 只保留有效的边界框对应的类别 。final_labels["cls"] = final_labels["cls"][good]# 检查第一张图像的标签信息中是否包含 "texts" 键。if "texts" in mosaic_labels[0]:# 如果包含,将 "texts" 信息添加到 final_labels 中。final_labels["texts"] = mosaic_labels[0]["texts"]# 返回最终的合并标签信息。return final_labels# _cat_labels 方法的作用是将多个图像的标签信息合并为一个完整的标签字典。具体步骤包括。初始化存储结构:初始化列表 cls 和 instances ,用于存储类别和实例信息。遍历标签信息:逐个处理每张图像的标签信息,提取类别和实例信息。合并标签信息:将所有图像的类别和实例信息合并为一个完整的标签字典。裁剪和过滤边界框:确保边界框不会超出 Mosaic 图像的边界,并移除无效的边界框。添加额外信息:如果存在 "texts" 信息,将其添加到最终的标签字典中。这种设计确保了 Mosaic 数据增强后的标签信息是完整且准确的,适用于目标检测和分割任务中的数据增强。
# Mosaic 类是一个用于实现 Mosaic 数据增强的工具类,继承自 BaseMixTransform 。它通过将多张图像拼接成一个更大的图像(如 2×2 或 3×3 网格),并相应地调整标签信息,增强了模型对多目标场景的学习能力。该类支持灵活的拼接数量(如 3、4 或 9 张图像),并通过随机选择拼接中心点和图像索引,确保了数据增强的多样性和随机性。通过 _mosaic3 、 _mosaic4 和 _mosaic9 等方法, Mosaic 类能够高效地处理不同数量的图像拼接,并通过 _update_labels 和 _cat_labels 方法确保标签信息的准确性和完整性。这种设计使得 Mosaic 类能够广泛应用于目标检测和分割任务,提升模型的泛化能力和性能。

6.class MixUp(BaseMixTransform): 

# 这段代码定义了 MixUp 类,用于实现 MixUp 数据增强方法。MixUp 是一种通过线性组合两张图像及其标签来生成新的训练样本的技术,旨在增强模型对不同图像混合特征的学习能力。
# 定义了 MixUp 类,继承自 BaseMixTransform 。 BaseMixTransform 提供了混合变换的基础框架,包括初始化、获取其他图像索引和更新标签信息等通用逻辑。
class MixUp(BaseMixTransform):# 将 MixUp 增强应用于图像数据集。# 此类实现了论文“mixup:超越经验风险最小化”(https://arxiv.org/abs/1710.09412) 中描述的 MixUp 增强技术。MixUp 使用随机权重组合两幅图像及其标签。# 方法:# get_indexes:从数据集返回随机索引。# _mix_transform:将 MixUp 增强应用于输入标签。"""Applies MixUp augmentation to image datasets.This class implements the MixUp augmentation technique as described in the paper "mixup: Beyond Empirical RiskMinimization" (https://arxiv.org/abs/1710.09412). MixUp combines two images and their labels using a random weight.Attributes:dataset (Any): The dataset to which MixUp augmentation will be applied.pre_transform (Callable | None): Optional transform to apply before MixUp.p (float): Probability of applying MixUp augmentation.Methods:get_indexes: Returns a random index from the dataset._mix_transform: Applies MixUp augmentation to the input labels.Examples:>>> from ultralytics.data.augment import MixUp>>> dataset = YourDataset(...)  # Your image dataset>>> mixup = MixUp(dataset, p=0.5)>>> augmented_labels = mixup(original_labels)"""# 定义了 MixUp 类的初始化方法,接受以下参数 :# 1.dataset :数据集对象,用于获取图像和标签信息。# 2.pre_transform :可选的预处理变换,用于在 MixUp 变换之前对图像和标签进行处理。# 3.p :应用 MixUp 数据增强的概率,默认为 0.0。def __init__(self, dataset, pre_transform=None, p=0.0) -> None:# 初始化 MixUp 增强对象。# MixUp 是一种图像增强技术,通过对两个图像的像素值和标签进行加权和来组合它们。此实现设计用于 Ultralytics YOLO 框架。"""Initializes the MixUp augmentation object.MixUp is an image augmentation technique that combines two images by taking a weighted sum of their pixelvalues and labels. This implementation is designed for use with the Ultralytics YOLO framework.Args:dataset (Any): The dataset to which MixUp augmentation will be applied.pre_transform (Callable | None): Optional transform to apply to images before MixUp.p (float): Probability of applying MixUp augmentation to an image. Must be in the range [0, 1].Examples:>>> from ultralytics.data.dataset import YOLODataset>>> dataset = YOLODataset("path/to/data.yaml")>>> mixup = MixUp(dataset, pre_transform=None, p=0.5)"""# 调用父类 BaseMixTransform 的初始化方法,将 dataset 、 pre_transform 和 p 传递给父类。这一步确保了 MixUp 类继承了父类的基本功能。super().__init__(dataset=dataset, pre_transform=pre_transform, p=p)# 定义了 get_indexes 方法,用于获取用于 MixUp 数据增强的另一张图像的索引。def get_indexes(self):# 从数据集中获取随机索引。# 此方法从数据集中返回单个随机索引,用于选择用于 MixUp 增强的图像。"""Get a random index from the dataset.This method returns a single random index from the dataset, which is used to select an image for MixUpaugmentation.Returns:(int): A random integer index within the range of the dataset length.Examples:>>> mixup = MixUp(dataset)>>> index = mixup.get_indexes()>>> print(index)42"""# 从数据集中随机选择一张图像的索引。# random.randint(0, len(self.dataset) - 1) 生成一个范围在 [0, len(self.dataset) - 1] 内的随机整数。# 返回的索引用于从数据集中获取另一张图像及其标签信息。return random.randint(0, len(self.dataset) - 1)# 定义了 _mix_transform 方法,用于实现 MixUp 数据增强的核心逻辑。它接受一个参数。# 1.labels :表示包含当前图像及其标签信息的字典。def _mix_transform(self, labels):# 将 MixUp 增强应用于输入标签。# 此方法实现了论文“mixup:超越经验风险最小化”(https://arxiv.org/abs/1710.09412) 中描述的 MixUp 增强技术。"""Applies MixUp augmentation to the input labels.This method implements the MixUp augmentation technique as described in the paper"mixup: Beyond Empirical Risk Minimization" (https://arxiv.org/abs/1710.09412).Args:labels (Dict): A dictionary containing the original image and label information.Returns:(Dict): A dictionary containing the mixed-up image and combined label information.Examples:>>> mixer = MixUp(dataset)>>> mixed_labels = mixer._mix_transform(labels)"""# 生成 MixUp 比例 r 。使用 Beta 分布( np.random.beta )生成一个随机数,参数 alpha=32.0 和 beta=32.0 。 这个比例用于控制两张图像混合时的权重。r = np.random.beta(32.0, 32.0)  # mixup ratio, alpha=beta=32.0# 从 labels["mix_labels"] 中获取另一张图像的标签信息。 labels["mix_labels"] 是一个列表,包含其他图像的标签信息。 labels2 是另一张图像的标签信息。labels2 = labels["mix_labels"][0]# 将当前图像和另一张图像按比例混合。# labels["img"] * r :当前图像乘以比例 r 。# labels2["img"] * (1 - r) :另一张图像乘以比例 1 - r 。# 将混合后的图像转换为 np.uint8 类型,确保像素值在 [0, 255] 范围内。labels["img"] = (labels["img"] * r + labels2["img"] * (1 - r)).astype(np.uint8)# 合并两张图像的实例信息。 使用 Instances.concatenate 方法将当前图像和另一张图像的实例信息(如边界框)合并。 合并后的实例信息存储在 labels["instances"] 中。labels["instances"] = Instances.concatenate([labels["instances"], labels2["instances"]], axis=0)# 合并两张图像的类别信息。 使用 np.concatenate 方法将当前图像和另一张图像的类别信息合并。 合并后的类别信息存储在 labels["cls"] 中。labels["cls"] = np.concatenate([labels["cls"], labels2["cls"]], 0)# 返回更新后的标签信息,包括混合后的图像和合并后的标签。return labels
# MixUp 类实现了 MixUp 数据增强方法,通过线性组合两张图像及其标签来生成新的训练样本。其主要功能包括。随机选择另一张图像:通过 get_indexes 方法从数据集中随机选择一张图像。生成混合比例:使用 Beta 分布生成混合比例 r ,控制两张图像的权重。混合图像:按比例线性组合两张图像的像素值。合并标签信息:将两张图像的实例信息和类别信息合并,确保标签的准确性。这种设计使得 MixUp 类能够灵活地应用于目标检测和分割任务中的数据增强,增强模型对不同图像混合特征的学习能力,从而提升模型的泛化性能。

7.class RandomPerspective: 

# 这段代码定义了一个名为 RandomPerspective 的类,用于对图像及其标注(如边界框、分割掩码和关键点)进行随机透视变换,包括旋转、缩放、平移、剪切和透视效果。此类通常用于数据增强,以提高模型对不同视角和形状变化的鲁棒性。
# 定义了一个名为 RandomPerspective 的类,用于实现随机透视变换。
class RandomPerspective:# 在图像和相应的注释上实现随机透视和仿射变换。# 此类将随机旋转、平移、缩放、剪切和透视变换应用于图像及其相关的边界框、段和关键点。它可以用作对象检测和实例分割任务的增强管道的一部分。# 方法:# affine_transform:将仿射变换应用于输入图像。# apply_bboxes:使用仿射矩阵变换边界框。# apply_segments:变换段并生成新的边界框。# apply_keypoints:使用仿射矩阵变换关键点。# __call__:将随机透视变换应用于图像和注释。# box_candidates:根据大小和纵横比过滤变换后的边界框。"""Implements random perspective and affine transformations on images and corresponding annotations.This class applies random rotations, translations, scaling, shearing, and perspective transformationsto images and their associated bounding boxes, segments, and keypoints. It can be used as part of anaugmentation pipeline for object detection and instance segmentation tasks.Attributes:degrees (float): Maximum absolute degree range for random rotations.translate (float): Maximum translation as a fraction of the image size.scale (float): Scaling factor range, e.g., scale=0.1 means 0.9-1.1.shear (float): Maximum shear angle in degrees.perspective (float): Perspective distortion factor.border (Tuple[int, int]): Mosaic border size as (x, y).pre_transform (Callable | None): Optional transform to apply before the random perspective.Methods:affine_transform: Applies affine transformations to the input image.apply_bboxes: Transforms bounding boxes using the affine matrix.apply_segments: Transforms segments and generates new bounding boxes.apply_keypoints: Transforms keypoints using the affine matrix.__call__: Applies the random perspective transformation to images and annotations.box_candidates: Filters transformed bounding boxes based on size and aspect ratio.Examples:>>> transform = RandomPerspective(degrees=10, translate=0.1, scale=0.1, shear=10)>>> image = np.random.randint(0, 255, (640, 640, 3), dtype=np.uint8)>>> labels = {"img": image, "cls": np.array([0, 1]), "instances": Instances(...)}>>> result = transform(labels)>>> transformed_image = result["img"]>>> transformed_instances = result["instances"]"""# 这段代码定义了 RandomPerspective 类的初始化方法 __init__ ,用于设置该类的参数和属性。# 定义了类的初始化方法 __init__ ,它接收以下参数 :# 1.degrees :允许的旋转角度范围(正负值)。例如, degrees=10 表示随机旋转角度在 -10° 到 +10° 之间。# 2.translate :平移范围,表示图像宽度和高度的比例。例如, translate=0.1 表示图像可以在水平和垂直方向上随机平移最大为图像尺寸的 10% 。# 3.scale :缩放范围,表示图像尺寸的缩放比例。例如, scale=0.5 表示图像尺寸可以在原始尺寸的 50% 到 150% 之间随机变化。# 4.shear :剪切角度范围(正负值)。例如, shear=10 表示随机剪切角度在 -10° 到 +10° 之间。# 5.perspective :透视变换的强度。值越大,透视效果越明显。# 6.border :图像的边界填充大小,通常用于处理边界外的内容。格式为 (top_bottom, left_right) 。# 7.pre_transform :可选的预处理函数,用于在透视变换之前对图像或标注进行额外处理。def __init__(self, degrees=0.0, translate=0.1, scale=0.5, shear=0.0, perspective=0.0, border=(0, 0), pre_transform=None):# 使用变换参数初始化 RandomPerspective 对象。# 此类在图像和相应的边界框、片段和关键点上实现随机透视和仿射变换。变换包括旋转、平移、缩放和剪切。"""Initializes RandomPerspective object with transformation parameters.This class implements random perspective and affine transformations on images and corresponding bounding boxes,segments, and keypoints. Transformations include rotation, translation, scaling, and shearing.Args:degrees (float): Degree range for random rotations.translate (float): Fraction of total width and height for random translation.scale (float): Scaling factor interval, e.g., a scale factor of 0.5 allows a resize between 50%-150%.shear (float): Shear intensity (angle in degrees).perspective (float): Perspective distortion factor.border (Tuple[int, int]): Tuple specifying mosaic border (top/bottom, left/right).pre_transform (Callable | None): Function/transform to apply to the image before starting the randomtransformation.Examples:>>> transform = RandomPerspective(degrees=10.0, translate=0.1, scale=0.5, shear=5.0)>>> result = transform(labels)  # Apply random perspective to labels"""# 将传入的参数保存为类的实例属性,供后续方法使用。# 保存旋转角度范围。self.degrees = degrees# 保存平移范围。self.translate = translate# 保存缩放范围。self.scale = scale# 保存剪切角度范围。self.shear = shear# 保存透视变换强度。self.perspective = perspective# 保存边界填充大小。self.border = border  # mosaic border# 保存预处理函数。self.pre_transform = pre_transform# 这段代码的作用是初始化 RandomPerspective 类的实例,设置其在图像和标注处理中使用的参数。这些参数定义了图像可以进行的随机变换范围,包括旋转、平移、缩放、剪切和透视效果,以及边界填充和预处理函数。通过这些参数,类可以灵活地对图像进行数据增强,以提高模型对不同视角和形状变化的鲁棒性。# 这段代码定义了 RandomPerspective 类中的 affine_transform 方法,用于对输入图像进行仿射变换,包括平移、旋转、缩放、剪切和透视效果。该方法还返回变换矩阵 M 和缩放比例 s ,以便后续处理标注数据。# 定义了 affine_transform 方法,接收两个参数。# 1.img :输入图像(通常是 NumPy 数组)。# 2.border :图像的边界填充大小,用于处理变换后的边界外内容。def affine_transform(self, img, border):# 应用以图像中心为中心的一系列仿射变换。# 此函数对输入图像执行一系列几何变换,包括平移、透视变化、旋转、缩放和剪切。变换以特定顺序应用以保持一致性。"""Applies a sequence of affine transformations centered around the image center.This function performs a series of geometric transformations on the input image, includingtranslation, perspective change, rotation, scaling, and shearing. The transformations areapplied in a specific order to maintain consistency.Args:img (np.ndarray): Input image to be transformed.border (Tuple[int, int]): Border dimensions for the transformed image.Returns:(Tuple[np.ndarray, np.ndarray, float]): A tuple containing:- np.ndarray: Transformed image.- np.ndarray: 3x3 transformation matrix.- float: Scale factor applied during the transformation.Examples:>>> import numpy as np>>> img = np.random.rand(100, 100, 3)>>> border = (10, 10)>>> transformed_img, matrix, scale = affine_transform(img, border)"""# Center# 创建了一个 平移矩阵 C ,用于将图像的中心点移动到坐标原点(0, 0)。这是后续变换的基础,确保变换操作围绕图像中心进行C = np.eye(3, dtype=np.float32)# 将图像在 x 轴方向平移 -img.shape[1] / 2 像素。C[0, 2] = -img.shape[1] / 2  # x translation (pixels)# 将图像在 y 轴方向平移 -img.shape[0] / 2 像素。C[1, 2] = -img.shape[0] / 2  # y translation (pixels)# Perspective# 创建了一个 透视变换矩阵 P ,用于引入透视效果。P = np.eye(3, dtype=np.float32)# 控制 x 轴方向的透视效果,值越大,图像在水平方向上的透视越明显。# random.uniform(-self.perspective, self.perspective) :随机生成透视强度,范围由 self.perspective 参数决定。P[2, 0] = random.uniform(-self.perspective, self.perspective)  # x perspective (about y)# 控制 y 轴方向的透视效果,值越大,图像在垂直方向上的透视越明显。P[2, 1] = random.uniform(-self.perspective, self.perspective)  # y perspective (about x)# Rotation and Scale# 创建了一个 旋转和缩放矩阵 R 。R = np.eye(3, dtype=np.float32)# 随机生成的旋转角度,范围由 self.degrees 参数决定。a = random.uniform(-self.degrees, self.degrees)# a += random.choice([-180, -90, 0, 90])  # add 90deg rotations to small rotations# 随机生成的缩放比例,范围由 self.scale 参数决定。s = random.uniform(1 - self.scale, 1 + self.scale)# s = 2 ** random.uniform(-scale, scale)# cv2.getRotationMatrix2D 生成一个 2x3 的旋转矩阵,包含旋转和缩放信息。 R[:2] 将旋转矩阵赋值给 R 的前两行,使其成为一个 3x3 的齐次变换矩阵。R[:2] = cv2.getRotationMatrix2D(angle=a, center=(0, 0), scale=s)# Shear# 创建了一个 剪切矩阵 S ,用于引入剪切效果。S = np.eye(3, dtype=np.float32)# 控制 x 轴方向的剪切效果,值越大,图像在水平方向上的倾斜越明显。# math.tan(...) :将剪切角度从度数转换为弧度,并计算其正切值,用于表示剪切强度。S[0, 1] = math.tan(random.uniform(-self.shear, self.shear) * math.pi / 180)  # x shear (deg)# 控制 y 轴方向的剪切效果,值越大,图像在垂直方向上的倾斜越明显。S[1, 0] = math.tan(random.uniform(-self.shear, self.shear) * math.pi / 180)  # y shear (deg)# Translation# 创建了一个 平移矩阵 T ,用于在图像的 x 和 y 方向上进行随机平移。T = np.eye(3, dtype=np.float32)# 随机生成 x 轴方向的平移距离,范围由 self.translate 参数决定。# self.size :图像的最终尺寸(宽度和高度),用于计算平移的绝对像素值。T[0, 2] = random.uniform(0.5 - self.translate, 0.5 + self.translate) * self.size[0]  # x translation (pixels)# 随机生成 y 轴方向的平移距离,范围由 self.translate 参数决定。T[1, 2] = random.uniform(0.5 - self.translate, 0.5 + self.translate) * self.size[1]  # y translation (pixels)# Combined rotation matrix# 将所有变换矩阵(平移、剪切、旋转缩放、透视、中心平移)组合成一个综合变换矩阵 M 。矩阵乘法的顺序从右到左依次为 :# C :将图像中心移动到原点。# P :应用透视变换。# R :应用旋转和缩放。# S :应用剪切。# T :应用平移。M = T @ S @ R @ P @ C  # order of operations (right to left) is IMPORTANT# Affine image# 这段代码的作用是判断是否需要对图像进行变换,并根据是否启用透视变换选择合适的方法对图像进行处理。# 这行代码判断是否需要对图像进行变换,条件包括 :# 边界填充不为零 : border[0] != 0 或 border[1] != 0 。如果边界填充大小不为零,说明需要对图像进行边界处理。# 变换矩阵不是单位矩阵 : (M != np.eye(3)).any() 。如果变换矩阵 M 不是单位矩阵(即存在非恒等变换),说明图像需要进行变换。# 如果满足以上任意一个条件,则认为图像需要被变换。if (border[0] != 0) or (border[1] != 0) or (M != np.eye(3)).any():  # image changed# 如果 self.perspective 为 True ,表示启用了透视变换。if self.perspective:# 此时使用 cv2.warpPerspective 方法对图像进行透视变换。# img :输入图像。# M :3x3 的透视变换矩阵。# dsize=self.size :变换后的图像尺寸,由 self.size 指定。# borderValue=(114, 114, 114) :指定变换后边界外的填充颜色为灰色(RGB 值为 (114, 114, 114) )。img = cv2.warpPerspective(img, M, dsize=self.size, borderValue=(114, 114, 114))# 如果 self.perspective 为 False ,表示仅进行仿射变换。else:  # affine# 此时使用 cv2.warpAffine 方法对图像进行仿射变换。# img :输入图像。# M[:2] :从 3x3 的变换矩阵 M 中提取前两行,形成 2x3 的仿射变换矩阵。# dsize=self.size :变换后的图像尺寸,由 self.size 指定。# borderValue=(114, 114, 114) :指定变换后边界外的填充颜色为灰色。img = cv2.warpAffine(img, M[:2], dsize=self.size, borderValue=(114, 114, 114))# 这段代码的核心逻辑是。判断是否需要变换:通过检查边界填充是否为零以及变换矩阵是否为单位矩阵,确定是否对图像进行变换。选择变换方法:如果启用透视变换( self.perspective=True ),使用 cv2.warpPerspective 。如果仅进行仿射变换( self.perspective=False ),使用 cv2.warpAffine 。填充颜色:变换后边界外的像素使用灰色 (114, 114, 114) 填充,这种颜色通常用于避免与常见物体颜色冲突。通过这种方式,代码能够灵活地处理图像的透视和仿射变换需求,同时确保边界外的像素不会引入干扰信息。# 返回 变换后的图像 、 综合变换矩阵 M 和 缩放比例 s 。return img, M, s# 这段代码实现了对图像的综合仿射变换,包括平移、旋转、缩放、剪切和透视效果。通过将这些变换组合成一个矩阵 M ,可以高效地对图像进行变换,同时保留变换矩阵用于后续处理标注数据(如边界框、分割掩码和关键点)。这种方法广泛应用于数据增强,能够提高模型对不同视角和形状变化的鲁棒性。# 这段代码定义了 RandomPerspective 类中的 apply_bboxes 方法,用于将仿射变换矩阵 M 应用于边界框(bounding boxes),并生成变换后的边界框。# 定义了 apply_bboxes 方法,接收两个参数。# 1.bboxes :输入的边界框数组,形状为 (n, 4) ,每行表示一个边界框的 [x1, y1, x2, y2] (左上角和右下角坐标)。# 2.M :3x3 的仿射变换矩阵,用于对边界框进行变换。def apply_bboxes(self, bboxes, M):# 将仿射变换应用于边界框。# 此函数使用提供的变换矩阵将仿射变换应用于一组边界框。# 参数:# bboxes (torch.Tensor):xyxy 格式的边界框,形状为 (N, 4),其中 N 是边界框的数量。# M (torch.Tensor):形状为 (3, 3) 的仿射变换矩阵。# 返回:# (torch.Tensor):xyxy 格式的变换边界框,形状为 (N, 4)。"""Apply affine transformation to bounding boxes.This function applies an affine transformation to a set of bounding boxes using the providedtransformation matrix.Args:bboxes (torch.Tensor): Bounding boxes in xyxy format with shape (N, 4), where N is the numberof bounding boxes.M (torch.Tensor): Affine transformation matrix with shape (3, 3).Returns:(torch.Tensor): Transformed bounding boxes in xyxy format with shape (N, 4).Examples:>>> bboxes = torch.tensor([[10, 10, 20, 20], [30, 30, 40, 40]])>>> M = torch.eye(3)>>> transformed_bboxes = apply_bboxes(bboxes, M)"""# 获取边界框的数量 n 。n = len(bboxes)# 如果边界框数量为零(即没有边界框)。if n == 0:# 直接返回空数组。return bboxes# 创建一个 (n * 4, 3) 的数组,初始化为 1,用于 齐次坐标表示 。xy = np.ones((n * 4, 3), dtype=bboxes.dtype)# 将每个边界框的四个角点(左上角、右下角、左下角、右上角)展开为齐次坐标。# bboxes[:, [0, 1, 2, 3, 0, 3, 2, 1]] :提取边界框的四个角点坐标 [x1, y1, x2, y2, x1, y2, x2, y1] 。# reshape(n * 4, 2) :将这些坐标重新排列为 (n * 4, 2) 的形状,表示所有角点的 (x, y) 坐标。xy[:, :2] = bboxes[:, [0, 1, 2, 3, 0, 3, 2, 1]].reshape(n * 4, 2)  # x1y1, x2y2, x1y2, x2y1# 对边界框的角点进行变换。# 将齐次坐标 xy 与变换矩阵 M 的转置相乘,完成变换。xy = xy @ M.T  # transform# 如果启用了透视变换( self.perspective=True ),则对齐次坐标进行透视归一化(即除以齐次坐标中的 w 值)。否则,直接取前两列(即 (x, y) 坐标)。# .reshape(n, 8) :将变换后的坐标重新排列为 (n, 8) 的形状,表示每个边界框的四个角点的 (x, y) 坐标。xy = (xy[:, :2] / xy[:, 2:3] if self.perspective else xy[:, :2]).reshape(n, 8)  # perspective rescale or affine# Create new boxes# 从变换后的角点坐标中提取 x 和 y 坐标。# 提取所有角点的 x 坐标 [x1, x2, x3, x4] 。x = xy[:, [0, 2, 4, 6]]# 提取所有角点的 y 坐标 [y1, y2, y3, y4] 。y = xy[:, [1, 3, 5, 7]]# 根据变换后的角点坐标,重新计算边界框的最小和最大坐标。# x.min(1) :计算每个边界框的最小 x 坐标。# y.min(1) :计算每个边界框的最小 y 坐标。# x.max(1) :计算每个边界框的最大 x 坐标。# y.max(1) :计算每个边界框的最大 y 坐标。# np.concatenate(...) :将这些坐标拼接为 [x_min, y_min, x_max, y_max] 的格式。# .reshape(4, n).T :将结果重新排列为 (n, 4) 的形状,表示变换后的边界框数组。return np.concatenate((x.min(1), y.min(1), x.max(1), y.max(1)), dtype=bboxes.dtype).reshape(4, n).T# 这段代码的作用是将仿射变换矩阵 M 应用于边界框,并生成变换后的边界框。其核心逻辑包括。将边界框的四个角点展开为齐次坐标。使用变换矩阵 M 对角点进行变换。如果启用了透视变换,则对齐次坐标进行归一化。根据变换后的角点坐标,重新计算边界框的最小和最大坐标。通过这种方式,代码能够高效地将边界框从原始坐标系变换到新的坐标系,同时保持边界框的语义不变。这种方法广泛应用于目标检测任务中,用于处理图像的几何变换。# 齐次坐标(Homogeneous Coordinates)是一种数学表示方法,用于将二维或三维空间中的点或向量扩展到更高维度,以便更方便地处理几何变换(如平移、旋转、缩放和透视变换)。齐次坐标的核心思想是通过引入一个额外的维度,将原本的坐标表示从笛卡尔坐标系转换为齐次坐标系,从而简化变换操作。# 齐次坐标的基本概念在二维空间中,一个点通常用两个坐标表示,例如 (x, y) 。在齐次坐标中,这个点会被表示为一个三元组 (x, y, w) ,其中 w 是一个非零的标量。当 w ≠ 0 时,齐次坐标 (x, y, w) 对应的笛卡尔坐标为 (x/w, y/w) 。例如 :# 齐次坐标 (2, 3, 1) 对应的笛卡尔坐标是 (2, 3) 。# 齐次坐标 (4, 6, 2) 也对应笛卡尔坐标 (2, 3) ,因为 (4/2, 6/2) = (2, 3) 。# 在三维空间中,一个点用 (x, y, z) 表示,而在齐次坐标中,它会被表示为 (x, y, z, w) ,其中 w 是非零标量。当 w ≠ 0 时,齐次坐标 (x, y, z, w) 对应的笛卡尔坐标为 (x/w, y/w, z/w) 。# 齐次坐标的优势 :齐次坐标的主要优势在于能够将多种几何变换(如平移、旋转、缩放和透视变换)统一表示为矩阵乘法的形式。这使得复杂的变换操作可以通过矩阵运算来实现,从而简化计算和编程。# 平移变换 :# 在笛卡尔坐标系中,平移操作不能通过矩阵乘法实现,因为平移涉及加法。然而,在齐次坐标中,平移可以通过矩阵乘法完成。例如,将点 (x, y) 平移 (tx, ty) 的齐次坐标表示为 :# ┌x'┐   ┌1 0 tx┐   ┌x┐# │y'│ = │0 1 ty│ · │y│# └1 ┘   └0 0 1 ┘   └1┘# 旋转变换 :# 旋转操作在齐次坐标中也可以通过矩阵乘法实现。例如,将点 (x, y) 绕原点旋转角度 θ 的齐次坐标表示为 :# ┌x'┐   ┌cosθ -sinθ 0┐   ┌x┐# │y'│ = │sinθ cosθ  0│ · │y│# └1 ┘   └0    0    1 ┘   └1 ┘# 缩放变换 :# 缩放操作同样可以通过齐次坐标实现。例如,将点 (x, y) 在 x 和 y 方向上分别缩放 sx 和 sy 的齐次坐标表示为 :# ┌x'┐   ┌sx  0 0┐   ┌x┐# │y'│ = │0  sy 0│ · │y│# └1 ┘   └0  0  1┘   └1┘# 透视变换 :# 透视变换是齐次坐标的一个重要应用,它无法通过传统的二维或三维矩阵表示。例如,将点 (x, y) 进行透视变换的齐次坐标表示为 :# ┌x'┐   ┌1 0 0┐   ┌x┐# │y'│ = │0 1 0│ · │y│# └1 ┘   └a b 1┘   └1┘# 其中, a 和 b 是透视参数, w' = ax + by + 1 。透视变换后的笛卡尔坐标为 (x'/w', y'/w') 。# 齐次坐标的应用 :# 齐次坐标广泛应用于计算机图形学、计算机视觉和机器人学中,尤其是在以下领域 :# 图像处理 :用于图像的几何变换,如平移、旋转、缩放和透视变换。# 三维建模 :用于三维空间中的点和向量变换。# 机器人学 :用于描述机器人的运动和姿态变换。# 计算机视觉 :用于处理相机投影和三维重建中的几何变换。# 通过齐次坐标,复杂的几何变换可以统一表示为矩阵运算,从而简化计算和编程。这种表示方法不仅提高了效率,还使得变换操作更加直观和易于理解。# 这段代码定义了 RandomPerspective 类中的 apply_segments 方法,用于将仿射变换矩阵 M 应用于分割掩码(segments),并生成变换后的分割掩码和对应的边界框(bboxes)。# 定义了 apply_segments 方法,接收两个参数。# 1.segments :输入的分割掩码数组,形状为 (n, num, 2) ,其中 n 是分割掩码的数量, num 是每个分割掩码的点数,每个点用 (x, y) 表示。# 2.M :3x3 的仿射变换矩阵,用于对分割掩码进行变换。def apply_segments(self, segments, M):# 将仿射变换应用于段并生成新的边界框。# 此函数将仿射变换应用于输入段并根据变换后的段生成新的边界框。它会剪切变换后的段以适合新的边界框。# 参数:# segments (np.ndarray):形状为 (N, M, 2) 的输入段,其中 N 是段数,M 是每个段中的点数。# M (np.ndarray):形状为 (3, 3) 的仿射变换矩阵。# 返回:# (Tuple[np.ndarray, np.ndarray]):包含以下内容的元组:# - 形状为 (N, 4) 的新边界框,采用 xyxy 格式。# - 形状为 (N, M, 2) 的变换和剪切段。"""Apply affine transformations to segments and generate new bounding boxes.This function applies affine transformations to input segments and generates new bounding boxes based onthe transformed segments. It clips the transformed segments to fit within the new bounding boxes.Args:segments (np.ndarray): Input segments with shape (N, M, 2), where N is the number of segments and M is thenumber of points in each segment.M (np.ndarray): Affine transformation matrix with shape (3, 3).Returns:(Tuple[np.ndarray, np.ndarray]): A tuple containing:- New bounding boxes with shape (N, 4) in xyxy format.- Transformed and clipped segments with shape (N, M, 2).Examples:>>> segments = np.random.rand(10, 500, 2)  # 10 segments with 500 points each>>> M = np.eye(3)  # Identity transformation matrix>>> new_bboxes, new_segments = apply_segments(segments, M)"""# 获取 分割掩码的数量 n 和 每个掩码的点数 num 。n, num = segments.shape[:2]# 如果分割掩码数量为零(即没有分割掩码)。if n == 0:# 直接返 回空的边界框列表 和 原始分割掩码 。return [], segments# 将分割掩码的点展开为齐次坐标。# 创建一个形状为 (n * num, 3) 的数组 xy ,初始化为 1,用于齐次坐标表示。xy = np.ones((n * num, 3), dtype=segments.dtype)# 将 segments 展平为 (n * num, 2) 的形状,表示所有点的 (x, y) 坐标。segments = segments.reshape(-1, 2)# 将点的 (x, y) 坐标赋值到 xy 的前两列。xy[:, :2] = segments# 对分割掩码的点进行变换。# 将齐次坐标 xy 与变换矩阵 M 的转置相乘,完成变换。xy = xy @ M.T  # transform# 对齐次坐标进行透视归一化(即除以齐次坐标中的 w 值),得到变换后的 (x, y) 坐标。xy = xy[:, :2] / xy[:, 2:3]# 将变换后的点重新组合为分割掩码的原始形状 (n, num, 2) 。segments = xy.reshape(n, -1, 2)# 为每个变换后的分割掩码生成对应的边界框。# segment2box(xy, self.size[0], self.size[1]) :假设这是一个函数,将分割掩码的点转换为边界框 [x_min, y_min, x_max, y_max] ,并确保边界框在图像尺寸范围内。# np.stack(..., 0) :将所有边界框堆叠为一个数组,形状为 (n, 4) 。bboxes = np.stack([segment2box(xy, self.size[0], self.size[1]) for xy in segments], 0)# 对变换后的分割掩码进行裁剪,确保所有点都在对应的边界框内。# 将 x 坐标裁剪到 [x_min, x_max] 范围内。segments[..., 0] = segments[..., 0].clip(bboxes[:, 0:1], bboxes[:, 2:3])# 将 y 坐标裁剪到 [y_min, y_max] 范围内。segments[..., 1] = segments[..., 1].clip(bboxes[:, 1:2], bboxes[:, 3:4])# 返回变换后的边界框和分割掩码。return bboxes, segments# 这段代码的作用是将仿射变换矩阵 M 应用于分割掩码,并生成对应的边界框。其核心逻辑包括。将分割掩码的点展开为齐次坐标。使用变换矩阵 M 对点进行变换。对齐次坐标进行透视归一化,得到变换后的 (x, y) 坐标。重新组合变换后的点为分割掩码。为每个分割掩码生成对应的边界框。对变换后的分割掩码进行裁剪,确保所有点都在边界框内。通过这种方式,代码能够高效地处理分割掩码的几何变换,同时生成准确的边界框,适用于目标检测和分割任务中的数据增强。# 这段代码定义了 RandomPerspective 类中的 apply_keypoints 方法,用于将仿射变换矩阵 M 应用于关键点(keypoints),并处理关键点的可见性。# 定义了 apply_keypoints 方法,接收两个参数。# 1.keypoints :输入的关键点数组,形状为 (n, nkpt, 3) ,其中 n 是实例数量, nkpt 是每个实例的关键点数量,每个关键点用 (x, y, visible) 表示,其中 visible 是关键点的可见性标记。# 2.M :3x3 的仿射变换矩阵,用于对关键点进行变换。def apply_keypoints(self, keypoints, M):# 将仿射变换应用于关键点。# 此方法使用提供的仿射变换矩阵变换输入关键点。它会在必要时处理透视重新缩放,并更新变换后超出图像边界的关键点的可见性。# 参数:# 关键点 (np.ndarray):形状为 (N, 17, 3) 的关键点数组,其中 N 是实例数,17 是每个实例的关键点数,3 代表 (x, y, 可见性)。# M (np.ndarray):3x3 仿射变换矩阵。# 返回:# (np.ndarray):与输入形状相同的变换后的关键点数组 (N, 17, 3)。"""Applies affine transformation to keypoints.This method transforms the input keypoints using the provided affine transformation matrix. It handlesperspective rescaling if necessary and updates the visibility of keypoints that fall outside the imageboundaries after transformation.Args:keypoints (np.ndarray): Array of keypoints with shape (N, 17, 3), where N is the number of instances,17 is the number of keypoints per instance, and 3 represents (x, y, visibility).M (np.ndarray): 3x3 affine transformation matrix.Returns:(np.ndarray): Transformed keypoints array with the same shape as input (N, 17, 3).Examples:>>> random_perspective = RandomPerspective()>>> keypoints = np.random.rand(5, 17, 3)  # 5 instances, 17 keypoints each>>> M = np.eye(3)  # Identity transformation>>> transformed_keypoints = random_perspective.apply_keypoints(keypoints, M)"""# 获取关键点数组的形状信息。 n 是实例数量。 nkpt 是每个实例的关键点数量。n, nkpt = keypoints.shape[:2]# 如果实例数量为零(即没有关键点)。if n == 0:# 直接返回原始关键点数组。return keypoints# 将关键点的 (x, y) 坐标展开为齐次坐标。# 创建一个形状为 (n * nkpt, 3) 的数组 xy ,初始化为 1,用于齐次坐标表示。xy = np.ones((n * nkpt, 3), dtype=keypoints.dtype)# 提取关键点的可见性标记 visible ,并将其形状调整为 (n * nkpt, 1) 。visible = keypoints[..., 2].reshape(n * nkpt, 1)# 提取关键点的 (x, y) 坐标,并将其形状调整为 (n * nkpt, 2) ,然后赋值到 xy 的前两列。xy[:, :2] = keypoints[..., :2].reshape(n * nkpt, 2)# 对关键点进行变换。# 将齐次坐标 xy 与变换矩阵 M 的转置相乘,完成变换。xy = xy @ M.T  # transform# 对齐次坐标进行透视归一化(即除以齐次坐标中的 w 值),得到变换后的 (x, y) 坐标。xy = xy[:, :2] / xy[:, 2:3]  # perspective rescale or affine# 创建一个掩码 out_mask ,用于 标记变换后超出图像边界的点 。如果变换后的点的 x 坐标小于 0 或大于图像宽度 self.size[0] ,或者 y 坐标小于 0 或大于图像高度 self.size[1] ,则该点标记为超出边界。out_mask = (xy[:, 0] < 0) | (xy[:, 1] < 0) | (xy[:, 0] > self.size[0]) | (xy[:, 1] > self.size[1])# 将超出边界的点的可见性标记设置为 0,表示这些点在变换后不可见。visible[out_mask] = 0# 将变换后的 (x, y) 坐标与更新后的可见性标记 visible 拼接起来,并将结果重新调整为原始形状 (n, nkpt, 3) ,表示变换后的关键点数组。return np.concatenate([xy, visible], axis=-1).reshape(n, nkpt, 3)# 这段代码的作用是将仿射变换矩阵 M 应用于关键点,并更新关键点的可见性。其核心逻辑包括。将关键点的 (x, y) 坐标展开为齐次坐标。使用变换矩阵 M 对关键点进行变换。对齐次坐标进行透视归一化,得到变换后的 (x, y) 坐标。检查变换后的关键点是否超出图像边界,并更新其可见性标记。将变换后的坐标和可见性标记重新组合为关键点数组。通过这种方式,代码能够高效地处理关键点的几何变换,并确保关键点的可见性信息与变换后的坐标一致。这种方法广泛应用于目标检测和关键点估计任务中的数据增强。# 这段代码定义了 RandomPerspective 类的 __call__ 方法,它是该类的核心功能,用于对输入的图像及其标注(包括边界框、分割掩码和关键点)应用随机透视变换。# 定义了 __call__ 方法,接收一个字典。# 1.labels :包含了图像及其标注信息。def __call__(self, labels):# 将随机透视和仿射变换应用于图像及其相关标签。# 此方法对输入图像执行一系列变换,包括旋转、平移、缩放、剪切和透视失真,并相应地调整相应的边界框、段和关键点。"""Applies random perspective and affine transformations to an image and its associated labels.This method performs a series of transformations including rotation, translation, scaling, shearing,and perspective distortion on the input image and adjusts the corresponding bounding boxes, segments,and keypoints accordingly.Args:labels (Dict): A dictionary containing image data and annotations.Must include:'img' (ndarray): The input image.'cls' (ndarray): Class labels.'instances' (Instances): Object instances with bounding boxes, segments, and keypoints.May include:'mosaic_border' (Tuple[int, int]): Border size for mosaic augmentation.Returns:(Dict): Transformed labels dictionary containing:- 'img' (np.ndarray): The transformed image.- 'cls' (np.ndarray): Updated class labels.- 'instances' (Instances): Updated object instances.- 'resized_shape' (Tuple[int, int]): New image shape after transformation.Examples:>>> transform = RandomPerspective()>>> image = np.random.randint(0, 255, (640, 640, 3), dtype=np.uint8)>>> labels = {...     "img": image,...     "cls": np.array([0, 1, 2]),...     "instances": Instances(bboxes=np.array([[10, 10, 50, 50], [100, 100, 150, 150]])),... }>>> result = transform(labels)>>> assert result["img"].shape[:2] == result["resized_shape"]"""# 如果定义了 pre_transform 函数且 labels 中没有 "mosaic_border" 键。if self.pre_transform and "mosaic_border" not in labels:# 则对 labels 应用预处理变换。这通常用于在透视变换之前进行其他数据增强操作。labels = self.pre_transform(labels)# 从 labels 中移除 "ratio_pad" 键(如果存在)。这表明 在后续处理中不需要考虑填充比例 。labels.pop("ratio_pad", None)  # do not need ratio pad# 这段代码是 RandomPerspective 类的 __call__ 方法的一部分,主要负责从输入的 labels 字典中提取图像和标注信息,并对标注(边界框、分割掩码和关键点)进行格式化和反归一化处理。随后,它计算图像的边界填充大小,并对图像进行仿射变换。# 从 labels 字典中提取以下信息。# 输入图像,通常是 NumPy 数组,形状为 (H, W, C) 。img = labels["img"]# 类别标签,表示每个实例的类别。cls = labels["cls"]# 包含标注信息的对象(如边界框、分割掩码和关键点)。 instances 是一个自定义类实例,支持后续的格式化和变换操作。instances = labels.pop("instances")# Make sure the coord formats are right# 对标注信息进行格式化和反归一化处理。# 将边界框的格式统一转换为 [x_min, y_min, x_max, y_max] ( xyxy 格式)。这是后续处理的默认格式。instances.convert_bbox(format="xyxy")# 将边界框坐标从归一化坐标(范围在 [0, 1] )反归一化到图像的实际像素坐标。# img.shape[:2][::-1] :提取图像的宽度和高度 (W, H) ,用于反归一化。instances.denormalize(*img.shape[:2][::-1])# 从 labels 中提取或使用 默认的边界填充大小 border 。如果 labels 中存在 "mosaic_border" 键,则使用其值;否则使用类初始化时定义的 self.border 。 border 通常是一个元组 (top_bottom, left_right) ,表示边界填充的大小。border = labels.pop("mosaic_border", self.border)# 计算变换后的图像尺寸。# img.shape[1] + border[1] * 2 :图像宽度加上两侧的边界填充。# img.shape[0] + border[0] * 2 :图像高度加上上下两侧的边界填充。# 结果存储在 self.size 中,格式为 (width, height) 。self.size = img.shape[1] + border[1] * 2, img.shape[0] + border[0] * 2  # w, h# M is affine matrix# Scale for func:`box_candidates`# 对图像进行仿射变换。调用 self.affine_transform(img, border) 方法,传入图像和边界填充大小。# img :变换后的图像。# M :3x3 的仿射变换矩阵,用于后续对标注信息(边界框、分割掩码和关键点)进行相同的变换。# scale :变换后的缩放比例,用于后续筛选边界框时计算面积比。img, M, scale = self.affine_transform(img, border)# 这段代码的核心功能包括。提取和准备数据:从 labels 中提取图像、类别标签和标注信息。确保标注信息的格式正确(边界框为 xyxy 格式)。将边界框坐标从归一化形式反归一化到像素坐标。计算边界填充和目标图像尺寸:从 labels 中提取或使用默认的边界填充大小。计算变换后的图像尺寸,考虑边界填充的影响。对图像进行仿射变换:调用 affine_transform 方法,对图像进行平移、旋转、缩放、剪切和透视变换。返回变换后的图像、变换矩阵 M 和缩放比例 scale 。这些步骤为后续对标注信息(边界框、分割掩码和关键点)进行相同的变换奠定了基础,确保图像和标注在变换后保持一致。# 这段代码的作用是将仿射变换矩阵 M 应用于标注信息(边界框、分割掩码和关键点),并创建一个新的 Instances 对象来存储变换后的标注信息。最后,对标注信息进行裁剪,确保所有内容都在图像边界内。# 调用 apply_bboxes 方法,将仿射变换矩阵 M 应用于边界框( instances.bboxes ),生成 变换后的边界框 bboxes 。 apply_bboxes 方法会根据变换矩阵更新边界框的坐标。bboxes = self.apply_bboxes(instances.bboxes, M)# 从 instances 对象中提取 分割掩码( segments )和 关键点 ( keypoints )。这些标注信息将根据相同的变换矩阵 M 进行更新。segments = instances.segmentskeypoints = instances.keypoints# Update bboxes if there are segments.# 如果存在分割掩码( segments )。if len(segments):# 调用 apply_segments 方法。 将仿射变换矩阵 M 应用于分割掩码,生成 变换后的分割掩码 。同时 更新与分割掩码对应的边界框 ( bboxes ),因为分割掩码的变换可能会影响边界框的范围。bboxes, segments = self.apply_segments(segments, M)# 如果存在关键点( keypoints )。if keypoints is not None:# 调用 apply_keypoints 方法,将仿射变换矩阵 M 应用于关键点。这会 更新关键点的坐标 ,并 处理关键点的可见性 (例如,关键点是否超出图像边界)。keypoints = self.apply_keypoints(keypoints, M)# 创建一个新的 Instances 对象 new_instances ,用于 存储变换后的标注信息 。# bboxes :变换后的边界框。# segments :变换后的分割掩码(如果存在)。# keypoints :变换后的关键点(如果存在)。# bbox_format="xyxy" :指定边界框的格式为 [x_min, y_min, x_max, y_max] 。# normalized=False :表示边界框坐标为像素坐标,未归一化。new_instances = Instances(bboxes, segments, keypoints, bbox_format="xyxy", normalized=False)# Clip# 调用 clip 方法,对 new_instances 中的所有标注信息(边界框、分割掩码和关键点)进行裁剪,确保所有内容都在变换后的图像边界内。# self.size :变换后的图像尺寸 (width, height) 。# 裁剪操作会将超出图像边界的标注内容调整到边界内,避免标注信息超出图像范围。new_instances.clip(*self.size)# 这段代码的核心功能是将仿射变换矩阵 M 应用于标注信息(边界框、分割掩码和关键点),并生成一个新的 Instances 对象来存储变换后的标注信息。其主要步骤包括。边界框变换:对边界框应用仿射变换。分割掩码变换(如果存在):对分割掩码应用仿射变换,并更新对应的边界框。关键点变换(如果存在):对关键点应用仿射变换,并处理关键点的可见性。创建新的标注对象:将变换后的标注信息存储到新的 Instances 对象中。裁剪标注信息:对标注信息进行裁剪,确保所有内容都在图像边界内。通过这些步骤,代码确保了标注信息在图像变换后仍然有效且与图像内容一致,适用于目标检测和分割任务中的数据增强。# 这段代码的作用是对变换后的标注实例(边界框、分割掩码和关键点)进行筛选,确保它们在几何变换后仍然有效。筛选的主要依据是边界框的面积变化、宽高比和尺寸阈值。# Filter instances# 对原始标注实例 instances 的边界框进行缩放,使其与变换后的图像尺寸保持一致。这里仅对边界框进行缩放,而不影响分割掩码和关键点。缩放比例由 scale 决定。instances.scale(scale_w=scale, scale_h=scale, bbox_only=True)# Make the bboxes have the same scale with new_bboxes# 调用 box_candidates 方法,筛选出合理的边界框。# box1=instances.bboxes.T :原始边界框(缩放后)。# box2=new_instances.bboxes.T :变换后的边界框。# area_thr :面积比阈值。如果存在分割掩码( len(segments) > 0 ),使用更宽松的阈值 0.01 ;否则使用默认阈值 0.10 。# box_candidates 方法会根据以下条件筛选边界框 :# 宽度和高度阈值 :边界框的宽度和高度必须大于设定的最小值(默认为 2)。# 宽高比阈值 :边界框的宽高比必须小于设定的最大值(默认为 100)。# 面积比阈值 :变换后边界框的面积与原始边界框面积的比值必须大于设定的阈值( area_thr )。# 返回值 i 是一个布尔数组,表示哪些边界框满足上述条件。i = self.box_candidates(box1=instances.bboxes.T, box2=new_instances.bboxes.T, area_thr=0.01 if len(segments) else 0.10)# 根据筛选结果 i ,更新 labels 字典中的标注信息。# 筛选出合理的 标注实例 (边界框、分割掩码和关键点)。labels["instances"] = new_instances[i]# 筛选出与合理边界框对应的 类别标签 。labels["cls"] = cls[i]# 更新 labels 字典中的图像信息。# 存储变换后的图像。labels["img"] = img# 存储变换后图像的尺寸 (H, W) 。labels["resized_shape"] = img.shape[:2]# 返回更新后的 labels 字典,其中包含 筛选后的标注信息 和 变换后的图像 。return labels# 这段代码的核心功能是对变换后的标注实例进行筛选,确保它们在几何变换后仍然有效。其主要步骤包括。缩放原始边界框:将原始边界框的尺寸调整到与变换后的图像一致。筛选边界框:使用 box_candidates 方法,根据宽度、高度、宽高比和面积比阈值筛选合理的边界框。更新标注信息:根据筛选结果,更新 labels 字典中的标注实例和类别标签。返回结果:返回包含筛选后标注信息和变换后图像的 labels 字典。通过这些步骤,代码能够有效地去除在几何变换过程中变得不合理或退化的标注实例,从而提高数据增强的鲁棒性和模型训练的准确性。# 这段代码实现了对图像及其标注(边界框、分割掩码和关键点)的随机透视变换。其核心逻辑包括。对图像进行仿射变换,生成变换矩阵 M 和缩放比例 scale 。将变换矩阵 M 应用于边界框、分割掩码和关键点。对变换后的标注进行裁剪,确保所有内容都在图像边界内。使用 box_candidates 方法筛选合理的边界框。更新 labels 字典,返回变换后的图像和标注信息。这种方法广泛应用于目标检测和实例分割任务中的数据增强,能够提高模型对不同视角和几何变化的鲁棒性。# 这段代码定义了一个静态方法 box_candidates ,用于筛选满足特定条件的边界框(bounding boxes)。该方法主要用于过滤掉在几何变换(如仿射变换或透视变换)后变得不合理或退化的边界框。@staticmethod# 义了一个静态方法 box_candidates ,接收以下参数 :# 1.box1 :变换前的边界框数组,形状为 (4, n) ,表示 [x_min, y_min, x_max, y_max] 。# 2.box2 :变换后的边界框数组,形状为 (4, n) ,表示 [x_min, y_min, x_max, y_max] 。# 3.wh_thr :宽度和高度的最小阈值,默认为 2。小于该阈值的边界框将被过滤掉。# 4.ar_thr :宽高比的最大阈值,默认为 100。超过该阈值的边界框将被过滤掉。# 5.area_thr :面积比的最小阈值,默认为 0.1。变换后边界框与变换前边界框的面积比小于该阈值的将被过滤掉。# 6.eps :一个极小值,用于避免除零错误。def box_candidates(box1, box2, wh_thr=2, ar_thr=100, area_thr=0.1, eps=1e-16):# 根据大小和纵横比标准计算候选框以供进一步处理。# 此方法比较增强前后的框,以确定它们是否满足指定的宽度、高度、纵横比和面积阈值。它用于过滤掉在增强过程中过度扭曲或缩小的框。# 参数:# box1 (numpy.ndarray):增强前的原始框,形状为 (4, N),其中 n 是框的数量。格式为 [x1, y1, x2, y2](绝对坐标)。# box2 (numpy.ndarray):变换后的增强框,形状为 (4, N)。格式为 [x1, y1, x2, y2](绝对坐标)。# wh_thr (float):宽度和高度阈值(以像素为单位)。任一维度上小于此阈值的框都会被拒绝。# ar_thr (float):纵横比阈值。纵横比大于此值的框会被拒绝。# area_thr (float):面积比阈值。面积比(新/旧)小于此值的框将被拒绝。# eps(浮点数):较小的 epsilon 值,以防止除以零。# 返回:# (numpy.ndarray):形状为(n)的布尔数组,表示哪些框是候选框。真值对应于满足所有条件的框。"""Compute candidate boxes for further processing based on size and aspect ratio criteria.This method compares boxes before and after augmentation to determine if they meet specifiedthresholds for width, height, aspect ratio, and area. It's used to filter out boxes that havebeen overly distorted or reduced by the augmentation process.Args:box1 (numpy.ndarray): Original boxes before augmentation, shape (4, N) where n is thenumber of boxes. Format is [x1, y1, x2, y2] in absolute coordinates.box2 (numpy.ndarray): Augmented boxes after transformation, shape (4, N). Format is[x1, y1, x2, y2] in absolute coordinates.wh_thr (float): Width and height threshold in pixels. Boxes smaller than this in eitherdimension are rejected.ar_thr (float): Aspect ratio threshold. Boxes with an aspect ratio greater than thisvalue are rejected.area_thr (float): Area ratio threshold. Boxes with an area ratio (new/old) less thanthis value are rejected.eps (float): Small epsilon value to prevent division by zero.Returns:(numpy.ndarray): Boolean array of shape (n) indicating which boxes are candidates.True values correspond to boxes that meet all criteria.Examples:>>> random_perspective = RandomPerspective()>>> box1 = np.array([[0, 0, 100, 100], [0, 0, 50, 50]]).T>>> box2 = np.array([[10, 10, 90, 90], [5, 5, 45, 45]]).T>>> candidates = random_perspective.box_candidates(box1, box2)>>> print(candidates)[True True]"""# 计算变换前和变换后边界框的宽度和高度。# 变换前边界框的 宽度 和 高度 。w1, h1 = box1[2] - box1[0], box1[3] - box1[1]# 变换后边界框的 宽度 和 高度 。w2, h2 = box2[2] - box2[0], box2[3] - box2[1]# 计算 变换后边界框的宽高比 (aspect ratio)。# w2 / (h2 + eps) 和 h2 / (w2 + eps) 分别计算宽度与高度的比值和高度与宽度的比值。# 使用 np.maximum 取两者的较大值,以确保宽高比的计算结果总是大于等于 1。# 加上 eps 是为了避免除零错误。ar = np.maximum(w2 / (h2 + eps), h2 / (w2 + eps))  # aspect ratio# 根据以下条件筛选合理的边界框。# 宽度和高度阈值 : w2 > wh_thr 和 h2 > wh_thr ,确保变换后的边界框宽度和高度大于最小阈值。# 面积比阈值 : w2 * h2 / (w1 * h1 + eps) > area_thr ,确保变换后边界框的面积与变换前边界框的面积比大于最小阈值。# 宽高比阈值 : ar < ar_thr ,确保变换后边界框的宽高比小于最大阈值。# 最终返回一个布尔数组,表示哪些边界框满足上述条件。return (w2 > wh_thr) & (h2 > wh_thr) & (w2 * h2 / (w1 * h1 + eps) > area_thr) & (ar < ar_thr)  # candidates# 这段代码的作用是筛选出在几何变换后仍然合理的边界框。其核心逻辑包括。计算变换前和变换后边界框的宽度、高度和宽高比。根据宽度、高度、面积比和宽高比的阈值条件,筛选出满足要求的边界框。返回一个布尔数组,表示哪些边界框是合理的。这种方法广泛应用于目标检测任务中,尤其是在数据增强阶段,用于过滤掉由于变换导致的不合理边界框,从而提高模型的鲁棒性和准确性。
# RandomPerspective 类是一个用于图像和标注数据增强的工具,它通过随机应用仿射变换(包括平移、旋转、缩放、剪切)和透视变换,对输入图像及其标注(边界框、分割掩码和关键点)进行几何变换。该类不仅能够高效地处理图像的变换,还能确保标注信息在变换后保持一致性和有效性。通过筛选合理的边界框和裁剪标注信息,它能够生成高质量的增强数据,从而提高模型对不同视角和几何变化的鲁棒性,广泛适用于目标检测、实例分割和关键点估计等任务。

8.class RandomHSV: 

# 这段代码定义了一个名为 RandomHSV 的类,用于对图像进行随机的 HSV(色相、饱和度、明度)调整,以实现数据增强。 
# 定义了一个名为 RandomHSV 的类,用于对图像的 HSV 通道进行随机调整。
class RandomHSV:# 随机调整图像的色调、饱和度和值 (HSV) 通道。# 此类将随机 HSV 增强应用于由 hgain、sgain 和 vgain 设置的预定义限制内的图像。# 方法:# __call__:将随机 HSV 增强应用于图像。"""Randomly adjusts the Hue, Saturation, and Value (HSV) channels of an image.This class applies random HSV augmentation to images within predefined limits set by hgain, sgain, and vgain.Attributes:hgain (float): Maximum variation for hue. Range is typically [0, 1].sgain (float): Maximum variation for saturation. Range is typically [0, 1].vgain (float): Maximum variation for value. Range is typically [0, 1].Methods:__call__: Applies random HSV augmentation to an image.Examples:>>> import numpy as np>>> from ultralytics.data.augment import RandomHSV>>> augmenter = RandomHSV(hgain=0.5, sgain=0.5, vgain=0.5)>>> image = np.random.randint(0, 255, (100, 100, 3), dtype=np.uint8)>>> labels = {"img": image}>>> augmenter(labels)>>> augmented_image = augmented_labels["img"]"""# 定义了类的初始化方法,接收三个参数。# 1.hgain :色相(Hue)的调整范围,默认为 0.5。# 2.sgain :饱和度(Saturation)的调整范围,默认为 0.5。# 3.vgain :明度(Value)的调整范围,默认为 0.5。这些参数控制了 HSV 通道的随机调整强度。def __init__(self, hgain=0.5, sgain=0.5, vgain=0.5) -> None:# 初始化 RandomHSV 对象以进行随机 HSV(色调、饱和度、值)增强。# 此类在指定限制内​​对图像的 HSV 通道应用随机调整。"""Initializes the RandomHSV object for random HSV (Hue, Saturation, Value) augmentation.This class applies random adjustments to the HSV channels of an image within specified limits.Args:hgain (float): Maximum variation for hue. Should be in the range [0, 1].sgain (float): Maximum variation for saturation. Should be in the range [0, 1].vgain (float): Maximum variation for value. Should be in the range [0, 1].Examples:>>> hsv_aug = RandomHSV(hgain=0.5, sgain=0.5, vgain=0.5)>>> hsv_aug(image)"""# 将传入的参数保存为类的属性,供后续方法使用。self.hgain = hgainself.sgain = sgainself.vgain = vgain# 定义了类的 __call__ 方法,接收一个字典。# 1.labels :其中包含图像及其标注信息。def __call__(self, labels):# 在预定义限制内对图像应用随机 HSV 增强。# 此方法通过随机调整其色调、饱和度和值 (HSV) 通道来修改输入图像。# 调整是在初始化期间由 hgain、sgain 和 vgain 设置的限制内进行的。"""Applies random HSV augmentation to an image within predefined limits.This method modifies the input image by randomly adjusting its Hue, Saturation, and Value (HSV) channels.The adjustments are made within the limits set by hgain, sgain, and vgain during initialization.Args:labels (Dict): A dictionary containing image data and metadata. Must include an 'img' key withthe image as a numpy array.Returns:(None): The function modifies the input 'labels' dictionary in-place, updating the 'img' keywith the HSV-augmented image.Examples:>>> hsv_augmenter = RandomHSV(hgain=0.5, sgain=0.5, vgain=0.5)>>> labels = {"img": np.random.randint(0, 255, (100, 100, 3), dtype=np.uint8)}>>> hsv_augmenter(labels)>>> augmented_img = labels["img"]"""# 从 labels 字典中提取 图像数据 ,图像通常是一个 NumPy 数组,格式为 BGR。img = labels["img"]# 如果任意一个调整范围( hgain 、 sgain 或 vgain )不为零,则对图像进行 HSV 调整。if self.hgain or self.sgain or self.vgain:# 生成随机调整因子。# np.random.uniform(-1, 1, 3) :生成一个长度为 3 的数组,值在 [-1, 1] 范围内。# 乘以 [self.hgain, self.sgain, self.vgain] :将随机值缩放到指定的调整范围内。# 加上 1 :将调整因子的范围从 [-1, 1] 转换为 [0, 2],确保调整后的值为正。r = np.random.uniform(-1, 1, 3) * [self.hgain, self.sgain, self.vgain] + 1  # random gains# 将图像从 BGR 格式转换为 HSV 格式,并分离出三个通道。# cv2.cvtColor(img, cv2.COLOR_BGR2HSV) :将图像从 BGR 转换为 HSV 格式。# cv2.split(...) :分离出 HSV 通道,分别存储为 hue (色相)、 sat (饱和度)和 val (明度)。hue, sat, val = cv2.split(cv2.cvtColor(img, cv2.COLOR_BGR2HSV))# 获取 图像的数据类型 (通常是 uint8 ),用于后续的像素值调整。dtype = img.dtype  # uint8# 创建一个从 0 到 255 的数组 x ,用于生成查找表(LUT)。x = np.arange(0, 256, dtype=r.dtype)# 生成每个通道的查找表。# 色相通道的查找表。色相值范围为 [0, 179],因此使用取模运算 % 180 。lut_hue = ((x * r[0]) % 180).astype(dtype)# 饱和度通道的查找表。饱和度值范围为 [0, 255],使用 np.clip 确保值在有效范围内。lut_sat = np.clip(x * r[1], 0, 255).astype(dtype)# 明度通道的查找表。明度值范围为 [0, 255],同样使用 np.clip 确保值在有效范围内。lut_val = np.clip(x * r[2], 0, 255).astype(dtype)# 使用查找表对 HSV 通道进行调整。# cv2.LUT(...) :根据查找表调整每个通道的像素值。# cv2.merge(...) :将调整后的 HSV 通道重新合并为一个图像。im_hsv = cv2.merge((cv2.LUT(hue, lut_hue), cv2.LUT(sat, lut_sat), cv2.LUT(val, lut_val)))# 将调整后的 HSV 图像转换回 BGR 格式,并直接将结果存储到原始图像变量 img 中。cv2.cvtColor(im_hsv, cv2.COLOR_HSV2BGR, dst=img)  # no return needed# 返回更新后的 labels 字典,其中图像已根据 HSV 调整完成。return labels
# RandomHSV 类通过随机调整图像的 HSV 通道(色相、饱和度和明度),实现了对图像的颜色增强。它通过生成查找表(LUT)对每个通道进行调整,并确保调整后的值在有效范围内。这种数据增强方法能够模拟不同的光照条件和颜色变化,从而提高模型对颜色多样性的鲁棒性。该类广泛适用于计算机视觉任务中的数据增强,尤其是在目标检测和图像分类中。

9.class RandomFlip: 

# 这段代码定义了一个名为 RandomFlip 的类,用于对图像及其标注(边界框、关键点等)进行随机翻转操作。翻转方向可以是水平(horizontal)或垂直(vertical),并且可以通过概率参数 p 控制翻转发生的概率。
# 定义了一个名为 RandomFlip 的类,用于实现图像和标注的随机翻转。
class RandomFlip:# 以给定概率对图像应用随机水平或垂直翻转。# 此类执行随机图像翻转并更新相应的实例注释,例如边界框和关键点。# 方法:# __call__:将随机翻转变换应用于图像及其注释。"""Applies a random horizontal or vertical flip to an image with a given probability.This class performs random image flipping and updates corresponding instance annotations such asbounding boxes and keypoints.Attributes:p (float): Probability of applying the flip. Must be between 0 and 1.direction (str): Direction of flip, either 'horizontal' or 'vertical'.flip_idx (array-like): Index mapping for flipping keypoints, if applicable.Methods:__call__: Applies the random flip transformation to an image and its annotations.Examples:>>> transform = RandomFlip(p=0.5, direction="horizontal")>>> result = transform({"img": image, "instances": instances})>>> flipped_image = result["img"]>>> flipped_instances = result["instances"]"""# 定义了类的初始化方法,接收以下参数 :# 1.p :翻转发生的概率,默认为 0.5。# 2.direction :翻转方向,可以是 "horizontal" (水平翻转)或 "vertical" (垂直翻转)。# 3.flip_idx :关键点翻转索引,用于处理关键点的水平翻转(例如,左眼和右眼的索引交换)。def __init__(self, p=0.5, direction="horizontal", flip_idx=None) -> None:# 使用概率和方向初始化 RandomFlip 类。# 此类以给定概率对图像应用随机水平或垂直翻转。# 它还会相应地更新任何实例(边界框、关键点等)。# 引发:# AssertionError:如果 direction 不是“水平”或“垂直”,或者 p 不在 0 和 1 之间。"""Initializes the RandomFlip class with probability and direction.This class applies a random horizontal or vertical flip to an image with a given probability.It also updates any instances (bounding boxes, keypoints, etc.) accordingly.Args:p (float): The probability of applying the flip. Must be between 0 and 1.direction (str): The direction to apply the flip. Must be 'horizontal' or 'vertical'.flip_idx (List[int] | None): Index mapping for flipping keypoints, if any.Raises:AssertionError: If direction is not 'horizontal' or 'vertical', or if p is not between 0 and 1.Examples:>>> flip = RandomFlip(p=0.5, direction="horizontal")>>> flip_with_idx = RandomFlip(p=0.7, direction="vertical", flip_idx=[1, 0, 3, 2, 5, 4])"""# 对输入参数进行校验。# 确保 direction 参数只能是 "horizontal" 或 "vertical" 。assert direction in {"horizontal", "vertical"}, f"Support direction `horizontal` or `vertical`, got {direction}"    # 支持方向 `水平` 或 `垂直`,得到{direction}。# 确保概率 p 在 [0, 1] 范围内。assert 0 <= p <= 1.0, f"The probability should be in range [0, 1], but got {p}."    # 概率应该在 [0, 1] 范围内,但得到的是 {p}。# 将初始化参数保存为类的属性,供后续方法使用。self.p = pself.direction = directionself.flip_idx = flip_idx# 定义了类的 __call__ 方法,接收一个字典。# 1.labels :其中包含图像及其标注信息。def __call__(self, labels):# 对图像应用随机翻转,并相应地更新边界框或关键点等实例。# 此方法根据初始化的概率和方向随机水平或垂直翻转输入图像。它还会更新相应的实例(边界框、关键点)以匹配翻转的图像。"""Applies random flip to an image and updates any instances like bounding boxes or keypoints accordingly.This method randomly flips the input image either horizontally or vertically based on the initializedprobability and direction. It also updates the corresponding instances (bounding boxes, keypoints) tomatch the flipped image.Args:labels (Dict): A dictionary containing the following keys:'img' (numpy.ndarray): The image to be flipped.'instances' (ultralytics.utils.instance.Instances): An object containing bounding boxes andoptionally keypoints.Returns:(Dict): The same dictionary with the flipped image and updated instances:'img' (numpy.ndarray): The flipped image.'instances' (ultralytics.utils.instance.Instances): Updated instances matching the flipped image.Examples:>>> labels = {"img": np.random.rand(640, 640, 3), "instances": Instances(...)}>>> random_flip = RandomFlip(p=0.5, direction="horizontal")>>> flipped_labels = random_flip(labels)"""# 从 labels 中提取图像和标注实例。# 输入图像。img = labels["img"]# 标注实例,包含边界框、分割掩码和关键点等信息。instances = labels.pop("instances")# 将边界框格式统一转换为 [x_center, y_center, width, height] ( xywh 格式)。instances.convert_bbox(format="xywh")# 获取图像的高度和宽度。h, w = img.shape[:2]# 如果标注是归一化的( instances.normalized 为 True ),则将 h 和 w 设置为 1。 否则,使用图像的实际高度和宽度。h = 1 if instances.normalized else hw = 1 if instances.normalized else w# Flip up-down# 如果翻转方向为垂直( vertical )且随机概率小于 self.p 。if self.direction == "vertical" and random.random() < self.p:# 使用 np.flipud 对图像进行垂直翻转。img = np.flipud(img)# 调用 instances.flipud(h) 对标注实例进行垂直翻转处理。instances.flipud(h)# 如果翻转方向为水平( horizontal )且随机概率小于 self.p 。if self.direction == "horizontal" and random.random() < self.p:# 使用 np.fliplr 对图像进行水平翻转。img = np.fliplr(img)# 调用 instances.fliplr(w) 对标注实例进行水平翻转处理。instances.fliplr(w)# For keypoints# 如果提供了关键点翻转索引( flip_idx )且标注中包含关键点。if self.flip_idx is not None and instances.keypoints is not None:# 则对关键点进行索引交换,以匹配水平翻转后的关键点位置。instances.keypoints = np.ascontiguousarray(instances.keypoints[:, self.flip_idx, :])# 将 翻转后的图像 和 标注实例 重新存入 labels 字典。labels["img"] = np.ascontiguousarray(img)labels["instances"] = instances# 返回更新后的 labels 字典。return labels
# RandomFlip 类实现了对图像及其标注(边界框、关键点等)的随机翻转操作。它支持水平和垂直翻转,并通过概率参数 p 控制翻转发生的概率。对于关键点,该类还提供了索引交换功能,以确保关键点在翻转后仍然正确。这种数据增强方法能够增加数据的多样性,提高模型对不同方向变化的鲁棒性,广泛适用于目标检测、关键点估计和实例分割任务。

10.class LetterBox: 

# 这段代码定义了一个名为 LetterBox 的类,用于对图像进行缩放和填充操作,以适应指定的目标尺寸。这种操作通常用于深度学习中的图像预处理,尤其是在目标检测和分割任务中。 LetterBox 的核心功能是将图像缩放到指定大小,同时保持原始图像的宽高比,并通过填充(通常是灰色)来补充剩余部分。
# 定义了一个名为 LetterBox 的类,用于对图像进行缩放和填充操作。
class LetterBox:# 调整图像大小和填充以进行检测、实例分割和姿势。# 此类调整图像大小并填充图像以使其达到指定形状,同时保持宽高比。它还会更新相应的标签和边界框。# 方法:# __call__:调整图像大小并填充图像,更新标签和边界框。"""Resize image and padding for detection, instance segmentation, pose.This class resizes and pads images to a specified shape while preserving aspect ratio. It also updatescorresponding labels and bounding boxes.Attributes:new_shape (tuple): Target shape (height, width) for resizing.auto (bool): Whether to use minimum rectangle.scaleFill (bool): Whether to stretch the image to new_shape.scaleup (bool): Whether to allow scaling up. If False, only scale down.stride (int): Stride for rounding padding.center (bool): Whether to center the image or align to top-left.Methods:__call__: Resize and pad image, update labels and bounding boxes.Examples:>>> transform = LetterBox(new_shape=(640, 640))>>> result = transform(labels)>>> resized_img = result["img"]>>> updated_instances = result["instances"]"""# 这段代码定义了 LetterBox 类的初始化方法 __init__ ,用于设置图像缩放和填充操作的参数。# 定义了 LetterBox 类的初始化方法,接收以下参数 :# 1.new_shape :目标图像的尺寸,默认为 (640, 640) 。如果传入一个整数,则表示目标尺寸为正方形。# 2.auto :是否自动调整填充,以确保填充后的图像尺寸是 stride 的倍数。默认为 False 。# 3.scaleFill :是否拉伸图像以填满整个目标尺寸,而不进行填充。默认为 False 。# 4.scaleup :是否允许放大图像(仅缩小图像以提高验证时的 mAP)。默认为 True 。# 5.center :是否将图像放置在填充区域的中心(否则放置在左上角)。默认为 True 。# 6.stride :用于自动调整填充的步长,默认为 32 。def __init__(self, new_shape=(640, 640), auto=False, scaleFill=False, scaleup=True, center=True, stride=32):# 初始化 LetterBox 对象以调整图像大小和填充。# 此类旨在调整图像大小和填充图像,以用于对象检测、实例分割和姿势估计任务。它支持各种调整大小模式,包括自动调整大小、缩放填充和信箱化。"""Initialize LetterBox object for resizing and padding images.This class is designed to resize and pad images for object detection, instance segmentation, and pose estimationtasks. It supports various resizing modes including auto-sizing, scale-fill, and letterboxing.Args:new_shape (Tuple[int, int]): Target size (height, width) for the resized image.auto (bool): If True, use minimum rectangle to resize. If False, use new_shape directly.scaleFill (bool): If True, stretch the image to new_shape without padding.scaleup (bool): If True, allow scaling up. If False, only scale down.center (bool): If True, center the placed image. If False, place image in top-left corner.stride (int): Stride of the model (e.g., 32 for YOLOv5).Attributes:new_shape (Tuple[int, int]): Target size for the resized image.auto (bool): Flag for using minimum rectangle resizing.scaleFill (bool): Flag for stretching image without padding.scaleup (bool): Flag for allowing upscaling.stride (int): Stride value for ensuring image size is divisible by stride.Examples:>>> letterbox = LetterBox(new_shape=(640, 640), auto=False, scaleFill=False, scaleup=True, stride=32)>>> resized_img = letterbox(original_img)"""# 将传入的参数保存为类的属性,供后续方法使用。# 目标图像尺寸。self.new_shape = new_shape# 是否自动调整填充。self.auto = auto# 是否拉伸填满。self.scaleFill = scaleFill# 是否允许放大图像。self.scaleup = scaleup# 步长,用于自动调整填充。self.stride = stride# 是否将图像放置在中心。self.center = center  # Put the image in the middle or top-left# 这段代码的作用是初始化 LetterBox 类的实例,设置图像缩放和填充操作的参数。这些参数决定了如何将输入图像调整为目标尺寸,同时保持原始图像的宽高比,并根据需要进行填充。具体功能包括。目标尺寸:通过 new_shape 参数指定目标图像的宽度和高度。自动调整填充:通过 auto 参数决定是否自动调整填充,以确保填充后的图像尺寸是 stride 的倍数。拉伸填满:通过 scaleFill 参数决定是否拉伸图像以填满整个目标尺寸。放大限制:通过 scaleup 参数决定是否允许放大图像。填充位置:通过 center 参数决定将图像放置在填充区域的中心还是左上角。步长:通过 stride 参数指定自动调整填充时的步长。这些参数的组合使得 LetterBox 类能够灵活地处理不同场景下的图像预处理需求,广泛应用于目标检测和分割任务中的数据增强和模型输入准备。# 这段代码定义了 LetterBox 类的 __call__ 方法,用于对图像进行缩放和填充操作,以适应指定的目标尺寸。该方法同时更新了图像和标注信息(如边界框、分割掩码和关键点),确保标注与图像的变换保持一致。# 定义了 LetterBox 类的 __call__ 方法,接收两个参数。# 1.labels :一个字典,包含图像及其标注信息(如边界框、分割掩码和关键点)。# 2.image :输入图像。如果未提供 image ,则从 labels 中提取图像。def __call__(self, labels=None, image=None):# 调整图像大小并填充图像以用于对象检测、实例分割或姿势估计任务。# 此方法将信箱化应用于输入图像,这涉及调整图像大小,同时保持其纵横比并添加填充以适应新形状。它还会相应地更新任何关联的标签。"""Resizes and pads an image for object detection, instance segmentation, or pose estimation tasks.This method applies letterboxing to the input image, which involves resizing the image while maintaining itsaspect ratio and adding padding to fit the new shape. It also updates any associated labels accordingly.Args:labels (Dict | None): A dictionary containing image data and associated labels, or empty dict if None.image (np.ndarray | None): The input image as a numpy array. If None, the image is taken from 'labels'.Returns:(Dict | Tuple): If 'labels' is provided, returns an updated dictionary with the resized and padded image,updated labels, and additional metadata. If 'labels' is empty, returns a tuple containing the resizedand padded image, and a tuple of (ratio, (left_pad, top_pad)).Examples:>>> letterbox = LetterBox(new_shape=(640, 640))>>> result = letterbox(labels={"img": np.zeros((480, 640, 3)), "instances": Instances(...)})>>> resized_img = result["img"]>>> updated_instances = result["instances"]"""# 这段代码是 LetterBox 类的 __call__ 方法的一部分,主要功能是初始化变量、提取图像信息,并计算图像的缩放比例。# 如果 labels 参数为 None 。if labels is None:# 则初始化为一个空字典。这确保了即使没有传入 labels ,代码也能正常运行。labels = {}# 从 labels 或直接从参数中提取图像。 如果 image 参数为 None ,则从 labels 中提取图像( labels["img"] )。 如果 image 参数已提供,则直接使用 image 。img = labels.get("img") if image is None else image# 提取 图像的原始尺寸。 img.shape[:2] 获取图像的高度和宽度,格式为 (height, width) 。shape = img.shape[:2]  # current shape [height, width]# 从 labels 中提取 目标尺寸 new_shape 。 如果 labels 中存在 "rect_shape" 键,则使用其值作为目标尺寸。 如果不存在,则使用类初始化时定义的默认值 self.new_shape 。new_shape = labels.pop("rect_shape", self.new_shape)# 如果 new_shape 是一个整数,则将其转换为一个元组 (new_shape, new_shape) ,表示目标尺寸为正方形。if isinstance(new_shape, int):new_shape = (new_shape, new_shape)# Scale ratio (new / old)# 计算图像的缩放比例 r 。# new_shape[0] / shape[0] :目标高度与原始高度的比值。# new_shape[1] / shape[1] :目标宽度与原始宽度的比值。# 使用 min 函数取较小值,以确保图像在缩放后不会超出目标尺寸,同时保持宽高比。r = min(new_shape[0] / shape[0], new_shape[1] / shape[1])# 如果 scaleup=False 。if not self.scaleup:  # only scale down, do not scale up (for better val mAP)# 则限制缩放比例 r 不超过 1.0 。 这意味着图像只会被缩小,而不会被放大。 这种做法通常用于验证阶段,以提高模型的 mAP(平均精度)。r = min(r, 1.0)# 这段代码的核心功能是初始化变量、提取图像信息,并计算图像的缩放比例。主要步骤包括。初始化 labels :如果未提供 labels ,则初始化为空字典。提取图像:从 labels 或直接从参数中提取图像。提取原始尺寸:获取图像的高度和宽度。提取目标尺寸:从 labels 中提取目标尺寸,如果不存在,则使用默认值。计算缩放比例:根据目标尺寸和原始尺寸计算缩放比例,确保图像在缩放后不会超出目标尺寸,同时保持宽高比。限制缩放比例:如果 scaleup=False ,则限制缩放比例不超过 1.0,避免放大图像。通过这些步骤,代码为后续的图像缩放和填充操作奠定了基础,确保图像在预处理阶段能够正确地调整为目标尺寸,同时保持标注信息的一致性。# 这段代码是 LetterBox 类的 __call__ 方法的一部分,用于计算图像在缩放后的填充量(padding),并根据不同的参数设置调整填充策略。# Compute padding# 将缩放比例 r 应用于宽度和高度,生成缩放比例元组 (r, r) 。这表示 图像的宽度和高度将使用相同的缩放比例 。ratio = r, r  # width, height ratios# 计算 缩放后的图像尺寸 (不包括填充)。# shape[1] * r :缩放后的宽度。# shape[0] * r :缩放后的高度。# 使用 int(round(...)) 将结果四舍五入为整数。new_unpad = int(round(shape[1] * r)), int(round(shape[0] * r))# 计算 宽度和高度方向的填充量 。# dw :目标宽度与缩放后宽度的差值。# dh :目标高度与缩放后高度的差值。dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1]  # wh padding# 如果 auto=True 。if self.auto:  # minimum rectangle# np.mod(x, y, out=None, where=None, casting='same_kind', order='K', dtype=None, subok=True[, signature, extobj])# 在 NumPy 中, np.mod 函数用于计算模运算,即对于给定的两个数组,它返回第一个数组中每个元素除以第二个数组中对应元素的余数。# 参数说明 :# x :被除数数组。# y :除数数组。 y 的形状必须与 x 相同,或者能够广播到 x 的形状。# out :(可选)用于存放结果的数组。# where :(可选)布尔数组,指定在哪些位置执行操作。# casting :(可选)控制如何处理不同数据类型之间的转换。# order :(可选)指定多维数组中元素的遍历顺序。# dtype :(可选)指定输出数组的数据类型。# subok :(可选)如果为 True,则返回的数组是 x 和 y 的子类。# signature :(可选)指定函数签名。# extobj :(可选)用于非常量参数的额外对象。# 返回值 :# 返回一个数组,包含 x 中每个元素除以 y 中对应元素的余数。# 需要注意的是, np.mod 的行为与 Python 内置的 % 运算符略有不同,特别是在处理负数时。 np.mod 总是返回一个非负余数,而 % 运算符则返回的余数的符号与被除数相同。# 则 调整填充量 ,确保填充后的图像尺寸是 stride 的倍数。 使用 np.mod(dw, self.stride) 和 np.mod(dh, self.stride) 计算填充量的余数。 这种方式可以确保填充后的图像尺寸能够被 stride 整除,通常用于深度学习模型中,以满足网络的输入要求。dw, dh = np.mod(dw, self.stride), np.mod(dh, self.stride)  # wh padding# 如果 scaleFill=True ,则拉伸图像以填满整个目标尺寸,不进行填充。elif self.scaleFill:  # stretch# 将填充量 dw 和 dh 设置为 0。dw, dh = 0.0, 0.0# 将缩放后的图像尺寸设置为目标尺寸 new_shape 。new_unpad = (new_shape[1], new_shape[0])# 重新计算缩放比例,分别对宽度和高度进行独立缩放。ratio = new_shape[1] / shape[1], new_shape[0] / shape[0]  # width, height ratios# 如果 center=True ,则将填充均匀分配到图像的两侧。if self.center:# 将宽度方向的填充量分为左右两侧。dw /= 2  # divide padding into 2 sides# 将高度方向的填充量分为上下两侧。dh /= 2# 这段代码的核心功能是计算图像在缩放后的填充量,并根据参数设置调整填充策略。其主要步骤包括。计算缩放后的图像尺寸:根据缩放比例 r 计算缩放后的宽度和高度。计算填充量:计算目标尺寸与缩放后尺寸的差值。调整填充策略:如果 auto=True ,则调整填充量以确保填充后的图像尺寸是 stride 的倍数。如果 scaleFill=True ,则拉伸图像以填满整个目标尺寸,不进行填充。均匀分配填充:如果 center=True ,则将填充均匀分配到图像的两侧。通过这些步骤,代码能够灵活地处理不同场景下的图像填充需求,确保图像在预处理阶段能够正确地调整为目标尺寸,同时保持标注信息的一致性。这种操作广泛应用于目标检测和分割任务中的图像预处理。# 这段代码是 LetterBox 类的 __call__ 方法的一部分,主要功能是对图像进行缩放和填充操作,并更新标注信息(如果存在)。# 检查 图像的原始尺寸 ( shape )是否与 缩放后的尺寸 ( new_unpad )不同。# shape[::-1] :将原始尺寸从 (height, width) 转换为 (width, height) ,以匹配 OpenCV 的输入格式。if shape[::-1] != new_unpad:  # resize# 如果尺寸不同,则使用 cv2.resize 对图像进行缩放,目标尺寸为 new_unpad 。 使用 cv2.INTER_LINEAR 插值方法进行缩放。img = cv2.resize(img, new_unpad, interpolation=cv2.INTER_LINEAR)# 根据是否启用 center 参数,计算填充的上下和左右边界。# 如果 center=True ,则将填充均匀分配到图像的两侧。# top, bottom :上下填充量。# left, right :左右填充量。# 如果 center=False ,则将填充添加到图像的顶部和左侧(即左上角对齐)。top, bottom = int(round(dh - 0.1)) if self.center else 0, int(round(dh + 0.1))left, right = int(round(dw - 0.1)) if self.center else 0, int(round(dw + 0.1))# 使用 cv2.copyMakeBorder 在图像周围添加填充。# top, bottom, left, right :指定填充的边界大小。# cv2.BORDER_CONSTANT :指定填充类型为常数值。# value=(114, 114, 114) :指定填充颜色为灰色。img = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=(114, 114, 114))  # add border# 如果 labels 中存在 "ratio_pad" 键。if labels.get("ratio_pad"):# 则更新其值,记录填充的偏移量(左和上)。这通常用于评估阶段,以记录填充对标注的影响。labels["ratio_pad"] = (labels["ratio_pad"], (left, top))  # for evaluation# 检查 labels 字典是否包含标注信息。 len(labels) 会返回字典中键值对的数量。如果 labels 不为空,则说明存在标注信息(如边界框、分割掩码或关键点)。if len(labels):# 如果存在标注信息,则调用 _update_labels 方法更新标注。 _update_labels 方法会根据图像的缩放比例( ratio )和填充偏移量( left, top )调整标注信息(如边界框、分割掩码和关键点)。 返回值是一个更新后的 labels 字典。labels = self._update_labels(labels, ratio, left, top)# 将 处理后的图像 (缩放和填充后的图像)存入 labels 字典中,键为 "img" 。labels["img"] = img# 将目标尺寸( new_shape )存入 labels 字典中,键为 "resized_shape" 。这记录了 图像处理后的尺寸 。labels["resized_shape"] = new_shape# 返回更新后的 labels 字典,其中包含处理后的图像和更新后的标注信息。return labels# 如果 labels 为空(即没有标注信息),则直接返回处理后的图像 img 。else:return img# 这段代码的核心功能是对图像进行缩放和填充操作,并根据需要更新标注信息。其主要步骤包括。图像缩放:如果图像的原始尺寸与目标尺寸不同,则对图像进行缩放。计算填充边界:根据是否启用 center 参数,计算填充的上下和左右边界。添加填充:使用 cv2.copyMakeBorder 在图像周围添加灰色填充。更新标注信息:如果存在标注信息,则调用 _update_labels 方法更新标注,并记录填充后的图像和目标尺寸。返回结果:根据 labels 是否存在,返回更新后的 labels 或填充后的图像。通过这些步骤,代码能够灵活地处理图像的缩放和填充需求,确保图像在预处理阶段能够正确地调整为目标尺寸,同时保持标注信息的一致性。这种操作广泛应用于目标检测和分割任务中的图像预处理。# 这段代码实现了对图像的缩放和填充操作,以适应指定的目标尺寸,同时保持原始图像的宽高比。其核心功能包括。计算缩放比例:根据目标尺寸和原始尺寸计算缩放比例,确保图像不会被放大(如果 scaleup=False )。计算填充量:根据目标尺寸和缩放后的尺寸计算填充量,并根据参数调整填充方式。图像缩放和填充:对图像进行缩放,并在周围添加灰色填充。更新标注信息:调用 _update_labels 方法,更新标注信息(如边界框、分割掩码和关键点),以匹配填充后的图像。这种预处理方法广泛应用于目标检测和分割任务中,尤其是在使用深度学习模型时。它确保了图像在输入模型之前具有一致的尺寸,同时保持了标注信息的准确性。# 这段代码定义了 LetterBox 类中的一个静态方法 _update_labels ,用于在对图像进行缩放和填充操作后,更新标注信息(如边界框、分割掩码和关键点)。@staticmethod# 定义了一个静态方法 _update_labels ,接收以下参数 :# 1.labels :包含标注信息的字典,例如边界框、分割掩码和关键点。# 2.ratio :图像的缩放比例,格式为 (width_ratio, height_ratio) 。# 3.padw :宽度方向的填充量。# 4.padh :高度方向的填充量。def _update_labels(labels, ratio, padw, padh):# 在将信箱效果应用于图像后更新标签。# 此方法修改标签中实例的边界框坐标,以考虑信箱效果期间应用的调整大小和填充。"""Updates labels after applying letterboxing to an image.This method modifies the bounding box coordinates of instances in the labelsto account for resizing and padding applied during letterboxing.Args:labels (Dict): A dictionary containing image labels and instances.ratio (Tuple[float, float]): Scaling ratios (width, height) applied to the image.padw (float): Padding width added to the image.padh (float): Padding height added to the image.Returns:(Dict): Updated labels dictionary with modified instance coordinates.Examples:>>> letterbox = LetterBox(new_shape=(640, 640))>>> labels = {"instances": Instances(...)}>>> ratio = (0.5, 0.5)>>> padw, padh = 10, 20>>> updated_labels = letterbox._update_labels(labels, ratio, padw, padh)"""# 将标注中的边界框格式转换为 [x_min, y_min, x_max, y_max] ( xyxy 格式)。这是后续处理的默认格式,确保边界框坐标与填充后的图像尺寸一致。labels["instances"].convert_bbox(format="xyxy")# 将边界框坐标从归一化形式反归一化到图像的实际像素坐标。# labels["img"].shape[:2][::-1] :提取图像的宽度和高度 (W, H) 。# denormalize 方法将归一化坐标(范围在 [0, 1] )转换为像素坐标。labels["instances"].denormalize(*labels["img"].shape[:2][::-1])# 根据缩放比例调整边界框的坐标。 scale 方法将边界框坐标乘以缩放比例 ratio ,以匹配缩放后的图像尺寸。labels["instances"].scale(*ratio)# 为边界框坐标添加填充偏移量。 add_padding 方法将填充量 (padw, padh) 添加到边界框的坐标中,以确保边界框在填充后的图像中保持正确的位置。labels["instances"].add_padding(padw, padh)# 返回更新后的 labels 字典,其中标注信息已根据图像的缩放和填充操作进行了调整。return labels# _update_labels 方法的核心功能是更新标注信息(如边界框、分割掩码和关键点),以适应图像的缩放和填充操作。其主要步骤包括。边界框格式转换:将边界框格式统一为 [x_min, y_min, x_max, y_max] 。反归一化:将边界框坐标从归一化形式转换为像素坐标。缩放:根据缩放比例调整边界框的坐标。添加填充偏移量:为边界框坐标添加填充偏移量,以匹配填充后的图像。通过这些步骤,该方法确保标注信息在图像预处理后仍然准确无误,适用于目标检测和分割任务中的数据增强和模型输入准备。
# LetterBox 类实现了对图像的缩放和填充操作,以适应指定的目标尺寸,同时保持原始图像的宽高比。它支持多种填充模式(如自动调整填充、拉伸填满等),并提供了选项将图像放置在填充区域的中心或左上角。此外,该类还提供了更新标注信息的功能,确保标注与填充后的图像保持一致。这种预处理方法广泛应用于目标检测和分割任务中,尤其是在使用深度学习模型时。

11.class CopyPaste(BaseMixTransform): 

# 这段代码定义了一个名为 CopyPaste 的类,继承自 BaseMixTransform ,用于实现图像和标注的混合增强操作。 CopyPaste 支持两种模式: flip 和 mixup ,并且可以随机选择其他图像的标注信息进行混合。
# 定义了一个名为 CopyPaste 的类,继承自 BaseMixTransform 。这表明它可能继承了一些基础的混合增强功能。
class CopyPaste(BaseMixTransform):# 用于将复制粘贴增强应用于图像数据集的 CopyPaste 类。# 此类实现了论文“简单的复制粘贴是实例分割的强大数据增强方法”(https://arxiv.org/abs/2012.07177) 中描述的复制粘贴增强技术。它结合了来自不同图像的对象以创建新的训练样本。# 方法:# get_indexes:从数据集返回一个随机索引。# _mix_transform:将复制粘贴增强应用于输入标签。# __call__:将复制粘贴转换应用于图像和注释。"""CopyPaste class for applying Copy-Paste augmentation to image datasets.This class implements the Copy-Paste augmentation technique as described in the paper "Simple Copy-Paste is a StrongData Augmentation Method for Instance Segmentation" (https://arxiv.org/abs/2012.07177). It combines objects fromdifferent images to create new training samples.Attributes:dataset (Any): The dataset to which Copy-Paste augmentation will be applied.pre_transform (Callable | None): Optional transform to apply before Copy-Paste.p (float): Probability of applying Copy-Paste augmentation.Methods:get_indexes: Returns a random index from the dataset._mix_transform: Applies Copy-Paste augmentation to the input labels.__call__: Applies the Copy-Paste transformation to images and annotations.Examples:>>> from ultralytics.data.augment import CopyPaste>>> dataset = YourDataset(...)  # Your image dataset>>> copypaste = CopyPaste(dataset, p=0.5)>>> augmented_labels = copypaste(original_labels)"""# 这段代码是 CopyPaste 类的初始化方法 __init__ ,用于设置该类的基本参数和行为。# 定义了 CopyPaste 类的初始化方法,接收以下参数 :# 1.dataset :数据集对象,用于获取其他图像和标注信息。如果为 None ,则不使用数据集。# 2.pre_transform :预处理函数,用于在混合增强操作之前对图像和标注进行处理。# 3.p :混合增强操作的概率,默认为 0.5。# 4.mode :混合增强的模式,可以是 "flip" 或 "mixup" 。def __init__(self, dataset=None, pre_transform=None, p=0.5, mode="flip") -> None:# 使用数据集、pre_transform 和应用 MixUp 的概率初始化 CopyPaste 对象。"""Initializes CopyPaste object with dataset, pre_transform, and probability of applying MixUp."""# 调用父类 BaseMixTransform 的初始化方法,将 dataset 、 pre_transform 和 p 传递给父类。这确保了 CopyPaste 类继承了父类的基本功能。super().__init__(dataset=dataset, pre_transform=pre_transform, p=p)# 校验 mode 参数是否为 "flip" 或 "mixup" 。 如果 mode 不在这两个值中,抛出一个断言错误,并提示用户预期的值。assert mode in {"flip", "mixup"}, f"Expected `mode` to be `flip` or `mixup`, but got {mode}."    # 预期“mode”为“flip”或“mixup”,但得到的是{mode}。# 将 mode 参数保存为类的属性 self.mode ,供后续方法使用。self.mode = mode# 这段代码的作用是初始化 CopyPaste 类的实例,设置其基本参数和行为。主要功能包括。继承父类:通过调用 super().__init__ ,继承了父类 BaseMixTransform 的基本功能,包括数据集和预处理函数的初始化。校验模式:确保 mode 参数只能是 "flip" 或 "mixup" ,避免用户传入无效值。保存模式:将 mode 参数保存为类的属性,以便后续方法根据模式执行不同的混合增强操作。通过这些步骤, CopyPaste 类能够灵活地支持两种混合增强模式,并确保用户输入的参数符合预期。这种设计使得该类能够广泛应用于目标检测和分割任务中的数据增强。# 这段代码定义了 CopyPaste 类中的 get_indexes 方法,用于随机选择一个图像索引。# 定义了一个名为 get_indexes 的方法,该方法属于 CopyPaste 类。def get_indexes(self):# 从数据集中返回用于复制粘贴增强的随机索引列表。"""Returns a list of random indexes from the dataset for CopyPaste augmentation."""# random.randint(a, b) :生成一个随机整数,范围在 [a, b] 之间(包括 a 和 b )。# len(self.dataset) - 1 :获取数据集的长度减 1,作为随机选择的最大索引值。# 该方法返回一个随机索引值,范围从 0 到 len(self.dataset) - 1 。return random.randint(0, len(self.dataset) - 1)# get_indexes 方法的作用是从数据集中随机选择一个图像的索引。它通常用于数据增强操作中,例如在 CopyPaste 或其他混合增强方法中,随机选择其他图像的标注信息进行混合。这种方法能够增加数据的多样性,提高模型对不同场景的鲁棒性。注意事项:数据集不能为空:在调用 get_indexes 方法之前,需要确保 self.dataset 不为空,否则会抛出 ValueError 。随机性:每次调用该方法时,都会返回一个随机的索引值,这使得增强操作具有随机性。# 这段代码定义了 CopyPaste 类中的 _mix_transform 方法,用于处理混合增强操作。它的主要功能是从 labels 中提取混合标注信息,并调用 _transform 方法来执行具体的混合操作。# 定义了 _mix_transform 方法,接收一个参数。# 1.labels :包含目标图像及其标注信息的字典,同时包含用于混合的其他图像的标注信息。def _mix_transform(self, labels):# 应用复制粘贴增强功能将另一幅图像中的对象组合到当前图像中。"""Applies Copy-Paste augmentation to combine objects from another image into the current image."""# 从 labels 中提取用于混合的标注信息 labels2 。# labels["mix_labels"] :一个列表,包含一个或多个用于混合的图像的标注信息。# [0] :提取第一个混合标注信息。这表明 _mix_transform 方法目前只处理一个混合标注。labels2 = labels["mix_labels"][0]# 调用 _transform 方法,将目标标注 labels 和混合标注 labels2 传递进去,执行具体的混合操作。# _transform 方法会根据混合模式(如 flip 或 mixup )和混合概率 p ,将 labels2 中的标注信息(如边界框、分割掩码等)复制到 labels 中。 返回值是更新后的 labels ,其中包含了混合后的图像和标注信息。return self._transform(labels, labels2)# _mix_transform 方法的作用是从 labels 中提取混合标注信息,并调用 _transform 方法来执行混合操作。其核心功能包括。提取混合标注信息:从 labels["mix_labels"] 中提取第一个混合标注信息 labels2 。执行混合操作:调用 _transform 方法,将 labels2 中的标注信息复制到 labels 中。返回更新后的标注信息:返回包含混合后图像和标注信息的 labels 字典。通过这种方式, _mix_transform 方法能够灵活地处理混合增强操作,支持不同的混合模式(如 flip 和 mixup ),并确保混合后的标注信息与图像内容一致。这种增强方法广泛应用于目标检测和分割任务中,能够增加数据的多样性,提高模型的鲁棒性。# 这段代码定义了 CopyPaste 类的 __call__ 方法,用于执行混合增强操作(如 flip 或 mixup )。该方法根据指定的模式( mode )和概率( p ),决定是否对输入的标注信息 labels 进行增强处理。# 定义了 CopyPaste 类的 __call__ 方法,接收一个参数。# 1.labels :包含目标图像及其标注信息的字典。def __call__(self, labels):# 将复制粘贴增强功能应用于图像及其标签。"""Applies Copy-Paste augmentation to an image and its labels."""# 检查是否需要执行混合增强操作。# 如果标注中没有分割掩码( segments ),或者混合概率 p 为 0,则直接返回原始标注信息 labels 。if len(labels["instances"].segments) == 0 or self.p == 0:return labels# 如果混合模式为 "flip" ,则直接调用 _transform 方法对标注信息进行翻转操作,并返回更新后的标注信息。if self.mode == "flip":return self._transform(labels)# Get index of one or three other images# 随机选择一个或多个图像索引。# 调用 get_indexes 方法获取随机索引。indexes = self.get_indexes()# 如果返回的是一个整数,则将其转换为列表,以便后续处理。if isinstance(indexes, int):indexes = [indexes]# Get images information will be used for Mosaic or MixUp# 根据随机选择的索引,从数据集中获取其他图像及其标注信息。# self.dataset.get_image_and_label(i) :获取索引为 i 的图像和标注信息。# mix_labels :一个列表,包含所有随机选择的图像的标注信息。mix_labels = [self.dataset.get_image_and_label(i) for i in indexes]# 如果定义了预处理函数 pre_transform ,则对每个混合标注信息进行预处理。if self.pre_transform is not None:# 遍历 mix_labels ,对每个标注信息调用 pre_transform 函数。for i, data in enumerate(mix_labels):mix_labels[i] = self.pre_transform(data)# 将混合标注信息存入 labels 字典中,键为 "mix_labels" 。labels["mix_labels"] = mix_labels# Update cls and texts# 调用 _update_label_text 方法更新标注信息中的类别标签和文本信息。labels = self._update_label_text(labels)# Mosaic or MixUp# 调用 _mix_transform 方法执行混合操作(如 Mosaic 或 MixUp )。 _mix_transform 方法会从 labels["mix_labels"] 中提取混合标注信息,并调用 _transform 方法进行具体的混合处理。labels = self._mix_transform(labels)# 移除 labels 字典中的 "mix_labels" 键,以清理不再需要的混合标注信息。labels.pop("mix_labels", None)# 返回更新后的标注信息 labels 。return labels# __call__ 方法的核心功能是根据指定的模式( mode )和概率( p ),决定是否对输入的标注信息 labels 进行混合增强操作。其主要步骤包括。检查是否需要增强:如果标注中没有分割掩码或混合概率为 0,则直接返回原始标注信息。选择混合模式:如果模式为 "flip" ,则直接调用 _transform 方法进行翻转操作。如果模式为 "mixup" 或其他混合模式,则继续执行混合操作。随机选择图像索引:从数据集中随机选择一个或多个图像的索引。获取混合标注信息:根据随机选择的索引,从数据集中获取其他图像及其标注信息。预处理混合标注:如果定义了预处理函数,则对每个混合标注信息进行预处理。更新标注信息:调用 _update_label_text 方法更新标注信息中的类别标签和文本。执行混合操作:调用 _mix_transform 方法,将混合标注信息复制到目标标注中。清理临时信息:移除不再需要的混合标注信息。返回更新后的标注信息:返回包含混合后图像和标注信息的 labels 字典。通过这些步骤, CopyPaste 类能够灵活地支持不同的混合增强模式,增加数据的多样性,提高模型对不同场景的鲁棒性。# 这段代码定义了 CopyPaste 类中的 _transform 方法,用于将一个图像的标注信息(边界框、分割掩码等)复制并粘贴到另一个图像上。该方法的核心是选择性地将某些标注从 labels2 复制到 labels1 ,同时确保这些标注不会与 labels1 中的现有标注发生过多重叠。# 定义了 _transform 方法,接收两个参数。# 1.labels1 :目标图像及其标注信息。# 2.labels2 :源图像及其标注信息(默认为空字典)。def _transform(self, labels1, labels2={}):# 应用复制粘贴增强功能将另一幅图像中的对象组合到当前图像中。"""Applies Copy-Paste augmentation to combine objects from another image into the current image."""# 这段代码是 CopyPaste 类的 _transform 方法的一部分,用于从目标图像的标注信息 labels1 中提取图像、类别标签和标注实例,并对标注实例进行格式化和反归一化处理。# 从 labels1 中提取目标图像 im ,通常是一个 NumPy 数组,格式为 (H, W, C) ,表示图像的高度、宽度和通道数。im = labels1["img"]# 从 labels1 中提取类别标签 cls ,通常是一个一维数组,表示每个标注实例的类别。cls = labels1["cls"]# 提取图像的 高度 h 和 宽度 w 。# im.shape[:2] :获取图像的前两个维度,即 高度 和 宽度 。h, w = im.shape[:2]# 从 labels1 中提取标注实例 instances ,并将其从字典中移除。 instances 通常是一个自定义类实例,包含边界框、分割掩码和关键点等标注信息。instances = labels1.pop("instances")# 将标注实例中的边界框格式转换为 [x_min, y_min, x_max, y_max] ( xyxy 格式)。这是后续处理的默认格式,确保边界框坐标与图像尺寸一致。instances.convert_bbox(format="xyxy")# 将边界框坐标从归一化形式反归一化到图像的实际像素坐标。# w 和 h :图像的宽度和高度。# 反归一化操作将边界框坐标从范围 [0, 1] 转换为像素坐标范围 [0, W] 和 [0, H] 。instances.denormalize(w, h)# 这段代码的核心功能是从目标图像的标注信息 labels1 中提取图像、类别标签和标注实例,并对标注实例进行格式化和反归一化处理。其主要步骤包括。提取图像和类别标签:从 labels1 中提取目标图像和类别标签。提取标注实例:从 labels1 中提取标注实例,并将其移除。边界框格式转换:将边界框格式统一转换为 [x_min, y_min, x_max, y_max] 。反归一化:将边界框坐标从归一化形式转换为图像的实际像素坐标。通过这些步骤,代码确保了标注信息的格式和坐标范围与图像的实际尺寸一致,为后续的混合增强操作奠定了基础。这种处理方式广泛应用于目标检测和分割任务中的数据增强。# 这段代码是 CopyPaste 类的 _transform 方法的一部分,用于从源标注信息 labels2 中选择性地复制标注实例(边界框和分割掩码),并将它们粘贴到目标图像 im 上。# 创建一个与目标图像 im 大小相同的空白图像 im_new ,数据类型为 np.uint8 。这个空白图像将用于 绘制分割掩码 。im_new = np.zeros(im.shape, np.uint8)# 从 labels2 中提取标注实例 instances2 ,如果 labels2 中没有标注实例,则返回 None 。instances2 = labels2.pop("instances", None)# 如果 labels2 中没有标注实例,则从目标标注 instances 中复制一份,并对其进行水平翻转。if instances2 is None:# 深拷贝 instances ,确保不会修改原始标注。instances2 = deepcopy(instances)# 对复制的标注实例进行水平翻转,宽度为 w 。instances2.fliplr(w)# 计算 instances2 中的边界框与 instances 中的边界框之间的交并比(Intersection over Area, IOA)。# instances2.bboxes :源标注的边界框。# instances.bboxes :目标标注的边界框。# ioa :交并比矩阵,形状为 (N, M) ,其中 N 是 instances2 中的边界框数量, M 是 instances 中的边界框数量。ioa = bbox_ioa(instances2.bboxes, instances.bboxes)  # intersection over area, (N, M)# 筛选出交并比小于 0.3 的边界框索引。# (ioa < 0.30).all(1) :检查每个边界框与目标标注的交并比是否都小于 0.3。# np.nonzero(...)[0] :获取满足条件的索引。indexes = np.nonzero((ioa < 0.30).all(1))[0]  # (N, )# 根据交并比的最大值对筛选出的索引进行排序。n = len(indexes)# ioa.max(1)[indexes] :获取 每个边界框与目标标注的最大交并比 。# np.argsort(...) :对最大交并比进行排序。sorted_idx = np.argsort(ioa.max(1)[indexes])# 重新排序筛选出的索引。indexes = indexes[sorted_idx]# 根据混合概率 p ,选择部分边界框进行复制。# round(self.p * n) :计算需要复制的边界框数量。# indexes[: round(self.p * n)] :选择前 round(self.p * n) 个索引。for j in indexes[: round(self.p * n)]:# 将选择的边界框的类别标签添加到目标类别标签 cls 中。# labels2.get("cls", cls)[[j]] :从 labels2 中获取类别标签,如果没有则使用 cls 。# np.concatenate(..., axis=0) :将新类别标签拼接到 cls 中。cls = np.concatenate((cls, labels2.get("cls", cls)[[j]]), axis=0)# 将 选择的边界框 及 其对应的标注实例添 加到目标标注实例 instances 中。# instances2[[j]] :选择第 j 个标注实例。# Instances.concatenate(..., axis=0) :将新标注实例拼接到 instances 中。instances = Instances.concatenate((instances, instances2[[j]]), axis=0)# 在空白图像 im_new 上绘制选择的分割掩码。# instances2.segments[[j]] :选择第 j 个分割掩码。# cv2.drawContours(..., cv2.FILLED) :绘制填充的轮廓。cv2.drawContours(im_new, instances2.segments[[j]].astype(np.int32), -1, (1, 1, 1), cv2.FILLED)# 这段代码的核心功能是从源标注 labels2 中选择性地复制标注实例(边界框和分割掩码),并将它们粘贴到目标图像 im 上。其主要步骤包括。创建空白图像:用于绘制分割掩码。提取和处理源标注:如果源标注为空,则复制目标标注并进行水平翻转。计算交并比:筛选出与目标标注重叠较小的边界框。排序和选择:根据交并比对边界框进行排序,并根据混合概率 p 选择部分边界框。复制标注:将选择的边界框及其类别标签和分割掩码添加到目标标注中。绘制分割掩码:在空白图像上绘制选择的分割掩码。通过这些步骤, CopyPaste 类能够有效地将一个图像的标注信息复制到另一个图像上,同时避免过多重叠,增加数据的多样性,提高模型对不同场景的鲁棒性。# 这段代码是 CopyPaste 类的 _transform 方法的一部分,用于将复制的标注信息(如分割掩码)粘贴到目标图像上,并更新目标图像的标注信息。# 从 labels2 中获取图像数据。# 如果 labels2 中存在 "img" 键,则使用其值作为 result 。# 如果不存在,则对目标图像 im 进行水平翻转( cv2.flip(im, 1) ),并将翻转后的图像作为 result 。# 这一步的作用是为粘贴的分割掩码提供一个背景图像,通常用于增强分割掩码的效果。result = labels2.get("img", cv2.flip(im, 1))  # augment segments# 将 im_new 转换为布尔类型, im_new 是一个空白图像,用于标记需要粘贴分割掩码的区域。 im_new 中的非零值表示需要替换的区域。 转换为布尔类型后, i 是一个掩码,表示哪些像素需要被替换。i = im_new.astype(bool)# 将 result 中的对应像素替换到目标图像 im 的指定区域。# im[i] :目标图像中需要替换的像素。# result[i] : result 中对应的像素。# 这一步实现了将分割掩码粘贴到目标图像上。im[i] = result[i]# 将更新后的目标图像 im 存回 labels1 字典中,键为 "img" 。labels1["img"] = im# 将更新后的类别标签 cls 存回 labels1 字典中,键为 "cls" 。labels1["cls"] = cls# 将更新后的标注实例 instances 存回 labels1 字典中,键为 "instances" 。 instances 包含了 目标图像 和 源图像的标注信息 。labels1["instances"] = instances# 返回更新后的 labels1 字典,其中包含了粘贴后的图像和更新后的标注信息。return labels1# 这段代码的核心功能是将复制的标注信息(如分割掩码)粘贴到目标图像上,并更新目标图像的标注信息。其主要步骤包括。获取背景图像:从 labels2 中获取图像或对目标图像进行水平翻转。创建掩码:将 im_new 转换为布尔掩码,标记需要粘贴的区域。粘贴分割掩码:将背景图像中的对应像素替换到目标图像的指定区域。更新标注信息:将更新后的图像和标注信息存回 labels1 字典中。通过这些步骤, CopyPaste 类能够有效地将一个图像的标注信息复制到另一个图像上,同时保持标注信息的一致性和准确性。这种增强方法广泛应用于目标检测和分割任务中,能够增加数据的多样性,提高模型的鲁棒性。# _transform 方法的核心功能是将一个图像的标注信息选择性地复制到另一个图像上,同时确保这些标注不会与目标图像中的现有标注发生过多重叠。其主要步骤包括。提取目标图像的标注信息:包括图像、类别标签和标注实例。选择源图像的标注信息:如果源图像没有标注,则复制目标图像的标注并进行水平翻转。计算交并比:选择交并比小于 0.3 的标注,避免过多重叠。复制标注:根据混合概率 p ,选择部分标注进行复制,并将其添加到目标图像中。更新目标图像:将复制的分割掩码绘制到目标图像上,并更新标注信息。通过这种方法, CopyPaste 类能够有效地增加数据的多样性,提高模型对不同场景的鲁棒性。
# CopyPaste 类是一种用于目标检测和分割任务的数据增强工具,它通过复制和粘贴其他图像的标注信息(如边界框、分割掩码等)到目标图像上,实现混合增强操作。该类支持两种模式: flip (翻转)和 mixup (混合),并可以根据指定的概率 p 选择性地执行增强。在 mixup 模式下, CopyPaste 会随机选择其他图像的标注信息,并确保这些标注与目标图像中的现有标注不会发生过多重叠。通过这种方式, CopyPaste 能够增加训练数据的多样性,提高模型对不同场景的鲁棒性,尤其适用于需要处理复杂背景和多样化目标的视觉任务。

12.class Albumentations: 

# 这段代码定义了一个名为 Albumentations 的类,用于对图像及其标注信息(如边界框)进行增强处理。它基于 Albumentations 库实现了一系列图像增强操作,并支持对边界框的同步变换。
# 定义了一个名为 Albumentations 的类,用于实现图像增强操作。
class Albumentations:# Albumentations 用于图像增强的转换。# 此类使用 Albumentations 库应用各种图像转换。它包括模糊、中值模糊、转换为灰度、对比度限制自适应直方图均衡 (CLAHE)、亮度和对比度的随机变化、RandomGamma 以及通过压缩降低图像质量等操作。# 方法:# __call__:将 Albumentations 转换应用于输入标签。# 注意事项:# - 必须安装 Albumentations 包才能使用此类。# - 如果未安装包或初始化期间发生错误,转换将设置为 None。# - 空间变换的处理方式不同,需要对边界框进行特殊处理。"""Albumentations transformations for image augmentation.This class applies various image transformations using the Albumentations library. It includes operations such asBlur, Median Blur, conversion to grayscale, Contrast Limited Adaptive Histogram Equalization (CLAHE), random changesin brightness and contrast, RandomGamma, and image quality reduction through compression.Attributes:p (float): Probability of applying the transformations.transform (albumentations.Compose): Composed Albumentations transforms.contains_spatial (bool): Indicates if the transforms include spatial operations.Methods:__call__: Applies the Albumentations transformations to the input labels.Examples:>>> transform = Albumentations(p=0.5)>>> augmented_labels = transform(labels)Notes:- The Albumentations package must be installed to use this class.- If the package is not installed or an error occurs during initialization, the transform will be set to None.- Spatial transforms are handled differently and require special processing for bounding boxes."""# 这段代码定义了 Albumentations 类的初始化方法 __init__ ,用于设置图像增强操作的参数,并根据 Albumentations 库的功能定义增强流程。# 定义了 Albumentations 类的初始化方法,接收一个参数。# 1.p :增强操作的概率,默认为 1.0(即总是执行增强)。def __init__(self, p=1.0):# 为 YOLO bbox 格式的参数初始化 Albumentations 变换对象。# 此类使用 Albumentations 库应用各种图像增强,包括模糊、中值模糊、转换为灰度、对比度限制自适应直方图均衡、亮度和对比度的随机变化、RandomGamma 以及通过压缩降低图像质量。# 引发:# ImportError:如果未安装 Albumentations 包。# Exception:对于初始化期间的任何其他错误。# 注意:# - 需要 Albumentations 版本 1.0.3 或更高版本。# - 空间变换的处理方式不同,以确保 bbox 兼容性。# - 默认情况下,某些变换的应用概率非常低(0.01)。"""Initialize the Albumentations transform object for YOLO bbox formatted parameters.This class applies various image augmentations using the Albumentations library, including Blur, Median Blur,conversion to grayscale, Contrast Limited Adaptive Histogram Equalization, random changes of brightness andcontrast, RandomGamma, and image quality reduction through compression.Args:p (float): Probability of applying the augmentations. Must be between 0 and 1.Attributes:p (float): Probability of applying the augmentations.transform (albumentations.Compose): Composed Albumentations transforms.contains_spatial (bool): Indicates if the transforms include spatial transformations.Raises:ImportError: If the Albumentations package is not installed.Exception: For any other errors during initialization.Examples:>>> transform = Albumentations(p=0.5)>>> augmented = transform(image=image, bboxes=bboxes, class_labels=classes)>>> augmented_image = augmented["image"]>>> augmented_bboxes = augmented["bboxes"]Notes:- Requires Albumentations version 1.0.3 or higher.- Spatial transforms are handled differently to ensure bbox compatibility.- Some transforms are applied with very low probability (0.01) by default."""# 将 增强概率 p 保存为类的属性。self.p = p# 初始化 self.transform 为 None ,表示尚未定义具体的增强操作。self.transform = None# 定义一个前缀字符串 prefix ,用于日志输出,显示与 albumentations 相关的信息。prefix = colorstr("albumentations: ")# 尝试导入 Albumentations 库。try:import albumentations as A# 并检查其版本是否满足最低要求(1.0.3)。如果版本不符合要求,将抛出异常。check_version(A.__version__, "1.0.3", hard=True)  # version requirement# List of possible spatial transforms# 定义了一个集合 spatial_transforms , 包含所有可能的空间变换操作名称 。这些操作会影响图像的几何结构(如平移、旋转、缩放等)。spatial_transforms = {"Affine","BBoxSafeRandomCrop","CenterCrop","CoarseDropout","Crop","CropAndPad","CropNonEmptyMaskIfExists","D4","ElasticTransform","Flip","GridDistortion","GridDropout","HorizontalFlip","Lambda","LongestMaxSize","MaskDropout","MixUp","Morphological","NoOp","OpticalDistortion","PadIfNeeded","Perspective","PiecewiseAffine","PixelDropout","RandomCrop","RandomCropFromBorders","RandomGridShuffle","RandomResizedCrop","RandomRotate90","RandomScale","RandomSizedBBoxSafeCrop","RandomSizedCrop","Resize","Rotate","SafeRotate","ShiftScaleRotate","SmallestMaxSize","Transpose","VerticalFlip","XYMasking",}  # from https://albumentations.ai/docs/getting_started/transforms_and_targets/#spatial-level-transforms# Transforms# 定义了一个 增强操作列表 T ,包含以下几种增强操作。T = [# 以 1% 的概率应用模糊效果。A.Blur(p=0.01),# 以 1% 的概率应用中值模糊。A.MedianBlur(p=0.01),# 以 1% 的概率将图像转换为灰度图。A.ToGray(p=0.01),# 以 1% 的概率应用对比度受限的自适应直方图均衡化。A.CLAHE(p=0.01),# 随机调整亮度和对比度(当前未启用)。A.RandomBrightnessContrast(p=0.0),# 随机调整伽马校正(当前未启用)。A.RandomGamma(p=0.0),# 随机调整图像压缩质量(当前未启用)。A.ImageCompression(quality_range=(75, 100), p=0.0),]# Compose transforms# 检查定义的增强操作中 是否包含空间变换 。 遍历 T 中的每个增强操作,检查其类名是否在 spatial_transforms 中。 如果包含空间变换,则 self.contains_spatial 为 True 。self.contains_spatial = any(transform.__class__.__name__ in spatial_transforms for transform in T)# 根据是否包含空间变换,定义 增强操作的组合 。self.transform = (# A.Compose(transforms, bbox_params=None, keypoint_params=None, mask_params=None, p=1.0)# albumentations.Compose 是 Albumentations 库中的一个函数,它用于组合多个图像变换(transforms)成一个复合变换(composition)。这个复合变换可以同时应用于图像和与之相关的标注(如边界框、关键点、掩码等)。# 参数说明 :# transforms : 一个包含多个变换的列表。每个变换可以是一个单独的变换类实例,也可以是一个变换序列。# bbox_params : 一个 BboxParams 对象,用于定义如何应用变换到边界框。如果为 None ,则不会对边界框应用任何变换。# keypoint_params : 一个 KeypointParams 对象,用于定义如何应用变换到关键点。如果为 None ,则不会对关键点应用任何变换。# mask_params : 一个 MaskParams 对象,用于定义如何应用变换到掩码。如果为 None ,则不会对掩码应用任何变换。# p : 一个浮点数,表示应用整个复合变换的概率。值范围从 0 到 1,其中 1 表示总是应用变换。# 返回值 :# 返回一个复合变换对象,可以被用于图像增强流程中。# A.BboxParams(format=None, label_fields=None, min_area=0.0, min_visibility=0.0, check_each_transform=True)# A.BboxParams() 是 Albumentations 库中用于定义边界框参数的类,一般用在 A.Compose() 实例的初始化参数中。# 参数说明 :# format : 边界框的格式。可以是 "coco" 、 "pascal_voc" 、 "albumentations" 或 "yolo" 。# "coco" 格式 : [x_min, y_min, width, height] 。# "pascal_voc" 格式 : [x_min, y_min, x_max, y_max] 。# "albumentations" 格式 : 与 "pascal_voc" 相同,但归一化到 [0, 1] 范围内。# "yolo" 格式 : [x, y, width, height] ,其中 x, y 指归一化的边界框中心, width, height 指归一化的边界框宽高。# label_fields : 与边界框相关联的标签字段列表。如果边界框和类别标签写在一起,则不需要此参数。# min_area : 边界框的最小面积(以像素为单位)。所有可见面积小于这个值的边界框将被移除。默认为 0.0 。# min_visibility : 边界框保持在列表中的最小可见面积比。默认为 0.0 。# check_each_transform : 如果为 True ,在每个双重变换后都会检查边界框是否满足限制。默认为 True 。# 使用 BboxParams 时,你需要根据你的数据集和边界框的格式来设置这些参数。这确保了在应用图像增强变换时,边界框能够被正确地处理和转换。# 如果包含空间变换,使用 A.Compose 并指定边界框参数( bbox_params ),支持 YOLO 格式的边界框( [x_center, y_center, width, height] )。A.Compose(T, bbox_params=A.BboxParams(format="yolo", label_fields=["class_labels"]))if self.contains_spatial# 如果不包含空间变换,仅使用 A.Compose 组合增强操作。else A.Compose(T))# 如果 Albumentations 版本 >= 1.4.21,且增强操作支持设置随机种子,则使用 PyTorch 的初始随机种子来确保增强操作的确定性。if hasattr(self.transform, "set_random_seed"):# Required for deterministic transforms in albumentations>=1.4.21self.transform.set_random_seed(torch.initial_seed())# 记录增强操作的信息。 使用 LOGGER.info 输出增强操作的描述。 过滤掉未启用的增强操作( x.p == 0 )。 移除日志中不必要的字符串(如 always_apply=False, )。LOGGER.info(prefix + ", ".join(f"{x}".replace("always_apply=False, ", "") for x in T if x.p))# 捕获可能的异常。# 如果 Albumentations 库未安装,则跳过增强操作。except ImportError:  # package not installed, skippass# 如果发生其他异常,记录异常信息。except Exception as e:LOGGER.info(f"{prefix}{e}")# Albumentations 类的初始化方法 __init__ 的主要功能是设置图像增强操作的参数,并根据 Albumentations 库的功能定义增强流程。其核心步骤包括。导入 Albumentations 库:检查库是否安装,并验证版本是否满足最低要求。定义空间变换操作:列出所有可能的空间变换操作名称。定义增强操作列表:定义一系列增强操作,包括模糊、颜色调整、对比度调整等。组合增强操作:根据是否包含空间变换,使用 A.Compose 组合增强操作。设置随机种子:如果增强操作支持设置随机种子,则使用 PyTorch 的初始随机种子来确保增强操作的确定性。记录增强操作信息:通过日志记录增强操作的详细信息,便于调试和验证。通过这些步骤, Albumentations 类能够灵活地定义和执行图像增强操作,支持对边界框的同步变换,确保标注信息与图像内容一致。这种设计使得该类能够无缝集成到现有的数据增强流程中,为计算机视觉任务提供强大的增强能力。# 这段代码定义了 Albumentations 类的 __call__ 方法,用于对输入的图像及其标注信息(如边界框)执行增强操作。# 定义了 Albumentations 类的 __call__ 方法,接收一个参数。# 1.labels :包含图像及其标注信息的字典,例如图像、类别标签、边界框等。def __call__(self, labels):# 将 Albumentations 转换应用于输入标签。# 此方法使用 Albumentations 库应用一系列图像增强。它可以对输入图像及其对应的标签执行空间和非空间转换。# 注意:# - 该方法以概率 self.p 应用变换。# - 空间变换更新边界框,而非空间变换仅修改图像。# - 需要安装 Albumentations 库。"""Applies Albumentations transformations to input labels.This method applies a series of image augmentations using the Albumentations library. It can perform bothspatial and non-spatial transformations on the input image and its corresponding labels.Args:labels (Dict): A dictionary containing image data and annotations. Expected keys are:- 'img': numpy.ndarray representing the image- 'cls': numpy.ndarray of class labels- 'instances': object containing bounding boxes and other instance informationReturns:(Dict): The input dictionary with augmented image and updated annotations.Examples:>>> transform = Albumentations(p=0.5)>>> labels = {...     "img": np.random.rand(640, 640, 3),...     "cls": np.array([0, 1]),...     "instances": Instances(bboxes=np.array([[0, 0, 1, 1], [0.5, 0.5, 0.8, 0.8]])),... }>>> augmented = transform(labels)>>> assert augmented["img"].shape == (640, 640, 3)Notes:- The method applies transformations with probability self.p.- Spatial transforms update bounding boxes, while non-spatial transforms only modify the image.- Requires the Albumentations library to be installed."""# 检查是否需要执行增强操作。# 如果未定义增强操作( self.transform is None ),或者随机概率大于指定的概率 p ,则直接返回原始标注信息。if self.transform is None or random.random() > self.p:return labels# 这段代码是 Albumentations 类的 __call__ 方法中处理空间变换的部分,用于对图像及其标注信息(如边界框)执行增强操作。# 检查是否包含空间变换操作。如果 self.contains_spatial 为 True ,则表示增强操作中包含空间变换(如平移、旋转、缩放等),需要对边界框进行同步处理。if self.contains_spatial:# 从 labels 中提取 类别标签 cls ,这是一个数组,表示 每个边界框的类别 。cls = labels["cls"]# 检查是否有边界框( cls 的长度是否大于 0)。如果没有边界框,则跳过后续处理。if len(cls):# 从 labels 中 提取图像 im ,这是一个 NumPy 数组,表示 输入图像 。im = labels["img"]# 将边界框格式转换为 [x_center, y_center, width, height] (YOLO 格式)。这是 Albumentations 支持的边界框格式。labels["instances"].convert_bbox("xywh")# 将边界框坐标归一化到 [0, 1] 范围。# im.shape[:2][::-1] :提取图像的 宽度 和 高度 (W, H) 。# normalize 方法将边界框坐标从像素值归一化到 [0, 1] 范围。labels["instances"].normalize(*im.shape[:2][::-1])# 提取归一化后的边界框坐标 bboxes 。bboxes = labels["instances"].bboxes# TODO: add supports of segments and keypoints    这是一个待办事项(TODO),表示后续需要添加对分割掩码(segments)和关键点(keypoints)的支持。# 调用 Albumentations 的增强操作。# self.transform 是一个组合的增强操作,支持对图像和边界框进行同步处理。# image=im :输入图像。# bboxes=bboxes :输入的边界框。# class_labels=cls :输入的类别标签。# 返回值 new 是一个字典,包含 增强后的图像和边界框信息 。new = self.transform(image=im, bboxes=bboxes, class_labels=cls)  # transformed# 检查增强后的图像中是否包含边界框。 如果 new["class_labels"] 的长度大于 0,则表示增强后的图像中包含边界框,需要更新标注信息。 如果没有边界框,则跳过后续更新。if len(new["class_labels"]) > 0:  # skip update if no bbox in new im# 更新图像 labels["img"] 为增强后的图像 new["image"] 。labels["img"] = new["image"]# 更新类别标签 labels["cls"] 为增强后的类别标签 new["class_labels"] 。labels["cls"] = np.array(new["class_labels"])# 更新边界框 bboxes 为增强后的边界框 new["bboxes"] ,并确保其数据类型为 float32 。bboxes = np.array(new["bboxes"], dtype=np.float32)# 更新标注实例 labels["instances"] 中的边界框信息为增强后的边界框 bboxes 。labels["instances"].update(bboxes=bboxes)# 这段代码的核心功能是处理包含空间变换的增强操作。其主要步骤包括。检查是否包含空间变换:如果包含空间变换,则对图像和边界框进行同步增强。提取图像和标注信息:从 labels 中提取图像、类别标签和边界框。转换和归一化边界框:将边界框格式转换为 YOLO 格式,并归一化到 [0, 1] 范围。执行增强操作:调用 Albumentations 的增强操作,对图像和边界框进行同步处理。更新标注信息:在增强操作后,更新图像、类别标签和边界框信息。通过这些步骤, Albumentations 类能够确保在执行空间变换时,边界框与图像内容保持一致,从而提高数据增强的准确性和有效性。这种增强方法广泛应用于目标检测和分割任务中,能够增加数据的多样性,提高模型的鲁棒性。# 如果不包含空间变换,仅对图像进行增强操作。else:labels["img"] = self.transform(image=labels["img"])["image"]  # transformed# 返回 更新后的标注信息 labels 。return labels# Albumentations 类的 __call__ 方法的核心功能是根据定义的增强操作和概率 p ,对输入的图像及其标注信息执行增强处理。其主要步骤包括。检查是否需要增强:根据随机概率和是否定义了增强操作来决定是否执行增强。处理空间变换:如果增强操作包含空间变换,对图像和边界框进行同步增强。将边界框格式转换为 YOLO 格式,并归一化到 [0, 1] 范围。调用 Albumentations 的增强操作,更新图像和边界框。处理非空间变换:如果不包含空间变换,仅对图像进行增强操作。更新标注信息:在增强操作后,更新图像、类别标签和边界框信息。通过这些步骤, Albumentations 类能够灵活地对图像及其标注信息进行增强处理,支持多种增强操作,确保标注信息与图像内容一致。这种增强方法广泛应用于目标检测和分割任务中,能够增加数据的多样性,提高模型对不同场景的鲁棒性。
# Albumentations 类基于 Albumentations 库实现了一系列图像增强操作,并支持对边界框的同步变换。其主要功能包括。初始化增强操作:根据指定的概率 p 和增强操作列表 T ,定义增强操作的组合。支持空间变换:如果增强操作包含空间变换(如平移、旋转、缩放等),则支持对边界框的同步变换。执行增强操作:根据随机概率决定是否对输入的图像和标注信息进行增强。更新标注信息:在增强操作后,更新图像、类别标签和边界框信息。通过这些步骤, Albumentations 类能够灵活地对图像及其标注信息(如边界框)进行增强处理。它支持多种增强操作,包括模糊、颜色调整、对比度调整等,并且能够同步更新边界框,确保标注信息与图像内容一致。这种增强方法广泛应用于目标检测和分割任务中,能够增加数据的多样性,提高模型对不同场景的鲁棒性。
# 此外, Albumentations 类还具备以下特点。概率控制:通过 p 参数控制增强操作的执行概率,使得增强操作可以随机化。空间变换支持:如果增强操作包含空间变换(如平移、旋转、缩放等),它会同步更新边界框,确保标注信息的准确性。日志记录:通过日志记录增强操作的详细信息,便于调试和验证。异常处理:在 Albumentations 库未安装或版本不兼容时,能够优雅地跳过增强操作,避免程序崩溃。这种设计使得 Albumentations 类能够无缝集成到现有的数据增强流程中,为计算机视觉任务提供强大的增强能力。

13.class Format: 

# 这段代码定义了一个名为 Format 的类,用于对图像及其标注信息(如边界框、分割掩码、关键点等)进行格式化处理。该类的主要功能是将标注信息转换为模型训练所需的格式,并支持多种选项,如归一化、掩码生成、关键点处理等。
# 定义了一个名为 Format 的类,用于对图像及其标注信息进行格式化处理。
class Format:# 用于为对象检测、实例分割和姿势估计任务格式化图像注释的类。# 此类标准化 PyTorch DataLoader 中的 `collat​​e_fn` 使用的图像和实例注释。# 方法:# __call__:使用图像、类、边界框以及可选的掩码格式化标签字典和关键点。# _format_img:将图像从 Numpy 数组转换为 PyTorch 张量。# _format_segments:将多边形点转换为位图掩膜。"""A class for formatting image annotations for object detection, instance segmentation, and pose estimation tasks.This class standardizes image and instance annotations to be used by the `collate_fn` in PyTorch DataLoader.Attributes:bbox_format (str): Format for bounding boxes. Options are 'xywh' or 'xyxy'.normalize (bool): Whether to normalize bounding boxes.return_mask (bool): Whether to return instance masks for segmentation.return_keypoint (bool): Whether to return keypoints for pose estimation.return_obb (bool): Whether to return oriented bounding boxes.mask_ratio (int): Downsample ratio for masks.mask_overlap (bool): Whether to overlap masks.batch_idx (bool): Whether to keep batch indexes.bgr (float): The probability to return BGR images.Methods:__call__: Formats labels dictionary with image, classes, bounding boxes, and optionally masks and keypoints._format_img: Converts image from Numpy array to PyTorch tensor._format_segments: Converts polygon points to bitmap masks.Examples:>>> formatter = Format(bbox_format="xywh", normalize=True, return_mask=True)>>> formatted_labels = formatter(labels)>>> img = formatted_labels["img"]>>> bboxes = formatted_labels["bboxes"]>>> masks = formatted_labels["masks"]"""# 这段代码定义了 Format 类的初始化方法 __init__ ,用于设置标注信息格式化处理的参数。# 定义了 Format 类的初始化方法,接收多个参数 :# 1.bbox_format :边界框的格式,默认为 "xywh" (表示 [x_center, y_center, width, height] )。# 2.normalize :是否对边界框和关键点进行归一化,默认为 True 。# 3.return_mask :是否返回分割掩码,默认为 False 。如果仅训练目标检测任务,通常设置为 False 。# 4.return_keypoint :是否返回关键点,默认为 False 。# 5.return_obb :是否返回定向边界框(Oriented Bounding Box),默认为 False 。# 6.mask_ratio :掩码的下采样比例,默认为 4 。# 7.mask_overlap :是否处理掩码重叠,默认为 True 。# 8.batch_idx :是否保留批次索引,默认为 True 。# 9.bgr :是否随机调整图像的 BGR 通道顺序,默认为 0.0 (不调整)。def __init__(self,bbox_format="xywh",normalize=True,return_mask=False,return_keypoint=False,return_obb=False,mask_ratio=4,mask_overlap=True,batch_idx=True,bgr=0.0,):# 使用给定的参数初始化 Format 类,用于图像和实例注释格式化。# 此类标准化图像和实例注释,用于对象检测、实例分割和姿势估计任务,为 PyTorch DataLoader 的 `collat​​e_fn` 做准备。"""Initializes the Format class with given parameters for image and instance annotation formatting.This class standardizes image and instance annotations for object detection, instance segmentation, and poseestimation tasks, preparing them for use in PyTorch DataLoader's `collate_fn`.Args:bbox_format (str): Format for bounding boxes. Options are 'xywh', 'xyxy', etc.normalize (bool): Whether to normalize bounding boxes to [0,1].return_mask (bool): If True, returns instance masks for segmentation tasks.return_keypoint (bool): If True, returns keypoints for pose estimation tasks.return_obb (bool): If True, returns oriented bounding boxes.mask_ratio (int): Downsample ratio for masks.mask_overlap (bool): If True, allows mask overlap.batch_idx (bool): If True, keeps batch indexes.bgr (float): Probability of returning BGR images instead of RGB.Attributes:bbox_format (str): Format for bounding boxes.normalize (bool): Whether bounding boxes are normalized.return_mask (bool): Whether to return instance masks.return_keypoint (bool): Whether to return keypoints.return_obb (bool): Whether to return oriented bounding boxes.mask_ratio (int): Downsample ratio for masks.mask_overlap (bool): Whether masks can overlap.batch_idx (bool): Whether to keep batch indexes.bgr (float): The probability to return BGR images.Examples:>>> format = Format(bbox_format="xyxy", return_mask=True, return_keypoint=False)>>> print(format.bbox_format)xyxy"""# 将初始化参数保存为类的属性,供后续方法使用。self.bbox_format = bbox_formatself.normalize = normalizeself.return_mask = return_mask  # set False when training detection onlyself.return_keypoint = return_keypointself.return_obb = return_obbself.mask_ratio = mask_ratioself.mask_overlap = mask_overlapself.batch_idx = batch_idx  # keep the batch indexesself.bgr = bgr# Format 类的初始化方法 __init__ 的主要功能是设置标注信息格式化处理的参数。这些参数决定了如何处理输入的标注信息(如边界框、分割掩码、关键点等),并将其转换为模型训练所需的格式。具体功能包括。边界框格式:指定边界框的格式(如 "xywh" 或 "xyxy" )。归一化:是否对边界框和关键点坐标进行归一化处理。返回分割掩码:是否生成并返回分割掩码。返回关键点:是否返回关键点信息。返回定向边界框:是否返回定向边界框(OBB)。掩码下采样比例:设置掩码的下采样比例。处理掩码重叠:是否处理掩码之间的重叠。保留批次索引:是否为每个标注实例保留批次索引。随机调整 BGR 通道顺序:是否随机调整图像的 BGR 通道顺序。通过这些参数, Format 类能够灵活地满足不同任务的需求,例如目标检测、分割和关键点估计等。# 这段代码定义了 Format 类的 __call__ 方法,用于对输入的图像及其标注信息(如边界框、分割掩码、关键点等)进行格式化处理。# 定义了 Format 类的 __call__ 方法,接收一个参数。# 1.labels :包含图像及其标注信息的字典。def __call__(self, labels):# 格式化图像注释以用于对象检测、实例分割和姿势估计任务。# 此方法标准化 PyTorch DataLoader 中的 `collat​​e_fn` 将使用的图像和实例注释。 它处理输入标签字典,将注释转换为指定格式并在需要时应用规范化。"""Formats image annotations for object detection, instance segmentation, and pose estimation tasks.This method standardizes the image and instance annotations to be used by the `collate_fn` in PyTorchDataLoader. It processes the input labels dictionary, converting annotations to the specified format andapplying normalization if required.Args:labels (Dict): A dictionary containing image and annotation data with the following keys:- 'img': The input image as a numpy array.- 'cls': Class labels for instances.- 'instances': An Instances object containing bounding boxes, segments, and keypoints.Returns:(Dict): A dictionary with formatted data, including:- 'img': Formatted image tensor.- 'cls': Class label's tensor.- 'bboxes': Bounding boxes tensor in the specified format.- 'masks': Instance masks tensor (if return_mask is True).- 'keypoints': Keypoints tensor (if return_keypoint is True).- 'batch_idx': Batch index tensor (if batch_idx is True).Examples:>>> formatter = Format(bbox_format="xywh", normalize=True, return_mask=True)>>> labels = {"img": np.random.rand(640, 640, 3), "cls": np.array([0, 1]), "instances": Instances(...)}>>> formatted_labels = formatter(labels)>>> print(formatted_labels.keys())"""# 从 labels 中提取图像、类别标签和标注实例。# 输入图像。img = labels.pop("img")# 图像的 高度 和 宽度 。h, w = img.shape[:2]# 类别标签。cls = labels.pop("cls")# 标注实例,包含边界框、分割掩码和关键点等信息。instances = labels.pop("instances")# 将边界框格式转换为目标格式,并将其反归一化到图像的实际尺寸。# 将边界框格式转换为指定的格式。instances.convert_bbox(format=self.bbox_format)# 将边界框坐标从归一化形式转换为 像素坐标 。instances.denormalize(w, h)# 标注实例的数量。nl = len(instances)# 这段代码是 Format 类的 __call__ 方法的核心部分,用于根据配置选项对图像及其标注信息进行格式化处理。# 检查 是否需要返回分割掩码 。如果 self.return_mask 为 True ,则执行以下操作。if self.return_mask:# 如果存在标注实例( nl > 0 )。if nl:# 调用 _format_segments 方法生成分割掩码 masks 。masks, instances, cls = self._format_segments(instances, cls, w, h)# 并将其转换为 PyTorch 张量。masks = torch.from_numpy(masks)# 如果没有标注实例( nl == 0 )。else:# 生成一个全零的张量作为掩码。 掩码的维度 取决于 是否处理掩码重叠 ( self.mask_overlap )。# 如果处理掩码重叠,生成一个形状为 (1, H // self.mask_ratio, W // self.mask_ratio) 的张量。# 如果不处理掩码重叠,生成一个形状为 (nl, H // self.mask_ratio, W // self.mask_ratio) 的张量。masks = torch.zeros(1 if self.mask_overlap else nl, img.shape[0] // self.mask_ratio, img.shape[1] // self.mask_ratio)# 将 生成的掩码 存储到 labels["masks"] 中。labels["masks"] = masks# 调用 _format_img 方法对图像进行格式化处理(如通道顺序调整),并将结果存储到 labels["img"] 中。labels["img"] = self._format_img(img)# 将 类别标签 cls 转换为 PyTorch 张量。如果没有标注实例( nl == 0 ),则生成一个全零的张量。labels["cls"] = torch.from_numpy(cls) if nl else torch.zeros(nl)# 将 边界框 instances.bboxes 转换为 PyTorch 张量。如果没有标注实例( nl == 0 ),则生成一个形状为 (nl, 4) 的全零张量。labels["bboxes"] = torch.from_numpy(instances.bboxes) if nl else torch.zeros((nl, 4))# 如果需要返回 关键点 ( self.return_keypoint == True )。if self.return_keypoint:# 将关键点 instances.keypoints 转换为 PyTorch 张量。labels["keypoints"] = torch.from_numpy(instances.keypoints)# 如果启用归一化( self.normalize == True )。if self.normalize:# 将关键点的 x 和 y 坐标分别除以图像的宽度和高度,归一化到 [0, 1] 范围。labels["keypoints"][..., 0] /= wlabels["keypoints"][..., 1] /= h# 如果需要返回 定向边界框 (Oriented Bounding Box, OBB)。if self.return_obb:# 将分割掩码 instances.segments 转换为定向边界框格式( xywhr )。 如果没有分割掩码( len(instances.segments) == 0 ),则生成一个形状为 (0, 5) 的全零张量。labels["bboxes"] = (xyxyxyxy2xywhr(torch.from_numpy(instances.segments)) if len(instances.segments) else torch.zeros((0, 5)))# 这段代码的核心功能是根据配置选项对图像及其标注信息进行格式化处理,以满足模型训练的要求。其主要步骤包括。处理分割掩码:如果需要返回分割掩码,根据是否存在标注实例生成相应的掩码张量。格式化图像:对图像进行格式化处理,如通道顺序调整。转换标注信息:将类别标签和边界框转换为 PyTorch 张量。如果需要返回关键点,将其转换为张量并进行归一化处理。如果需要返回定向边界框,将其转换为定向边界框格式。归一化:如果启用归一化,将关键点和边界框坐标归一化到 [0, 1] 范围。通过这些步骤, Format 类能够灵活地处理多种标注信息,确保它们与图像内容一致,适用于目标检测、分割和关键点估计等任务中的数据预处理。# 这段代码是 Format 类的 __call__ 方法的一部分,用于对标注信息(特别是定向边界框 OBB)进行归一化处理,并为后续的批次处理( collate_fn )准备数据。# NOTE: need to normalize obb in xywhr format for width-height consistency    这是一条注释,说明需要将定向边界框(OBB)归一化为 xywhr 格式,以确保宽度和高度的一致性。 xywhr 格式表示为 [x_center, y_center, width, height, rotation] 。# 如果启用了归一化( self.normalize == True ),则对边界框的宽度和高度进行归一化处理。if self.normalize:# 将边界框的 x 坐标和宽度归一化到 [0, 1] 范围,除以图像的宽度 w 。labels["bboxes"][:, [0, 2]] /= w# 将边界框的 y 坐标和高度归一化到 [0, 1] 范围,除以图像的高度 h 。labels["bboxes"][:, [1, 3]] /= h# Then we can use collate_fn    这是一条注释,说明在归一化处理后,可以使用 collate_fn 函数将多个样本组合成一个批次。 collate_fn 是 PyTorch 数据加载器( DataLoader )中用于处理批次数据的函数。# 如果需要保留批次索引( self.batch_idx == True ),则为每个标注实例生成一个批次索引。if self.batch_idx:# 生成一个形状为 (nl,) 的全零张量,表示所有标注实例属于同一个批次。labels["batch_idx"] = torch.zeros(nl)# 返回更新后的 labels 字典,其中包含了格式化和归一化后的标注信息。return labels# 这段代码的核心功能是对标注信息(特别是定向边界框 OBB)进行归一化处理,并为后续的批次处理准备数据。其主要步骤包括。归一化边界框:如果启用了归一化,将边界框的宽度和高度归一化到 [0, 1] 范围。生成批次索引:如果需要保留批次索引,为每个标注实例生成一个批次索引。通过这些步骤, Format 类能够确保标注信息与图像内容一致,并满足模型训练的要求。这种处理方式广泛应用于目标检测和分割任务中的数据预处理,能够提高数据的一致性和模型的训练效率。# Format 类的 __call__ 方法的主要功能是将图像及其标注信息(如边界框、分割掩码、关键点等)转换为模型训练所需的格式。其核心步骤包括。提取图像和标注信息:从 labels 中提取图像、类别标签和标注实例。转换边界框格式:将边界框格式转换为目标格式,并将其反归一化到图像的实际尺寸。生成分割掩码:如果需要返回分割掩码,则调用 _format_segments 方法生成掩码。格式化图像:对图像进行格式化处理(如通道顺序调整)。转换为 PyTorch 张量:将类别标签、边界框和关键点转换为 PyTorch 张量。归一化:如果启用归一化,则将边界框和关键点坐标归一化到 [0, 1] 范围。保留批次索引:如果需要保留批次索引,则生成一个全零的张量。通过这些步骤, Format 类能够灵活地满足不同任务的需求,确保标注信息与图像内容一致,适用于目标检测、分割和关键点估计等任务中的数据预处理。# 这段代码定义了 Format 类中的 _format_img 方法,用于对输入图像进行格式化处理,以满足模型训练的要求。# 定义了 _format_img 方法,接收一个参数。# 1.img :输入图像,通常是一个 NumPy 数组。def _format_img(self, img):# 将 YOLO 的图像从 Numpy 数组格式化为 PyTorch 张量。# 此函数执行以下操作:# 1. 确保图像有 3 个维度(如果需要,添加一个通道维度)。# 2. 将图像从 HWC 转置为 CHW 格式。# 3. 可选地将颜色通道从 RGB 翻转为 BGR。# 4. 将图像转换为连续数组。# 5. 将 Numpy 数组转换为 PyTorch 张量。"""Formats an image for YOLO from a Numpy array to a PyTorch tensor.This function performs the following operations:1. Ensures the image has 3 dimensions (adds a channel dimension if needed).2. Transposes the image from HWC to CHW format.3. Optionally flips the color channels from RGB to BGR.4. Converts the image to a contiguous array.5. Converts the Numpy array to a PyTorch tensor.Args:img (np.ndarray): Input image as a Numpy array with shape (H, W, C) or (H, W).Returns:(torch.Tensor): Formatted image as a PyTorch tensor with shape (C, H, W).Examples:>>> import numpy as np>>> img = np.random.rand(100, 100, 3)>>> formatted_img = self._format_img(img)>>> print(formatted_img.shape)torch.Size([3, 100, 100])"""# 检查图像的维度。# 如果图像的维度小于 3(即图像是灰度图,形状为 (H, W) ),则通过 np.expand_dims 在最后一个维度上扩展为三通道,形状变为 (H, W, 1) 。if len(img.shape) < 3:img = np.expand_dims(img, -1)# 将图像的通道顺序从 (H, W, C) 转换为 (C, H, W) ,以满足 PyTorch 的输入要求。img = img.transpose(2, 0, 1)# 根据 self.bgr 参数决定是否随机调整图像的通道顺序。# random.uniform(0, 1) > self.bgr :生成一个随机数,如果大于 self.bgr ,则调整通道顺序。# img[::-1] :将图像的通道顺序从 RGB 转换为 BGR。# np.ascontiguousarray :确保数组在内存中是连续的,这对于后续的张量操作是必要的。img = np.ascontiguousarray(img[::-1] if random.uniform(0, 1) > self.bgr else img)# 将 NumPy 数组转换为 PyTorch 张量。img = torch.from_numpy(img)# 返回格式化后的图像张量。return img# _format_img 方法的主要功能是对输入图像进行格式化处理,以满足模型训练的要求。其核心步骤包括。检查图像维度:如果图像是灰度图,扩展为三通道。调整通道顺序:将图像的通道顺序从 (H, W, C) 转换为 (C, H, W) 。随机调整通道顺序:根据 self.bgr 参数,随机决定是否将通道顺序从 RGB 转换为 BGR。转换为 PyTorch 张量:将 NumPy 数组转换为 PyTorch 张量。通过这些步骤, _format_img 方法能够确保输入图像的格式与模型的要求一致,同时提供随机调整通道顺序的功能,以增加数据的多样性。这种格式化方法广泛应用于计算机视觉任务中,尤其是在使用 PyTorch 框架时。# 这段代码定义了 Format 类中的 _format_segments 方法,用于将标注实例中的多边形分割掩码( segments )转换为像素掩码( masks )。该方法还处理了掩码重叠的情况,并根据需要对掩码进行下采样。# 定义了 _format_segments 方法,接收以下参数 :# 1.instances :标注实例对象,包含边界框、分割掩码等信息。# 2.cls :类别标签数组。# 3.w :图像的宽度。# 4.h :图像的高度。def _format_segments(self, instances, cls, w, h):# 将多边形片段转换为位图掩膜。# 注意事项:# - 如果 self.mask_overlap 为 True,则掩膜会重叠并按面积排序。# - 如果 self.mask_overlap 为 False,则每个掩膜将单独表示。# - 根据 self.mask_ratio 对掩膜进行下采样。"""Converts polygon segments to bitmap masks.Args:instances (Instances): Object containing segment information.cls (numpy.ndarray): Class labels for each instance.w (int): Width of the image.h (int): Height of the image.Returns:masks (numpy.ndarray): Bitmap masks with shape (N, H, W) or (1, H, W) if mask_overlap is True.instances (Instances): Updated instances object with sorted segments if mask_overlap is True.cls (numpy.ndarray): Updated class labels, sorted if mask_overlap is True.Notes:- If self.mask_overlap is True, masks are overlapped and sorted by area.- If self.mask_overlap is False, each mask is represented separately.- Masks are downsampled according to self.mask_ratio."""# 从 instances 中提取分割掩码( segments ),这些通常是多边形的顶点坐标,表示每个实例的分割轮廓。segments = instances.segments# 这段代码是 _format_segments 方法的核心部分,用于处理分割掩码的生成和重叠问题。# 检查是否需要处理掩码重叠。如果 self.mask_overlap 为 True ,则表示需要处理掩码之间的重叠部分。if self.mask_overlap:# 调用 polygons2masks_overlap 函数,将多边形分割掩码( segments )转换为像素掩码( masks ),并处理重叠部分。# (h, w) :目标图像的高度和宽度。# segments :多边形分割掩码的顶点坐标。# downsample_ratio=self.mask_ratio :掩码的下采样比例。# masks :生成的像素掩码。# sorted_idx :排序后的索引,表示掩码的优先级(通常用于处理重叠部分)。# def polygons2masks_overlap(imgsz, segments, downsample_ratio=1): -> 用于处理多个多边形的重叠情况,并生成一个掩码图像,其中每个像素值表示该像素所属的多边形索引。如果多个多边形重叠,则像素值属于面积较大的多边形。返回最终的掩码图像 masks 和多边形的排序索引 index 。 -> return masks, indexmasks, sorted_idx = polygons2masks_overlap((h, w), segments, downsample_ratio=self.mask_ratio)# 将生成的掩码维度从 (H, W) 扩展为 (1, H, W) ,以满足 PyTorch 的输入要求。这一步是为了将掩码转换为一个批次的形式。masks = masks[None]  # (640, 640) -> (1, 640, 640)# 根据 排序后的索引 sorted_idx 重新排序 标注实例 instances 和 类别标签 cls ,以确保它们与掩码的顺序一致。instances = instances[sorted_idx]cls = cls[sorted_idx]# 如果不需要处理掩码重叠( self.mask_overlap=False )。else:# 则直接调用 polygons2masks 函数将多边形分割掩码转换为像素掩码。# (h, w) :目标图像的高度和宽度。# segments :多边形分割掩码的顶点坐标。# color=1 :指定掩码的颜色值为 1。# downsample_ratio=self.mask_ratio :掩码的下采样比例。# 返回值 :生成的像素掩码 masks 。# def polygons2masks(imgsz, polygons, color, downsample_ratio=1):# -> 用于将多个多边形坐标批量转换为掩码图像数组,并支持下采样操作。将所有生成的掩码图像组成一个 NumPy 数组并返回。最终返回的数组形状为 (num_polygons, height, width) ,其中 num_polygons 是多边形的数量, height 和 width 是掩码图像的尺寸。# -> return np.array([polygon2mask(imgsz, [x.reshape(-1)], color, downsample_ratio) for x in polygons])masks = polygons2masks((h, w), segments, color=1, downsample_ratio=self.mask_ratio)# 这段代码的核心功能是根据是否需要处理掩码重叠,选择合适的方法将多边形分割掩码转换为像素掩码。其主要步骤包括。检查是否需要处理掩码重叠:如果需要处理重叠部分,调用 polygons2masks_overlap 函数,并返回排序后的掩码和索引。如果不需要处理重叠部分,直接调用 polygons2masks 函数。调整掩码维度:将生成的掩码维度从 (H, W) 扩展为 (1, H, W) ,以满足 PyTorch 的输入要求。重新排序标注实例和类别标签:根据排序后的索引 sorted_idx ,确保标注实例和类别标签与掩码的顺序一致。通过这些步骤, _format_segments 方法能够灵活地处理分割掩码的生成和重叠问题,确保标注信息与图像内容一致,适用于目标检测和分割任务中的数据预处理。# 返回生成的掩码、更新后的标注实例和类别标签。return masks, instances, cls# _format_segments 方法的主要功能是将多边形分割掩码( segments )转换为像素掩码( masks ),并处理掩码重叠的情况。其核心步骤包括。提取分割掩码:从标注实例中提取多边形分割掩码。处理掩码重叠:如果 self.mask_overlap=True ,使用 polygons2masks_overlap 处理重叠部分,并返回排序后的掩码和索引。如果 self.mask_overlap=False ,直接使用 polygons2masks 转换分割掩码。调整掩码维度:将掩码的维度从 (H, W) 扩展为 (1, H, W) ,以满足 PyTorch 的输入要求。返回结果:返回生成的掩码、更新后的标注实例和类别标签。通过这些步骤, _format_segments 方法能够灵活地处理分割掩码的生成和重叠问题,确保标注信息与图像内容一致,适用于目标检测和分割任务中的数据预处理。
# Albumentations 类是一个用于图像增强的工具类,它基于 Albumentations 库实现了一系列图像增强操作。该类支持多种增强操作,如模糊、颜色调整、对比度调整等,并能够同步处理图像及其标注信息(如边界框)。它还支持空间变换操作(如平移、旋转、缩放),并可以对边界框进行同步更新。此外, Albumentations 类还提供了概率控制,允许用户指定增强操作的执行概率,从而实现随机化的增强效果。通过这些功能, Albumentations 类能够有效增加数据的多样性,提高模型对不同场景的鲁棒性,广泛适用于目标检测、分割和分类等计算机视觉任务。

14.class RandomLoadText: 

# 这段代码定义了一个名为 RandomLoadText 的类,用于随机加载和处理文本提示(prompts),并根据类别标签对文本进行采样和填充。该类主要用于生成用于对比学习或分类任务的文本数据。
# 定义了一个名为 RandomLoadText 的类,用于随机加载和处理文本提示。
class RandomLoadText:# 随机抽取正文本和负文本并相应地更新类索引。# 此类负责从给定的一组类文本中抽取文本,包括正样本(存在于图像中)和负样本(不存在于图像中)。它更新类索引以反映抽样的文本,并可以选择将文本列表填充为固定长度。# 方法:# __call__:处理输入标签并返回更新的类和文本。"""Randomly samples positive and negative texts and updates class indices accordingly.This class is responsible for sampling texts from a given set of class texts, including both positive(present in the image) and negative (not present in the image) samples. It updates the class indicesto reflect the sampled texts and can optionally pad the text list to a fixed length.Attributes:prompt_format (str): Format string for text prompts.neg_samples (Tuple[int, int]): Range for randomly sampling negative texts.max_samples (int): Maximum number of different text samples in one image.padding (bool): Whether to pad texts to max_samples.padding_value (str): The text used for padding when padding is True.Methods:__call__: Processes the input labels and returns updated classes and texts.Examples:>>> loader = RandomLoadText(prompt_format="Object: {}", neg_samples=(5, 10), max_samples=20)>>> labels = {"cls": [0, 1, 2], "texts": [["cat"], ["dog"], ["bird"]], "instances": [...]}>>> updated_labels = loader(labels)>>> print(updated_labels["texts"])['Object: cat', 'Object: dog', 'Object: bird', 'Object: elephant', 'Object: car']"""# 这段代码定义了 RandomLoadText 类的初始化方法 __init__ ,用于设置文本加载和处理的参数。# 定义了 RandomLoadText 类的初始化方法,接收以下参数 :# 1.prompt_format :文本提示的格式,默认为 "{}" ,表示直接使用原始文本。# 2.neg_samples :负样本数量的范围,格式为 (min, max) ,默认为 (80, 80) 。# 3.max_samples :最大采样数量,默认为 80 。# 4.padding :是否对文本进行填充,默认为 False 。# 5.padding_value :填充时使用的值,默认为空字符串 "" 。def __init__(self,prompt_format: str = "{}",neg_samples: Tuple[int, int] = (80, 80),max_samples: int = 80,padding: bool = False,padding_value: str = "",) -> None:# 初始化 RandomLoadText 类,用于随机抽取正文本和负文本样本。# 此类用于随机抽取正文本和负文本样本,并根据样本数量更新类索引。可用于基于文本的对象检测任务。"""Initializes the RandomLoadText class for randomly sampling positive and negative texts.This class is designed to randomly sample positive texts and negative texts, and update the classindices accordingly to the number of samples. It can be used for text-based object detection tasks.Args:prompt_format (str): Format string for the prompt. Default is '{}'. The format string shouldcontain a single pair of curly braces {} where the text will be inserted.neg_samples (Tuple[int, int]): A range to randomly sample negative texts. The first integerspecifies the minimum number of negative samples, and the second integer specifies themaximum. Default is (80, 80).max_samples (int): The maximum number of different text samples in one image. Default is 80.padding (bool): Whether to pad texts to max_samples. If True, the number of texts will alwaysbe equal to max_samples. Default is False.padding_value (str): The padding text to use when padding is True. Default is an empty string.Attributes:prompt_format (str): The format string for the prompt.neg_samples (Tuple[int, int]): The range for sampling negative texts.max_samples (int): The maximum number of text samples.padding (bool): Whether padding is enabled.padding_value (str): The value used for padding.Examples:>>> random_load_text = RandomLoadText(prompt_format="Object: {}", neg_samples=(50, 100), max_samples=120)>>> random_load_text.prompt_format'Object: {}'>>> random_load_text.neg_samples(50, 100)>>> random_load_text.max_samples120"""# 将初始化参数保存为类的属性,供后续方法使用。self.prompt_format = prompt_formatself.neg_samples = neg_samplesself.max_samples = max_samplesself.padding = paddingself.padding_value = padding_value# RandomLoadText 类的初始化方法 __init__ 的主要功能是设置文本加载和处理的参数。这些参数决定了如何从输入的标注信息中采样文本提示,并对文本进行格式化和填充。具体功能包括。文本提示格式:指定文本提示的格式化方式。负样本数量:定义负样本数量的范围,用于随机采样。最大采样数量:限制采样的最大数量。文本填充:是否对文本进行填充,以及填充时使用的值。通过这些参数, RandomLoadText 类能够灵活地处理文本数据,生成多样化的文本提示,适用于对比学习、分类和其他自然语言处理任务。# 这段代码定义了 RandomLoadText 类的 __call__ 方法,用于随机加载和处理文本提示(prompts),并根据类别标签对文本进行采样和填充。# 定义了 RandomLoadText 类的 __call__ 方法,接收一个参数。# 1.labels :包含文本和标注信息的字典。def __call__(self, labels: dict) -> dict:# 随机抽样正文本和负文本并相应地更新类索引。# 此方法根据图像中现有的类标签抽样正文本,并从剩余的类中随机选择负文本。然后,它更新类索引以匹配新的采样文本顺序。"""Randomly samples positive and negative texts and updates class indices accordingly.This method samples positive texts based on the existing class labels in the image, and randomlyselects negative texts from the remaining classes. It then updates the class indices to match thenew sampled text order.Args:labels (Dict): A dictionary containing image labels and metadata. Must include 'texts' and 'cls' keys.Returns:(Dict): Updated labels dictionary with new 'cls' and 'texts' entries.Examples:>>> loader = RandomLoadText(prompt_format="A photo of {}", neg_samples=(5, 10), max_samples=20)>>> labels = {"cls": np.array([[0], [1], [2]]), "texts": [["dog"], ["cat"], ["bird"]]}>>> updated_labels = loader(labels)"""# 检查 labels 中是否存在 "texts" 键,确保有文本数据可供处理。assert "texts" in labels, "No texts found in labels."    # 标签中未找到文本。# 提取 类别文本 class_texts ,这是一个列表,每个元素对应一个类别的文本提示。class_texts = labels["texts"]# 计算 类别数量 num_classes 。num_classes = len(class_texts)# 提取 类别标签 cls ,并将其转换为整数数组。cls = np.asarray(labels.pop("cls"), dtype=int)# 获取 所有正样本的类别标签 pos_labels 。pos_labels = np.unique(cls).tolist()# 如果正样本的数量超过 max_samples ,则随机采样 max_samples 个正样本。if len(pos_labels) > self.max_samples:pos_labels = random.sample(pos_labels, k=self.max_samples)# 计算负样本的数量,确保总采样数量不超过 max_samples 。neg_samples = min(min(num_classes, self.max_samples) - len(pos_labels), random.randint(*self.neg_samples))# 从所有类别中筛选出负样本的类别标签 neg_labels 。neg_labels = [i for i in range(num_classes) if i not in pos_labels]# 随机采样 neg_samples 个负样本。neg_labels = random.sample(neg_labels, k=neg_samples)# 将正样本和负样本的类别标签合并,并随机打乱顺序。sampled_labels = pos_labels + neg_labelsrandom.shuffle(sampled_labels)# 创建一个 从类别标签到新索引的映射 label2ids 。label2ids = {label: i for i, label in enumerate(sampled_labels)}# 初始化一个布尔数组 valid_idx ,用于标记有效的实例。valid_idx = np.zeros(len(labels["instances"]), dtype=bool)new_cls = []# 遍历所有类别标签,将有效的实例标记为 True ,并更新类别标签 new_cls 。for i, label in enumerate(cls.squeeze(-1).tolist()):if label not in label2ids:continuevalid_idx[i] = Truenew_cls.append([label2ids[label]])# 根据 valid_idx 过滤 labels["instances"] 和 labels["cls"] 。labels["instances"] = labels["instances"][valid_idx]labels["cls"] = np.array(new_cls)# Randomly select one prompt when there's more than one prompts# 初始化一个空列表 texts ,用于存储采样后的文本提示。texts = []# 遍历所有采样后的类别标签,随机选择一个文本提示,并格式化为指定的格式。for label in sampled_labels:prompts = class_texts[label]assert len(prompts) > 0prompt = self.prompt_format.format(prompts[random.randrange(len(prompts))])# 将格式化后的文本提示添加到 texts 列表中。texts.append(prompt)# 如果启用了填充( self.padding == True )。if self.padding:# 计算有效的类别数量 valid_labels 。valid_labels = len(pos_labels) + len(neg_labels)# 计算需要填充的数量 num_padding 。num_padding = self.max_samples - valid_labels# 如果需要填充,则使用 self.padding_value 填充到 texts 列表中。if num_padding > 0:texts += [self.padding_value] * num_padding# 将处理后的文本提示存储到 labels["texts"] 中。labels["texts"] = texts# 返回更新后的 labels 字典。return labels# RandomLoadText 类的 __call__ 方法的主要功能是随机加载和处理文本提示,生成用于对比学习或分类任务的文本数据。其核心步骤包括。提取和验证文本数据:从 labels 中提取类别文本和类别标签。采样正样本和负样本:根据 max_samples 和 neg_samples 参数,随机采样正样本和负样本。随机打乱样本顺序:将正样本和负样本的类别标签合并并随机打乱。更新标注信息:根据采样后的类别标签更新标注实例和类别标签。生成文本提示:随机选择每个类别的文本提示,并格式化为指定的格式。填充文本数据:如果启用了填充,使用指定的值填充到指定数量。返回更新后的标注信息:将处理后的文本提示存储到 labels 中,并返回更新后的字典。通过这些步骤, RandomLoadText 类能够灵活地生成多样化的文本数据,适用于对比学习、分类和其他自然语言处理任务。
# RandomLoadText 类的主要功能是随机加载和处理文本提示,生成用于对比学习或分类任务的文本数据。其核心步骤包括。提取和验证文本数据:从 labels 中提取类别文本和类别标签。采样正样本和负样本:根据 max_samples 和 neg_samples 参数,随机采样正样本和负样本。随机打乱样本顺序:将正样本和负样本的类别标签合并并随机打乱。更新标注信息:根据采样后的类别标签更新标注实例和类别标签。生成文本提示:随机选择每个类别的文本提示,并格式化为指定的格式。填充文本数据:如果启用了填充,使用指定的值填充到指定数量。返回更新后的标注信息:将处理后的文本提示存储到 labels 中,并返回更新后的字典。通过这些步骤, RandomLoadText 类能够灵活地生成多样化的文本数据,适用于对比学习、分类和其他自然语言处理任务。

15.def v8_transforms(dataset, imgsz, hyp, stretch=False): 

# 这段代码定义了一个函数 v8_transforms ,用于构建一个综合的数据增强流程,适用于目标检测和分割任务。该函数根据传入的参数和配置,组合了多种增强操作,以提高模型的鲁棒性和泛化能力。
# 定义了 v8_transforms 函数,接收以下参数 :
# 1.dataset :数据集对象,包含图像和标注信息。
# 2.imgsz :目标图像尺寸。
# 3.hyp :超参数对象,包含各种增强操作的概率和参数。
# 4.stretch :是否启用拉伸操作,影响 LetterBox 的行为。
def v8_transforms(dataset, imgsz, hyp, stretch=False):# 应用一系列图像变换进行训练。# 此函数创建图像增强技术的组合,为 YOLO 训练准备图像。# 它包括马赛克、复制粘贴、随机透视、混合和各种颜色调整等操作。"""Applies a series of image transformations for training.This function creates a composition of image augmentation techniques to prepare images for YOLO training.It includes operations such as mosaic, copy-paste, random perspective, mixup, and various color adjustments.Args:dataset (Dataset): The dataset object containing image data and annotations.imgsz (int): The target image size for resizing.hyp (Namespace): A dictionary of hyperparameters controlling various aspects of the transformations.stretch (bool): If True, applies stretching to the image. If False, uses LetterBox resizing.Returns:(Compose): A composition of image transformations to be applied to the dataset.Examples:>>> from ultralytics.data.dataset import YOLODataset>>> from ultralytics.utils import IterableSimpleNamespace>>> dataset = YOLODataset(img_path="path/to/images", imgsz=640)>>> hyp = IterableSimpleNamespace(mosaic=1.0, copy_paste=0.5, degrees=10.0, translate=0.2, scale=0.9)>>> transforms = v8_transforms(dataset, imgsz=640, hyp=hyp)>>> augmented_data = transforms(dataset[0])"""# 这段代码定义了 v8_transforms 函数中与 Mosaic 和 RandomPerspective 增强操作相关的部分,并根据 CopyPaste 模式将它们组合成一个预处理流程 pre_transform 。# 创建一个 Mosaic 增强操作实例。# dataset :数据集对象,用于获取其他图像和标注信息。# imgsz :目标图像尺寸。# p :执行 Mosaic 增强的概率。mosaic = Mosaic(dataset, imgsz=imgsz, p=hyp.mosaic)# 创建一个 RandomPerspective 增强操作实例。# degrees 、 translate 、 scale 、 shear 、 perspective :控制随机透视变换的参数。# pre_transform :如果 stretch=False ,则在透视变换之前应用 LetterBox ,以确保图像被正确填充到目标尺寸。affine = RandomPerspective(degrees=hyp.degrees,translate=hyp.translate,scale=hyp.scale,shear=hyp.shear,perspective=hyp.perspective,pre_transform=None if stretch else LetterBox(new_shape=(imgsz, imgsz)),)# 将 Mosaic 和 RandomPerspective 增强操作组合成一个预处理流程 pre_transform 。pre_transform = Compose([mosaic, affine])# 根据 hyp.copy_paste_mode 的值,决定如何插入 CopyPaste 增强操作。# 如果模式为 "flip" ,则将 CopyPaste 插入到 pre_transform 的第二个位置。if hyp.copy_paste_mode == "flip":pre_transform.insert(1, CopyPaste(p=hyp.copy_paste, mode=hyp.copy_paste_mode))# 否则,将 CopyPaste 添加到 pre_transform 的末尾。else:pre_transform.append(CopyPaste(dataset,pre_transform=Compose([Mosaic(dataset, imgsz=imgsz, p=hyp.mosaic), affine]),p=hyp.copy_paste,mode=hyp.copy_paste_mode,))# 这段代码的核心功能是构建一个预处理流程 pre_transform ,用于对图像及其标注信息进行增强处理。其主要步骤包括。创建 Mosaic 增强操作:用于将多个图像拼接在一起,形成一个更大的图像。创建 RandomPerspective 增强操作:用于对图像进行随机透视变换,支持在变换之前应用 LetterBox 。组合增强操作:将 Mosaic 和 RandomPerspective 组合成一个预处理流程。插入 CopyPaste 增强操作:如果模式为 "flip" ,则将 CopyPaste 插入到预处理流程的第二个位置。否则,将 CopyPaste 添加到预处理流程的末尾。通过这些步骤, v8_transforms 函数能够灵活地组合多种增强操作,生成多样化的训练数据,提高模型的鲁棒性和泛化能力。这种增强流程广泛应用于目标检测和分割任务中,能够显著提升模型的性能。# 这段代码是 v8_transforms 函数的一部分,用于处理关键点(keypoints)的翻转增强,并构建最终的数据增强流程。# 从数据集的配置中提取 flip_idx ,这是一个用于关键点翻转的索引数组。如果未定义,则默认为空列表。flip_idx = dataset.data.get("flip_idx", [])  # for keypoints augmentation# 检查数据集是否启用了关键点增强。if dataset.use_keypoints:# 从数据集的配置中提取关键点的形状 kpt_shape ,表示每个实例的关键点数量。kpt_shape = dataset.data.get("kpt_shape", None)# 如果 flip_idx 为空,但水平翻转的概率 hyp.fliplr 大于 0 。if len(flip_idx) == 0 and hyp.fliplr > 0.0:# 则将 hyp.fliplr 设置为 0。hyp.fliplr = 0.0# 并发出警告。LOGGER.warning("WARNING ⚠️ No 'flip_idx' array defined in data.yaml, setting augmentation 'fliplr=0.0'")    # 警告⚠️ data.yaml 中未定义“flip_idx”数组,设置增强“fliplr=0.0”。# 如果 flip_idx 不为空,但其长度与关键点数量 kpt_shape[0] 不匹配,则抛出错误。elif flip_idx and (len(flip_idx) != kpt_shape[0]):raise ValueError(f"data.yaml flip_idx={flip_idx} length must be equal to kpt_shape[0]={kpt_shape[0]}")    # data.yaml flip_idx={flip_idx} 长度必须等于 kpt_shape[0]={kpt_shape[0]}。# 构建并返回一个 综合的数据增强流程 。return Compose([# 预处理流程,包含 Mosaic 和 RandomPerspective 。pre_transform,# 混合增强操作,用于将两个图像的标注信息混合在一起。MixUp(dataset, pre_transform=pre_transform, p=hyp.mixup),# 基于 Albumentations 库的增强操作。Albumentations(p=1.0),# 随机调整图像的 HSV 通道。RandomHSV(hgain=hyp.hsv_h, sgain=hyp.hsv_s, vgain=hyp.hsv_v),# 随机垂直和水平翻转图像,支持关键点的翻转索引。RandomFlip(direction="vertical", p=hyp.flipud),RandomFlip(direction="horizontal", p=hyp.fliplr, flip_idx=flip_idx),])  # transforms# 这段代码的核心功能是处理关键点的翻转增强,并构建最终的数据增强流程。其主要步骤包括。提取 flip_idx :从数据集配置中提取关键点翻转索引。检查关键点增强:如果启用了关键点增强,确保 flip_idx 的长度与关键点数量一致。调整翻转概率:如果未定义 flip_idx ,但水平翻转概率大于 0,则将翻转概率设置为 0,并发出警告。构建增强流程:将多种增强操作组合成一个综合的数据增强流程,包括 Mosaic、MixUp、HSV 调整、垂直和水平翻转等。通过这些步骤, v8_transforms 函数能够灵活地处理关键点的翻转增强,并生成多样化的训练数据,提高模型的鲁棒性和泛化能力。这种增强流程广泛应用于目标检测和关键点估计任务中,能够显著提升模型的性能。
# v8_transforms 函数是一个综合的数据增强流程构建器,用于为目标检测和分割任务生成多样化的训练数据。它根据传入的超参数和数据集配置,组合了多种增强操作,包括 Mosaic 拼接、随机透视变换(Random Perspective)、MixUp 混合、HSV 调整、随机翻转(支持关键点翻转索引)以及 Albumentations 库提供的增强操作。此外,该函数还处理关键点的翻转增强,确保在水平翻转时正确调整关键点的位置。通过这些增强操作, v8_transforms 能够显著提高模型的鲁棒性和泛化能力,适用于各种计算机视觉任务。

16.def classify_transforms(size=224, mean=DEFAULT_MEAN, std=DEFAULT_STD, interpolation="BILINEAR", crop_fraction: float = DEFAULT_CROP_FRACTION,): 

# Classification augmentations -----------------------------------------------------------------------------------------
# 这段代码定义了一个名为 classify_transforms 的函数,用于生成图像分类任务中常用的图像预处理流程。它接收多个参数,包括目标图像大小、均值和标准差(用于归一化)、插值方式以及裁剪比例,并返回一个由 torchvision.transforms 组成的预处理流程。
# 定义了一个函数 classify_transforms ,接收以下参数 :
# 1.size :目标图像大小,默认为 224。如果是一个整数,则表示目标图像的宽和高;如果是一个元组或列表,则分别表示宽和高。
# 2.mean :归一化时使用的均值,默认为 DEFAULT_MEAN 。
# 3.std :归一化时使用的标准差,默认为 DEFAULT_STD 。
# 4.interpolation :插值方式,默认为 "BILINEAR" ,表示双线性插值。
# 5.crop_fraction :裁剪比例,默认为 DEFAULT_CROP_FRACTION ,用于计算缩放后的图像大小。
def classify_transforms(size=224,mean=DEFAULT_MEAN,std=DEFAULT_STD,interpolation="BILINEAR",crop_fraction: float = DEFAULT_CROP_FRACTION,
):# 为分类任务创建图像变换组合。# 此函数生成一系列 torchvision 变换,适用于在评估或推理期间对分类模型的图像进行预处理。变换包括调整大小、中心裁剪、转换为张量和规范化。"""Creates a composition of image transforms for classification tasks.This function generates a sequence of torchvision transforms suitable for preprocessing imagesfor classification models during evaluation or inference. The transforms include resizing,center cropping, conversion to tensor, and normalization.Args:size (int | tuple): The target size for the transformed image. If an int, it defines the shortest edge. If atuple, it defines (height, width).mean (tuple): Mean values for each RGB channel used in normalization.std (tuple): Standard deviation values for each RGB channel used in normalization.interpolation (str): Interpolation method of either 'NEAREST', 'BILINEAR' or 'BICUBIC'.crop_fraction (float): Fraction of the image to be cropped.Returns:(torchvision.transforms.Compose): A composition of torchvision transforms.Examples:>>> transforms = classify_transforms(size=224)>>> img = Image.open("path/to/image.jpg")>>> transformed_img = transforms(img)"""# 导入 torchvision.transforms 模块并将其命名为 T ,以便后续使用其提供的各种图像变换功能。import torchvision.transforms as T  # scope for faster 'import ultralytics'# 判断 size 是否为元组或列表。如果是,说明 size 包含两个值(宽和高),分别表示目标图像的宽度和高度。if isinstance(size, (tuple, list)):# 如果 size 是元组或列表,检查其长度是否为 2。如果不是,抛出断言错误,提示用户元组的长度必须为 2。 这是为了确保 size 的格式正确,避免后续逻辑出错。assert len(size) == 2, f"'size' tuples must be length 2, not length {len(size)}"    # 'size' 元组的长度必须为 2,而不是长度 {len(size) 。# 如果 size 是元组或列表,计算缩放后的宽和高。# 遍历 size 中的每个值(宽和高)。# 使用 math.floor(x / crop_fraction) 计算每个维度的缩放值, crop_fraction 是裁剪比例,用于控制缩放后的大小。# 将计算结果转换为元组,存储为 scale_size 。scale_size = tuple(math.floor(x / crop_fraction) for x in size)# 如果 size 不是元组或列表(即为整数),说明目标图像的宽和高是相同的。else:# 使用 math.floor(size / crop_fraction) 计算缩放后的大小。这里假设目标图像为正方形,因此宽和高相同。scale_size = math.floor(size / crop_fraction)# 将计算得到的缩放大小转换为元组形式 (scale_size, scale_size) ,以便后续代码能够统一处理宽和高。scale_size = (scale_size, scale_size)# 这是一条注释,说明了接下来的处理逻辑。保持宽高比:在缩放图像时,不会扭曲图像的宽高比。中心裁剪:裁剪图像的中心区域。不添加边界:不会通过填充边界来调整图像大小。图像可能丢失部分区域:由于裁剪操作,图像的某些部分可能会被裁掉。# Aspect ratio is preserved, crops center within image, no borders are added, image is lost# 如果 scale_size 的宽和高相等(即目标图像为正方形)。if scale_size[0] == scale_size[1]:# Simple case, use torchvision built-in Resize with the shortest edge mode (scalar size arg)# T.InterpolationMode# T.InterpolationMode 是 torchvision.transforms 模块中定义的一个枚举类,用于指定图像变换中的插值方式。在图像处理中,插值是一种用于估算图像在缩放、旋转或其他变换过程中新像素值的方法。不同的插值方式会影响图像的质量和性能。# 以下是 T.InterpolationMode 中定义的常见插值模式及其含义 :# 枚举成员 :# NEAREST :# 最近邻插值(Nearest Neighbor Interpolation)。# 每个新像素的值直接取最近的原始像素值。# 特点 :计算速度快,但可能会导致图像出现锯齿效果,适用于不需要高质量的场景。# BILINEAR :# 双线性插值(Bilinear Interpolation)。# 每个新像素的值通过其周围四个像素的线性组合计算得出。# 特点 :计算复杂度适中,图像质量较好,是默认的插值方式,适用于大多数场景。# BICUBIC :# 双三次插值(Bicubic Interpolation)。# 每个新像素的值通过其周围 16 个像素的三次多项式计算得出。# 特点 :计算复杂度较高,但图像质量更好,适用于需要高质量图像的场景。# BOX :# 箱式插值(Box Interpolation)。# 每个新像素的值通过其周围像素的平均值计算得出。# 特点 :计算速度快,但可能会导致图像模糊。# HAMMING :# 汉明窗插值(Hamming Window Interpolation)。# 使用汉明窗函数进行插值,适用于频域处理。# LANCZOS :# 兰索斯插值(Lanczos Interpolation)。# 使用兰索斯窗函数进行插值,能够有效减少图像的振铃效应。# 特点:计算复杂度较高,但图像质量较好,适用于高质量图像处理。# 使用示例 :# 在 torchvision.transforms 的某些变换中,例如 T.Resize 、 T.RandomResizedCrop 等,可以通过 interpolation 参数指定插值方式。例如 :# from torchvision.transforms import Resize, InterpolationMode# transform = Resize(size=(224, 224), interpolation=InterpolationMode.BILINEAR)# 使用 torchvision.transforms.Resize 的“最短边模式”进行缩放。# T.Resize 的第一个参数是目标尺寸(标量值),表示将图像的最短边缩放到该尺寸,同时保持宽高比。# interpolation 参数指定了插值方式,通过 getattr(T.InterpolationMode, interpolation) 动态获取对应的插值模式(例如 BILINEAR 表示双线性插值)。tfl = [T.Resize(scale_size[0], interpolation=getattr(T.InterpolationMode, interpolation))]# 如果 scale_size 的宽和高不相等(即目标图像为矩形)。else:# Resize the shortest edge to matching target dim for non-square target# 使用 torchvision.transforms.Resize 的“元组模式”进行缩放。# 在这种情况下, T.Resize 的参数是一个元组 (width, height) ,表示将图像的宽和高分别缩放到指定的尺寸,同时保持宽高比。# 这种方式适用于目标图像尺寸为矩形的情况。tfl = [T.Resize(scale_size)]# 将以下变换添加到变换列表 tfl 中。tfl.extend([# 对缩放后的图像进行中心裁剪,裁剪的目标大小由 size 参数决定。 如果 size 是整数,裁剪为正方形;如果是元组或列表,则裁剪为指定的宽高。T.CenterCrop(size),# 将图像从 PIL 图像或 NumPy 数组转换为 PyTorch 张量。 转换后的张量范围为 [0, 1] 。T.ToTensor(),# 对图像张量进行归一化处理。 使用指定的均值 mean 和标准差 std 对每个通道进行标准化,公式为 : output = (input-mean)/std 。# mean 和 std 通常根据数据集的统计特性预先计算得到。T.Normalize(mean=torch.tensor(mean), std=torch.tensor(std)),])# 使用 torchvision.transforms.Compose 将所有变换组合成一个完整的变换序列。 返回的 T.Compose 对象可以作为 PyTorch 数据预处理管道的一部分,对输入图像依次应用所有定义的变换。return T.Compose(tfl)
# 这段代码实现了一个通用的图像分类预处理流程,主要包括以下步骤。根据目标大小和裁剪比例计算缩放后的图像大小。对图像进行缩放,保留宽高比。对缩放后的图像进行中心裁剪,裁剪为目标大小。将图像转换为 PyTorch 张量。对图像进行归一化处理。通过这种方式,可以将输入图像标准化为适合深度学习模型的格式,同时保留图像的关键特征。

17.def classify_augmentations(size=224, mean=DEFAULT_MEAN, std=DEFAULT_STD, scale=None, ratio=None, hflip=0.5, vflip=0.0, auto_augment=None, hsv_h=0.015, hsv_s=0.4, hsv_v=0.4, force_color_jitter=False, erasing=0.0, interpolation="BILINEAR",): 

# Classification training augmentations --------------------------------------------------------------------------------
# 这段代码定义了一个名为 classify_augmentations 的函数,用于生成图像分类任务中的数据增强(Data Augmentation)变换序列。它结合了多种增强技术,包括随机裁剪、水平/垂直翻转、自动增强策略(如 AutoAugment、RandAugment、AugMix)、颜色抖动(Color Jitter)和随机擦除(Random Erasing)。
# 定义了一个函数 classify_augmentations ,接收以下参数 :
# 1.size :目标图像大小,默认为 224。
# 2.mean 和 3.std :归一化时使用的均值和标准差。
# 4.scale 和 5.ratio :随机裁剪的缩放范围和宽高比范围。
# 6.hflip 和 7.vflip :水平翻转和垂直翻转的概率。
# 8.auto_augment :自动增强策略的类型(如 "autoaugment" 、 "randaugment" 、 "augmix" )。
# 9.hsv_h 、 10.hsv_s 、 11.hsv_v :颜色抖动的参数,分别表示色调(Hue)、饱和度(Saturation)和亮度(Value)的变化范围。
# 12.force_color_jitter :是否强制启用颜色抖动,即使启用了自动增强策略。
# 13.erasing :随机擦除的概率。
# 14.interpolation :插值方式,默认为 "BILINEAR" 。
def classify_augmentations(size=224,mean=DEFAULT_MEAN,std=DEFAULT_STD,scale=None,ratio=None,hflip=0.5,vflip=0.0,auto_augment=None,hsv_h=0.015,  # image HSV-Hue augmentation (fraction)hsv_s=0.4,  # image HSV-Saturation augmentation (fraction)hsv_v=0.4,  # image HSV-Value augmentation (fraction)force_color_jitter=False,erasing=0.0,interpolation="BILINEAR",
):# 为分类任务创建图像增强变换组合。# 此函数生成一组适合训练分类模型的图像变换。它包括调整大小、翻转、颜色抖动、自动增强和随机擦除的选项。"""Creates a composition of image augmentation transforms for classification tasks.This function generates a set of image transformations suitable for training classification models. It includesoptions for resizing, flipping, color jittering, auto augmentation, and random erasing.Args:size (int): Target size for the image after transformations.mean (tuple): Mean values for normalization, one per channel.std (tuple): Standard deviation values for normalization, one per channel.scale (tuple | None): Range of size of the origin size cropped.ratio (tuple | None): Range of aspect ratio of the origin aspect ratio cropped.hflip (float): Probability of horizontal flip.vflip (float): Probability of vertical flip.auto_augment (str | None): Auto augmentation policy. Can be 'randaugment', 'augmix', 'autoaugment' or None.hsv_h (float): Image HSV-Hue augmentation factor.hsv_s (float): Image HSV-Saturation augmentation factor.hsv_v (float): Image HSV-Value augmentation factor.force_color_jitter (bool): Whether to apply color jitter even if auto augment is enabled.erasing (float): Probability of random erasing.interpolation (str): Interpolation method of either 'NEAREST', 'BILINEAR' or 'BICUBIC'.Returns:(torchvision.transforms.Compose): A composition of image augmentation transforms.Examples:>>> transforms = classify_augmentations(size=224, auto_augment="randaugment")>>> augmented_image = transforms(original_image)"""# Transforms to apply if Albumentations not installed# 导入 torchvision.transforms 模块并命名为 T ,以便后续使用其提供的变换功能。import torchvision.transforms as T  # scope for faster 'import ultralytics'# 这段代码是 classify_augmentations 函数的核心部分,主要负责定义图像增强流程中的主变换( primary_tfl )。这些主变换包括随机裁剪、水平翻转和垂直翻转。# 检查 size 是否为整数。如果不是,抛出 TypeError 。 这是因为 T.RandomResizedCrop 要求目标大小必须是一个整数,表示裁剪后的图像尺寸。if not isinstance(size, int):raise TypeError(f"classify_transforms() size {size} must be integer, not (list, tuple)")    # classify_transforms() 大小 {size} 必须是整数,而不是 (列表,元组) 。# 如果 scale 参数未指定,则使用默认值 (0.08, 1.0) 。 scale 是一个元组,表示 随机裁剪的面积范围 ,例如 (0.08, 1.0) 表示裁剪面积在原图面积的 8% 到 100% 之间。scale = tuple(scale or (0.08, 1.0))  # default imagenet scale range# 如果 ratio 参数未指定,则使用默认值 (3.0 / 4.0, 4.0 / 3.0) 。 ratio 是一个元组,表示 随机裁剪的宽高比范围 ,例如 (3.0 / 4.0, 4.0 / 3.0) 表示宽高比在 0.75 到 1.33 之间。ratio = tuple(ratio or (3.0 / 4.0, 4.0 / 3.0))  # default imagenet ratio range# 根据传入的 interpolation 参数(如 "BILINEAR" ),获取 对应的插值模式 。 T.InterpolationMode 是 torchvision.transforms 中定义的插值模式枚举类。interpolation = getattr(T.InterpolationMode, interpolation)# 初始化 主变换列表 primary_tfl ,并添加 T.RandomResizedCrop 。 T.RandomResizedCrop 是一个常用的增强操作,它会随机裁剪图像的一部分,并将其缩放到目标大小 size 。# size :目标图像大小。# scale :裁剪面积的范围。# ratio :裁剪的宽高比范围。# interpolation :缩放时使用的插值方式。primary_tfl = [T.RandomResizedCrop(size, scale=scale, ratio=ratio, interpolation=interpolation)]# 如果水平翻转的概率 hflip 大于 0,则将 T.RandomHorizontalFlip 添加到主变换列表中。 T.RandomHorizontalFlip(p=hflip) 会以概率 hflip 对图像进行水平翻转。if hflip > 0.0:primary_tfl.append(T.RandomHorizontalFlip(p=hflip))# 如果垂直翻转的概率 vflip 大于 0,则将 T.RandomVerticalFlip 添加到主变换列表中。 T.RandomVerticalFlip(p=vflip) 会以概率 vflip 对图像进行垂直翻转。if vflip > 0.0:primary_tfl.append(T.RandomVerticalFlip(p=vflip))# 这段代码定义了图像增强流程中的主变换部分,主要包括以下功能。随机裁剪与缩放:使用 T.RandomResizedCrop 随机裁剪图像的一部分,并将其缩放到目标大小。支持指定裁剪的面积范围( scale )和宽高比范围( ratio )。使用指定的插值方式( interpolation )进行缩放。水平和垂直翻转:根据指定的概率( hflip 和 vflip ),对图像进行水平或垂直翻转。这些增强操作是图像分类任务中常用的技巧,可以有效增加数据多样性,提升模型的泛化能力。# 这段代码定义了图像增强流程中的辅助变换部分( secondary_tfl ),主要负责根据用户指定的自动增强策略( auto_augment )选择合适的增强方法。同时,它还控制是否禁用颜色抖动( ColorJitter )。# 初始化一个空列表 secondary_tfl ,用于 存储辅助变换 。secondary_tfl = []# 初始化一个布尔变量 disable_color_jitter ,默认值为 False ,用于 控制是否禁用颜色抖动 。disable_color_jitter = False# 检查 auto_augment 是否被启用。如果启用,确保其值是一个字符串。 如果不是字符串,抛出断言错误,提示用户输入的类型不正确。if auto_augment:assert isinstance(auto_augment, str), f"Provided argument should be string, but got type {type(auto_augment)}"    # 提供的参数应该是字符串,但得到的类型是 {type(auto_augment)。# color jitter is typically disabled if AA/RA on,# this allows override without breaking old hparm cfgs# 如果启用了自动增强策略(如 AutoAugment 、 RandAugment 或 AugMix ),通常会禁用颜色抖动( ColorJitter ),因为这些策略已经包含了颜色相关的增强。# 但是,如果用户显式地设置了 force_color_jitter=True ,则会强制启用颜色抖动,即使启用了自动增强策略。disable_color_jitter = not force_color_jitter# 如果用户选择的自动增强策略是 "randaugment" 。if auto_augment == "randaugment":# 检查 torchvision 的版本是否大于等于 0.11.0。if TORCHVISION_0_11:# torchvision.transforms.RandAugment(num_ops: int = 2, magnitude: int = 9, num_magnitude_bins: int = 31, interpolation: InterpolationMode = InterpolationMode.NEAREST, fill: Optional[Union[int, float, Sequence[int], Sequence[float]]] = None)# T.RandAugment 是 torchvision.transforms 模块中提供的一种自动增强策略,属于随机增强(Random Augmentation)的实现。它通过随机选择一组增强操作并随机应用这些操作,从而增加数据的多样性,提升模型的泛化能力。 RandAugment 是一种简单而有效的增强方法,广泛应用于图像分类任务中。# 参数说明 :# num_ops (int, 默认值为 2) :每次增强中随机选择并应用的操作数量。例如, num_ops=2 表示从预定义的操作集中随机选择两个操作并依次应用。# magnitude (int, 默认值为 9) :每个增强操作的强度(magnitude)。 magnitude 的取值范围通常在 [0, 30] 之间,具体取决于操作的定义。较大的 magnitude 表示更强的增强效果。# num_magnitude_bins (int, 默认值为 31) :将 magnitude 分成的离散区间数量。 magnitude 的值会被离散化到 [0, num_magnitude_bins - 1] 的整数区间中。例如, num_magnitude_bins=31 表示 magnitude 的取值范围为 [0, 30] 。# interpolation ( InterpolationMode , 默认值为 InterpolationMode.NEAREST ) :在图像变换过程中使用的插值方式。可以选择 NEAREST 、 BILINEAR 、 BICUBIC 等。# fill (Optional, 默认值为 None ) :用于填充图像的值。可以是一个标量(如 0 或 255 ),也可以是一个与通道数匹配的序列(如 [0, 0, 0] 表示黑色填充)。如果为 None ,则不进行填充。# RandAugment 的工作原理 :# RandAugment 的核心思想是从一个预定义的操作集中随机选择若干个操作,并随机调整这些操作的强度(magnitude)。预定义的操作集通常包括以下几种常见的图像增强操作 :# 平移(Translate) :水平或垂直移动图像。# 旋转(Rotate) :随机旋转图像。# 缩放(Scale) :随机缩放图像。# 颜色增强(Color) :调整图像的亮度、对比度或饱和度。# 剪切(Shear) :对图像进行剪切变换。# 高斯模糊(Gaussian Blur) :对图像进行高斯模糊处理。# 每次增强时, RandAugment 会随机选择 num_ops 个操作,并根据 magnitude 参数调整每个操作的强度。通过这种方式, RandAugment 可以生成多样化的增强图像,从而提升模型的鲁棒性。# T.RandAugment 是一种简单而强大的自动增强方法,通过随机选择和应用一组增强操作,能够有效增加数据的多样性并提升模型的泛化能力。它特别适用于图像分类任务,可以作为数据增强流程中的重要组成部分。# 如果满足条件,将 T.RandAugment 添加到辅助变换列表 secondary_tfl 中。secondary_tfl.append(T.RandAugment(interpolation=interpolation))# 如果不满足条件,记录警告信息并禁用该增强策略。else:LOGGER.warning('"auto_augment=randaugment" requires torchvision >= 0.11.0. Disabling it.')    # “auto_augment=randaugment” 需要 torchvision >= 0.11.0。禁用它。# 如果用户选择的自动增强策略是 "augmix" 。elif auto_augment == "augmix":# 检查 torchvision 的版本是否大于等于 0.13.0。if TORCHVISION_0_13:# torchvision.transforms.AugMix(severity: int = 3, mixture_width: int = 3, chain_depth: int = -1, alpha: float = 1.0, all_ops: bool = True, interpolation: InterpolationMode = InterpolationMode.BILINEAR, fill: Optional[Union[int, float, Sequence[int], Sequence[float]]] = None)# T.AugMix 是 torchvision.transforms 模块中实现的一种数据增强方法,基于论文 “AugMix: A Simple Data Processing Method to Improve Robustness and Uncertainty” 提出的技术。它通过混合多种随机增强操作后的图像,显著提高了模型的鲁棒性和对不确定性的处理能力。# 参数说明 :# severity (int, 默认值为 3) :基础增强操作的强度(severity)。取值范围通常为 [1, 10] ,数值越大表示增强的强度越高。# mixture_width (int, 默认值为 3) :混合的增强链(augmentation chains)数量。表示有多少种不同的增强组合会被混合在一起。# chain_depth (int, 默认值为 -1) :每个增强链的深度。负值表示随机深度,从区间 [1, 3] 中采样。# alpha (float, 默认值为 1.0) :用于概率分布的超参数,控制混合权重的分布(如 Dirichlet 分布)。# all_ops (bool, 默认值为 True) :是否使用所有增强操作(包括亮度、对比度、颜色和锐度等)。如果为 False ,则仅使用部分操作。# interpolation ( InterpolationMode , 默认值为 InterpolationMode.BILINEAR ) :插值方式,用于图像变换。支持的插值模式包括 NEAREST 和 BILINEAR 。# fill (可选,int、float 或序列) :用于填充变换后图像外部区域的像素值。如果是数字,则所有通道使用相同的值。# 工作原理 :# AugMix 的核心思想是通过以下步骤增强图像 :# 定义增强操作集 :包括剪切(Shear)、平移(Translate)、旋转(Rotate)、对比度调整(Posterize)、亮度调整(Brightness)等。# 生成增强链 :随机选择若干增强操作组成一个增强链,每条链的深度可以是随机的(由 chain_depth 控制)。# 混合增强图像 :对输入图像应用多条增强链,生成多个增强后的图像。 使用 Dirichlet 分布生成混合权重,将这些增强后的图像混合在一起。# 混合原始图像 :将混合后的增强图像与原始图像按一定比例混合,比例由 Beta 分布控制。# 通过这种方式, AugMix 能够生成多样化的增强图像,同时保留原始图像的特征,从而提高模型的鲁棒性和泛化能力。# T.AugMix 是一种强大的数据增强方法,特别适用于需要提高模型鲁棒性和泛化能力的场景。它通过混合多种增强操作后的图像,能够有效模拟现实世界中的数据分布变化,从而提升模型在不同条件下的表现。# 如果满足条件,将 T.AugMix 添加到辅助变换列表 secondary_tfl 中。secondary_tfl.append(T.AugMix(interpolation=interpolation))# 如果不满足条件,记录警告信息并禁用该增强策略。else:LOGGER.warning('"auto_augment=augmix" requires torchvision >= 0.13.0. Disabling it.')    # “auto_augment=augmix” 需要 torchvision >= 0.13.0。禁用它。# 如果用户选择的自动增强策略是 "autoaugment" 。elif auto_augment == "autoaugment":# 检查 torchvision 的版本是否大于等于 0.10.0。if TORCHVISION_0_10:# torchvision.transforms.AutoAugment(policy: AutoAugmentPolicy = AutoAugmentPolicy.IMAGENET, interpolation: InterpolationMode = InterpolationMode.NEAREST, fill: Optional[Union[int, float, Sequence[int], Sequence[float]]] = None)# T.AutoAugment 是 torchvision.transforms 模块中实现的一种自动数据增强方法,基于论文 “AutoAugment: Learning Augmentation Strategies from Data” 提出的技术。它通过自动学习最优的数据增强策略来提高模型的泛化能力和鲁棒性。# 参数说明 :# policy ( AutoAugmentPolicy , 默认值为 AutoAugmentPolicy.IMAGENET ) :指定使用的增强策略。 AutoAugmentPolicy 是一个枚举类,定义了不同的预训练策略 :# AutoAugmentPolicy.IMAGENET :在 ImageNet 数据集上学习到的策略。# AutoAugmentPolicy.CIFAR10 :在 CIFAR-10 数据集上学习到的策略。# AutoAugmentPolicy.SVHN :在 SVHN 数据集上学习到的策略。# interpolation ( InterpolationMode , 默认值为 InterpolationMode.NEAREST ) :指定插值方式,用于图像变换。支持的插值模式包括 :# InterpolationMode.NEAREST :最近邻插值。# InterpolationMode.BILINEAR :双线性插值。# InterpolationMode.BICUBIC :双三次插值。# fill (可选,int、float 或序列,默认值为 None ) :用于填充变换后图像外部区域的像素值。可以是一个标量(如 0 ),也可以是一个与通道数匹配的序列(如 [0, 0, 0] 表示黑色填充)。如果为 None ,则不进行填充。# 工作原理 :# AutoAugment 的核心思想是通过强化学习或搜索算法自动学习最优的数据增强策略。具体步骤如下 :# 定义搜索空间 :搜索空间包含多种图像增强操作(如旋转、平移、剪切、对比度调整等)。# 每个操作有两个参数 :操作的概率 和 操作的强度(magnitude)。# 生成增强策略 :一个增强策略由多个子策略组成,每个子策略包含两个增强操作。 在训练过程中,每个 mini-batch 中的图像随机选择一个子策略进行增强。# 优化策略 :使用强化学习或其他优化算法,根据验证集上的表现自动调整策略,以找到最优的增强策略。# 应用策略 :将学习到的最优策略应用于训练数据,以提高模型的泛化能力。# T.AutoAugment 是一种强大的自动数据增强方法,通过自动学习最优的增强策略,能够显著提高模型的泛化能力和鲁棒性。它特别适用于需要在大规模数据集上训练的场景,如 ImageNet、CIFAR-10 和 SVHN 等。通过使用预定义的策略,用户可以轻松地将自动增强集成到数据预处理流程中。# 如果满足条件,将 T.AutoAugment 添加到辅助变换列表 secondary_tfl 中。secondary_tfl.append(T.AutoAugment(interpolation=interpolation))# 如果不满足条件,记录警告信息并禁用该增强策略。else:LOGGER.warning('"auto_augment=autoaugment" requires torchvision >= 0.10.0. Disabling it.')    # “auto_augment=autoaugment” 需要 torchvision >= 0.10.0。禁用它。# 如果用户指定的 auto_augment 不是 "randaugment" 、 "augmix" 或 "autoaugment" ,抛出值错误。 这确保了用户只能选择支持的自动增强策略。else:raise ValueError(f'Invalid auto_augment policy: {auto_augment}. Should be one of "randaugment", '    # 无效的自动增强策略:{auto_augment}。f'"augmix", "autoaugment" or None'    # 应为“randaugment”、“augmix”、“autoaugment”或 None 之一。)# 这段代码的核心功能是根据用户指定的自动增强策略( auto_augment )动态选择合适的增强方法,并将其添加到辅助变换列表 secondary_tfl 中。同时,它还根据以下规则控制颜色抖动的启用或禁用。自动增强策略优先:如果启用了自动增强策略(如 RandAugment 、 AugMix 或 AutoAugment ),通常会禁用颜色抖动,因为这些策略已经包含了颜色相关的增强。强制启用颜色抖动:如果用户显式地设置了 force_color_jitter=True ,则会强制启用颜色抖动,即使启用了自动增强策略。此外,代码还根据 torchvision 的版本动态启用或禁用某些增强策略,确保代码的兼容性。如果当前版本不支持某些增强策略,会记录警告信息并禁用该策略,避免因版本不兼容而导致的错误。# 这段代码是 classify_augmentations 函数的最后部分,主要负责完成图像增强流程的定义,并将所有增强操作组合成一个完整的变换序列。# 如果没有禁用颜色抖动( disable_color_jitter 为 False )。if not disable_color_jitter:# 则将 T.ColorJitter 添加到辅助变换列表 secondary_tfl 中。# T.ColorJitter 是一种增强操作,用于随机调整图像的亮度、对比度、饱和度和色调。# brightness=hsv_v :亮度调整范围为 [max(0, 1 - hsv_v), 1 + hsv_v] 。# contrast=hsv_v :对比度调整范围为 [max(0, 1 - hsv_v), 1 + hsv_v] 。# saturation=hsv_s :饱和度调整范围为 [max(0, 1 - hsv_s), 1 + hsv_s] 。# hue=hsv_h :色调调整范围为 [-hsv_h, hsv_h] ,单位为色调的分数(例如 ±0.5 表示 ±50% 的色调变化)。secondary_tfl.append(T.ColorJitter(brightness=hsv_v, contrast=hsv_v, saturation=hsv_s, hue=hsv_h))# 定义 最终变换列表 final_tfl ,包含以下操作。final_tfl = [# 将图像从 PIL 图像或 NumPy 数组转换为 PyTorch 张量。 转换后的张量范围为 [0, 1] 。T.ToTensor(),# 对图像张量进行归一化处理,使用指定的均值和标准差对每个通道进行标准化。T.Normalize(mean=torch.tensor(mean), std=torch.tensor(std)),# 以概率 erasing 对图像进行随机擦除操作。 inplace=True 表示直接在原张量上进行操作,节省内存。T.RandomErasing(p=erasing, inplace=True),]# 将 主变换列表 primary_tfl 、辅助变换列表 secondary_tfl 和最终变换列表 final_tfl 合并为一个完整的变换序列。 使用 T.Compose 将所有变换组合在一起,返回一个可以应用于图像的变换对象。return T.Compose(primary_tfl + secondary_tfl + final_tfl)# 这段代码完成了图像增强流程的定义,并将所有增强操作组合成一个完整的变换序列。整个流程包括以下部分。主变换( primary_tfl ):包括随机裁剪、水平翻转和垂直翻转。辅助变换( secondary_tfl ):根据用户选择的自动增强策略(如 RandAugment 、 AugMix 、 AutoAugment )动态添加增强操作。如果没有禁用颜色抖动,则添加 T.ColorJitter 。最终变换( final_tfl ):包括将图像转换为张量、归一化和随机擦除。通过这种方式,函数可以根据用户指定的参数生成一个灵活且强大的图像增强流程,适用于图像分类任务中的数据增强。
# classify_augmentations 函数是一个用于生成图像分类任务数据增强流程的工具,它结合了多种增强技术,包括随机裁剪、水平/垂直翻转、自动增强策略(如 AutoAugment、RandAugment 和 AugMix)、颜色抖动以及随机擦除。函数根据用户指定的参数动态选择增强方法,并根据 torchvision 的版本兼容性动态启用或禁用某些增强策略。最终,它将所有增强操作组合成一个完整的变换序列,返回一个可以应用于图像的 torchvision.transforms.Compose 对象,从而为图像分类模型提供多样化且鲁棒性强的训练数据。

18.class ClassifyLetterBox: 

# NOTE: keep this class for backward compatibility    注意:保留此类是为了向后兼容。
# 这段代码定义了一个名为 ClassifyLetterBox 的类,用于对图像进行缩放和填充,使其适应指定的尺寸,同时保持图像的宽高比。这种操作通常被称为“LetterBox”变换,因为它会在图像周围添加填充(padding),使其看起来像一个信封(letterbox)。
# 定义了一个名为 ClassifyLetterBox 的类,用于实现图像的 LetterBox 变换。
class ClassifyLetterBox:# 用于调整图像大小和填充图像以进行分类任务的类。# 此类旨在成为转换管道的一部分,例如 T.Compose([LetterBox(size), ToTensor()])。它将图像调整大小并填充到指定大小,同时保持原始纵横比。# 方法:# __call__:将信箱转换应用于输入图像。"""A class for resizing and padding images for classification tasks.This class is designed to be part of a transformation pipeline, e.g., T.Compose([LetterBox(size), ToTensor()]).It resizes and pads images to a specified size while maintaining the original aspect ratio.Attributes:h (int): Target height of the image.w (int): Target width of the image.auto (bool): If True, automatically calculates the short side using stride.stride (int): The stride value, used when 'auto' is True.Methods:__call__: Applies the letterbox transformation to an input image.Examples:>>> transform = ClassifyLetterBox(size=(640, 640), auto=False, stride=32)>>> img = np.random.randint(0, 255, (480, 640, 3), dtype=np.uint8)>>> result = transform(img)>>> print(result.shape)(640, 640, 3)"""# 定义了类的初始化方法,接收以下参数 :# 1.size :目标图像的尺寸,默认为 (640, 640) 。如果传入一个整数,则宽高均为此值。# 2.auto :布尔值,表示是否自动计算短边的尺寸,以确保图像的宽高均能被 stride 整除。# 3.stride :步长,用于与 auto 参数配合,确保图像尺寸符合模型的输入要求。def __init__(self, size=(640, 640), auto=False, stride=32):# 初始化 ClassifyLetterBox 对象以进行图像预处理。# 此类旨在成为图像分类任务转换管道的一部分。它将图像调整大小并填充到指定大小,同时保持原始纵横比。"""Initializes the ClassifyLetterBox object for image preprocessing.This class is designed to be part of a transformation pipeline for image classification tasks. It resizes andpads images to a specified size while maintaining the original aspect ratio.Args:size (int | Tuple[int, int]): Target size for the letterboxed image. If an int, a square image of(size, size) is created. If a tuple, it should be (height, width).auto (bool): If True, automatically calculates the short side based on stride. Default is False.stride (int): The stride value, used when 'auto' is True. Default is 32.Attributes:h (int): Target height of the letterboxed image.w (int): Target width of the letterboxed image.auto (bool): Flag indicating whether to automatically calculate short side.stride (int): Stride value for automatic short side calculation.Examples:>>> transform = ClassifyLetterBox(size=224)>>> img = np.random.randint(0, 255, (480, 640, 3), dtype=np.uint8)>>> result = transform(img)>>> print(result.shape)(224, 224, 3)"""# 调用父类的初始化方法(尽管这里没有明确的父类, super().__init__() 是一个通用的初始化调用,通常用于多继承场景)。super().__init__()# 如果 size 是一个整数,则将宽高均设置为该值;如果 size 是一个元组或列表,则直接将其解包为宽高。self.h, self.w = (size, size) if isinstance(size, int) else size# 将 auto 参数赋值给类的属性 self.auto ,用于 后续判断是否自动计算短边尺寸 。self.auto = auto  # pass max size integer, automatically solve for short side using stride# 将 stride 参数赋值给类的属性 self.stride ,用于 自动计算尺寸时确保宽高符合步长要求 。self.stride = stride  # used with auto# 定义了类的 __call__ 方法,使其可以像函数一样被调用。输入参数 1.im 是需要处理的图像。def __call__(self, im):# 使用 letterbox 方法调整图像大小并填充图像。# 此方法调整输入图像的大小以适应指定的尺寸,同时保持其纵横比,然后填充调整大小后的图像以匹配目标大小。# 参数:# im (numpy.ndarray):输入图像作为具有形状 (H, W, C) 的 numpy 数组。# 返回:# (numpy.ndarray):调整大小并填充图像作为具有形状 (hs, ws, 3) 的 numpy 数组,其中 hs 和 ws 分别是目标高度和宽度。"""Resizes and pads an image using the letterbox method.This method resizes the input image to fit within the specified dimensions while maintaining its aspect ratio,then pads the resized image to match the target size.Args:im (numpy.ndarray): Input image as a numpy array with shape (H, W, C).Returns:(numpy.ndarray): Resized and padded image as a numpy array with shape (hs, ws, 3), where hs and ws arethe target height and width respectively.Examples:>>> letterbox = ClassifyLetterBox(size=(640, 640))>>> image = np.random.randint(0, 255, (720, 1280, 3), dtype=np.uint8)>>> resized_image = letterbox(image)>>> print(resized_image.shape)(640, 640, 3)"""# 获取输入图像的 高度 和 宽度 。imh, imw = im.shape[:2]# 计算 缩放比例 r ,取宽高缩放比例的较小值,以确保图像在缩放后不会超出目标尺寸。r = min(self.h / imh, self.w / imw)  # ratio of new/old dimensions# 根据缩放比例 r ,计算 缩放后的图像尺寸 (高度和宽度)。h, w = round(imh * r), round(imw * r)  # resized image dimensions# Calculate padding dimensions# 如果 auto 为 True ,则自动计算填充后的尺寸,确保宽高均能被 stride 整除。# 如果 auto 为 False ,则直接使用目标尺寸 self.h 和 self.w 。hs, ws = (math.ceil(x / self.stride) * self.stride for x in (h, w)) if self.auto else (self.h, self.w)# 计算 填充的上下边距 top 和 左右边距 left ,使图像在填充后的画布中居中。top, left = round((hs - h) / 2 - 0.1), round((ws - w) / 2 - 0.1)# Create padded image# 创建一个 填充后的图像 im_out ,其尺寸为 (hs, ws) ,填充颜色为 RGB (114, 114, 114) (通常用于 YOLO 等模型的背景填充)。im_out = np.full((hs, ws, 3), 114, dtype=im.dtype)# 使用 OpenCV 的 cv2.resize 方法将输入图像缩放到 (w, h) ,并将其放置在填充后的图像 im_out 的相应位置。im_out[top : top + h, left : left + w] = cv2.resize(im, (w, h), interpolation=cv2.INTER_LINEAR)# 返回 填充后的图像 im_out 。return im_out
# ClassifyLetterBox 类实现了一个图像的 LetterBox 变换,其主要功能包括。保持宽高比缩放:图像被缩放到目标尺寸的范围内,同时保持其原始宽高比。填充:在缩放后的图像周围添加填充,使其达到目标尺寸。自动计算尺寸:如果启用了 auto 参数,会自动计算填充后的尺寸,确保宽高均能被 stride 整除,这在使用基于网格的模型(如 YOLO)时非常有用。这种变换方式广泛应用于计算机视觉任务中,特别是在图像分类和目标检测任务中,用于将输入图像标准化为模型所需的格式。

19.class CenterCrop: 

# NOTE: keep this class for backward compatibility    注意:保留此类是为了向后兼容。
# 这段代码定义了一个名为 CenterCrop 的类,用于对图像进行中心裁剪并缩放到指定的尺寸。
# 定义了一个名为 CenterCrop 的类,用于实现图像的中心裁剪功能。
class CenterCrop:# 对图像应用中心裁剪以进行分类任务。# 此类对输入图像执行中心裁剪,将其调整为指定大小,同时保持纵横比。它旨在成为转换管道的一部分,例如 T.Compose([CenterCrop(size), ToTensor()])。# 方法:# __call__:将中心裁剪转换应用于输入图像。"""Applies center cropping to images for classification tasks.This class performs center cropping on input images, resizing them to a specified size while maintaining the aspectratio. It is designed to be part of a transformation pipeline, e.g., T.Compose([CenterCrop(size), ToTensor()]).Attributes:h (int): Target height of the cropped image.w (int): Target width of the cropped image.Methods:__call__: Applies the center crop transformation to an input image.Examples:>>> transform = CenterCrop(640)>>> image = np.random.randint(0, 255, (1080, 1920, 3), dtype=np.uint8)>>> cropped_image = transform(image)>>> print(cropped_image.shape)(640, 640, 3)"""# 定义了类的初始化方法,接收一个参数。# 1.size :目标图像的宽和高。 默认值为 640  ,如果传入一个整数,则裁剪后的图像为正方形;如果传入一个元组或列表,则分别表示目标图像的宽和高。def __init__(self, size=640):# 初始化 CenterCrop 对象以进行图像预处理。# 此类旨在成为转换管道的一部分,例如 T.Compose([CenterCrop(size), ToTensor()])。它对输入图像执行中心裁剪以达到指定大小。"""Initializes the CenterCrop object for image preprocessing.This class is designed to be part of a transformation pipeline, e.g., T.Compose([CenterCrop(size), ToTensor()]).It performs a center crop on input images to a specified size.Args:size (int | Tuple[int, int]): The desired output size of the crop. If size is an int, a square crop(size, size) is made. If size is a sequence like (h, w), it is used as the output size.Returns:(None): This method initializes the object and does not return anything.Examples:>>> transform = CenterCrop(224)>>> img = np.random.rand(300, 300, 3)>>> cropped_img = transform(img)>>> print(cropped_img.shape)(224, 224, 3)"""# 调用父类的初始化方法。虽然这里没有明确的父类,但这是一个通用的初始化调用,通常用于多继承场景。super().__init__()# 如果 size 是一个整数,则将宽高均设置为该值;如果 size 是一个元组或列表,则直接将其解包为宽高。self.h, self.w = (size, size) if isinstance(size, int) else size# 定义了类的 __call__ 方法,使其可以像函数一样被调用,接收一个参数。# 1.im :需要处理的图像。def __call__(self, im):# 对输入图像应用中心裁剪。# 此方法使用信箱方法调整图像大小并裁剪图像中心。它在将原始图像调整到指定尺寸的同时保持原始图像的纵横比。"""Applies center cropping to an input image.This method resizes and crops the center of the image using a letterbox method. It maintains the aspectratio of the original image while fitting it into the specified dimensions.Args:im (numpy.ndarray | PIL.Image.Image): The input image as a numpy array of shape (H, W, C) or aPIL Image object.Returns:(numpy.ndarray): The center-cropped and resized image as a numpy array of shape (self.h, self.w, C).Examples:>>> transform = CenterCrop(size=224)>>> image = np.random.randint(0, 255, (640, 480, 3), dtype=np.uint8)>>> cropped_image = transform(image)>>> assert cropped_image.shape == (224, 224, 3)"""# 检查输入图像是否为 PIL 图像。如果是,则将其转换为 NumPy 数组,因为后续操作需要基于 NumPy 数组进行。if isinstance(im, Image.Image):  # convert from PIL to numpy array if requiredim = np.asarray(im)# 获取输入图像的 高度 和 宽度 。imh, imw = im.shape[:2]# 计算图像的最小维度(宽或高),这将决定裁剪的大小。m = min(imh, imw)  # min dimension# 计算 裁剪的起始位置 。# top :从图像顶部到裁剪区域顶部的距离。# left :从图像左侧到裁剪区域左侧的距离。# 这里使用 (imh - m) // 2 和 (imw - m) // 2 确保裁剪区域位于图像的中心。top, left = (imh - m) // 2, (imw - m) // 2# 从输入图像中裁剪出中心区域,大小为 (m, m) 。 使用 OpenCV 的 cv2.resize 方法将裁剪后的图像缩放到目标尺寸 (self.w, self.h) 。 使用双线性插值( cv2.INTER_LINEAR )进行缩放。return cv2.resize(im[top : top + m, left : left + m], (self.w, self.h), interpolation=cv2.INTER_LINEAR)
# CenterCrop 类实现了一个简单的图像中心裁剪和缩放功能,其主要功能包括。中心裁剪:从输入图像中裁剪出最小维度的正方形区域,确保裁剪区域位于图像的中心。缩放:将裁剪后的图像缩放到指定的目标尺寸,使用双线性插值以保证图像质量。这种变换方式广泛应用于图像预处理中,特别是在需要将图像标准化为固定尺寸的场景中,例如图像分类任务。它特别适用于处理非正方形的图像,通过裁剪和缩放将其转换为模型所需的输入格式。

20.class ToTensor: 

# NOTE: keep this class for backward compatibility    注意:保留此类是为了向后兼容。
# 这段代码定义了一个名为 ToTensor 的类,用于将图像从 NumPy 数组转换为 PyTorch 张量,并进行必要的预处理操作。
# 定义了一个名为 ToTensor 的类,用于将图像转换为 PyTorch 张量。
class ToTensor:# 将图像从 numpy 数组转换为 PyTorch 张量。# 此类旨在成为转换管道的一部分,例如 T.Compose([LetterBox(size), ToTensor()])。# 方法:# __call__:将张量转换应用于输入图像。"""Converts an image from a numpy array to a PyTorch tensor.This class is designed to be part of a transformation pipeline, e.g., T.Compose([LetterBox(size), ToTensor()]).Attributes:half (bool): If True, converts the image to half precision (float16).Methods:__call__: Applies the tensor conversion to an input image.Examples:>>> transform = ToTensor(half=True)>>> img = np.random.randint(0, 255, (640, 640, 3), dtype=np.uint8)>>> tensor_img = transform(img)>>> print(tensor_img.shape, tensor_img.dtype)torch.Size([3, 640, 640]) torch.float16Notes:The input image is expected to be in BGR format with shape (H, W, C).The output tensor will be in RGB format with shape (C, H, W), normalized to [0, 1]."""# 定义了类的初始化方法,接收一个参数。# 1.half :表示是否将图像数据类型转换为半精度浮点数( torch.float16 )。 默认值为 False ,即默认使用单精度浮点数( torch.float32 )。def __init__(self, half=False):# 初始化 ToTensor 对象以将图像转换为 PyTorch 张量。# 此类旨在用作 Ultralytics YOLO 框架中图像预处理的转换管道的一部分。它将 numpy 数组或 PIL 图像转换为 PyTorch 张量,并提供半精度 (float16) 转换选项。"""Initializes the ToTensor object for converting images to PyTorch tensors.This class is designed to be used as part of a transformation pipeline for image preprocessing in theUltralytics YOLO framework. It converts numpy arrays or PIL Images to PyTorch tensors, with an optionfor half-precision (float16) conversion.Args:half (bool): If True, converts the tensor to half precision (float16). Default is False.Examples:>>> transform = ToTensor(half=True)>>> img = np.random.rand(640, 640, 3)>>> tensor_img = transform(img)>>> print(tensor_img.dtype)torch.float16"""# 调用父类的初始化方法。虽然这里没有明确的父类,但这是一个通用的初始化调用,通常用于多继承场景。super().__init__()# 将 half 参数赋值给类的属性 self.half ,用于后续判断是否需要将数据类型转换为半精度浮点数。self.half = half# 定义了类的 __call__ 方法,使其可以像函数一样被调用,接收一个参数。# 1.im :需要处理的图像(通常是 NumPy 数组)。def __call__(self, im):# 将图像从 numpy 数组转换为 PyTorch 张量。# 此方法将输入图像从 numpy 数组转换为 PyTorch 张量,应用可选的半精度转换和规范化。图像从 HWC 转置为 CHW 格式,颜色通道从 BGR 反转为 RGB。# 参数:# im (numpy.ndarray):输入图像作为 numpy 数组,形状为 (H, W, C),按 BGR 顺序排列。# 返回:# (torch.Tensor):转换后的图像作为 float32 或 float16 的 PyTorch 张量,规范化为 [0, 1],形状为 (C, H, W),按 RGB 顺序排列。"""Transforms an image from a numpy array to a PyTorch tensor.This method converts the input image from a numpy array to a PyTorch tensor, applying optionalhalf-precision conversion and normalization. The image is transposed from HWC to CHW format andthe color channels are reversed from BGR to RGB.Args:im (numpy.ndarray): Input image as a numpy array with shape (H, W, C) in BGR order.Returns:(torch.Tensor): The transformed image as a PyTorch tensor in float32 or float16, normalizedto [0, 1] with shape (C, H, W) in RGB order.Examples:>>> transform = ToTensor(half=True)>>> img = np.random.randint(0, 255, (640, 640, 3), dtype=np.uint8)>>> tensor_img = transform(img)>>> print(tensor_img.shape, tensor_img.dtype)torch.Size([3, 640, 640]) torch.float16"""# 对输入图像进行以下处理。# im.transpose((2, 0, 1)) :将图像从 HWC(高度×宽度×通道)格式转换为 CHW(通道×高度×宽度)格式,以符合 PyTorch 的张量格式。# [::-1] :将通道顺序从 BGR 转换为 RGB(假设输入图像是 BGR 格式)。# np.ascontiguousarray :确保数组在内存中是连续的,这有助于提高后续操作的效率。im = np.ascontiguousarray(im.transpose((2, 0, 1))[::-1])  # HWC to CHW -> BGR to RGB -> contiguous# 使用 torch.from_numpy 将 NumPy 数组转换为 PyTorch 张量。im = torch.from_numpy(im)  # to torch# 根据 self.half 的值,将张量的数据类型转换为半精度浮点数( torch.float16 )或单精度浮点数( torch.float32 )。 如果输入图像是 uint8 类型(范围为 0-255),则将其转换为浮点数类型。im = im.half() if self.half else im.float()  # uint8 to fp16/32# 将像素值从范围 [0, 255] 归一化到 [0.0, 1.0] ,以便后续处理。im /= 255.0  # 0-255 to 0.0-1.0# 返回处理后的 PyTorch 张量。return im
# ToTensor 类实现了一个图像预处理流程,将输入图像从 NumPy 数组转换为 PyTorch 张量,并进行以下操作。格式转换:将图像从 HWC 格式转换为 CHW 格式。将通道顺序从 BGR 转换为 RGB。数据类型转换:将图像数据类型从 uint8 转换为 torch.float16 或 torch.float32 ,具体取决于 half 参数。归一化:将像素值从 [0, 255] 归一化到 [0.0, 1.0] 。这种转换方式广泛应用于深度学习任务中,特别是在将图像数据输入到卷积神经网络(CNN)之前。通过将图像转换为 PyTorch 张量并进行归一化,可以确保模型能够正确处理输入数据。

 

 

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词