一个实时的人脸检测系统——Part2

上一章节我们得到了一张人脸五个关键点——左眼中心,右眼中心,鼻子尖端,左嘴中心,右嘴中心。
这一章节用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为最终的调整结果。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容