这一篇主要关于deformable DETR的数据集实现。其核心部分使用的是COCO API,因此我们这么关注的是其数据增强如何实现。
datasets/torchvision_datasets/coco.py
该文件是COCODataset的基类,实现了初始化、__get_item__
方法以及cache_images
方法,cache_images
方法是将数据提前存入缓存,以减少读写的时间消耗。图像格式是RGB。
datasets/coco.py
CocoDetection
类首先定义了datasets/torchvision_datasets/coco.py
中COCODataset
的一个子类CocoDetection
,该类是deformable DETR直接使用的数据类。该类实现的功能可以分成三步:利用父类的__getitem__
读取图像和标签,对标签数据进行整合(即CovertCocoPolysToMask
类),最后对图像和标签同时进行增强(即make_coco_transforms
方法)。
CovertCocoPolysToMask
类该类的主要功能:
boxes, labels, masks, image_id, keypoints, area, is_crowd, orig_size, size
, 其中最后两个分别表示每个box的原始和现在的size,目前原始和现在的size还是一致的。make_coco_transforms
方法该方法针对不同的数据子集给定了不同的数据变换方法。
RandomSelect
表示参数里的两种变换二选一,而两个变换分别是:图像缩放到目标尺寸和图像任意裁剪后缩放到目标尺寸,目标尺寸是指max_size=1333
datasets/transforms.py
图像的各种缩放、翻转通过torchvision.transforms
中提供的接口就可以实现,但为了能够让target进行同样的变换,代码对transforms进行了进一步封装,具体而言就是封装类中通过get_params
得到变换参数,然后利用该参数变换target。
RandomCrop
类,给定size,在图像中随机裁剪出对应size的区域,其对应的调整target的方法为crop(image, target,region)
, 主要功能是利用裁剪框左上角的坐标调整box的坐标,同时利用裁剪框右下角坐标对box进行裁剪和筛选。其他的因此导致的改变量包括area, masks, size
RandomSizeCrop
类与RandomCrop
的唯一区别在于指定了size的区间范围,随机生成一个size,然后进行裁剪。
CenterCrop
类和RandomCrop
类的区别是指定裁剪区域的中心和image的中心重叠。
RandomHorizontalFlip
类以0.5的概率对图像进行反转,翻转的同时注意对box和mask的反转,见hflip
方法。
RandomSelect
输入是两种transform,默认0.5的概率随机选择一种
-ToTensor
类将图像转成tensor类型,应为target在CovertCocoPolysToMask
类中已经转换成tensor
Normalize
类值得注意,该类中不仅对图像进行了归一化处理,还对box进行了归一化处理,并且将box的格式由xyxy转换成了cxcywh
Compose
类是多个transforms的串行,类似nn.Sequential
RandomResize
类对图像和target进行尺寸的缩放,其输入为size的list,这里的size是希望变换后图像的最短边的长度,该类从list中随机均匀选择一个尺寸进行resize操作,调用resize方法实现。
来看resize
方法:
- get_size
方法如果给定的size是元组或list,则返回(oh, ow), 如果size是个scale,则调用get_size_with_aspect_ratio
进行等比例的缩放。
- get_size_with_aspect_ratio
方法中如果没有指定max_size
则将最短边缩放至size
,同时场边等比例缩放。如果指定了max_size
那么最长边为max_size
,短边成比例缩放。
- 获得两条边的伸缩比例,对box和area,size进行调整,为什么两条边的伸缩比例不同?是因为int()
的使用,对缩放的边长取整。
另外还有两个定义没使用的变换类:
RandomErasing
类调用torchvision.transforms
中的RandomErasing
实现随机擦除,
Randomly selects a rectangle region in an image and erases its pixels. 'Random Erasing Data Augmentation’ by Zhong et al. See https://arxiv.org/abs/1708.04896
RandomPad
类的目的是希望实现image的右下角的padding,但该代码中的pad
方法实现有问题。此处做一个修改:
def pad(image, target, padding):
# assumes that we only pad on the bottom right corners
padded_image = F.pad(image, (0, padding[0], 0, padding[1]))
if target is None:
return padded_image, None
target = target.copy()
# should we do something wrt the original size?
target["size"] = torch.tensor(padded_image.size[::-1])
if "masks" in target:
target['masks'] = torch.nn.functional.pad(target['masks'], (0, padding[0], 0, padding[1]))
return padded_image, target
我们也注意到CocoDetection提供的图像尺寸是不太相同的,所以在collect_fn
方法中需要对batch内的图像进行padding统一,使用的方法即utils/misc.py
中的nested_tensor_from_tensor_list
。这个方法中
max_size = _max_by_axis([list(img.shape) for img in tensor_list])
是在每个维度上选择最大值,然后将所有的图像嵌入在左上角,也就是完成了右下角的padding,注意这个函数提供了mask的设置,True表示的是padding的部分,False表示原始图像的位置。这个也就是在position_encoding.py
中PositionEmbeddingSine
的forward方法中的mask。
联系客服