内容
1.前言
2.带注释代码
3.具体分析
前言
在深度学习中,图像的数据增强是很重要的部分,本篇文章以CPN (Cascaded Pyramid Network for Multi-Person Pose Estimation)中的数据增强代码为例,进行分析。
带注释代码
def data_augmentation(self, img, label, operation):
#随机裁剪
height, width = img.shape[0], img.shape[1]
center = (width / 2., height / 2.)
n = label.shape[0] #label:[num_class,3],num_class=17,3=[x,y,valid]
affrat = random.uniform(self.scale_factor[0], self.scale_factor[1]) #关于裁剪的参数
halfl_w = min(width - center[0], (width - center[0]) / 1.25 * affrat) #affrat大于1.25的时候不进行裁剪
halfl_h = min(height - center[1], (height - center[1]) / 1.25 * affrat)
img = skimage.transform.resize(img[int(center[1] - halfl_h): int(center[1] + halfl_h + 1),
int(center[0] - halfl_w): int(center[0] + halfl_w + 1)], (height, width))
#因为[a:b]实际上是取到a~b-1,所以后面要+1
for i in range(n):
label[i][0] = (label[i][0] - center[0]) / halfl_w * (width - center[0]) + center[0] #x
label[i][1] = (label[i][1] - center[1]) / halfl_h * (height - center[1]) + center[1] #y
label[i][2] *= (
(label[i][0] >= 0) & (label[i][0] < width) & (label[i][1] >= 0) & (label[i][1] < height)) #valid
# 随机水平翻转
if operation == 1:
img = cv2.flip(img, 1)
#cv2.flip(img,args)
# args =1 水平翻转
# args =0 垂直翻转
# args =-1 水平垂直翻转
cod = []
allc = []
for i in range(n):
x, y = label[i][0], label[i][1]
if x >= 0:
x = width - 1 - x #对关键点label里的x进行水平翻转
cod.append((x, y, label[i][2]))
for (q, w) in self.symmetry: # symmetry = [(1, 2), (3, 4), (5, 6), (7, 8), (9, 10), (11, 12), (13, 14), (15, 16)]
cod[q], cod[w] = cod[w], cod[q] #水平翻转之后左右也会发生改变,例如人的左手镜像后会变成右手
for i in range(n):
allc.append(cod[i][0])
allc.append(cod[i][1])
allc.append(cod[i][2])
label = np.array(allc).reshape(n, 3) #经过水平翻转,以及左右标注调整之后的label
# 随机旋转
if operation > 1:
angle = random.uniform(0, self.rot_factor) # rot_factor=45,随机45度内的旋转
if random.randint(0, 1): # 得到(0,1)之间的一个随机int数,所以只有0或者1,这里保证可以+45度或者-45度进行旋转
angle *= -1
rotMat = cv2.getRotationMatrix2D(center, angle, 1.0) # rotMat仿射变换矩阵,参数1表示进行等比例的缩放
img = cv2.warpAffine(img, rotMat, (width, height)) # warpAffine进行仿射变换。
allc = []
for i in range(n):
x, y = label[i][0], label[i][1]
v = label[i][2]
coor = np.array([x, y])
if x >= 0 and y >= 0:
R = rotMat[:, : 2]
W = np.array([rotMat[0][2], rotMat[1][2]])
coor = np.dot(R, coor) + W
allc.append(int(coor[0]))
allc.append(int(coor[1]))
v *= ((coor[0] >= 0) & (coor[0] < width) & (coor[1] >= 0) & (coor[1] < height))
# 假如x或者y不在图内了,则v变为0,即不可见
allc.append(int(v))
label = np.array(allc).reshape(n, 3).astype(np.int)# 经过随机旋转,调整之后的label
return img, label
具体分析
1.随机裁剪
for i in range(n):
label[i][0] = (label[i][0] - center[0]) / halfl_w * (width - center[0]) + center[0] #x
label[i][1] = (label[i][1] - center[1]) / halfl_h * (height - center[1]) + center[1] #y
label[i][2] *= (
(label[i][0] >= 0) & (label[i][0] < width) & (label[i][1] >= 0) & (label[i][1] < height)) #valid
对于label中的坐标x来说,不失一般性,设x<center[0]。
a阶段:裁剪之后,center[0]变为halfl_w,
b阶段:图像resize到(height, width),所以又resize回center[0]。
裁剪时保持不变的部分为center[0]-x部分。所以这一部分在a和b阶段对于整幅图的比例是保持不变的。所以从a阶段变为b阶段后,这一部分大小放缩为(center[0]-x)/halfl_w*center[0],所以x此时大小为center[0]-(center[0]-x)/halfl_w*center[0]=center[0]+(x-center[0])/halfl_w*center[0]。因为width - center[0]= center[0],所以分析结果同上面代码一致。
2.随机旋转
for i in range(n):
x, y = label[i][0], label[i][1]
v = label[i][2]
coor = np.array([x, y])
if x >= 0 and y >= 0:
R = rotMat[:, : 2]
W = np.array([rotMat[0][2], rotMat[1][2]])
coor = np.dot(R, coor) + W
opencv中得到的仿射矩阵rotMat如下(下图转载自此):
由此可以直观的认识R和W的具体作用,所以coor = np.dot(R, coor) + W即为将关键点的坐标进行一个仿射变换。
以上代码的注释都是个人的理解,转载请注明出处,要是有错误或考虑不周的地方欢迎指出。