在本文中,将探讨如何使用OpenCV和dlib这两个强大的库来从给定的图像中提取人脸,并尝试将两张不同图像中的人脸特征点进行匹配。简而言之,将尝试将两张不同图像中的人脸特征点进行网格化匹配。将使用一个预训练的模型来从人脸中提取特征点(68个特征点检测)。
Snapchat是当今年轻一代中非常流行的一款应用,它允许用户在面部上应用多种滤镜。类似地,可以将人脸网格匹配功能添加到Snapchat或其他类似软件中,以吸引更多用户,从而增加下载量。
增强现实软件也可以在其某些用例中使用这一功能,以展示和创造更具创意的效果。
import cv2
import numpy as np
import dlib
import requests
from PIL import Image
以下是安装dlib库的步骤,这可能有些复杂,需要遵循以下步骤:
下载预训练模型shape_predictor:
def extract_index_nparray(nparray):
index = None
for num in nparray[0]:
index = num
break
return index
接下来,将从互联网上的URL加载源图像,并调整其大小。
image1 = Image.open(requests.get('https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSx8Pu1tW1uCiZPfj9K1EL6uHxbg3bOKO9XkA&usqp=CAU', stream=True).raw)
image1 = image1.resize((300,300))
同样,将从互联网上的URL加载目标图像,并调整其大小。
image2 = Image.open(requests.get('https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTYX1dyl9INRo5cbvDeTILRcZVzfcMsCsE0kg&usqp=CAU', stream=True).raw)
image2 = image2.resize((300,300))
img = np.array(image1)
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
mask = np.zeros_like(img_gray)
img2 = np.array(image2)
img2_gray = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
为了创建与源图像大小相同的零值掩码图像,首先使用dlib加载面部检测器和面部特征点预测器,然后找到高度、宽度和通道数。
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")
height, width, channels = img2.shape
img2_new_face = np.zeros((height, width, channels), np.uint8)
首先,需要将图像传递给检测器,然后使用该对象提取特征点。之后,将提取的特征点(x和y)存储到特征点列表中。将面部网格化为三角形。这一步是面部交换的核心。这里,将每个三角形与目标图像中的相应三角形进行交换。目标图像的三角化需要与源图像的三角化具有相同的模式。这意味着连接符号的连接方式必须相同。因此,在对源图像进行三角化之后,从该三角化中获取(x和y)的索引,以便可以在目标图像上复制相同的三角化。一旦有了三角形的索引,就循环遍历它们并对目标面部进行三角化。
faces = detector(img_gray)
for face in faces:
landmarks = predictor(img_gray, face)
landmarks_points = []
for n in range(0, 68):
x = landmarks.part(n).x
y = landmarks.part(n).y
landmarks_points.append((x, y))
points = np.array(landmarks_points, np.int32)
convexhull = cv2.convexHull(points)
cv2.fillConvexPoly(mask, convexhull, 255)
Delaunay三角化是一种用于三角化图像的算法。一旦切割并包装了所有的三角形,需要将它们连接在一起。然后,需要使用三角化模式重建面部,唯一的区别是这次放置了包装的三角形。
img2_face_mask = np.zeros_like(img2_gray)
img2_head_mask = cv2.fillConvexPoly(img2_face_mask, convexhull2, 255)
img2_face_mask = cv2.bitwise_not(img2_head_mask)
现在面部已经准备好被替换了。取新面部和没有面部的目标图像,并将它们连接在一起。
img2_head_noface = cv2.bitwise_and(img2, img2, mask=img2_face_mask)
result = cv2.add(img2_head_noface, img2_new_face)
最后,使用OpenCV内置的“seamless clone”函数自动执行此操作。需要取新面部(在第6步创建的),取原始目标图像及其掩码以切出面部,需要获取面部的中间部分。
(x, y, w, h) = cv2.boundingRect(convexhull2)
center_face2 = (int((x + x + w) / 2), int((y + y + h) / 2))
seamlessclone = cv2.seamlessClone(result, img2, img2_head_mask, center_face2, cv2.NORMAL_CLONE)
最后,将输出的NumPy图像转换为Pillow的Image对象以进行可视化。
Image.fromarray(seamlessclone)