PaddleHub实战——人像美颜

随着AI时代的进步,如今各式各类的美颜相机出现在大众面前。今天作者带领大家深入了解下AI美颜的背后技术原理。

AI美颜核心技术之一就是人脸关键点检测。PaddleHub已经开源了人脸关键点检测模型face_landmark_localization。人脸关键点检测是人脸识别和分析领域中的关键一步,它是诸如自动人脸识别、表情分析、三维人脸重建及三维动画等其它人脸相关问题的前提和突破口。该模型转换自 https://github.com/lsy17096535/face-landmark ,支持同一张图中的多个人脸检测。它可以识别人脸中的68个关键点。


那么如何利用人脸关键点检测模型完成美颜功能呢?

一、加载待美颜图片,检测关键点

以教程中的示例图片为例展示检测到的人脸关键点。

NOTE:在运行本教程代码时,由于本代码示例是效果叠加的演示,美颜效果叠加代码请勿重复运行,否则出现怪异的图片展示属于正常情况。

importcv2

importpaddlehubashub

importmatplotlib.pyplotasplt

importmatplotlib.imageasmpimg

importnumpyasnp

importmath

src_img=cv2.imread('./test_sample.jpg')

module=hub.Module(name="face_landmark_localization")

result=module.keypoint_detection(images=[src_img])

tmp_img=src_img.copy()

forindex,pointinenumerate(result[0]['data'][0]):

    # cv2.putText(img, str(index), (int(point[0]), int(point[1])), cv2.FONT_HERSHEY_COMPLEX, 3, (0,0,255), -1)

    cv2.circle(tmp_img, (int(point[0]),int(point[1])),2, (0,0,255),-1)

res_img_path='face_landmark.jpg'

cv2.imwrite(res_img_path,tmp_img)

img=mpimg.imread(res_img_path)

# 展示预测68个关键点结果

plt.figure(figsize=(10,10))

plt.imshow(img)

plt.axis('off')

plt.show()

[2020-04-17 00:39:19,108] [    INFO] - Installing face_landmark_localization module

[2020-04-17 00:39:19,110] [    INFO] - Module face_landmark_localization already installed in /home/aistudio/.paddlehub/modules/face_landmark_localization

[2020-04-17 00:39:19,111] [    INFO] - Installing ultra_light_fast_generic_face_detector_1mb_640 module

[2020-04-17 00:39:19,144] [    INFO] - Module ultra_light_fast_generic_face_detector_1mb_640 already installed in /home/aistudio/.paddlehub/modules/ultra_light_fast_generic_face_detector_1mb_640


二、实现美颜方法

1. 瘦脸

首先介绍如何利用识别到的68个关键点完成瘦脸功能。 利用其中3号点到5号点距离作为瘦左脸距离,13号点到15号点距离作为瘦右脸距离。同时利用局部平移算法完成瘦脸.

defthin_face(image,face_landmark):

"""

   实现自动人像瘦脸

   image: 人像图片

   face_landmark: 人脸关键点

   """

end_point=face_landmark[30]

# 瘦左脸,3号点到5号点的距离作为瘦脸距离

dist_left=np.linalg.norm(face_landmark[3]-face_landmark[5])

image=local_traslation_warp(image,face_landmark[3],end_point,dist_left)

# 瘦右脸,13号点到15号点的距离作为瘦脸距离

dist_right=np.linalg.norm(face_landmark[13]-face_landmark[15])

image=local_traslation_warp(image,face_landmark[13],end_point,dist_right)

returnimage

deflocal_traslation_warp(image,start_point,end_point,radius):

"""

   局部平移算法

   """

radius_square=math.pow(radius,2)

image_cp=image.copy()

dist_se=math.pow(np.linalg.norm(end_point-start_point),2)

height,width,channel=image.shape

foriinrange(width):

forjinrange(height):

# 计算该点是否在形变圆的范围之内

# 优化,第一步,直接判断是会在(start_point[0], start_point[1])的矩阵框中

ifmath.fabs(i-start_point[0])>radiusandmath.fabs(j-start_point[1])>radius:

continue

distance= (i-start_point[0])*(i-start_point[0])+(j-start_point[1])*(j-start_point[1])

if(distance<radius_square):

# 计算出(i,j)坐标的原坐标

# 计算公式中右边平方号里的部分

ratio= (radius_square-distance)/(radius_square-distance+dist_se)

ratio=ratio*ratio

# 映射原位置

new_x=i-ratio*(end_point[0]-start_point[0])

new_y=j-ratio*(end_point[1]-start_point[1])

new_x=new_xifnew_x>=0else0

new_x=new_xifnew_x<height-1elseheight-2

new_y=new_yifnew_y>=0else0

new_y=new_yifnew_y<width-1elsewidth-2.4

# 根据双线性插值法得到new_x, new_y的值

image_cp[j,i] =bilinear_insert(image,new_x,new_y)


returnimage_cp

defbilinear_insert(image,new_x,new_y):

"""

   双线性插值法

   """

w,h,c=image.shape

ifc==3:

x1=int(new_x)

x2=x1+1

y1=int(new_y)

y2=y1+1

part1=image[y1,x1].astype(np.float)*(float(x2)-new_x)*(float(y2)-new_y)

part2=image[y1,x2].astype(np.float)*(new_x-float(x1))*(float(y2)-new_y)

part3=image[y2,x1].astype(np.float)*(float(x2)-new_x)*(new_y-float(y1))

part4=image[y2,x2].astype(np.float)*(new_x-float(x1))*(new_y-float(y1))

insertValue=part1+part2+part3+part4

returninsertValue.astype(np.int8)

face_landmark=np.array(result[0]['data'][0],dtype='int')

src_img=thin_face(src_img,face_landmark)

res_img_path='res.jpg'

cv2.imwrite(res_img_path,src_img)

img=mpimg.imread(res_img_path)

# 展示瘦脸图片

plt.figure(figsize=(10,10))

plt.imshow(img)

plt.axis('off')

plt.show()


2. 大眼

完成瘦脸之后,我们还可以对人像中的眼睛进行放大。在识别到的左右眼中的一个位置,对其进行缩放(图像局部缩放),实现大眼。

defenlarge_eyes(image,face_landmark,radius=15,strength=10):

"""

   放大眼睛

   image: 人像图片

   face_landmark: 人脸关键点

   radius: 眼睛放大范围半径

   strength:眼睛放大程度

   """

# 以左眼最低点和最高点之间的中点为圆心

left_eye_top=face_landmark[37]

left_eye_bottom=face_landmark[41]

left_eye_center= (left_eye_top+left_eye_bottom)/2

# 以右眼最低点和最高点之间的中点为圆心

right_eye_top=face_landmark[43]

right_eye_bottom=face_landmark[47]

right_eye_center= (right_eye_top+right_eye_bottom)/2

# 放大双眼

local_zoom_warp(image,left_eye_center,radius=radius,strength=strength)

local_zoom_warp(image,right_eye_center,radius=radius,strength=strength)

deflocal_zoom_warp(image,point,radius,strength):

"""

   图像局部缩放算法

   """

height=image.shape[0]

width=image.shape[1]

left=int(point[0]-radius)ifpoint[0]-radius>=0else0

top=int(point[1]-radius)ifpoint[1]-radius>=0else0

right=int(point[0]+radius)ifpoint[0]+radius<widthelsewidth-1

bottom=int(point[1]+radius)ifpoint[1]+radius<heightelseheight-1

radius_square=math.pow(radius,2)

foryinrange(top,bottom):

offset_y=y-point[1]

forxinrange(left,right):

offset_x=x-point[0]

dist_xy=offset_x*offset_x+offset_y*offset_y

ifdist_xy<=radius_square:

scale=1-dist_xy/radius_square

scale=1-strength/100*scale

new_x=offset_x*scale+point[0]

new_y=offset_y*scale+point[1]

new_x=new_xifnew_x>=0else0

new_x=new_xifnew_x<height-1elseheight-2

new_y=new_yifnew_y>=0else0

new_y=new_yifnew_y<width-1elsewidth-2

image[y,x] =bilinear_insert(image,new_x,new_y)

# 在瘦脸的基础上,继续放大双眼

enlarge_eyes(src_img,face_landmark,radius=13,strength=13)

cv2.imwrite(res_img_path,src_img)

img=mpimg.imread(res_img_path)

plt.figure(figsize=(10,10))

plt.imshow(img)

plt.axis('off')

plt.show()


3. 红唇

目前已经叠加了瘦脸、大眼的美颜功能,我们还可以给人像增添气色,给人像画上红唇。我们只需将识别到的唇部位置给涂上红色即可达到相应的目的。

defrouge(image,face_landmark,ruby=True):

"""

   自动涂口红

   image: 人像图片

   face_landmark: 人脸关键点

   ruby:是否需要深色口红

   """

image_cp=image.copy()

ifruby:

rouge_color= (0,0,255)

else:

    rouge_color= (0,0,200)

points=face_landmark[48:68]


hull=cv2.convexHull(points)

cv2.drawContours(image, [hull],-1,rouge_color,-1)

cv2.addWeighted(image,0.2,image_cp,0.9,0,image_cp)

returnimage_cp

# 继续叠加红唇

src_img=rouge(src_img,face_landmark)

cv2.imwrite(res_img_path,src_img)

img=mpimg.imread(res_img_path)

plt.figure(figsize=(10,10))

plt.imshow(img)

plt.axis('off')

plt.show()


4. 美白

人像涂上了口红吗,显得气色更佳了些。同时,很多人还会追求白皙的皮肤。最后我们还可以加上美肤功能。由于标记出来的68个关键点没有涵盖额头的位置,我们需要预估额头位置。为了简单估计二头所在区域,本教程以0号、16号点所在线段为直径的半圆为额头位置。

defwhitening(img,face_landmark):

    """

    美白

    """

    # 简单估计额头所在区域

    # 根据0号、16号点画出额头(以0号、16号点所在线段为直径的半圆)

    radius=(np.linalg.norm(face_landmark[0]-face_landmark[16])/2).astype('int32')

    center_abs=tuple(((face_landmark[0]+face_landmark[16])/2).astype('int32'))

    angle=np.degrees(np.arctan((lambdal:l[1]/l[0])(face_landmark[16]-face_landmark[0]))).astype('int32')

    face=np.zeros_like(img)

    cv2.ellipse(face,center_abs,(radius,radius),angle,180,360,(255,255,255),2)

    points=face_landmark[0:17]

    hull=cv2.convexHull(points)

    cv2.polylines(face, [hull],True, (255,255,255),2)

    index=face>0

    face[index] =img[index]

    dst=np.zeros_like(face)

    # v1:磨皮程度

    v1=3

    # v2: 细节程度

    v2=2

    tmp1=cv2.bilateralFilter(face,v1*5,v1*12.5,v1*12.5)

    tmp1=cv2.subtract(tmp1,face)

    tmp1=cv2.add(tmp1,(10,10,10,128))

    tmp1=cv2.GaussianBlur(tmp1,(2*v2-1,2*v2-1),0)

    tmp1=cv2.add(img,tmp1)

    dst=cv2.addWeighted(img,0.1,tmp1,0.9,0.0)

    dst=cv2.add(dst,(10,10,10,255))

    index=dst>0

    img[index] =dst[index]

    returnimg

# 美白

src_img=whitening(src_img,face_landmark)

cv2.imwrite(res_img_path,src_img)


img=mpimg.imread(res_img_path)

plt.figure(figsize=(10,10))

plt.imshow(img)

plt.axis('off')

plt.show()


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

推荐阅读更多精彩内容