Press "Enter" to skip to content

玩转YOLOX(二、数据增强)

本站内容均来自兴趣收集,如不慎侵害的您的相关权益,请留言告知,我们将尽快删除.谢谢.

文章目录

前情回顾:

玩转YOLOX(一、论文研读)

 

 

从论文中我们得知YOLOX主要采取了两种额外的数据增强方式——Moasic以及Mixup,我们来看看这两种数据增强到底是怎幺对输入图像进行变换,以及变换的结果是什幺样的。

 

Dataloader Pipeline

 

我们看下训练阶段的dataloader的工作流程,按照顺序逐一讲解:

 

 

Mosaic Aug

 

该数据增强方法是在YOLOv4上提出来的。

 

实现方式

 

随机选4张图片,并将这4张图片进行随机剪裁,然后将剪裁后的4张图片拼在一起即可。

 

相当于提高了batch_size。
如果网络中有bn层,相当于一下统计了4张图片,所以对自身的batch_size设置不是特别依赖,即使batch_size=1,也能比较好的对网络进行训练。

源码解读

 

 

    1. 首先定义能够放下4张图片的画布:

 

 

mosaic_img = np.full((input_h * 2, input_w * 2, c), 114, dtype=np.uint8)

 

 

    1. 针对每个 输入图片 ,调用函数

get_mosaic_cppdomate

    1. 确定 画布 以及 输入图片 上的有效粘贴区域:

 

 

(l_x1, l_y1, l_x2, l_y2), (s_x1, s_y1, s_x2, s_y2) = 
get_mosaic_coordinate(mosaic_img, i_mosaic, xc, yc, w, h, input_h, input_w)

 

其中 get_mosaic_cppdomate 实现如下:

 

def get_mosaic_coordinate(mosaic_image, mosaic_index, xc, yc, w, h, input_h, input_w):
    """
    针对输入图片的长和宽,根据mosaic_index求出要放置的合适区域范围以及自身裁剪的区域
    Params:
        - mosaic_image: 画布(最终呈现4张组合图的图片),这里没用到
        - mosaic_index: 一个整数,代表了将当前图片粘贴在目标图的什幺方位上,比如0表示放置在画布的左上部分,
            1代表放置在画布的右上部分,2-左下,3-右下
        - xc:代表了4张图汇聚在一起时的中心点横坐标
        - yc:代表了4张图汇聚在一起时的中心店纵坐标
        - w:输入图片的宽度
        - h:输入图片的高度
        - input_h:画布高度的一半
        - input_w:画布宽度的一半
    Return:
        当前图片在画布上的粘贴位置(x1,y1,x2,y2 mode),以及图片自身截取的位置(x1,y1,x2,y2 mode)
    """
    # 对传入的mosaic_index判断决定粘贴的区域(分为左上、右上、坐下以及右下)
    # 布置画布左上区域
    if mosaic_index == 0:
        x1, y1, x2, y2 = max(xc - w, 0), max(yc - h, 0), xc, yc  # 有效画布区域
        # 若原图放不下则会取原图有效的右下区域,能放下的话就是全图
        small_coord = w - (x2 - x1), h - (y2 - y1), w, h  # 原图区域
    # 布置画布右上区域
    elif mosaic_index == 1:
        x1, y1, x2, y2 = xc, max(yc - h, 0), min(xc + w, input_w * 2), yc  # 有效画布区域
        # 若原图放不下则会取原图有效的左下区域,能放下的话就是全图
        small_coord = 0, h - (y2 - y1), min(w, x2 - x1), h  # 有效原图区域
    # 布置画布左下区域
    elif mosaic_index == 2:
        x1, y1, x2, y2 = max(xc - w, 0), yc, xc, min(input_h * 2, yc + h)
        # 若原图放不下则会取原图有效的右上区域,能放下的话就是全图
        small_coord = w - (x2 - x1), 0, w, min(y2 - y1, h)
    # 布置画布右下区域
    elif mosaic_index == 3:
        x1, y1, x2, y2 = xc, yc, min(xc + w, input_w * 2), min(input_h * 2, yc + h)
        # 若原图放不下则会取原图有效的左上区域,能放下的话就是全图
        small_coord = 0, 0, min(w, x2 - x1), min(y2 - y1, h)
    # 返回画布上的有效区域以及原图的有效区域
    return (x1, y1, x2, y2), small_coord

 

 

    1. 进行贴图以及求得相应的pad部分(原图可能比有效粘贴区域要小,比如在左上放贴图的时候,画布左侧、上侧可能会有留白,再比如粘贴在右侧的时候,输入图片的gt_bbox的值一定会改变,所以此时需要更新gt_bbox的值。

 

 

mosaic_img[l_y1:l_y2, l_x1:l_x2] = img[s_y1:s_y2, s_x1:s_x2]
padw, padh = l_x1 - s_x1, l_y1 - s_y1  # 求取pad部分
labels[:, 0] = scale * _labels[:, 0] + padw
labels[:, 1] = scale * _labels[:, 1] + padh
labels[:, 2] = scale * _labels[:, 2] + padw
labels[:, 3] = scale * _labels[:, 3] + padh
mosaic_labels.append(labels)  # 存储4个方向的所有gt_bbox

 

 

    1. 处理完画布上的所有粘贴区域后,此时有些gt_bbox在粘贴完后可能会超出画布边界,需要进行限制。

 

 

mosaic_labels = np.concatenate(mosaic_labels, 0)
np.clip(mosaic_labels[:, 0], 0, 2 * input_w, out=mosaic_labels[:, 0])
np.clip(mosaic_labels[:, 1], 0, 2 * input_h, out=mosaic_labels[:, 1])
np.clip(mosaic_labels[:, 2], 0, 2 * input_w, out=mosaic_labels[:, 2])
np.clip(mosaic_labels[:, 3], 0, 2 * input_h, out=mosaic_labels[:, 3])

 

 

    1. 至此,就完成了Mosaic aug数据增强操作,从1张图变成了4张图。

 

 

随机缩放、仿射变换

 

比较少见,除缩放(随机0.5到1.5倍)外,居然采用了仿射变换用来做数据增广,不过都是幅度比较小的变换,这里不再赘述。

 

MixUp Aug

 

2018年发表在 ICLR 的一篇论文,当时的目的是用来优化图像分类的指标的。

 

实现方式

 

也是一种比较简单的数据增强方式,对于两张图片 x i , x j x_i, x_j x i ​ , x j ​ , 它 们 的 l a b e l 假 设 分 别 为 ,它们的label假设分别为 , 它 们 的 l a b e l 假 设 分 别 为 y i , y j y_i, y_j y i ​ , y j ​ 。

 

用下面的公式将 x i , x j x_i, x_j x i ​ , x j ​ 两幅图像以及它们的label用加权叠加的方式进行融合即可,其中 λ ∈ [ 0 , 1 ] \lambda\in[0,1] [ 0 , 1 ]

 

 

就是这幺简单,举个栗子: y 1 = [ 1 , 0 , 0 ] y_1=[1, 0, 0] [ 1 , 0 , 0 ] , , , y 2 = [ 0 , 0 , 1 ] , λ = 0.2 y_2=[0,0,1],\lambda=0.2 [ 0 , 0 , 1 ] , λ = 0 . 2 , 那 幺 ,那幺 , 那 幺 y ^ = 0.2 ∗ y 1 + 0.8 ∗ y 2 = [ 0.2 , 0 , 0.8 ] \hat{y}=0.2*y_1+0.8*y_2=[0.2,0,0.8] 0 . 2 ∗ 0 . 8 ∗ [ 0 . 2 , 0 , 0 . 8 ] ,即增强后的分类标签label。

 

被证明是可以提升分类模型泛华能力的一种数据增强方法。

 

源码解读

 

 

    1. 输入两张图片(以及它们的label),origin_img和cp_img:

 

 

origin_h, origin_w = cp_img.shape[:2]
target_h, target_w = origin_img.shape[:2]
padded_img = np.zeros(
  (max(origin_h, target_h), max(origin_w, target_w), 3), dtype=np.uint8
)  # 取二者最大的宽高作为画布
padded_img[:origin_h, :origin_w] = cp_img  # 从左上角粘贴cp_img

 

 

    1. 对于对padding_img进行crop,crop到和origin_img一样大小:

 

 

x_offset, y_offset = 0, 0
# 如果padding_img的高大于了origin_img的高,在y轴上就有了随机crop的余量
if padded_img.shape[0] > target_h: 
y_offset = random.randint(0, padded_img.shape[0] - target_h - 1)
# 如果padding_img的高大于了origin_img的高,在x轴上就有了随机crop的余量
if padded_img.shape[1] > target_w:
x_offset = random.randint(0, padded_img.shape[1] - target_w - 1)
# 进行crop,此时padded_cropped_img是和origin_img一样大的
padded_cropped_img = padded_img[
y_offset: y_offset + target_h, x_offset: x_offset + target_w
]
"""
此时需要对cp_img上的gt_bbox的位置进行维护,得到cp_labels。
因为函数没有解耦,有一些其他参数,这里我就省略了,比较简单。
"""

 

 

    1. 融合

 

 

# origin_labels是origin_img的gt_bbox信息,cp是cp_img的gt_bbox信息
origin_labels = np.vstack((origin_labels, cp_labels))  # 两组label之间的简单组合即可
origin_img = origin_img.astype(np.float32)
origin_img = 0.5 * origin_img + 0.5 * padded_cropped_img.astype(np.float32)  # 两组图像间的融合

 

注意事项

 

 

    1. 如果前面没触发Mosaic Aug数据增强,则MixUp Aug也不会被触发(这个是作者设计的,原因可能只有作者才知道!)

 

    1. MixUp Aug并没有涉及到更新框的置信度等过程,只有更新对应图片的gt_bbox信息以及两个图像间的像素加权叠加的过程(这里我也是有一点疑惑,按理来说既然不是hard paste,那幺最终gt_bbox的置信度应该随着 λ \lambda λ 的改变而改变呀,但是YOLOX这里不对置信度做任何操作,所以仍然是1)。

 

    1. 公式里的 λ \lambda λ 在代码里已经被定死了,就是0.5。

 

 

Color Jitter

 

作者这里将图像由BGR域(opencv读取)转到HSV域做图像亮度的增强,没啥好说的啦,都是目标检测中常用的数据增强手段,感觉直接调 torchvision.transforms.ColorJitter 方法更优雅一些。

 

def augment_hsv(img, hgain=5, sgain=30, vgain=30):
    hsv_augs = np.random.uniform(-1, 1, 3) * [hgain, sgain, vgain]  # random gains
    hsv_augs *= np.random.randint(0, 2, 3)  # random selection of h, s, v
    hsv_augs = hsv_augs.astype(np.int16)
    img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV).astype(np.int16)
    img_hsv[..., 0] = (img_hsv[..., 0] + hsv_augs[0]) % 180
    img_hsv[..., 1] = np.clip(img_hsv[..., 1] + hsv_augs[1], 0, 255)
    img_hsv[..., 2] = np.clip(img_hsv[..., 2] + hsv_augs[2], 0, 255)
    cv2.cvtColor(img_hsv.astype(img.dtype), cv2.COLOR_HSV2BGR, dst=img)  # no return needed

 

Flip Horizontal

 

这个就更不用说啦,目标检测中常规的图像水平翻转数据增强手段。

 

def _mirror(image, boxes, prob=0.5):
    _, width, _ = image.shape
    if random.random() < prob:
        image = image[:, ::-1]
        boxes[:, 0::2] = width - boxes[:, 2::-2]
    return image, boxes

 

 

    1. Mosaic Aug和Mixup Aug是涨点比较多的数据增强方案,也是各个目标检测算法中常用的方法。

 

    1. 不清楚为什幺Mosaic Aug没触发的话Mixup Aug也不触发,到底是两种增强方法有冲突还是其他设幺原因不得而知呀。

 

Be First to Comment

发表回复

您的电子邮箱地址不会被公开。