上一章节我们得到了一张人脸五个关键点——左眼中心,右眼中心,鼻子尖端,左嘴中心,右嘴中心。
这一章节用python来实现下人脸的重构工作。
我们先定义一个二维数组 landmark,它的五个元素分别是:
左眼(landmark[0][0], landmark[0][1])
右眼(landmark[1][0], landmark[0][1])
鼻子(landmark[2][0], landmark[2][1])
左嘴(landmark[3][0], landmark[3][1])
右嘴(landmark[4][0], landmark[4][1])
再定义两个函数:
def imrotate(img, angle):
imgh = img.shape[0]
imgw = img.shape[1]
center = (imgw/2.0, imgh/2.0)
rot = cv2.getRotationMatrix2D(center, angle, 1.0)
newh = imgh*math.cos(angle/180.0*math.pi) + imgw*abs(math.sin(angle/180.0*math.pi))
neww = imgw*math.cos(angle/180.0*math.pi) + imgh*abs(math.sin(angle/180.0*math.pi))
rot[0][2] += (neww/2.0 - center[0])
rot[1][2] += (newh/2.0 - center[1])
img_rot = cv2.warpAffine(img, rot, ((int)(neww),(int)(newh)))
return img_rot
rot = cv2.getRotationMatrix2D(center, angle, 1.0)的作用是取得图像绕图像的中心逆时针旋转angle角度并且缩放比列为1.0时的旋转矩阵。至于什么是旋转矩阵大家可以看看www.myexception.cn/image/1958561.html
得到的旋转矩阵rot是一个2*3的矩阵。newh 和 neww表示的是旋转后新生成的图片的高和宽。
rot[0][2] += (neww/2.0 - center[0])
rot[1][2] += (newh/2.0 - center[1])
这是一个平移的过程,所以整个过程将会是图像先绕着中心旋转angle角,然后我们通过计算得到新图像的高和宽。为了使原来的图像的中心和原图像的中心重合,我们又做了一次平移的操作。
def transform(x, y,angle, src_shape, dst_shape):
x0 = x - src_shape[1]/2.0
y0 = y - src_shape[0]/2.0
xx = x0*math.cos(angle) - y0*math.sin(angle) + dst_shape[1]/2.0
xx = round(xx)
yy = x0*math.sin(angle) + y0*math.cos(angle) + dst_shape[0]/2.0
yy = round(yy)
return (xx, yy)
transform的操作其实和imrotate中的图片中心的变化过程是一样的。transform是使用getRotationMatrix2D得到了旋转矩阵再平移,imrotate则是自己构建了整个过程。
输入(x,y)为原始的输入点,(xx,yy)为旋转后的点。
有了这两个函数,我们就可以进行重构了。
#获取左眼和右眼的正切值ang_tan和夹角angle
ang_tan = (landmark[0][1]-landmark[1][1])/(landmark[0][0]-landmark[1][0])
angle = math.atan(ang_tan) / math.pi * 180.0
#根据角度旋转照片,并计算旋转后眼睛和嘴巴的中点。
img_rot = imrotate(img, angle)
x = (landmark[0][0]+landmark[1][0]) / 2
y = (landmark[0][1]+landmark[1][1]) / 2
angle = - angle / 180.0 * math.pi
eyec = transform(x, y, angle, img.shape, img_rot.shape)
x = (landmark[3][0]+landmark[4][0]) / 2
y = (landmark[3][1]+landmark[4][1]) / 2
mouthc = transform(x, y, angle, img.shape, img_rot.shape)
resize_scale = ec_mc_y / (mouthc[1]-eyec[1])
resize_shape = [img_rot.shape[0],img_rot.shape[1]]
resize_shape[0] = max(size, math.ceil(resize_shape[0] * resize_scale))
resize_shape[1] = max(size, math.ceil(resize_shape[1] * resize_scale))
resize_shape = ((int)(resize_shape[1]),(int)(resize_shape[0]))
ec_mc_y是我们手动设置的眼睛和嘴巴之间的垂直距离,设置的依据是最后图片输出时的size大小,而下面出现的ec_y则是眼睛中点的y轴坐标,这也是通过size来调整的。通过计算ec_mc_y和旋转后图片的垂直距离的比值,可以得到一个缩放比例resize_scale,如果缩放后的图片大小为:resize_shape[0] *resize_scale,resize_shape[1] * resize_scale,如果此时二者中任一个小于size,都将把它替换成size,都大于就保持不变。
img_resize = cv2.resize(img_rot, resize_shape)
eyec2 = [(eyec[0]-(img_rot.shape[1]/2.0))*resize_scale + (img_resize.shape[1]/2.0),
(eyec[1]-(img_rot.shape[0]/2.0))*resize_scale + (img_resize.shape[0]/2.0)]
eyec2 = ((int)(round(eyec2[0])), (int)(round(eyec2[1])))
img_crop = np.zeros((size, size, img_rot.shape[2]))
crop_y = eyec2[1] - ec_y #计算此时眼睛中点和设置中点差值
crop_y_end = crop_y + size - 1 #计算裁剪窗口的移动距离。
crop_y = min(img_resize.shape[0],max(1, crop_y))
crop_y_end = min(img_resize.shape[0],max(1, crop_y_end))
crop_x = eyec2[0] - (int)(math.floor(size/2.0))
crop_x_end = crop_x + size - 1
crop_x = min(img_resize.shape[1],max(1, crop_x))
crop_x_end = min(img_resize.shape[1],max(1, crop_x_end))
用计算得到的resize_shape把对图像进行缩放,cv2.resize的功能是对图像进行缩放。接着我们需要重新计算缩放后图像眼睛的中点。先把eyec平移到原点,之后进行缩放,然后再平移。接下来需要计算裁剪的范围,计算eyec的y值和ec_y的差值,来计算y轴的裁剪范围,通过计算eyec的x值和图片中点的差值,来计算x轴的裁剪范围。定义一个img_crop为了接收最后裁剪得到的图像。之后我们计算裁剪的范围 x方向为 crop_x ~ crop_x_end y方向为 crop_y ~ crop_y_end
box = np.array([crop_x, crop_x_end, crop_y, crop_y_end])
img_crop[(box[2]-crop_y+1):(box[3]-crop_y+1), (box[0]-crop_x+1):(box[1]-crop_x+1), :] = img_resize[box[2]:box[3],box[0]:box[1],:]
cropped = img_crop/255.0
img_final = cv2.resize(cropped, (size, size))
img_final = np.uint8(img_final*255.0)
img_resize中的 crop_y ~ crop_y_end , crop_x ~ crop_x_end区域复制到 img_crop中的 1~crop_y_end - crop_y+1 1~crop_x_end - crop_x + 1的区域
cropped = img_crop/255.0是将像素做归一化,得到的cropped做最后一次缩放得到img_final,img_final*255.0为恢复原来的像素值,得到的img_final为最终的调整结果。
网友评论