换脸操作实践
- 换脸 (face swapping)
- 换脸操作实现
- 相关链接
换脸 (face swapping)
换脸是指照片中的人脸自动替换:将一个人脸的某些部分与另一个人脸的其他部分相结合以形成新的面部图像。它可以被视为另一种类型的面部融合技术。在本节中,我们将使用面部关键点和单应性来实现换脸操作。
换脸操作实现
(1) 导入所有必需的库:
import cv2
import dlib
import numpy as np
import matplotlib.pylab as plt
import imutils
(2) 使用人脸检测器从输入面部图像中检测面部边界框,然后使用形状预测器从每个输入图像中提取 68
个面部关键点。我们需要使用除下颌线以外的所有关键点(关键点 17-68
),并定义用于面部融合的的其他常量(羽化值、颜色校正模糊):
predictor_path = "models/shape_predictor_68_face_landmarks.dat"
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor(predictor_path)feather_amount = 11
color_correction_blur = 0.5keypoints = list(range(17,68))
left_eye_points, right_eye_points = list(range(42,48)), list(range(36,42))
可以在 GitCode 中下载 shape_predictor_68_face_landmarks.dat
文件。
(3) 定义函数 draw_convex_hull()
以在给定图像上的给定点集周围以给定颜色绘制凸包:
def draw_convex_hull(im, points, color):points = cv2.convexHull(points)cv2.fillConvexPoly(im, points, color=color)
(4) 定义函数 get_landmarks()
通过使用形状预测器返回给定图像的面部关键点:
def get_landmarks(img): rects = detector(img, 0)if len(rects) == 0:return -1return np.array([[p.x, p.y] for p in predictor(img, rects[0]).parts()])
(5) 定义函数 get_face_mask()
返回给定面部图像和面部标志的掩码,在关键点周围绘制一个填充的凸包(用白色填充),使区域平滑,并返回结果:
def get_face_mask(im, landmarks):im = np.zeros(im.shape[:2], dtype=np.float64)for group in [keypoints]:draw_convex_hull(im, landmarks[group], color=1)im = np.array([im, im, im]).transpose((1, 2, 0))im = cv2.GaussianBlur(im, (feather_amount, feather_amount), 0) > 0im = im * 1.0im = cv2.GaussianBlur(im, (feather_amount, feather_amount), 0)return im
(6) 定义函数 correct_colours()
根据给定的第一张面部图片和关键点校正扭曲的第二张面部图像的颜色。使用第一图像中眼睛的平均色差的范数来计算模糊量,校正扭曲的第二张图像面部颜色以匹配第一张图片的面部颜色:
def correct_colours(im1, im2, landmarks1):mean_left = np.mean(landmarks1[left_eye_points], axis=0)mean_right = np.mean(landmarks1[right_eye_points], axis=0)blur_amount = color_correction_blur * np.linalg.norm(mean_left - mean_right)blur_amount = int(blur_amount)if blur_amount % 2 == 0: # make the blur kernel size oddblur_amount += 1im1_blur = cv2.GaussianBlur(im1, (blur_amount, blur_amount), 0)im2_blur = cv2.GaussianBlur(im2, (blur_amount, blur_amount), 0)# avoid division errorsim2_blur += (128 * (im2_blur <= 1.0)).astype(im2_blur.dtype)return (im2.astype(np.float64) * im1_blur.astype(np.float64) / im2_blur.astype(np.float64))
(7) 定义函数 plot_image_landmarks()
绘制输入图像上的关键点:
def plot_image_landmarks(img, img_landmarks, swap_img, swap_img_landmarks):img = img.copy()for mark in img_landmarks.tolist():cv2.circle(img, (mark[0], mark[1]), 1, (0,0,255), 2, cv2.LINE_AA)swap_img = swap_img.copy()for mark in swap_img_landmarks.tolist():cv2.circle(swap_img, (mark[0], mark[1]), 1, (0,0,255), 2, cv2.LINE_AA) plt.figure(figsize=(15,10))plt.subplots_adjust(0,0,1,0.95,0.01,0.01)plt.subplot(121), plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)), plt.axis('off')plt.subplot(122), plt.imshow(cv2.cvtColor(swap_img.astype(np.uint8), cv2.COLOR_BGR2RGB)), plt.axis('off')plt.suptitle('Facial landmarks computed for the faces to be swapped (with dlib shape-predictor)', size=10)plt.show()
(8) 最后,定义函数 face_swap_filter()
执行换脸操作,此函数接受两个输入面部图像文件,并使用第二个图像中的面部替换第一个图像的面部。首先,提取并绘制面部关键点;使用 cv2.findHomography()
函数,使用图像中的关键点计算投影变换的单应性矩阵(用于将第二张图像的面部投影到第一张图像的面部上);使用单应性矩阵计算并扭曲来自第二张图像的面部掩码;此外,通过使用单应性矩阵将第二张图像的面部扭曲映射到到第一张图像上;最后,使用掩码组合第一张图像和扭曲后的第二张图像,并进行颜色校正:
def face_swap_filter(img_file, swap_img_file):img = imutils.resize(cv2.imread(img_file), width=400)swap_img = imutils.resize(cv2.imread(swap_img_file), width=400)img_landmarks = get_landmarks(cv2.cvtColor(img, cv2.COLOR_BGR2GRAY))swap_img_landmarks = get_landmarks(cv2.cvtColor(swap_img, cv2.COLOR_BGR2GRAY))plot_image_landmarks(img, img_landmarks, swap_img, swap_img_landmarks)Hmat, status = cv2.findHomography(swap_img_landmarks[keypoints], img_landmarks[keypoints])mask = get_face_mask(swap_img, swap_img_landmarks)warped_mask = cv2.warpPerspective(mask, Hmat, (img.shape[1], img.shape[0]))combined_mask = np.max([get_face_mask(img, img_landmarks), warped_mask], axis=0) warped_swap = cv2.warpPerspective(swap_img, Hmat, (img.shape[1], img.shape[0]))output_img = np.clip(img * (1.0 - combined_mask) + warped_swap * combined_mask, 0, 255)warped_corrected_swap = correct_colours(img, warped_swap, img_landmarks)output_img_corrected = np.clip(img * (1.0 - combined_mask) + warped_corrected_swap * combined_mask, 0, 255)return (output_img, output_img_corrected)
(9) 绘制输出图像,即用第二张图像中的面部替换的第一张图像中的面部:
output_img, output_img_corrected = face_swap_filter('1.png', '2.png')plt.figure(figsize=(15,10))
plt.subplots_adjust(0,0,1,0.925,0.01,0.01)
plt.subplot(121), plt.imshow(cv2.cvtColor(output_img.astype(np.uint8), cv2.COLOR_BGR2RGB)), plt.axis('off')
plt.title('Before color correction', size=10)
plt.subplot(122), plt.imshow(cv2.cvtColor(output_img_corrected.astype(np.uint8), cv2.COLOR_BGR2RGB)), plt.axis('off')
plt.title('After color correction', size=10)
plt.suptitle('Face Swapping Output', size=12)
plt.show()
相关链接
Python图像处理【1】图像与视频处理基础
Python图像处理【2】探索Python图像处理库
Python图像处理【3】Python图像处理库应用
Python图像处理【4】图像线性变换
Python图像处理【5】图像扭曲/逆扭曲
Python图像处理【6】通过哈希查找重复和类似的图像
Python图像处理【7】采样、卷积与离散傅里叶变换
Python图像处理【8】使用低通滤波器模糊图像
Python图像处理【9】使用高通滤波器执行边缘检测
Python图像处理【10】基于离散余弦变换的图像压缩
Python图像处理【11】利用反卷积执行图像去模糊
Python图像处理【12】基于小波变换执行图像去噪
Python图像处理【13】使用PIL执行图像降噪
Python图像处理【14】基于非线性滤波器的图像去噪
Python图像处理【15】基于非锐化掩码锐化图像
Python图像处理【16】OpenCV直方图均衡化
Python图像处理【17】指纹增强和细节提取
Python图像处理【18】边缘检测详解
Python图像处理【19】基于霍夫变换的目标检测
Python图像处理【20】图像金字塔
Python图像处理【21】基于卷积神经网络增强微光图像
Python图像处理【22】基于卷积神经网络的图像去雾
Python图像处理【23】分布式图像处理
Python图像处理【24】面部变形(face morphing)