剪辑自:https://zhuanlan.zhihu.com/p/43665254
引言
数据增广是深度学习中常用的技巧之一,主要用于增加训练数据集,让数据集尽可能的多样化,使得训练的模型具有更强的泛化能力.现有的各大深度学习框架都已经自带了数据增广,但是平时在用的使用只是直接调用了对应的接口函数,而没有进行详细的分析.在实际应用中,并非所有的增广方式都适用当前的训练数据,你需要根据自己的数据集特征来确定应该使用哪几种数据增广方式.这篇文章的目的是为了更好地理解各种增广方式及其背后的真正原理.
目前数据增广主要包括:水平/垂直翻转,旋转,缩放,裁剪,剪切,平移,对比度,色彩抖动,噪声等,这里因为时间问题,有部分还每有完成,后续会进行更新.
数据增广
所有的数据增广在操作的时候默认是以图像中心点进行的.从数学角度来看,任何操作都可以分成以下几个步骤:1). 首先将旋转点移动到原点处 ;2). 执行如2所描述的绕原点的旋转;3). 再将旋转点移回到原来的位置;这里为了更好地理解,给出一个示例:

假设图像的原始坐标为,平移后的坐标为
,则平移前和平移后的坐标关系为:
图像平移
平移是指所有的像素在x和y方向各平移和,平移变换对应的数学矩阵为
这里给出平移后的具体实例(这里平移后我采用的是倒映填充):

图像翻转(图像镜像)
图像翻转包括水平翻转和垂直翻转.水平翻转的变换矩阵为:
垂直翻转的变换矩阵为:
图像旋转
图像旋转是指以某个点(默认为图像中心点)为中心进行任意角度的旋转,其变换矩阵为:
这里给出旋转后的具体实例(这里平移后我采用的是倒映填充):
图像缩放
图像缩放是指对当前图像进行任意尺度的缩放,其变换矩阵为:
这里给出缩放后的具体实例(这里平移后我采用的是倒映填充):
图像错切
图像,其变换矩阵为:
这里给出错切后的具体实例(这里平移后我采用的是倒映填充):
图像裁剪
深度学习的裁剪的常用做法是将图片缩放到原图的1.1倍,然后在缩放后的图像上进行裁剪操作,具体的裁剪实例如下:
组合变换
在深度学习中的数据增广一般会采用多种增广方式的组合,这里就会涉及到矩阵乘法运算,根据其运算的规则,可以知道不同的组合顺序结果是不一样的,即线性代数中的 ,当然特例除外.
为了更好地解释,假设给定平移变换矩阵 ,旋转矩阵
,缩放矩阵
,为了说明这里我给出两个不同的组合变换.对于组合变换一,其组合后的矩阵如下:
;对于组合变换二,其组合后的矩阵如下:
,对于两种不同的组合其结果如下:
在深度学习中的数据增广一般会采用多种增广方式的组合,这里就会涉及到矩阵乘法运算,根据其运算的规则,可以知道不同的组合顺序结果是不一样的,即线性代数中的 ,当然特例除外.
为了更好地解释,假设给定平移变换矩阵 ,旋转矩阵
,缩放矩阵
,为了说明这里我给出两个不同的组合变换.对于组合变换一,其组合后的矩阵如下:
;对于组合变换二,其组合后的矩阵如下:
,对于两种不同的组合其结果如下:
数据增广之源码实现
这里使用python和opencv来实现上述中的各种变换,具体的源码如下:
#coding=utf-8################################################# 数据增广,包括# 2018.09.02 add################################################import numpy as npimport osimport cv2import copyclass DataAugment:def __init__(self,debug=False):self.debug=debugprint("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.shapetransform_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_matrixdef 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_TRANSPARENTreturn outputdef 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_imgdef random_vector(self,min,max):"""生成范围矩阵"""min=np.array(min)max=np.array(max)print(min.shape,max.shape)assert min.shape==max.shapeassert len(min.shape) == 1return 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_imgdef 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_imgdef 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_imgdef 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_imgdef 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_imgif __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)
