【数据增广】数据增广之详细理解

来自 | 知乎

地址 | https://zhuanlan.zhihu.com/p/43665254

作者 | 燕小花

编辑 | 机器学习算法与自然语言处理公众号

本文仅作学术分享,若侵权,请联系后台删文处理

 

引言

数据增广是深度学习中常用的技巧之一,主要用于增加训练数据集,让数据集尽可能的多样化,使得训练的模型具有更强的泛化能力.现有的各大深度学习框架都已经自带了数据增广,但是平时在用的使用只是直接调用了对应的接口函数,而没有进行详细的分析.在实际应用中,并非所有的增广方式都适用当前的训练数据,你需要根据自己的数据集特征来确定应该使用哪几种数据增广方式.这篇文章的目的是为了更好地理解各种增广方式及其背后的真正原理. 
目前数据增广主要包括:水平/垂直翻转,旋转,缩放,裁剪,剪切,平移,对比度,色彩抖动,噪声等,这里因为时间问题,有部分还每有完成,后续会进行更新.

 

数据增广

所有的数据增广在操作的时候默认是以图像中心点进行的.从数学角度来看,任何操作都可以分成以下几个步骤:1). 首先将旋转点移动到原点处 ;2). 执行如2所描述的绕原点的旋转;3). 再将旋转点移回到原来的位置;这里为了更好地理解,给出一个示例:

【数据增广】数据增广之详细理解_第1张图片

假设图像的原始坐标为  ,平移后的坐标为  ,则平移前和平移后的坐标关系为:

【数据增广】数据增广之详细理解_第2张图片

图像平移

平移是指所有的像素在x和y方向各平移和,平移变换对应的数学矩阵为

【数据增广】数据增广之详细理解_第3张图片

这里给出平移后的具体实例(这里平移后我采用的是倒映填充):

【数据增广】数据增广之详细理解_第4张图片

图像翻转(图像镜像)

图像翻转包括水平翻转和垂直翻转.水平翻转的变换矩阵为:

【数据增广】数据增广之详细理解_第5张图片

垂直翻转的变换矩阵为:

【数据增广】数据增广之详细理解_第6张图片

这里给出翻转后的具体实例(这里平移后我采用的是倒映填充):

【数据增广】数据增广之详细理解_第7张图片

图像旋转

图像旋转是指以某个点(默认为图像中心点)为中心进行任意角度的旋转,其变换矩阵为:

【数据增广】数据增广之详细理解_第8张图片

这里给出旋转后的具体实例(这里平移后我采用的是倒映填充):

【数据增广】数据增广之详细理解_第9张图片

图像缩放

图像缩放是指对当前图像进行任意尺度的缩放,其变换矩阵为:

【数据增广】数据增广之详细理解_第10张图片

这里给出缩放后的具体实例(这里平移后我采用的是倒映填充):

【数据增广】数据增广之详细理解_第11张图片

图像错切

图像,其变换矩阵为:

【数据增广】数据增广之详细理解_第12张图片

这里给出错切后的具体实例(这里平移后我采用的是倒映填充):

【数据增广】数据增广之详细理解_第13张图片

图像裁剪

深度学习的裁剪的常用做法是将图片缩放到原图的1.1倍,然后在缩放后的图像上进行裁剪操作,具体的裁剪实例如下:

【数据增广】数据增广之详细理解_第14张图片

组合变换

在深度学习中的数据增广一般会采用多种增广方式的组合,这里就会涉及到矩阵乘法运算,根据其运算的规则,可以知道不同的组合顺序结果是不一样的,即线性代数中的  ,当然特例除外.

为了更好地解释,假设给定平移变换矩阵 ,旋转矩阵  ,缩放矩阵,为了说明这里我给出两个不同的组合变换.对于组合变换一,其组合后的矩阵如下:;对于组合变换二,其组合后的矩阵如下: ,对于两种不同的组合其结果如下:

【数据增广】数据增广之详细理解_第15张图片

数据增广之源码实现

这里使用python和opencv来实现上述中的各种变换,具体的源码如下:


#coding=utf-8
################################################
# 数据增广,包括
# 2018.09.02 add
################################################
import numpy as np
import os
import cv2
import copy


class DataAugment:
    def __init__(self,debug=False):
        self.debug=debug
        print("Data augment...")

    def basic_matrix(self,translation):
        """基础变换矩阵"""
        return np.array([[1,0,translation[0]],[0,1,translation[1]],[0,0,1]])

    def adjust_transform_for_image(self,img,trans_matrix):
        """根据图像调整当前变换矩阵"""
        transform_matrix=copy.deepcopy(trans_matrix)
        height, width, channels = img.shape
        transform_matrix[0:2, 2] *= [width, height]
        center = np.array((0.5 * width, 0.5 * height))
        transform_matrix = np.linalg.multi_dot([self.basic_matrix(center), transform_matrix, self.basic_matrix(-center)])
        return transform_matrix

    def apply_transform(self,img,transform):
        """仿射变换"""
        output = cv2.warpAffine(img, transform[:2, :], dsize=(img.shape[1], img.shape[0]),
                                flags=cv2.INTER_LINEAR, borderMode=cv2.BORDER_REFLECT, borderValue=0,)   #cv2.BORDER_REPLICATE,cv2.BORDER_TRANSPARENT
        return output

    def apply(self,img,trans_matrix):
        """应用变换"""
        tmp_matrix=self.adjust_transform_for_image(img, trans_matrix)
        out_img=self.apply_transform(img, tmp_matrix)
        if self.debug:
            self.show(out_img)
        return out_img

    def random_vector(self,min,max):
        """生成范围矩阵"""
        min=np.array(min)
        max=np.array(max)
        print(min.shape,max.shape)
        assert min.shape==max.shape
        assert len(min.shape) == 1
        return np.random.uniform(min, max)

    def show(self,img):
        """可视化"""
        cv2.imshow("outimg",img)
        cv2.waitKey()

    def random_transform(self,img,min_translation,max_translation):
        """平移变换"""
        factor=self.random_vector(min_translation,max_translation)
        trans_matrix=np.array([[1, 0, factor[0]],[0, 1, factor[1]],[0, 0, 1]])
        out_img=self.apply(img,trans_matrix)
        return trans_matrix, out_img

    def random_flip(self,img,factor):
        """水平或垂直翻转"""
        flip_matrix = np.array([[factor[0], 0, 0],[0, factor[1], 0],[0, 0, 1]])
        out_img=self.apply(img,flip_matrix)
        return flip_matrix, out_img

    def random_rotate(self,img,factor):
        """随机旋转"""
        angle=np.random.uniform(factor[0],factor[1])
        print("angle:{}".format(angle))
        rotate_matrix=np.array([[np.cos(angle), -np.sin(angle), 0],[np.sin(angle), np.cos(angle), 0],[0, 0, 1]])
        out_img=self.apply(img,rotate_matrix)
        return rotate_matrix, out_img

    def random_scale(self,img,min_translation,max_translation):
        """随机缩放"""
        factor=self.random_vector(min_translation, max_translation)
        scale_matrix = np.array([[factor[0], 0, 0],[0, factor[1], 0],[0, 0, 1]])
        out_img=self.apply(img,scale_matrix)
        return scale_matrix, out_img

    def random_shear(self,img,factor):
        """随机剪切,包括横向和众向剪切"""
        angle = np.random.uniform(factor[0], factor[1])
        print("fc:{}".format(angle))
        crop_matrix = np.array([[1, factor[0], 0], [factor[1], 1, 0], [0, 0, 1]])
        out_img=self.apply(img,crop_matrix)
        return crop_matrix, out_img


if __name__=="__main__":
    demo=DataAugment(debug=True)
    img=cv2.imread("/pathto/dataArgu/wr.jpg")

    # 平移测试
    _,outimg=demo.random_transform(img,(0.1,0.1),(0.2,0.2))  #(-0.3,-0.3),(0.3,0.3)


   # 垂直变换测试
    _, outimg =demo.random_flip(img,(1.0,-1.0))

    # 水平变换测试
    _, outimg =demo.random_flip(img, (-1.0, 1.0))
   

    # 旋转变换测试
     _, outimg =demo.random_rotate(img,(0.5,0.8))
   
    # # 缩放变换测试
     _, outimg =demo.random_scale(img,(1.2, 1.2),(1.3,1.3))

    # 随机裁剪测试
    _, outimg =demo.random_shear(img,(0.2,0.3))

    # 组合变换
    t1,_=demo.random_transform(img,(-0.3,-0.3),(0.3,0.3))
    t2,_=demo.random_rotate(img,(0.5,0.8))
    t3,_=demo.random_scale(img,(1.5,1.5),(1.7,1.7))
    tmp=np.linalg.multi_dot([t1,t2,t3])
    print("tmp:{}".format(tmp))
    out=demo.apply(img,tmp)

本章中还有几个方法未实现,后续会补上.如果文中有理解错误的地方,欢迎指正.

 

重磅!忆臻自然语言处理-学术微信交流群已成立

可以扫描下方二维码,小助手将会邀请您入群交流,

注意:请大家添加时修改备注为 [学校/公司 + 姓名 + 方向]

例如 —— 哈工大+张三+对话系统。

号主,微商请自觉绕道。谢谢!

推荐阅读:

【长文详解】从Transformer到BERT模型

赛尔译文 | 从头开始了解Transformer

百闻不如一码!手把手教你用Python搭一个Transformer

你可能感兴趣的:(机器学习)