小 200 行 Python 代码做了一个换脸程序
admin
2023-07-30 22:56:35
0

简介

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

这个过程分四步:

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

1.使用 dlib 提取面部标记

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

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

1234567891011121314 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,使得下面这个表达式:

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

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

1234567891011121314151617181920 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 -= c2     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.])])

代码实现了这几步:

1.将输入矩阵转换为浮点数。这是后续操作的基础。

2.每一个点集减去它的矩心。一旦为点集找到了一个最佳的缩放和旋转方法,这两个矩心 c1 和 c2 就可以用来找到完整的解决方案。

3.同样,每一个点集除以它的标准偏差。这会消除组件缩放偏差的问题。

4.使用奇异值分解计算旋转部分。可以在维基百科上看到关于解决正交 Procrustes 问题的细节。

5.利用仿射变换矩阵返回完整的转化。

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

123456789 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

对齐结果如下:

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

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

这个问题是两ing-the-second-image\”>3.校正第二张图像的颜色

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

这个问题是两a>。

简介

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

这个过程分四步:

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

1.使用 dlib 提取面部标记

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

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

1234567891011121314 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,使得下面这个表达式:

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

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

1234567891011121314151617181920 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 -= c2     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.])])

代码实现了这几步:

1.将输入矩阵转换为浮点数。这是后续操作的基础。

2.每一个点集减去它的矩心。一旦为点集找到了一个最佳的缩放和旋转方法,这两个矩心 c1 和 c2 就可以用来找到完整的解决方案。

3.同样,每一个点集除以它的标准偏差。这会消除组件缩放偏差的问题。

4.使用奇异值分解计算旋转部分。可以在维基百科上看到关于解决正交 Procrustes 问题的细节。

5.利用仿射变换矩阵返回完整的转化。

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

123456789 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

相关内容

热门资讯

Mobi、epub格式电子书如... 在wps里全局设置里有一个文件关联,打开,勾选电子书文件选项就可以了。
500 行 Python 代码... 语法分析器描述了一个句子的语法结构,用来帮助其他的应用进行推理。自然语言引入了很多意外的歧义,以我们...
定时清理删除C:\Progra... C:\Program Files (x86)下面很多scoped_dir开头的文件夹 写个批处理 定...
scoped_dir32_70... 一台虚拟机C盘总是莫名奇妙的空间用完,导致很多软件没法再运行。经过仔细检查发现是C:\Program...
65536是2的几次方 计算2... 65536是2的16次方:65536=2⁶ 65536是256的2次方:65536=256 6553...
小程序支付时提示:appid和... [Q]小程序支付时提示:appid和mch_id不匹配 [A]小程序和微信支付没有进行关联,访问“小...
pycparser 是一个用... `pycparser` 是一个用 Python 编写的 C 语言解析器。它可以用来解析 C 代码并构...
微信小程序使用slider实现... 众所周知哈,微信小程序里面的音频播放是没有进度条的,但最近有个项目呢,客户要求音频要有进度条控制,所...
Apache Doris 2.... 亲爱的社区小伙伴们,我们很高兴地向大家宣布,Apache Doris 2.0.0 版本已于...
python清除字符串里非数字... 本文实例讲述了python清除字符串里非数字字符的方法。分享给大家供大家参考。具体如下: impor...