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()


©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容