YOLOV5数据增强有这些!---也可自己新增数据增强方法(裁剪、平移 、旋转、改变亮度、加噪声)

目录

1   rectangular(同个batch里做rectangle宽高等比变换, 加快训练 ,对于多余的黑边做到最小,实现降低计算量。)

2 HSV变换

3. 随机旋转、平移、缩放、裁剪,错切/非垂直投影 、透视变换(从0开始)

3-1 旋转

3-2 平移src为左边图片, dst为右边旋转缩放变换后的图片, xy为横纵坐标, M为平移矩阵,x轴平移参数主要是M[0,2]起作用,y轴平移参数主要是M[1,2]起作用

3-3  错切/非垂直投影

3-4 翻转

3-5 四图拼接

3-6 图像互相融合(是简单地将两张图叠加到一起, 通过不同的透明度进行区分。)

3-7  分割填补(分割出图像的目标后, 需要计算该目标边框与填补图片中的所有目标边框IOU<0.3(实现参数))


1   rectangular

(同个batch里做rectangle宽高等比变换, 加快训练 ,对于多余的黑边做到最小,实现降低计算量。)

code:

# 文件位置:utils/datasets.py
# 6、为Rectangular Training作准备:即减少大小不同图片处理时,对于多余的黑边做到最小,实现降低计算量
        # 这里主要是注意shapes的生成 这一步很重要 因为如果采样矩形训练那么整个batch的形状要一样 就要计算这个符合整个batch的shape
        # 而且还要对数据集按照高宽比进行排序 这样才能保证同一个batch的图片的形状差不多相同 再选择一个共同的shape代价也比较小
        if self.rect:
            #  所有训练图片的shape
            s = self.shapes  # wh
            # 计算高宽比
            ar = s[:, 1] / s[:, 0]  # aspect ratio
            irect = ar.argsort()    # 根据高宽比排序
            self.img_files = [self.img_files[i] for i in irect] # 获取排序后的img_files
            self.label_files = [self.label_files[i] for i in irect]  # 获取排序后的label_files
            self.labels = [self.labels[i] for i in irect]   # 获取排序后的labels
            self.shapes = s[irect]   # 获取排序后的wh
            ar = ar[irect]   # 获取排序后的wh
 
            # 计算每个batch采用的统一尺度 Set training image shapes
            shapes = [[1, 1]] * nb
            for i in range(nb):
                # 同一个batch的图片提取出来
                ari = ar[bi == i]
                mini, maxi = ari.min(), ari.max()   # 获取第i个batch中,最小和最大高宽比
                if maxi < 1:
                    # [H,W]如果高/宽小于1(w > h),宽大于高,矮胖型,(img_size*maxi,img_size)(保证原图像尺度不变进行缩放)
                    shapes[i] = [maxi, 1]
                elif mini > 1:
                    # [H,W]如果高/宽大于1(w < h),宽小于高,瘦高型,(img_size,img_size *(1/mini))(保证原图像尺度不变进行缩放)
                    shapes[i] = [1, 1 / mini]
            # 计算每个batch输入网络的shape值(向上设置为32的整数倍)
            # 要求每个batch_shapes的高宽都是32的整数倍,所以要先除以32,取整再乘以32(不过img_size如果是32倍数这里就没必要了)
            self.batch_shapes = np.ceil(np.array(shapes) * img_size / stride + pad).astype(np.int) * stride

2 HSV变换

  • HSV-Hue augmentation (fraction), 色调

  • HSV-Saturation augmentation (fraction), 饱和度

  • HSV-Value augmentation (fraction), 曝光度

  • code:

  • # 调用函数的文件位置:文件位置:utils/datasets.py
    # 色域空间增强Augment colorspace:H色调、S饱和度、V亮度
    # 通过一些随机值改变hsv,实现数据增强
    augment_hsv(img, hgain=hyp['hsv_h'], sgain=hyp['hsv_s'], vgain=hyp['hsv_v'])
     
    # 被调用的函数位置:utils/augmentations.py
    def augment_hsv(im, hgain=0.5, sgain=0.5, vgain=0.5):
        # HSV color-space augmentation
        if hgain or sgain or vgain:
            r = np.random.uniform(-1, 1, 3) * [hgain, sgain, vgain] + 1  # random gains
            hue, sat, val = cv2.split(cv2.cvtColor(im, cv2.COLOR_BGR2HSV))
            dtype = im.dtype  # uint8
     
            x = np.arange(0, 256, dtype=r.dtype)
            lut_hue = ((x * r[0]) % 180).astype(dtype)
            lut_sat = np.clip(x * r[1], 0, 255).astype(dtype)
            lut_val = np.clip(x * r[2], 0, 255).astype(dtype)
     
            im_hsv = cv2.merge((cv2.LUT(hue, lut_hue), cv2.LUT(sat, lut_sat), cv2.LUT(val, lut_val)))
            cv2.cvtColor(im_hsv, cv2.COLOR_HSV2BGR, dst=im)  # no return needed

    3. 随机旋转、平移、缩放、裁剪,错切/非垂直投影 、透视变换(从0开始)

  • 3-1 旋转:

  • src为左边图片,dst为右边旋转缩放变换后的图片, xy为横纵坐标, M为旋转缩放矩阵

  • 旋转参数主要是M[0,1], M[1, 0]起作用, 且M[0,1], M[1, 0]互为相反数

  • 缩放参数主要是M[0,0], M[1,1]起作用

  •  3-2 平移(

  • src为左边图片, dst为右边旋转缩放变换后的图片, xy为横纵坐标, M为平移矩阵
  • x轴平移参数主要是M[0,2]起作用
  • y轴平移参数主要是M[1,2]起作用
  • 3-3  错切/非垂直投影

  • 错切的类似于固定图片一边, 对另外平行一边施加一个推力形成的变形

  • src为左边图片, dst为右边错切变换后的图片, xy为横纵坐标, M为错切矩阵

  • 错切参数主要是M[0,1], M[1, 0]起作用

  • 3-4 透视变换(

  • src为左边图片, dst为右边透视变换后的图片, xy为横纵坐标, M为变换矩阵

  • 变换参数主要是M[2,0], M[2,1]起作用

  • # 调用函数地址:utils/datasets.py
    # Augment
            # random_perspective Augment  随机透视变换 [1280, 1280, 3] => [640, 640, 3]
            # 对mosaic整合后的图片进行随机旋转、平移、缩放、裁剪,透视变换,并resize为输入大小img_size
    img4, labels4 = random_perspective(img4, labels4, segments4,
                                               degrees=self.hyp['degrees'], # 旋转
                                               translate=self.hyp['translate'], # 平移
                                               scale=self.hyp['scale'], # 缩放
                                               shear=self.hyp['shear'], # 错切/非垂直投影
                                               perspective=self.hyp['perspective'], # 透视变换
                                               border=self.mosaic_border)  # border to remove
     
    # 被调用的函数地址:utils/augmentations.py
    def random_perspective(im, targets=(), segments=(), degrees=10, translate=.1, scale=.1, shear=10, perspective=0.0,
                           border=(0, 0)):
        # torchvision.transforms.RandomAffine(degrees=(-10, 10), translate=(0.1, 0.1), scale=(0.9, 1.1), shear=(-10, 10))
        # targets = [cls, xyxy]
     
        height = im.shape[0] + border[0] * 2  # shape(h,w,c)
        width = im.shape[1] + border[1] * 2
     
        # Center
        C = np.eye(3)
        C[0, 2] = -im.shape[1] / 2  # x translation (pixels)
        C[1, 2] = -im.shape[0] / 2  # y translation (pixels)
     
        # Perspective # 透视变换
        P = np.eye(3)
        P[2, 0] = random.uniform(-perspective, perspective)  # x perspective (about y)
        P[2, 1] = random.uniform(-perspective, perspective)  # y perspective (about x)
     
        # Rotation and Scale 旋转+缩放
        R = np.eye(3)
        a = random.uniform(-degrees, degrees)
        # a += random.choice([-180, -90, 0, 90])  # add 90deg rotations to small rotations
        s = random.uniform(1 - scale, 1 + scale)
        # s = 2 ** random.uniform(-scale, scale)
        R[:2] = cv2.getRotationMatrix2D(angle=a, center=(0, 0), scale=s)
     
        # Shear 错切/非垂直投影
        S = np.eye(3)
        S[0, 1] = math.tan(random.uniform(-shear, shear) * math.pi / 180)  # x shear (deg)
        S[1, 0] = math.tan(random.uniform(-shear, shear) * math.pi / 180)  # y shear (deg)
     
        # Translation 平移
        T = np.eye(3)
        T[0, 2] = random.uniform(0.5 - translate, 0.5 + translate) * width  # x translation (pixels)
        T[1, 2] = random.uniform(0.5 - translate, 0.5 + translate) * height  # y translation (pixels)
     
        # Combined rotation matrix
        # 将所有变换矩阵连乘得到最终的变换矩阵
        M = T @ S @ R @ P @ C  # order of operations (right to left) is IMPORTANT
        if (border[0] != 0) or (border[1] != 0) or (M != np.eye(3)).any():  # image changed
            if perspective:
                im = cv2.warpPerspective(im, M, dsize=(width, height), borderValue=(114, 114, 114))
            else:  # affine
                im = cv2.warpAffine(im, M[:2], dsize=(width, height), borderValue=(114, 114, 114))
     
        # Visualize
        # import matplotlib.pyplot as plt
        # ax = plt.subplots(1, 2, figsize=(12, 6))[1].ravel()
        # ax[0].imshow(im[:, :, ::-1])  # base
        # ax[1].imshow(im2[:, :, ::-1])  # warped
     
        # Transform label coordinates
        n = len(targets)
        if n:
            use_segments = any(x.any() for x in segments)
            new = np.zeros((n, 4))
            if use_segments:  # warp segments
                segments = resample_segments(segments)  # upsample
                # 其中 segment.shape = [n, 2], 表示物体轮廓各个坐标点
                for i, segment in enumerate(segments):
                    xy = np.ones((len(segment), 3))
                    xy[:, :2] = segment
                    xy = xy @ M.T  # transform 应用旋转矩阵
                    xy = xy[:, :2] / xy[:, 2:3] if perspective else xy[:, :2]  # perspective rescale or affine
     
                    # clip
                    new[i] = segment2box(xy, width, height)
     
            else:  # warp boxes 如果是box坐标, 这里targets每行为[x1,y1,x2,y2],n为行数,表示目标边框个数:
                xy = np.ones((n * 4, 3))
                xy[:, :2] = targets[:, [1, 2, 3, 4, 1, 4, 3, 2]].reshape(n * 4, 2)  # x1y1, x2y2, x1y2, x2y1
                xy = xy @ M.T  # transform 应用旋转矩阵
                # 如果透视变换参数perspective不为0, 就需要做rescale,透视变换参数为0, 则无需做rescale。
                xy = (xy[:, :2] / xy[:, 2:3] if perspective else xy[:, :2]).reshape(n, 8)  # perspective rescale or affine
     
                # create new boxes
                x = xy[:, [0, 2, 4, 6]]
                y = xy[:, [1, 3, 5, 7]]
                new = np.concatenate((x.min(1), y.min(1), x.max(1), y.max(1))).reshape(4, n).T
     
                # clip 将坐标clip到[0, width],[0,height]区间内
                new[:, [0, 2]] = new[:, [0, 2]].clip(0, width)
                new[:, [1, 3]] = new[:, [1, 3]].clip(0, height)
     
            # filter candidates 进一步过滤,留下那些w,h>2,宽高比<20,变换后面积比之前比>0.1的那些xy
            i = box_candidates(box1=targets[:, 1:5].T * s, box2=new.T, area_thr=0.01 if use_segments else 0.10)
            targets = targets[i]
            targets[:, 1:5] = new[i]
     
        return im, targets

    代码逻辑描述3-4 翻转

  • 3-5 四图拼接(

  • 初始化整个背景图, 大小为(2 × image_size, 2 × image_size, 3)

  • 随机取一个中心点

  • 基于中心点分别将4个图放到左上,右上,左下,右下,,此部分可能会由于中心点小于4张图片的宽高

  • 所以拼接的时候可能会进行裁剪重新将打标边框的偏移量计算上

  •  需要裁剪
  • code:
  •  # 代码位置:utils/datasets.py
        def load_mosaic(self, index):
            """用在LoadImagesAndLabels模块的__getitem__函数 进行mosaic数据增强
                将四张图片拼接在一张马赛克图像中  loads images in a 4-mosaic
                :param index: 需要获取的图像索引
                :return: img4: mosaic和随机透视变换后的一张图片  numpy(640, 640, 3)
                         labels4: img4对应的target  [M, cls+x1y1x2y2]
                """
            # labels4: 用于存放拼接图像(4张图拼成一张)的label信息(不包含segments多边形)
            # segments4: 用于存放拼接图像(4张图拼成一张)的label信息(包含segments多边形)
            labels4, segments4 = [], []
            s = self.img_size # 一般的图片大小
            # 随机初始化拼接图像的中心点坐标  [0, s*2]之间随机取2个数作为拼接图像的中心坐标
            yc, xc = (int(random.uniform(-x, 2 * s + x)) for x in self.mosaic_border)  # mosaic center x, y
            # 从dataset中随机寻找额外的三张图像进行拼接 [14, 26, 2, 16] 再随机选三张图片的index
            indices = [index] + random.choices(self.indices, k=3)  # 3 additional image indices
            random.shuffle(indices)
            # 遍历四张图像进行拼接 4张不同大小的图像 => 1张[1472, 1472, 3]的图像
            for i, index in enumerate(indices):
                # load image   每次拿一张图片 并将这张图片resize到self.size(h,w)
                img, _, (h, w) = self.load_image(index)
     
                # place img in img4
                if i == 0:  # top left  原图[375, 500, 3] load_image->[552, 736, 3]   hwc
                    # 创建马赛克图像 [1472, 1472, 3]=[h, w, c]
                    img4 = np.full((s * 2, s * 2, img.shape[2]), 114, dtype=np.uint8)  # base image with 4 tiles
                    # 计算马赛克图像中的坐标信息(将图像填充到马赛克图像中)   w=736  h = 552  马赛克图像:(x1a,y1a)左上角 (x2a,y2a)右下角
                    x1a, y1a, x2a, y2a = max(xc - w, 0), max(yc - h, 0), xc, yc  # xmin, ymin, xmax, ymax (large image)
                    # 计算截取的图像区域信息(以xc,yc为第一张图像的右下角坐标填充到马赛克图像中,丢弃越界的区域)  图像:(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), yc
                    # 计算截取的图像区域信息(以xc,yc为第二张图像的左下角坐标填充到马赛克图像中,丢弃越界的区域)
                    x1b, y1b, x2b, y2b = 0, h - (y2a - y1a), min(w, x2a - x1a), h
                elif i == 2:  # bottom left
                    # 计算马赛克图像中的坐标信息(将图像填充到马赛克图像中)
                    x1a, y1a, x2a, y2a = max(xc - w, 0), yc, xc, min(s * 2, yc + h)
                    # 计算截取的图像区域信息(以xc,yc为第三张图像的右上角坐标填充到马赛克图像中,丢弃越界的区域)
                    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)
                    # 计算截取的图像区域信息(以xc,yc为第四张图像的左上角坐标填充到马赛克图像中,丢弃越界的区域)
                    x1b, y1b, x2b, y2b = 0, 0, min(w, x2a - x1a), min(y2a - y1a, h)
     
                # 将截取的图像区域填充到马赛克图像的相应位置   img4[h, w, c]
                # 将图像img的【(x1b,y1b)左上角 (x2b,y2b)右下角】区域截取出来填充到马赛克图像的【(x1a,y1a)左上角 (x2a,y2a)右下角】区域
                img4[y1a:y2a, x1a:x2a] = img[y1b:y2b, x1b:x2b]  # img4[ymin:ymax, xmin:xmax]
                # 计算pad(当前图像边界与马赛克边界的距离,越界的情况padw/padh为负值)  用于后面的label映射
                padw = x1a - x1b    # 当前图像与马赛克图像在w维度上相差多少
                padh = y1a - y1b    # 当前图像与马赛克图像在h维度上相差多少
     
                # labels: 获取对应拼接图像的所有正常label信息(如果有segments多边形会被转化为矩形label)
                # segments: 获取对应拼接图像的所有不正常label信息(包含segments多边形也包含正常gt)
                # 在新图中更新坐标值
                labels, segments = self.labels[index].copy(), self.segments[index].copy()
                if labels.size:
                    labels[:, 1:] = xywhn2xyxy(labels[:, 1:], w, h, padw, padh)  # normalized xywh to pixel xyxy format
                    segments = [xyn2xy(x, w, h, padw, padh) for x in segments]
                labels4.append(labels) # 更新labels4
                segments4.extend(segments) # 更新segments4
     
            # Concat/clip labels4 把labels4([(2, 5), (1, 5), (3, 5), (1, 5)] => (7, 5))压缩到一起
            labels4 = np.concatenate(labels4, 0)
            # 防止越界  label[:, 1:]中的所有元素的值(位置信息)必须在[0, 2*s]之间,小于0就令其等于0,大于2*s就等于2*s   out: 返回
            for x in (labels4[:, 1:], *segments4):
                np.clip(x, 0, 2 * s, out=x)  # clip when using random_perspective()
            # img4, labels4 = replicate(img4, labels4)  # replicate

    3-6 图像互相融合(是简单地将两张图叠加到一起, 通过不同的透明度进行区分。)

  • code:

  • # 调用函数地址:utils/datasets.py
    if random.random() < hyp['mixup']: # hyp['mixup']=0 默认为0则关闭 默认为1则100%打开
        # *load_mosaic(self, random.randint(0, self.n - 1)) 随机从数据集中任选一张图片和本张图片进行mixup数据增强
        # img:   两张图片融合之后的图片 numpy (640, 640, 3)
        # labels: 两张图片融合之后的标签label [M+N, cls+x1y1x2y2]
        img, labels = mixup(img, labels, *self.load_mosaic(random.randint(0, self.n - 1)))
     
    # 被调用函数地址:utils/augmentations.py
    def mixup(im, labels, im2, labels2):
        # Applies MixUp augmentation https://arxiv.org/pdf/1710.09412.pdf
        r = np.random.beta(32.0, 32.0)  # mixup ratio, alpha=beta=32.0
        im = (im * r + im2 * (1 - r)).astype(np.uint8)
        labels = np.concatenate((labels, labels2), 0)
        return im, labels

    3-7  分割填补(分割出图像的目标后, 需要计算该目标边框与填补图片中的所有目标边框IOU<0.3(实现参数))

  • code:

  • # 调用函数地址:utils/datasets.py
    img4, labels4, segments4 = copy_paste(img4, labels4, segments4, p=self.hyp['copy_paste'])
     
    # 被调用函数地址:utils/augmentations.py
    def copy_paste(im, labels, segments, p=0.5):
        # Implement Copy-Paste augmentation https://arxiv.org/abs/2012.07177, labels as nx5 np.array(cls, xyxy)
        n = len(segments)
        if p and n:
            h, w, c = im.shape  # height, width, channels
            im_new = np.zeros(im.shape, np.uint8)
            for j in random.sample(range(n), k=round(p * n)):
                l, s = labels[j], segments[j]
                box = w - l[3], l[2], w - l[1], l[4]
                ioa = bbox_ioa(box, labels[:, 1:5])  # intersection over area
                if (ioa < 0.30).all():  # allow 30% obscuration of existing labels
                    labels = np.concatenate((labels, [[l[0], *box]]), 0)
                    segments.append(np.concatenate((w - s[:, 0:1], s[:, 1:2]), 1))
                    cv2.drawContours(im_new, [segments[j].astype(np.int32)], -1, (255, 255, 255), cv2.FILLED)
     
            result = cv2.bitwise_and(src1=im, src2=im_new)
            result = cv2.flip(result, 1)  # augment segments (flip left-right)
            i = result > 0  # pixels to replace
            # i[:, :] = result.max(2).reshape(h, w, 1)  # act over ch
            im[i] = result[i]  # cv2.imwrite('debug.jpg', im)  # debug
     
        return im, labels, segments
  • DIY数据增强(裁剪、平移 、旋转、改变亮度、加噪声)

  • 参考(如有侵权,请联系删除):

  • YOLO数据集实现数据增强的方法(裁剪、平移 、旋转、改变亮度、加噪声等)_yolov5数据增强_路人贾'ω'(考公中)的博客-CSDN博客YOLO数据集实现数据增强的方法(裁剪、平移 、旋转、改变亮度、加噪声等)_yolov5数据增强_路人贾'ω'的博客-CSDN博客

  • 增强后:

基于YOLOv5和VOC2007-RUS-CHN数据集实现手骨骨龄检测可以按以下步骤进行: 第一步,准备数据集:首先,收集VOC2007-RUS-CHN数据集中与手骨骨龄相关的图像和标注数据。确保每个图像都有对应的手骨骨龄标注,以便训练算法进行检测。 第二步,安装YOLOv5:在计算机上安装YOLOv5,并设置相关的环境依赖。可以从GitHub上的YOLOv5仓库下载代码,并根据文档中的说明进行安装和配置。 第三步,数据预处理:使用数据预处理工具(如OpenCV)对图像进行预处理,例如调整大小、裁剪、归一化等,以便于模型训练。 第四步,标注转换:将VOC2007-RUS-CHN数据集中的标注信息转换为YOLOv5可用的格式。YOLOv5使用txt文件存储每个图像的标注信息,其中包括物体类别、边界框坐标等。 第五步,训练模型:使用YOLOv5的训练脚本对数据集进行训练。根据训练脚本的要求,指定训练数据、模型架构和超参数等,并运行训练命令进行模型训练。 第六步,模型评估:训练完成后,使用YOLOv5进行模型评估,计算模型在测试集上的准确率、召回率等指标,以评估模型的性能。 第七步,推理和检测:使用训练好的模型对新的手骨骨龄图像进行推理和检测。载训练好的模型,输入待检测的图像,模型会输出检测结果,包括检测到的手骨骨龄和其对应的位置。 最后,根据实际需求,可以根据模型的检测结果进行进一步的分析和处理,例如根据骨龄进行年龄预测和疾病诊断等。以上是基于YOLOv5和VOC2007-RUS-CHN数据集实现手骨骨龄检测的大致步骤。具体的实现细节和调整可以根据实际情况进行进一步优化。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值