用 Python — OpenCV 实现换脸技术 !

在开始之前先看一下效果图(提前声明一下:图片来源于网络侵删),因为人脸反差有点大因此有点辣眼睛,,,

用 Python — OpenCV 实现换脸技术 !_第1张图片 左右原图,中间为生成图

把图片中的角色互换,再来看一下转换后人脸替换的效果:

用 Python — OpenCV 实现换脸技术 !_第2张图片 角色转换人脸替换图

emm,结果怎么说呢,效果感觉还是不错的(产生的替换接缝不会那么失真、突兀),但是感觉生成新的人脸就是畸形的呢。

1,Face Swap 技术介绍

好了,下面将详细介绍 人脸替换技术并用 OpenCV 来实现;介绍到这里,如果没有看过之前写的几篇文章 ,

建议你提前了解一下,因为本文将用到这里面将用到 人脸特征点提取、Delaunay 三角剖分。

人脸替换技术是相对较难的,原因之一为人与人之间脸型的区别较大(纹理、脸型、区域凹凸部位等),使得替换后人脸区域与周围的皮肤组织差别较大,产生失真、不自然的视觉效果,

就下面两张图给出的最终人脸替换图,之所以结果非常辣眼睛,主要的原因如下几点:

1,年龄差别,脸部皱纹、纹理区域差别较大;

2,种族差别;一个是亚裔黄种人,一个是白种人,眼睛凹陷程度面部构造差别较大;

3,性别差别;男士和女士的脸型还是会有细微的区别的;

用 Python — OpenCV 实现换脸技术 !_第3张图片

2,人脸替换技术需要解决难点

1,脸部区域大小不一致问题,例如一个体型较胖的和题型较瘦的人大小是明显不一样的,直接替换操作明显是不合适的,需要我们提前统一脸型才能有后续的操作;

2,人脸替换后,替换的人脸区域跟周围的皮肤组织会明显存在颜色差别,灯光等问题使得替换缝隙比较突兀,如下图,这个问题如果得不到解决,最终处理后的图像会非常 “假”;

用 Python — OpenCV 实现换脸技术 !_第4张图片 combina.jpg

3,人脸拍摄角度问题,有的图片展示的是正脸、有的展示的是侧脸;

4,最终皮肤问题,需要把替换的的脸部区域纹理与周围组织的保持一致,最终变化差别不大;

由于技术有限,本文利用 OpenCV 只解决了问题 1 和 2,问题 3 和 4 的解决方法有兴趣的同学可以继续深挖以下

3,OpenCV 实现人脸识别;

1,特征点提取、找到 Convexhull

利用 dlib 程序包进行人脸特征点提取,根据特征点 find Convexhull(凸包)人脸区域轮廓勾勒 (这里只需要把脸部轮廓勾勒出来即可,不需要脸部中间的特征点), 人脸特征点提取请参考:

fillbox.png fillbox.png

2,Delaunay 三角剖分

利用步骤 1 计算得到的 Convexhull(凸包),进行 Delaunay 三角剖分,结果如下图,三角剖分的具体操作请参考:

fillbox_tri.png fillbox_tri.png

3,仿射变换

对 2 中的每个三角区域计算 仿射变换矩阵,并应用到人脸区域,最终实现初步对齐:

用 Python — OpenCV 实现换脸技术 !_第5张图片 combina.jpg

4,Seamless Cloning(无缝克隆)

步骤 3 中得到凸显边缘处缝隙明显,失真程度较大,这里最终借助于 OpenCV 的 Seamless Cloning 函数进行后处理,这里需要一个 人脸 Mask ( 借助于fillConvexPoly 函数),一个比对图,以及要处理的图像

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

看起来还不错吧

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 即可获取。

你可能感兴趣的:(OpenCv)