上一章節(jié)我們得到了一張人臉五個關(guān)鍵點(diǎn)——左眼中心蠢熄,右眼中心逗噩,鼻子尖端,左嘴中心吓肋,右嘴中心凳怨。
這一章節(jié)用python來實(shí)現(xiàn)下人臉的重構(gòu)工作。
我們先定義一個二維數(shù)組 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])
再定義兩個函數(shù):
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)的作用是取得圖像繞圖像的中心逆時針旋轉(zhuǎn)angle角度并且縮放比列為1.0時的旋轉(zhuǎn)矩陣猿棉。至于什么是旋轉(zhuǎn)矩陣大家可以看看www.myexception.cn/image/1958561.html
得到的旋轉(zhuǎn)矩陣rot是一個2*3的矩陣磅叛。newh 和 neww表示的是旋轉(zhuǎn)后新生成的圖片的高和寬。
rot[0][2] += (neww/2.0 - center[0])
rot[1][2] += (newh/2.0 - center[1])
這是一個平移的過程萨赁,所以整個過程將會是圖像先繞著中心旋轉(zhuǎn)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的操作其實(shí)和imrotate中的圖片中心的變化過程是一樣的。transform是使用getRotationMatrix2D得到了旋轉(zhuǎn)矩陣再平移慰安,imrotate則是自己構(gòu)建了整個過程腋寨。
輸入(x,y)為原始的輸入點(diǎn),(xx,yy)為旋轉(zhuǎn)后的點(diǎn)化焕。
有了這兩個函數(shù)萄窜,我們就可以進(jìn)行重構(gòu)了。
#獲取左眼和右眼的正切值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
#根據(jù)角度旋轉(zhuǎn)照片撒桨,并計算旋轉(zhuǎn)后眼睛和嘴巴的中點(diǎn)查刻。
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是我們手動設(shè)置的眼睛和嘴巴之間的垂直距離,設(shè)置的依據(jù)是最后圖片輸出時的size大小凤类,而下面出現(xiàn)的ec_y則是眼睛中點(diǎn)的y軸坐標(biāo)穗泵,這也是通過size來調(diào)整的。通過計算ec_mc_y和旋轉(zhuǎn)后圖片的垂直距離的比值谜疤,可以得到一個縮放比例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 #計算此時眼睛中點(diǎn)和設(shè)置中點(diǎn)差值
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把對圖像進(jìn)行縮放,cv2.resize的功能是對圖像進(jìn)行縮放撕攒。接著我們需要重新計算縮放后圖像眼睛的中點(diǎn)陡鹃。先把eyec平移到原點(diǎn),之后進(jìn)行縮放抖坪,然后再平移萍鲸。接下來需要計算裁剪的范圍,計算eyec的y值和ec_y的差值擦俐,來計算y軸的裁剪范圍脊阴,通過計算eyec的x值和圖片中點(diǎn)的差值,來計算x軸的裁剪范圍。定義一個img_crop為了接收最后裁剪得到的圖像嘿期。之后我們計算裁剪的范圍 x方向?yàn)? crop_x ~ crop_x_end y方向?yàn)? 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區(qū)域復(fù)制到 img_crop中的 1~crop_y_end - crop_y+1 1~crop_x_end - crop_x + 1的區(qū)域
cropped = img_crop/255.0是將像素做歸一化,得到的cropped做最后一次縮放得到img_final备徐,img_final*255.0為恢復(fù)原來的像素值萄传,得到的img_final為最終的調(diào)整結(jié)果。