PaddleHub實(shí)戰(zhàn)——人像美顏

隨著AI時(shí)代的進(jìn)步,如今各式各類的美顏相機(jī)出現(xiàn)在大眾面前。今天作者帶領(lǐng)大家深入了解下AI美顏的背后技術(shù)原理。

AI美顏核心技術(shù)之一就是人臉關(guān)鍵點(diǎn)檢測(cè)。PaddleHub已經(jīng)開源了人臉關(guān)鍵點(diǎn)檢測(cè)模型face_landmark_localization。人臉關(guān)鍵點(diǎn)檢測(cè)是人臉識(shí)別和分析領(lǐng)域中的關(guān)鍵一步,它是諸如自動(dòng)人臉識(shí)別、表情分析、三維人臉重建及三維動(dòng)畫等其它人臉相關(guān)問題的前提和突破口。該模型轉(zhuǎn)換自 https://github.com/lsy17096535/face-landmark ,支持同一張圖中的多個(gè)人臉檢測(cè)。它可以識(shí)別人臉中的68個(gè)關(guān)鍵點(diǎn)。

?


?

那么如何利用人臉關(guān)鍵點(diǎn)檢測(cè)模型完成美顏功能呢?

一、加載待美顏圖片,檢測(cè)關(guān)鍵點(diǎn)

以教程中的示例圖片為例展示檢測(cè)到的人臉關(guān)鍵點(diǎn)。

NOTE:在運(yùn)行本教程代碼時(shí),由于本代碼示例是效果疊加的演示,美顏效果疊加代碼請(qǐng)勿重復(fù)運(yùn)行,否則出現(xiàn)怪異的圖片展示屬于正常情況。

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)

# 展示預(yù)測(cè)68個(gè)關(guān)鍵點(diǎn)結(jié)果

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


二、實(shí)現(xiàn)美顏方法

1. 瘦臉

首先介紹如何利用識(shí)別到的68個(gè)關(guān)鍵點(diǎn)完成瘦臉功能。 利用其中3號(hào)點(diǎn)到5號(hào)點(diǎn)距離作為瘦左臉距離,13號(hào)點(diǎn)到15號(hào)點(diǎn)距離作為瘦右臉距離。同時(shí)利用局部平移算法完成瘦臉.

defthin_face(image,face_landmark):

"""

?? 實(shí)現(xiàn)自動(dòng)人像瘦臉

?? image: 人像圖片

?? face_landmark: 人臉關(guān)鍵點(diǎn)

?? """

end_point=face_landmark[30]

?

# 瘦左臉,3號(hào)點(diǎn)到5號(hào)點(diǎn)的距離作為瘦臉距離

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

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

?

# 瘦右臉,13號(hào)點(diǎn)到15號(hào)點(diǎn)的距離作為瘦臉距離

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

# 計(jì)算該點(diǎn)是否在形變圓的范圍之內(nèi)

# 優(yōu)化,第一步,直接判斷是會(huì)在(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):

# 計(jì)算出(i,j)坐標(biāo)的原坐標(biāo)

# 計(jì)算公式中右邊平方號(hào)里的部分

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

?

# 根據(jù)雙線性插值法得到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. 大眼

完成瘦臉之后,我們還可以對(duì)人像中的眼睛進(jìn)行放大。在識(shí)別到的左右眼中的一個(gè)位置,對(duì)其進(jìn)行縮放(圖像局部縮放),實(shí)現(xiàn)大眼。

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

"""

?? 放大眼睛

?? image: 人像圖片

?? face_landmark: 人臉關(guān)鍵點(diǎn)

?? radius: 眼睛放大范圍半徑

?? strength:眼睛放大程度

?? """

# 以左眼最低點(diǎn)和最高點(diǎn)之間的中點(diǎn)為圓心

left_eye_top=face_landmark[37]

left_eye_bottom=face_landmark[41]

left_eye_center= (left_eye_top+left_eye_bottom)/2

# 以右眼最低點(diǎn)和最高點(diǎn)之間的中點(diǎn)為圓心

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)

# 在瘦臉的基礎(chǔ)上,繼續(xù)放大雙眼

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. 紅唇

目前已經(jīng)疊加了瘦臉、大眼的美顏功能,我們還可以給人像增添氣色,給人像畫上紅唇。我們只需將識(shí)別到的唇部位置給涂上紅色即可達(dá)到相應(yīng)的目的。

defrouge(image,face_landmark,ruby=True):

"""

?? 自動(dòng)涂口紅

?? image: 人像圖片

?? face_landmark: 人臉關(guān)鍵點(diǎn)

?? 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

# 繼續(xù)疊加紅唇

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. 美白

人像涂上了口紅嗎,顯得氣色更佳了些。同時(shí),很多人還會(huì)追求白皙的皮膚。最后我們還可以加上美膚功能。由于標(biāo)記出來的68個(gè)關(guān)鍵點(diǎn)沒有涵蓋額頭的位置,我們需要預(yù)估額頭位置。為了簡(jiǎn)單估計(jì)二頭所在區(qū)域,本教程以0號(hào)、16號(hào)點(diǎn)所在線段為直徑的半圓為額頭位置。

defwhitening(img,face_landmark):

? ? """

? ? 美白

? ? """

? ? # 簡(jiǎn)單估計(jì)額頭所在區(qū)域

? ? # 根據(jù)0號(hào)、16號(hào)點(diǎn)畫出額頭(以0號(hào)、16號(hào)點(diǎn)所在線段為直徑的半圓)

? ? 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: 細(xì)節(jié)程度

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


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容