換臉技術,用Python — OpenCV 實現!

在開始之前先看一下效果圖(提前聲明一下:圖片來源於網絡侵刪),因爲人臉反差有點大因此有點辣眼睛,,,

左右原圖,中間爲生成圖

把圖片中的角色互換,再來看一下轉換後人臉替換的效果:

角色轉換人臉替換圖

emm,結果怎麼說呢,效果感覺還是不錯的(產生的替換接縫不會那麼失真、突兀),但是感覺生成新的人臉就是畸形的呢。

1,Face Swap 技術介紹

好了,下面將詳細介紹 人臉替換技術並用 OpenCV 來實現;介紹到這裏,如果沒有看過之前寫的幾篇文章 ,
實現人臉識別、人臉68個特徵點提取,或許這個 Python 庫能幫到你!
利用 OpenCV-Python 進行人臉 Delaunay 三角剖分(人臉檢測核心技術之一)
建議你提前瞭解一下,因爲本文將用到這裏面將用到 人臉特徵點提取、Delaunay 三角剖分。

人臉替換技術是相對較難的,原因之一爲人與人之間臉型的區別較大(紋理、臉型、區域凹凸部位等),使得替換後人臉區域與周圍的皮膚組織差別較大,產生失真、不自然的視覺效果,

就下面兩張圖給出的最終人臉替換圖,之所以結果非常辣眼睛,主要的原因如下幾點:

1,年齡差別,臉部皺紋、紋理區域差別較大;

2,種族差別;一個是亞裔黃種人,一個是白種人,眼睛凹陷程度面部構造差別較大;

3,性別差別;男士和女士的臉型還是會有細微的區別的;

2,人臉替換技術需要解決難點

1,臉部區域大小不一致問題,例如一個體型較胖的和題型較瘦的人大小是明顯不一樣的,直接替換操作明顯是不合適的,需要我們提前統一臉型纔能有後續的操作;

2,人臉替換後,替換的人臉區域跟周圍的皮膚組織會明顯存在顏色差別,燈光等問題使得替換縫隙比較突兀,如下圖,這個問題如果得不到解決,最終處理後的圖像會非常 “假”;

combina.jpg

3,人臉拍攝角度問題,有的圖片展示的是正臉、有的展示的是側臉;

4,最終皮膚問題,需要把替換的的臉部區域紋理與周圍組織的保持一致,最終變化差別不大;

由於技術有限,本文利用 OpenCV 只解決了問題 1 和 2,問題 3 和 4 的解決方法有興趣的同學可以繼續深挖以下

3,OpenCV 實現人臉識別;

1,特徵點提取、找到 Convexhull

利用 dlib 程序包進行人臉特徵點提取,根據特徵點 **find Convexhull(凸包)**人臉區域輪廓勾勒 (這裏只需要把臉部輪廓勾勒出來即可,不需要臉部中間的特徵點), 人臉特徵點提取請參考:
實現人臉識別、人臉68個特徵點提取,或許這個 Python 庫能幫到你!
在這裏插入圖片描述

2,Delaunay 三角剖分

利用步驟 1 計算得到的 Convexhull(凸包),進行 Delaunay 三角剖分,結果如下圖,三角剖分的具體操作請參考:
利用 OpenCV-Python 進行人臉 Delaunay 三角剖分(人臉檢測核心技術之一)
在這裏插入圖片描述
3,仿射變換

對 2 中的每個三角區域計算 仿射變換矩陣,並應用到人臉區域,最終實現初步對齊:

combina.jpg

4,Seamless Cloning(無縫克隆)

步驟 3 中得到凸顯邊緣處縫隙明顯,失真程度較大,這裏最終藉助於 OpenCV 的 Seamless Cloning 函數進行後處理,這裏需要一個 人臉 Mask ( 藉助於fillConvexPoly 函數),一個比對圖,以及要處理的圖像

output = cv2.seamlessClone(np.uint8(img1Warped),img2,mask,center,cv2.NORMAL_CLONE)

在這裏插入圖片描述
看起來還不錯吧

4,小總結

本文用到的技術比較多,涉及 dlib 特徵點提取,Subdiv2D 計算 Delaunay 三角剖分,find Convexhull計算多邊形區域凸包,fillConvexpoly 多邊形區域填充等技術

本文也算 OpenCV 的一個進階應用,對於剛瞭解的小夥伴們來說,完全掌握需要一些時間,還是建議跟着代碼敲一遍理解一下 Coding 的基本流程順序,由於篇幅原因,核心代碼放在下面:

def warpTriangle(img1,img2,t1,t2):

    # Find bounding rectangle for each triangle
    r1 = cv2.boundingRect(np.float32([t1]))
    r2 = cv2.boundingRect(np.float32([t2]))

    # Offset points by left top corner of respective rectangles

    t1Rect =[]
    t2Rect = []
    t2RectInt = []

    for i in range(0,3):
        t1Rect.append(((t1[i][0]-r1[0]),(t1[i][1]-r1[1])))
        t2Rect.append(((t2[i][0]-r2[0]),(t2[i][1]-r2[1])))
        t2RectInt.append(((t2[i][0] - r2[0]),(t2[i][1]-r2[1])))


    # Get mask by filling triangle
    mask = np.zeros((r2[3],r2[2],3),dtype = np.float32)
    cv2.fillConvexPoly(mask,np.int32(t2RectInt),(1.0,1.0,1.0),16,0)

    # Apply warpImage to small rectangular patches
    img1Rect = img1[r1[1]:r1[1]+r1[3],r1[0]:r1[0]+r1[2]]

    size = (r2[2],r2[3])
    img2Rect = applyAffineTransform(img1Rect,t1Rect,t2Rect,size)

    img2Rect = img2Rect*mask

    # Copy triangular region of the rectangular patch to the output image

    img2[r2[1]:r2[1]+r2[3],r2[0]:r2[0]+r2[2]] = img2[r2[1]:r2[1]+r2[3],r2[0]:r2[0]+r2[2]] *((1.0,1.0,1.0)-mask)
    img2[r2[1]:r2[1] +r2[3],r2[0]:r2[0]+r2[2]] = img2[r2[1]:r2[1]+r2[3],r2[0]:r2[0]+r2[2]] +img2Rect

文章涉及的代碼文件已上傳至 Github,關注公衆號: Z先生點記 後臺回覆關鍵詞 Face Swap 即可獲取。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章