小 200 行 Python 代码制作一个换脸程序


Posted in Python onMay 12, 2020

小 200 行 Python 代码制作一个换脸程序

简介

在这篇文章中我将介绍如何写一个简短(200行)的 Python 脚本,来自动地将一幅图片的脸替换为另一幅图片的脸。

这个过程分四步:

  • 检测脸部标记。
  • 旋转、缩放、平移和第二张图片,以配合第一步。
  • 调整第二张图片的色彩平衡,以适配第一张图片。
  • 把第二张图像的特性混合在第一张图像中。

1.使用 dlib 提取面部标记

该脚本使用 dlib 的 Python 绑定来提取面部标记:

小 200 行 Python 代码制作一个换脸程序

Dlib 实现了 Vahid Kazemi 和 Josephine Sullivan 的《使用回归树一毫秒脸部对准》论文中的算法。算法本身非常复杂,但dlib接口使用起来非常简单:

PREDICTOR_PATH = "/home/matt/dlib-18.16/shape_predictor_68_face_landmarks.dat" 
detector = dlib.get_frontal_face_detector() 
predictor = dlib.shape_predictor(PREDICTOR_PATH) 
def get_landmarks(im): 
  rects = detector(im, 1) 
  if len(rects) > 1: 
    raise TooManyFaces 
  if len(rects) == 0: 
    raise NoFaces 
  return numpy.matrix([[p.x, p.y] for p in predictor(im, rects[0]).parts()])

get_landmarks()函数将一个图像转化成numpy数组,并返回一个68×2元素矩阵,输入图像的每个特征点对应每行的一个x,y坐标。

特征提取器(predictor)需要一个粗糙的边界框作为算法输入,由一个传统的能返回一个矩形列表的人脸检测器(detector)提供,其每个矩形列表在图像中对应一个脸。

2.用 Procrustes 分析调整脸部

现在我们已经有了两个标记矩阵,每行有一组坐标对应一个特定的面部特征(如第30行的坐标对应于鼻头)。我们现在要解决如何旋转、翻译和缩放第一个向量,使它们尽可能适配第二个向量的点。一个想法是可以用相同的变换在第一个图像上覆盖第二个图像。

将这个问题数学化,寻找T,s 和 R,使得下面这个表达式:

小 200 行 Python 代码制作一个换脸程序

结果最小,其中R是个2×2正交矩阵,s是标量,T是二维向量,pi和qi是上面标记矩阵的行。

事实证明,这类问题可以用“常规 Procrustes 分析法”解决:

def transformation_from_points(points1, points2): 
  points1 = points1.astype(numpy.float64) 
  points2 = points2.astype(numpy.float64) 
  c1 = numpy.mean(points1, axis=0) 
  c2 = numpy.mean(points2, axis=0) 
  points1 -= c1 
  points2 -= c 
  s1 = numpy.std(points1) 
  s2 = numpy.std(points2) 
  points1 /= s1 
  points2 /= s2 
  U, S, Vt = numpy.linalg.svd(points1.T * points2) 
  R = (U * Vt).T 
  return numpy.vstack([numpy.hstack(((s2 / s1) * R, 
                    c2.T - (s2 / s1) * R * c1.T)), 
             numpy.matrix([0., 0., 1.])])

代码实现了这几步:

  • 将输入矩阵转换为浮点数。这是后续操作的基础。
  • 每一个点集减去它的矩心。一旦为点集找到了一个最佳的缩放和旋转方法,这两个矩心 c1 和 c2 就可以用来找到完整的解决方案。
  • 同样,每一个点集除以它的标准偏差。这会消除组件缩放偏差的问题。
  • 使用奇异值分解计算旋转部分。可以在维基百科上看到关于解决正交 Procrustes 问题的细节。
  • 利用仿射变换矩阵返回完整的转化。

其结果可以插入 OpenCV 的 cv2.warpAffine 函数,将图像二映射到图像一:

def warp_im(im, M, dshape): 
  output_im = numpy.zeros(dshape, dtype=im.dtype) 
  cv2.warpAffine(im, 
          M[:2], 
          (dshape[1], dshape[0]), 
          dst=output_im, 
          borderMode=cv2.BORDER_TRANSPARENT, 
          flags=cv2.WARP_INVERSE_MAP) 
  return output_im

对齐结果如下:

小 200 行 Python 代码制作一个换脸程序

3.校正第二张图像的颜色

如果我们试图直接覆盖面部特征,很快会看到这个问题:

小 200 行 Python 代码制作一个换脸程序

这个问题是两幅图像之间不同的肤色和光线造成了覆盖区域的边缘不连续。我们试着修正:

COLOUR_CORRECT_BLUR_FRAC = 0.6 
LEFT_EYE_POINTS = list(range(42, 48)) 
RIGHT_EYE_POINTS = list(range(36, 42)) 
def correct_colours(im1, im2, landmarks1): 
  blur_amount = COLOUR_CORRECT_BLUR_FRAC * numpy.linalg.norm( 
               numpy.mean(landmarks1[LEFT_EYE_POINTS], axis=0) - 
               numpy.mean(landmarks1[RIGHT_EYE_POINTS], axis=0)) 
  blur_amount = int(blur_amount) 
  if blur_amount % 2 == 0: 
    blur_amount += 1 
  im1_blur = cv2.GaussianBlur(im1, (blur_amount, blur_amount), 0) 
  im2_blur = cv2.GaussianBlur(im2, (blur_amount, blur_amount), 0) 
  # Avoid divide-by-zero errors. 
  im2_blur += 128 * (im2_blur <= 1.0) 
  return (im2.astype(numpy.float64) * im1_blur.astype(numpy.float64) / 
                        im2_blur.astype(numpy.float64))

结果如下:

小 200 行 Python 代码制作一个换脸程序

此函数试图改变 im2 的颜色来适配 im1。它通过用 im2 除以 im2 的高斯模糊值,然后乘以im1的高斯模糊值。这里的想法是用RGB缩放校色,但并不是用所有图像的整体常数比例因子,每个像素都有自己的局部比例因子。

用这种方法两图像之间光线的差异只能在某种程度上被修正。例如,如果图像1是从一侧照亮,但图像2是被均匀照亮的,色彩校正后图像2也会出现未照亮一侧暗一些的问题。

也就是说,这是一个相当简陋的办法,而且解决问题的关键是一个适当的高斯核函数大小。如果太小,第一个图像的面部特征将显示在第二个图像中。过大,内核之外区域像素被覆盖,并发生变色。这里的内核用了一个0.6 *的瞳孔距离。

4.把第二张图像的特征混合在第一张图像中

用一个遮罩来选择图像2和图像1的哪些部分应该是最终显示的图像:

小 200 行 Python 代码制作一个换脸程序

值为1(显示为白色)的地方为图像2应该显示出的区域,值为0(显示为黑色)的地方为图像1应该显示出的区域。值在0和1之间为图像1和图像2的混合区域。

我们把上述过程分解:

  • get_face_mask()的定义是为一张图像和一个标记矩阵生成一个遮罩,它画出了两个白色的凸多边形:一个是眼睛周围的区域,一个是鼻子和嘴部周围的区域。之后它由11个像素向遮罩的边缘外部羽化扩展,可以帮助隐藏任何不连续的区域。
  • 这样一个遮罩同时为这两个图像生成,使用与步骤2中相同的转换,可以使图像2的遮罩转化为图像1的坐标空间。
  • 之后,通过一个element-wise最大值,这两个遮罩结合成一个。结合这两个遮罩是为了确保图像1被掩盖,而显现出图像2的特性。

最后,使用遮罩得到最终的图像:

output_im = im1 * (1.0 - combined_mask) + warped_corrected_im2 * combined_mask

小 200 行 Python 代码制作一个换脸程序

总结

到此这篇关于小 200 行 Python 代码制作一个换脸程序的文章就介绍到这了,更多相关python 换脸程序内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
Python入门之三角函数atan2()函数详解
Nov 08 Python
TensorFlow中权重的随机初始化的方法
Feb 11 Python
Python 实现在文件中的每一行添加一个逗号
Apr 29 Python
python网络应用开发知识点浅析
May 28 Python
Django使用unittest模块进行单元测试过程解析
Aug 02 Python
django创建超级用户过程解析
Sep 18 Python
Window10下python3.7 安装与卸载教程图解
Sep 30 Python
Flask 上传自定义头像的实例详解
Jan 09 Python
python如何获取apk的packagename和activity
Jan 10 Python
python读取tif图片时保留其16bit的编码格式实例
Jan 13 Python
matlab中imadjust函数的作用及应用举例
Feb 27 Python
pandas中DataFrame重置索引的几种方法
May 24 Python
python3用PyPDF2解析pdf文件,用正则匹配数据方式
May 12 #Python
Python如何根据时间序列数据作图
May 12 #Python
python logging.info在终端没输出的解决
May 12 #Python
你应该知道的Python3.6、3.7、3.8新特性小结
May 12 #Python
python将logging模块封装成单独模块并实现动态切换Level方式
May 12 #Python
Python PyQt5模块实现窗口GUI界面代码实例
May 12 #Python
从0到1使用python开发一个半自动答题小程序的实现
May 12 #Python
You might like
PDO::errorInfo讲解
2019/01/28 PHP
JavaScript去除空格的三种方法(正则/传参函数/trim)
2013/02/06 Javascript
js使用onmousemove和onmouseout获取鼠标坐标的方法
2015/03/31 Javascript
jQuery中(function($){})(jQuery)详解
2015/07/15 Javascript
jquery+json实现动态商品内容展示的方法
2016/01/14 Javascript
基于JavaScript实现瀑布流布局(二)
2016/01/26 Javascript
Angularjs在初始化未完毕时出现闪烁问题的解决方法分析
2016/08/05 Javascript
不间断循环滚动效果的实例代码(必看篇)
2016/10/08 Javascript
JavaScript自动点击链接 防止绕过浏览器访问的方法
2017/01/19 Javascript
ES6解构赋值的功能与用途实例分析
2017/10/31 Javascript
js实现搜索栏效果
2018/11/16 Javascript
详解Node.js amqplib 连接 Rabbit MQ最佳实践
2019/01/24 Javascript
详解如何探测小程序返回到webview页面
2019/05/14 Javascript
微信小程序动态显示项目倒计时
2019/06/20 Javascript
微信小程序Echarts图表组件使用方法详解
2019/06/25 Javascript
8个有意思的JavaScript面试题
2019/07/30 Javascript
Javascript地址引用代码实例解析
2020/02/25 Javascript
使用JS实现动态时钟
2020/03/12 Javascript
关于vue属性使用和不使用冒号的区别说明
2020/10/22 Javascript
[28:48]《真视界》- 2017年国际邀请赛
2017/09/27 DOTA
Sanic框架路由用法实例分析
2018/07/16 Python
Python pandas DataFrame操作的实现代码
2019/06/21 Python
python多继承(钻石继承)问题和解决方法简单示例
2019/10/21 Python
python多进程 主进程和子进程间共享和不共享全局变量实例
2020/04/25 Python
Pytorch之Tensor和Numpy之间的转换的实现方法
2020/09/03 Python
详解移动端HTML5页面端去掉input输入框的白色背景和边框(兼容Android和ios)
2016/12/15 HTML / CSS
澳洲女装时尚在线:Blue Bungalow
2018/05/05 全球购物
我的网上商城创业计划书
2013/12/26 职场文书
《画家乡》教学反思
2014/04/22 职场文书
党的群众路线教育实践活动党员个人整改措施
2014/10/27 职场文书
初中语文教学随笔
2015/08/15 职场文书
导游词之桂林
2019/08/20 职场文书
教你如何使用Python Tkinter库制作记事本
2021/06/10 Python
利用Python读取微信朋友圈的多种方法总结
2021/08/23 Python
CSS实现九宫格布局(自适应)的示例代码
2022/02/12 HTML / CSS
win11无法添加打印机怎么办? 提示windows无法打开添加打印机的解决办法
2022/04/05 数码科技