数据增强¶
数据增强是训练的重要组成部分。 Detectron2的数据增强系统旨在解决以下目标:
允许同时增强多种数据类型(例如,图像及其边界框和掩码)
允许应用一系列静态声明的增强操作
允许添加自定义的新数据类型以增强功能(如旋转边界框、视频片段等)
处理和操作由增强功能应用的操作
前两个特性涵盖了大多数常见用例,并且在其他库如albumentations中也可用。 支持其他特性会增加detectron2增强API的一些开销,我们将在本教程中对此进行说明。
本教程重点介绍在编写新数据加载器时如何使用数据增强,以及如何编写新的增强方法。如果您使用detectron2中的默认数据加载器,它已经支持接收用户提供的自定义增强列表,如数据加载器教程中所述。
基本用法¶
特征(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(prob=0.5),
T.RandomCrop("absolute", (640, 640))
]) # type: T.Augmentation
# Define the augmentation input ("image" required, others optional):
input = T.AugInput(image, boxes=boxes, sem_seg=sem_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)
这里涉及三个基本概念,分别是:
T.Augmentation 定义了修改输入的"策略"。
它的
__call__(AugInput) -> Transform方法会原地增强输入数据,并返回所应用的操作
T.Transform 实现了实际的数据转换操作
它拥有诸如
apply_image、apply_coords等方法,用于定义如何转换每种数据类型
T.AugInput 存储了
T.Augmentation所需的输入及其应如何转换的信息。 这个概念适用于某些高级用法场景。 对于所有常见用例来说,直接使用这个类应该就足够了, 因为不在T.AugInput中的额外数据可以通过返回的transform进行增强,如上例所示。
编写新的数据增强方法¶
大多数2D增强只需要了解输入图像。这样的增强可以像这样轻松实现:
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)
除了图像外,只要属于函数签名的一部分,给定AugInput的任何属性都可以使用,例如:
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)
新的变换操作也可以通过继承T.Transform类来添加。
高级用法¶
我们列举了一些由我们的系统实现的高级用法示例。这些选项可能对新研究很有吸引力,尽管在标准使用场景中通常不需要更改它们。
自定义转换策略¶
detectron2的Augmentation不仅返回增强后的数据,还会将操作作为T.Transform返回。
这使得用户能够对自己的数据应用自定义的转换策略。
我们以关键点数据为例进行说明。
关键点是(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(axis=1)
# btw, detectron2's `transform_keypoint_annotations` function chooses to label such keypoints "visible":
# keypoints_xy = transform.apply_coords(keypoints_xy)
# visibility &= (keypoints_xy >= [0, 0] & keypoints_xy <= [W, H]).all(axis=1)
几何反转变换¶
如果在推理前图像经过增强预处理,预测结果(如分割掩码)将定位在增强后的图像上。我们希望使用inverse() API来反转应用的增强效果,从而在原始图像上获得结果:
transform = augs(input)
pred_mask = make_prediction(input.image)
inv_transform = transform.inverse()
pred_mask_orig = inv_transform.apply_segmentation(pred_mask)
添加新的数据类型¶
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(width=800)
transformed_rotated_boxes = t.apply_rotated_boxes(rotated_boxes) # func will be called
扩展 T.AugInput¶
An augmentation can only access attributes available in the given input. T.AugInput defines “image”, “boxes”, “sem_seg”, which are sufficient for common augmentation strategies to decide how to augment. If not, a custom implementation is needed.
通过在AugInput中重新实现"transform()"方法,还可以实现相互依赖的不同字段增强。这种用例并不常见(例如基于增强后的掩码进行边界框后处理),但系统允许这种操作。