2026/9/10 16:08:18

Detectron2 数据增强系统实战指南:Augmentation / Transform / AugInput 架构、自定义增强与进阶用法

Detectron2 数据增强系统实战指南:Augmentation / Transform / AugInput 架构、自定义增强与进阶用法 Detectron2 数据增强系统实战指南Augmentation / Transform / AugInput 架构、自定义增强与进阶用法【免费下载链接】detectron2Detectron2 is a platform for object detection, segmentation and other visual recognition tasks.项目地址: https://gitcode.com/GitHub_Trending/de/detectron2数据增强Data Augmentation是目标检测、实例分割等视觉模型训练中不可或缺的一环。本指南以 Detectron2 官方教程 docs/tutorials/augmentation.md 为核心骨架深入剖析其增强系统的三大核心抽象 ——Augmentation策略、Transform操作、AugInput输入容器并结合仓库源码detectron2/data/transforms/augmentation.py、augmentation_impl.py、transform.py补充底层实现细节。读完本文你将能够在自定义数据加载器中组合多类型数据图像 边界框 掩码的增强流水线编写全新的 Augmentation 与 Transform掌握 keypoint 水平翻转、变换求逆、新数据类型注册等进阶技巧并将自定义增强无缝接入默认的DatasetMapper训练管线。设计目标为什么 Detectron2 需要这样一套增强系统在 docs/tutorials/augmentation.md 的开篇官方明确了增强系统的四个核心目标允许同时增强多种数据类型例如图像连同其边界框、掩码一起增强允许应用一串静态声明的增强序列即代码中按顺序组合多个增强允许为增强添加自定义的新数据类型如旋转边界框 rotated boxes、视频片段等允许对增强所施加的操作operations进行读取与操控例如拿到实际生效的翻转操作手动处理带语义的关键点标注。前两个目标覆盖了绝大多数常见用例这也是 albumentations 等库已经具备的能力后两个目标则为新的研究场景提供了灵活的扩展点但也会给 API 带来一定复杂度 —— 这正是本教程要讲清楚的部分。从源码看这一设计最终落地为三个互相协作的抽象类详见 detectron2/data/transforms/augmentation.py 的__all__导出列表Augmentation定义策略policy即决定对输入施加何种修改。调用其__call__(AugInput) - Transform会在原地修改输入并返回实际施加的操作。Transform实现真正的操作operation即执行数据的变换。它提供apply_image、apply_coords、apply_box、apply_segmentation、apply_polygons等方法分别定义如何变换每种数据类型。AugInput存放Augmentation所需的输入数据及其变换方式。对于常见场景直接使用此类即可因为不在AugInput中的额外数据可以用返回的Transform单独变换。从代码层面看这套策略与操作分离的设计有一个关键收益Transform是确定性的。正如 augmentation.py 的类文档所强调的返回的Transform描述的是确定性变换因此可以被重新施加到关联数据上——例如一张图像的几何变换必须与其分割掩码同步进行。这意味着先由Augmentation随机生成一次Transform再把这个Transform复用到图像、标注、mask 等多个数据上保证它们变换完全一致。基本用法一条静态声明的多数据增强流水线完整示例与运行机制官方文档给出的基本用法对应特性 1 与 2如下from detectron2.data import transforms as T # Define a sequence of augmentations: augs T.AugmentationList([ T.RandomBrightness(0.9, 1.1), T.RandomFlip(prob0.5), T.RandomCrop(absolute, (640, 640)) ]) # type: T.Augmentation # Define the augmentation input (image required, others optional): input T.AugInput(image, boxesboxes, sem_segsem_seg) # Apply the augmentation: transform augs(input) # type: T.Transform image_transformed input.image # new image sem_seg_transformed input.sem_seg # new semantic segmentation # For any extra data that needs to be augmented together, use transform, e.g.: image2_transformed transform.apply_image(image2) polygons_transformed transform.apply_polygons(polygons)这里涉及三个基本概念逐一拆解Augmentation定义策略它的__call__(AugInput) - Transform方法会原地增强输入并返回被施加的操作。注意它是策略而非操作——它只负责根据输入数据决定要做什么具体怎么做由返回的Transform负责。Transform实现操作它提供apply_image、apply_coords等方法来定义每种数据类型的变换方式。示例最后两行正是这一思想的体现对于不在AugInput里的额外数据第二张图、多边形标注等直接用返回的transform去变换。AugInput存储输入它存放Augmentation需要的输入数据以及这些数据应当如何被变换。直接使用这个类即可覆盖所有常见场景因为AugInput之外的额外数据可以通过返回的transform变换如上例所示。底层实现AugmentationList与AugInput.__call__的调用链从源码看AugmentationList本质是一个特殊的Augmentation子类augmentation.py。它的__init__会把列表中的每一项统一包装成Augmentation若传入的是Transform则通过_transform_to_aug包一层_TransformToAug其__call__会按顺序依次施加每个增强并收集所有Transformdef __call__(self, aug_input) - TransformList: tfms [] for x in self.augs: tfm x(aug_input) tfms.append(tfm) return TransformList(tfms)需要注意AugmentationList的get_transform方法被设计为不可调用调用会报错因为要施加第 k 个增强必须先施加第 k-1 个增强才能得到第 k 个增强所需的输入。单次Augmentation.__call__的完整调用链augmentation.py是_get_aug_input_args通过inspect.signature解析get_transform的参数名。若只有一个参数则默认为image保证向后兼容否则按参数名从AugInput中getattr取出对应属性若属性不存在则抛出AttributeError提示该增强需要什么输入。调用self.get_transform(*args)执行策略生成一个或一组Transform。校验返回值必须是Transform或TransformList。调用aug_input.transform(tfm)原地变换AugInput中的所有属性。AugInput即StandardAugInput的别名见 augmentation.py标准地提供image、boxes、sem_seg三个属性其transform方法对三个属性分别调用apply_image、apply_box、apply_segmentationaugmentation.py。它还有便捷方法apply_augmentations等价于AugmentationList(augmentations)(self)模块级函数apply_augmentations/apply_transform_gens则提供了兼容旧版TransformGenAPI 的入口若传入的是裸 ndarray 会自动包成AugInputaugmentation.py。输入约定与数据格式在自定义增强前需要了解AugInput与get_transform对数据格式的约定augmentation.pyimage(H,W)或(H,W,C)的 ndarrayuint8取值[0, 255]或浮点取值[0, 1]/[0, 255]boxes(N,4)的 float32 ndarray表示 N 个实例边界框采用XYXY 绝对坐标格式sem_seg(H,W)的 uint8 ndarray每个元素是像素的整数类别标签。同时 augmentation.py 的_check_img_dtype会校验输入必须是 ndarrayuint8或浮点类型且维度为 2 或 3。除此之外的类型没有内建约定detectron2 中也没有使用其他类型的内建Augmentation。编写新的 Augmentation最简单的写法只依赖图像绝大多数二维增强只需要知道输入图像即可。官方文档给出的模板如下class MyColorAugmentation(T.Augmentation): def get_transform(self, image): r np.random.rand(2) return T.ColorTransform(lambda x: x * r[0] r[1] * 10) class MyCustomResize(T.Augmentation): def get_transform(self, image): old_h, old_w image.shape[:2] new_h, new_w int(old_h * np.random.rand()), int(old_w * 1.5) return T.ResizeTransform(old_h, old_w, new_h, new_w) augs MyCustomResize() transform augs(input)这里出现的内建Transform来自 transform.pyColorTransform光度变换的通用包装。它只影响颜色空间不影响坐标空间——因此apply_coords直接原样返回坐标apply_segmentation原样返回分割inverse返回NoOpTransformtransform.py。其子类PILColorTransform则接收一个作用于 PIL Image 的可调用对象。ResizeTransform图像缩放到目标尺寸。apply_image对 uint8 图像走 PIL 插值对浮点图像走torch.nn.functional.interpolatePIL 只支持 uint8apply_coords按new/old比例缩放坐标apply_segmentation使用最近邻插值避免类别标签被污染inverse()返回一个尺寸互换的ResizeTransformtransform.py。另有两个值得注意的内建TransformRotationTransformcv2 实现inverse()返回反向旋转 裁剪的TransformList若expandFalse则无法求逆并抛NotImplementedError与ExtentTransform基于 PIL 的子区域提取 缩放transform.py。依赖多种输入利用函数签名自动注入Augmentation不仅能访问image——只要某个属性存在于给定的AugInput中且它出现在get_transform的函数签名里就会被自动注入这正是前文_get_aug_input_args用inspect.signature解析参数名的原因。官方示例class MyCustomCrop(T.Augmentation): def get_transform(self, image, sem_seg): # decide where to crop using both image and sem_seg return T.CropTransform(...) augs MyCustomCrop() assert hasattr(input, image) and hasattr(input, sem_seg) transform augs(input)仓库内建增强中就有这类多输入依赖的实例可以作为参考RandomCrop_CategoryAreaConstraintaugmentation_impl.pyget_transform(self, image, sem_seg)会结合语义分割真值寻找裁剪窗口使得在裁剪后的分割图中没有任何单一类别占比超过single_category_max_area可缓解语义分割训练中的类别失衡最多尝试 10 次。MinIoURandomCropaugmentation_impl.pyget_transform(self, image, boxes)基于边界框做随机裁剪要求裁剪块与原图/框的最小 IoU 不低于从min_ious(0.1, 0.3, 0.5, 0.7, 0.9)中随机选取的阈值且保证框中心落在裁剪块内。注意自定义get_transform若使用*args/**kwargs变长参数默认的__call__会直接抛出TypeErroraugmentation.py——此时需要自行重写__call__方法。另外新增的变换操作也可以通过继承T.Transform来实现T.Transform的基类定义在fvcore.transforms.transformdetectron2/data/transforms/init.py 将其作为基础导出。内建 Augmentation 速查表augmentation_impl.py 提供了大量可直接使用的内建增强其__all__列表如下增强类功能关键参数RandomFlip按概率水平/垂直翻转prob默认 0.5、horizontal、vertical二者不能同时为 TrueResize缩放到固定尺寸shape(h,w)元组或整数、interpPIL 插值方式默认双线性ResizeShortestEdge保持长宽比缩放短边对齐目标长度short_edge_length、max_size、sample_stylerange或choiceResizeScale随机缩放目标尺寸后等比缩放图像Google resize_and_crop 风格min_scale、max_scale、target_height、target_widthRandomResize从给定形状列表中随机选一个缩放shape_list(h,w)列表RandomRotation随机角度旋转angle、expand、center、sample_styleRandomCrop随机裁剪crop_typerelative/relative_range/absolute/absolute_range、crop_sizeFixedSizeCrop裁剪 必要时填充到固定尺寸crop_size、pad、pad_value默认 128、seg_pad_value默认 255RandomCrop_CategoryAreaConstraint带类别面积约束的随机裁剪同RandomCropsingle_category_max_area、ignored_categoryMinIoURandomCrop带最小 IoU 约束的随机裁剪min_ious、min_crop_size、mode_trials、crop_trialsRandomExtent随机子区域提取可含图像外区域填零scale_range、shift_rangeRandomBrightness/RandomContrast随机亮度/对比度调整intensity_min、intensity_max1 保持不变RandomSaturation随机饱和度调整RGBintensity_min、intensity_maxRandomLightingAlexNet 式 PCA 光照抖动RGBscale主成分权重标准差RandomApply以给定概率包一层地施加另一增强tfm_or_aug、prob其中RandomCrop的四种crop_type语义augmentation_impl.py值得展开relative裁剪(H*ch, W*cw)区域比例需在(0,1]relative_range从[crop_size[0], 1]与[crop_size[1], 1]均匀采样比例后再相对裁剪absolute直接裁剪(crop_size[0], crop_size[1])像素区域不能大于输入absolute_range在[crop_size[0], min(H, crop_size[1])]内均匀采样高度、宽度后裁剪。RandomFlip则规定水平、垂直翻转不能同时开启需用两个RandomFlip实例替代augmentation_impl.py。进阶用法以下用法由该系统架构天然支持对标准用例通常无需改动但对新的研究工作很有价值。自定义变换策略以关键点为例由于Augmentation返回的是操作T.Transform用户可以据此对数据施加自定义的变换策略。关键点keypoints是最典型的例子它们是(x, y)坐标但由于携带语义含义如左眼右眼变换起来并不简单这种语义只有用户自己知道因此用户可以查看返回的transform手动增强关键点。例如图像水平翻转时需要交换左眼和右眼的标注此逻辑默认包含在 detectron2 的默认数据加载器中# augs, input are defined as in previous examples transform augs(input) # type: T.Transform keypoints_xy transform.apply_coords(keypoints_xy) # transform the coordinates # get a list of all transforms that were applied transforms T.TransformList([transform]).transforms # check if it is flipped for odd number of times do_hflip sum(isinstance(t, T.HFlipTransform) for t in transforms) % 2 1 if do_hflip: keypoints_xy keypoints_xy[flip_indices_mapping]关键点另一个常见需求是可见性字段处理一串增强可能把可见关键点变换到图像边界之外如裁剪之后又被其他增强拉回边界内如填充。如果希望把越界的关键点标为不可见可见性检查必须在每个变换步骤之后进行transform augs(input) # type: T.TransformList assert isinstance(transform, T.TransformList) for t in transform.transforms: keypoints_xy t.apply_coords(keypoints_xy) visibility (keypoints_xy [0, 0] keypoints_xy [W, H]).all(axis1)注detectron2 自带的transform_keypoint_annotations函数选择将此类关键点标为可见。上述手动逻辑在仓库中的正式实现位于 detectron2/data/detection_utils.py 的transform_keypoint_annotations先对坐标apply_coords将越界点标记为visibility0unlabeled再通过sum(isinstance(t, T.HFlipTransform) ...) % 2 1判断是否经历了奇数次水平翻转若翻转则依据keypoint_hflip_indices交换左右对应的关键点索引例如 COCO 中left_eye与right_eye互换最终按 COCO 约定把visibility0的点坐标清零。keypoint_hflip_indices由 detection_utils.py 的create_keypoint_hflip_indices根据数据集元数据keypoint_names与keypoint_flip_map生成并在DatasetMapper.from_config中自动传入dataset_mapper.py。几何求逆把预测结果映射回原图如果图像在推理前经过增强预处理那么预测结果如分割掩码定位在增强后的图像上。若希望把结果映射回原图可用inverse()APItransform augs(input) pred_mask make_prediction(input.image) inv_transform transform.inverse() pred_mask_orig inv_transform.apply_segmentation(pred_mask)inverse()是Transform的可选能力并非所有变换都支持如ResizeTransform.inverse()返回尺寸互换的缩放transform.pyRotationTransform.inverse()返回反向旋转 裁剪的组合expandFalse时因信息丢失抛NotImplementedErrortransform.pyColorTransform.inverse()返回NoOpTransform光度变换不可逆transform.py。对TransformList求逆时注意应对列表做反向组合该逻辑由 fvcore 基类实现。注册新的数据类型T.Transform内建支持图像、坐标、掩码、边界框、多边形等常见类型并允许注册新数据类型。官方示例是注册旋转框的变换方法T.HFlipTransform.register_type(rotated_boxes) def func(flip_transform: T.HFlipTransform, rotated_boxes: Any): # do the work return flipped_rotated_boxes t HFlipTransform(width800) transformed_rotated_boxes t.apply_rotated_boxes(rotated_boxes) # func will be calledregister_type机制在仓库中有真实用例由于旋转边界框rotated boxes格式为(x_center, y_center, width, height, angle_degrees)是一种独立数据类型transform.py 为其注册了两种内置变换——HFlip_rotated_box翻转x_center并取反角度与Resize_rotated_box按ResizeTransform的缩放因子近似变换中心、宽高与角度分别通过HFlipTransform.register_type(rotated_box, ...)与ResizeTransform.register_type(rotated_box, ...)挂载。测试 tests/data/test_transforms.py 中的test_apply_rotated_boxes等用例即用于验证该机制的坐标变换正确性。注册完成后用户即可通过transform.apply_rotated_box(boxes)统一调用。扩展 T.AugInput增强只能访问给定输入中存在的属性。T.AugInput即StandardAugInput定义了image、boxes、sem_seg三个属性对常见增强策略已经足够如果不够就需要自定义实现。从源码看自定义AugInput需要满足两个条件augmentation.py以属性访问getattr的形式提供数据——例如某增强需要image与sem_seg参数其输入就必须具备同名属性提供一个transform(tfm: Transform) - None方法用于原地变换所有属性。此外通过重写AugInput的transform()方法还可以让不同字段以相互依赖的方式被增强例如基于增强后的掩码后处理边界框。这类用例不常见但系统允许这样做。把自定义增强接入训练管线DatasetMapper 与配置本教程聚焦于编写新数据加载器时如何使用增强以及如何编写新增强。如果你使用 detectron2 的默认数据加载器它已经支持接收用户提供的自定义增强列表详见 数据加载教程。默认的映射器DatasetMapperdetectron2/data/dataset_mapper.py展示了增强系统的完整落地方式构造参数augmentations接收List[Union[T.Augmentation, T.Transform]]在__init__中被包装为T.AugmentationList(augmentations)from_config(cfg, is_train)调用utils.build_augmentation(cfg, is_train)从配置构建默认增强训练时使用ResizeShortestEdge(cfg.INPUT.MIN_SIZE_TRAIN, cfg.INPUT.MAX_SIZE_TRAIN, sample_stylecfg.INPUT.MIN_SIZE_TRAIN_SAMPLING)若cfg.INPUT.RANDOM_FLIP ! none则追加RandomFlip水平/垂直取决于配置值推理时固定sample_stylechoice当cfg.INPUT.CROP.ENABLED且为训练时会在列表最前面插入RandomCrop(cfg.INPUT.CROP.TYPE, cfg.INPUT.CROP.SIZE)detection_utils.py__call__中读取图像与语义分割真值后构造T.AugInput(image, sem_segsem_seg_gt)调用self.augmentations(aug_input)完成增强再通过transform_instance_annotations内部调用apply_box、apply_polygons/apply_segmentation、transform_keypoint_annotationsdetection_utils.py与annotations_to_instances把标注转成模型需要的Instances格式。若想替换默认增强只需向build_detection_train_loader传入自定义 mapper。来自 数据加载教程 的最小示例import detectron2.data.transforms as T from detectron2.data import DatasetMapper # the default mapper dataloader build_detection_train_loader(cfg, mapperDatasetMapper(cfg, is_trainTrue, augmentations[ T.Resize((800, 800)) ])) # use this dataloader instead of the default更彻底的自定义 mapper 写法例如把所有图像缩放到固定尺寸并手动变换标注from detectron2.data import detection_utils as utils # Show how to implement a minimal mapper, similar to the default DatasetMapper def mapper(dataset_dict): dataset_dict copy.deepcopy(dataset_dict) # it will be modified by code below # can use other ways to read image image utils.read_image(dataset_dict[file_name], formatBGR) # See Data Augmentation tutorial for details usage auginput T.AugInput(image) transform T.Resize((800, 800))(auginput) image torch.from_numpy(auginput.image.transpose(2, 0, 1)) annos [ utils.transform_instance_annotations(annotation, [transform], image.shape[1:]) for annotation in dataset_dict.pop(annotations) ] return { # create the format that the model expects image: image, instances: utils.annotations_to_instances(annos, image.shape[1:]) } dataloader build_detection_train_loader(cfg, mappermapper)这段代码清晰展示了本教程核心概念的组合方式AugInput装图像 →Resize增强原地变换并返回Transform→ 用transform配合transform_instance_annotations同步变换边界框、掩码与关键点标注。从源码结构看这套用法完全沿用了默认DatasetMapper的既有链路因此自定义增强与默认训练流程的兼容性是有保证的。小结Detectron2 的增强系统通过三个职责分明的抽象解决了多数据类型协同增强、静态增强序列、可扩展数据类型、操作可操控四个设计目标Augmentation决定做什么策略Transform负责怎么做操作AugInput集中管理输入与原地变换策略与操作分离使得一次随机生成的Transform可以确定性地复用于图像、框、掩码、关键点等所有关联数据编写新增强只需继承Augmentation并实现get_transform参数名即决定了它依赖AugInput中的哪些字段关键点翻转语义处理、inverse()求逆、register_type注册新数据类型、自定义AugInput等进阶能力为新的研究方向提供了充分的灵活性默认训练管线DatasetMapperbuild_augmentation已经完整集成了这套系统用户通过mapper参数传入自定义增强列表即可无缝替换相关实现可对照 detectron2/data/dataset_mapper.py、detectron2/data/detection_utils.py 与 detectron2/data/transforms/ 目录深入阅读。【免费下载链接】detectron2Detectron2 is a platform for object detection, segmentation and other visual recognition tasks.项目地址: https://gitcode.com/GitHub_Trending/de/detectron2创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考