如何构建换脸应用程序:计算机视觉算法

2021年11月28日04:56:40 发表评论 1,323 次浏览

Python如何实现换脸?这篇文章是 3 篇文章中的第一篇,我们将在其中构建一个将自动执行面部交换的移动应用程序。

  • 第 1 部分:计算机视觉算法
  • 第 2 部分:Rest API - 待定
  • 第 3 部分:移动应用程序 - 待定

计算机视觉已被证明是计算机科学领域最具革命性的领域之一,因为它为世界上一些最具挑战性的问题提供了解决方案。

它还提供了创新技术,为全球技术的进步做出了积极贡献。我们可以说计算机视觉是我们最接近数字世界与物理世界交互的地方。

它使自动驾驶汽车能够理解和解读周围环境。计算机视觉在增强现实和虚拟现实技术中也发挥着主导作用,而且人工智能的许多现代应用也基于计算机视觉的几个概念。

这使得 AI 应用程序的性能优于大部分经典方法。

因此,面部识别是计算机视觉的另一种应用,它为一些现有的解决方案提出了许多解决方案,无论是监视还是犯罪和盗窃预防。

面部识别基本上是使用相机识别人脸。面部识别系统的一个流行示例是手机中的面部锁定。它使用人脸识别算法进行检测,然后将检测到的人脸与数据库中的人脸进行比较以进行屏幕锁定,如果匹配则自动解锁手机。

如何构建换脸应用程序?面部交换是计算机视觉的另一个应用,它也基于面部识别。本文将介绍计算机视觉中人脸交换的基础知识以及在 OpenCV 中执行的方法,包括详细换脸程序实现示例。文章将遵循以下结构:

  • 什么是换脸?
  • 如何进行换脸?
  • 地标检测指南
  • 什么是 Dlib?
  • 实现是OpenCV和Dlib
  • 结论

什么是换脸?

你们中的大多数人可能都见过过滤器,可以让你交换照片中的两个人的面孔,或者与名人或动物交换面孔。

许多人在社交媒体和 Snapchat 等应用程序上都会遇到这些类型的过滤器。

这在理论上是一个相当简单的概念,但在实践中并非如此。人眼在面部识别方面非常有效,可以轻松检测面部是真还是假。

它还可以轻松检测面部边界和面部特征,如眼睛和鼻子。但是让计算机解释所有这些信息并不像人类那么容易。

但是,相对于计算机视觉的其他应用程序来看,换脸的概念并不那么困难。

换脸的搞笑结果
换脸程序实现示例:换脸的搞笑结果

如何进行换脸?

自从先进的计算机视觉技术出现以来,已经多次尝试开发面部交换技术。从那时起,许多软件,如 adobe,已经在他们的 photoshop 软件中集成了执行面部交换的工具。

同样,人工智能领域的人们也开发了实现换脸的模型。DeepFacLab 为实现人脸交换而实施的深度学习模型就是一个示例,它提供了一个集成框架来执行图片之间的人脸交换。

Python如何实现换脸?除此之外,dlib 中用于面部交换的方法也基于基于机器学习的解决方案。它使用 ML 模型进行图片中的地标检测,有助于面部识别。这种使用地标检测的面部识别可以扩展到在图像之间执行面部交换。综上所述,换脸方法包括:

  • Adobe 软件中的经典 Photoshop。
  • 基于人工智能的方法
    • 深度人脸实验室
    • 使用 dlib 进行地标检测。

地标检测提供了交换过程的面部识别部分。识别后,我们通过删除除人脸之外的部分从一张图像中提取人脸,在第二张图片中,我们删除人脸。

然后,我们将从一张图像中提取的人脸放在删除人脸的图像上,并将其放置在发生删除的位置。这显然不能提供好的结果,因为新人脸的轮廓不会与新图像匹配或融合。因此,我们必须平滑人脸的边界,并在边界处对两张图像进行一点处理,使新人脸与新图像很好地融合在一起,看起来更逼真。


地标检测指南

地标检测是一种计算机视觉技术,我们从人脸中检测关键点并跟踪它们。

如何构建换脸应用程序?虽然计算机视觉中的地标检测与人脸有关,但地标检测一般不限于人脸。它是指通过在对象周围创建边界框来检测和识别图像中的对象。所以,这样的算法通常会为我们输出两件事:

  • 当前对象的概率。
  • 在对象存在的情况下,包围它的边界框的坐标。

通常,图像中需要由神经网络检测的关键点被称为地标。这些地标不一定是 4 个点以创建边界框,但可以根据应用程序进行任何计数。

在这样的应用中,我们需要多个地标来识别物体或表面,我们希望模型输出检测到的地标的坐标 (x,y),而不是之前的边界框。

让我们看一个具体示例,说明我们希望神经网络模型在何处识别人类嘴唇的两个角。这将为每个人脸提供两个地标并输出四个数字,即:

  • (x1, y1)
  • (x2, y2)

这是相当简单的执行。但是,如果我们希望我们的神经网络不仅检测唇角,还检测整个嘴唇的外衬以及眼睛、鼻子和脸上的其他重要标志,点(标志)的数量将增加到n

(x1, y1), (x2, y2), (x3, y3) …...... (xn,yn)

为了使用神经网络执行如此广泛的操作,我们首先需要决定地标的位置,然后用决定的地标标记整个训练集。

如果训练数据集很大,这可能会成为一项繁忙的任务。地标的顺序在这里也很重要,它需要在整个数据集中保持一致,例如,如果你将第一个地标放置在鼻尖,那么所有图像都应将第一个地标放在鼻尖上。鼻子。同样,整个数据集都将被标记。

在整个标记过程之后,训练集就准备好了,然后我们将其输入到神经网络中开始训练过程,最好是卷积神经网络。

查看我们关于使用 Python 检测人脸特征的文章, 以了解有关这个有趣主题的更多信息。

地标识别
地标识别

什么是 Dlib?

Dlib 是一个基于 C++ 的现代工具包,用于开发机器学习算法。它还包含用 C++ 开发复杂软件以解决实际问题的工具。

该工具广泛应用于工业和学术界,广泛应用于嵌入式系统设计、手机、机器人和高性能大型计算环境等领域。

此外,它是一个有助于使用的开源库,因为任何人都可以将它用于想要构建的任何应用程序。

与许多其他开源项目不同,Dlib 为库中存在的每个类和模块提供了广泛而精确的文档,以便任何使用它的人都能简单理解。

它还具有良好的单元测试覆盖率,因为它定期在 MS Windows、Mac OSS 和 Linux 系统上进行测试。这个库是独立的,它的代码的正常执行不需要其他包。

它提供了大量用于开发的机器学习和数值算法。它还包含深度学习工具,因此可以使用它来创建从回归、支持向量机到深度神经网络的 ML 模型。

图书馆提供的其他广泛的工具包括:

  • 图形模型接口算法
  • 图像处理
  • 穿线
  • 联网
  • 图形用户界面
  • 数据压缩和完整性算法
  • 测试

使用OpenCV和Dlib实现换脸程序

与往常一样,你可以在Google Colab上访问本文的完整代码 ,我们接下来将对其进行描述和解释。

换脸程序实现示例开始:在开始代码之前,我们需要安装 Dlib 和 OpenCV。使用 python pip 命令安装使用:

pip install Dlib
pip install opencv-contrib-python

安装库后,我们需要下载我们将在 Dlib 中使用的预训练的地标检测模型。在此处下载模型 。

现在我们可以开始实施了。

import cv2
import dlib
import numpy as np
from matplotlib import pyplot as plt

# Loading base images and coverting them to grayscale
face = cv2.imread("chris.jpeg")
body = cv2.imread("trump.jpeg")

如何构建换脸应用程序?我们首先导入依赖项,即 OpenCV、Dlib、matplotlib 和 numpy。Matplotlib 用于图像的可视化绘图。Numpy 用于数学计算。然后我们使用 OpenCV 的“imread”函数导入两张图像,一张用于面部,另一张用作身体。

face_gray = cv2.cvtColor(face, cv2.COLOR_BGR2GRAY)
body_gray = cv2.cvtColor(body, cv2.COLOR_BGR2GRAY)

# Create empty matrices in the images' shapes
height, width = face_gray.shape
mask = np.zeros((height, width), np.uint8)

height, width, channels = body.shape

换脸程序实现示例:现在,将图像从彩色图像转换为灰度图像。然后,我们初始化一个矩阵,其大小与我们将使用的人脸图像的形状相同。然后我们提取身体图像的形状。

# Loading models and predictors of the dlib library to detect landmarks in both faces
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor("./shape_predictor_68_face_landmarks.dat")


# Getting landmarks for the face that will be swapped into to the body
rect = detector(face_gray)[0]

# This creates a with 68 pairs of integer values — these values are the (x, y)-coordinates of the facial structures 
landmarks = predictor(face_gray, rect)
landmarks_points = [] 

def get_landmarks(landmarks, landmarks_points):
    for n in range(68):
        x = landmarks.part(n).x
        y = landmarks.part(n).y
        landmarks_points.append((x, y))

get_landmarks(landmarks, landmarks_points)

points = np.array(landmarks_points, np.int32)

Python如何实现换脸?现在我们使用我们下载的预训练的人脸标志检测器来识别第一张图像中的人脸。正如在 for 循环中所见,我们检测了 68 个地标来识别图像中的人脸。我们提取 for 循环中 68 个地标中每一个的坐标。

convexhull = cv2.convexHull(points) 

face_cp = face.copy()
plt.imshow(cv2.cvtColor((cv2.polylines(face_cp, [convexhull], True, (255,255,255), 3)), cv2.COLOR_BGR2RGB))

face_image_1 = cv2.bitwise_and(face, face, mask=mask)
如何构建换脸应用程序:计算机视觉算法

接下来,我们使用 OpenCV 库的凸包模块绘制从地标检测模型检测到的人脸周围的轮廓。

rect = cv2.boundingRect(convexhull)

subdiv = cv2.Subdiv2D(rect) # Creates an instance of Subdiv2D
subdiv.insert(landmarks_points) # Insert points into subdiv
triangles = subdiv.getTriangleList()
triangles = np.array(triangles, dtype=np.int32)

indexes_triangles = []
face_cp = face.copy()

def get_index(arr):
    index = 0
    if arr[0]:
        index = arr[0][0]
    return index

for triangle in triangles :

    # Gets the vertex of the triangle
    pt1 = (triangle[0], triangle[1])
    pt2 = (triangle[2], triangle[3])
    pt3 = (triangle[4], triangle[5])
    
    # Draws a line for each side of the triangle
    cv2.line(face_cp, pt1, pt2, (255, 255, 255), 3,  0)
    cv2.line(face_cp, pt2, pt3, (255, 255, 255), 3,  0)
    cv2.line(face_cp, pt3, pt1, (255, 255, 255), 3,  0)

    index_pt1 = np.where((points == pt1).all(axis=1))
    index_pt1 = get_index(index_pt1)
    index_pt2 = np.where((points == pt2).all(axis=1))
    index_pt2 = get_index(index_pt2)
    index_pt3 = np.where((points == pt3).all(axis=1))
    index_pt3 = get_index(index_pt3)

    # Saves coordinates if the triangle exists and has 3 vertices
    if index_pt1 is not None and index_pt2 is not None and index_pt3 is not None:
        vertices = [index_pt1, index_pt2, index_pt3]
        indexes_triangles.append(vertices)

# Draw delaunay triangles
plt.imshow(cv2.cvtColor(face_cp, cv2.COLOR_BGR2RGB))  
如何构建换脸应用程序:计算机视觉算法

在这里,我们从每个地标到检测到的特征创建三角形。

# Getting landmarks for the face that will have the first one swapped into
rect2 = detector(body_gray)[0]

# This creates a with 68 pairs of integer values — these values are the (x, y)-coordinates of the facial structures 
landmarks_2 = predictor(body_gray, rect2)
landmarks_points2 = []

# Uses the function declared previously to get a list of the landmark coordinates
get_landmarks(landmarks_2, landmarks_points2)

# Generates a convex hull for the second person
points2 = np.array(landmarks_points2, np.int32)
convexhull2 = cv2.convexHull(points2)

body_cp = body.copy()
plt.imshow(cv2.cvtColor((cv2.polylines(body_cp, [convexhull2], True, (255,255,255), 3)), cv2.COLOR_BGR2RGB))
如何构建换脸应用程序:计算机视觉算法

现在,我们对第二张图像执行与第一张相同的面部识别方法。

lines_space_new_face = np.zeros((height, width, channels), np.uint8)
body_new_face = np.zeros((height, width, channels), np.uint8)

height, width = face_gray.shape
lines_space_mask = np.zeros((height, width), np.uint8)


for triangle in indexes_triangles:

    # Coordinates of the first person's delaunay triangles
    pt1 = landmarks_points[triangle[0]]
    pt2 = landmarks_points[triangle[1]]
    pt3 = landmarks_points[triangle[2]]

    # Gets the delaunay triangles
    (x, y, widht, height) = cv2.boundingRect(np.array([pt1, pt2, pt3], np.int32))
    cropped_triangle = face[y: y+height, x: x+widht]
    cropped_mask = np.zeros((height, widht), np.uint8)

    # Fills triangle to generate the mask
    points = np.array([[pt1[0]-x, pt1[1]-y], [pt2[0]-x, pt2[1]-y], [pt3[0]-x, pt3[1]-y]], np.int32)
    cv2.fillConvexPoly(cropped_mask, points, 255)

    # Draws lines for the triangles
    cv2.line(lines_space_mask, pt1, pt2, 255)
    cv2.line(lines_space_mask, pt2, pt3, 255)
    cv2.line(lines_space_mask, pt1, pt3, 255)

    lines_space = cv2.bitwise_and(face, face, mask=lines_space_mask)

    # Calculates the delaunay triangles of the second person's face

    # Coordinates of the first person's delaunay triangles
    pt1 = landmarks_points2[triangle[0]]
    pt2 = landmarks_points2[triangle[1]]
    pt3 = landmarks_points2[triangle[2]]

    # Gets the delaunay triangles
    (x, y, widht, height) = cv2.boundingRect(np.array([pt1, pt2, pt3], np.int32))
    cropped_mask2 = np.zeros((height,widht), np.uint8)

    # Fills triangle to generate the mask
    points2 = np.array([[pt1[0]-x, pt1[1]-y], [pt2[0]-x, pt2[1]-y], [pt3[0]-x, pt3[1]-y]], np.int32)
    cv2.fillConvexPoly(cropped_mask2, points2, 255)

    # Deforms the triangles to fit the subject's face : https://docs.opencv.org/3.4/d4/d61/tutorial_warp_affine.html
    points =  np.float32(points)
    points2 = np.float32(points2)
    M = cv2.getAffineTransform(points, points2)  # Warps the content of the first triangle to fit in the second one
    dist_triangle = cv2.warpAffine(cropped_triangle, M, (widht, height))
    dist_triangle = cv2.bitwise_and(dist_triangle, dist_triangle, mask=cropped_mask2)

    # Joins all the distorted triangles to make the face mask to fit in the second person's features
    body_new_face_rect_area = body_new_face[y: y+height, x: x+widht]
    body_new_face_rect_area_gray = cv2.cvtColor(body_new_face_rect_area, cv2.COLOR_BGR2GRAY)

    # Creates a mask
    masked_triangle = cv2.threshold(body_new_face_rect_area_gray, 1, 255, cv2.THRESH_BINARY_INV)
    dist_triangle = cv2.bitwise_and(dist_triangle, dist_triangle, mask=masked_triangle[1])

    # Adds the piece to the face mask
    body_new_face_rect_area = cv2.add(body_new_face_rect_area, dist_triangle)
    body_new_face[y: y+height, x: x+widht] = body_new_face_rect_area
  
plt.imshow(cv2.cvtColor(body_new_face, cv2.COLOR_BGR2RGB))
如何构建换脸应用程序:计算机视觉算法

Python如何实现换脸?在这里,我们将第一张图像中的人脸转换为人脸的方向和大小,以适应第二张图像中的人脸。

body_face_mask = np.zeros_like(body_gray)
body_head_mask = cv2.fillConvexPoly(body_face_mask, convexhull2, 255)
body_face_mask = cv2.bitwise_not(body_head_mask)

body_maskless = cv2.bitwise_and(body, body, mask=body_face_mask)
result = cv2.add(body_maskless, body_new_face)

plt.imshow(cv2.cvtColor(result, cv2.COLOR_BGR2RGB))
如何构建换脸应用程序:计算机视觉算法
换脸程序实现示例

换脸程序实现示例:现在,我们将图像 2 中的人脸替换为图像 1 中转换后的人脸。

# Gets the center of the face for the body
(x, y, widht, height) = cv2.boundingRect(convexhull2)
center_face2 = (int((x+x+widht)/2), int((y+y+height)/2))

seamlessclone = cv2.seamlessClone(result, body, body_head_mask, center_face2, cv2.NORMAL_CLONE)

plt.imshow(cv2.cvtColor(seamlessclone, cv2.COLOR_BGR2RGB))

cv2.imwrite("./result.png", seamlessclone)
如何构建换脸应用程序:计算机视觉算法
Python如何实现换脸

最后,我们使用OpenCV的seamlessClone模块将人脸的边界与新的身体合并。


结论

计算机视觉是一个具有大量应用的广泛领域。换脸就是其中之一。

如何构建换脸应用程序?基于神经网络和深度学习的现代计算机视觉是开发换脸模型的最有效方式。Dlib 和 OpenCV 可用于创建人脸交换模型。

Dlib 是一个基于 C++ 的扩展库,可用于开发机器学习模型。它为面部识别的地标检测提供了一个预先训练的模型。这证明了基于深度学习的计算机视觉解决方案的潜力。

如果你喜欢这篇文章并想了解有关计算机视觉应用的更多信息,请查看我们的其他研究:

  • 使用 OpenCV 进行对象跟踪
  • 使用 AI 去除背景
  • 使用深度学习生成图像
  • 用 Python 检测人脸特征
木子山

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: