基于Mediapipe的换脸
前言
Mediapipe是一个开源跨平台框架,用于构建多媒体处理管道。支持各种任务,如面部识别、手势识别、姿态估计等。下面是基于Mediapipe的换脸方法与过程的叙述与总结。
一、方法
- 面部检测与跟踪:首先使用Mediapipe的人脸检测模型来检测图像或视频中的人脸,并跟踪面部关键点。
- 面部特征提取:基于检测到的面部关键点,提取面部的特征,如眼睛、鼻子、嘴巴等。
- 面部融合:将源人脸的面部特征与目标人脸的面部特征进行融合。这个过程通常涉及图像变形、颜色校正等。
- 渲染输出:将融合后的面部特征贴回目标人脸,生成最终的换脸图像或视频。
二、过程
- 环境搭建:
○ 准备输入数据:选择需要换脸的源图像和目标图像。 - 面部检测与跟踪:
○ 使用Mediapipe的FaceMesh或FaceDetection模型来检测和跟踪源图像和目标图像中的人脸。 - 面部特征提取:
○ 根据检测到的面部关键点,提取源人脸和目标人脸的面部特征。 - 面部融合:
○ 使用图像处理技术,如三角剖分和变形,将源人脸的面部特征映射到目标人脸的相应位置。
○ 对融合区域进行颜色校正,使源人脸和目标人脸的颜色更加一致。 - 渲染输出:
○ 将融合后的面部特征合并到目标人脸图像中,生成最终的换脸结果。
○ 如果是视频,需要对每一帧图像进行上述处理,并合成视频输出。
1.引入库
代码如下:
import mediapipe as mp
from mediapipe.tasks import python
from mediapipe.tasks.python import vision
import cv2
import numpy as np
2.算法流程
流程图:
流程图解释:
- 输入图像(Input Image)
用户提供源图像和目标图像。 - 面部检测与特征提取(Face Detection & Landmark Extraction)
使用 MediaPipe 的 Face Detection 模块检测图像中的面部。
通过 Face Mesh 模块,提取面部关键点(通常是 468 个点)。 - 关键点对齐与匹配(Landmark Matching & Alignment)
将源图像的面部关键点与目标图像的面部关键点进行匹配。
对关键点进行对齐,以确保两个面部的比例、方向等一致。 - 面部区域提取(Face Region Extraction)
根据检测到的关键点提取源图像中的面部区域。
采用 凸包(Convex Hull) 或 Delaunay 三角化(Delaunay Triangulation) 来描述面部轮廓,确保换脸时的过渡更加自然。 - 换脸(Face Swap)
将目标面部区域与源图像中的面部区域进行融合。
通过纹理映射(Texture Mapping)将源面部图像贴到目标面部区域。 - 颜色和光照调整(Color and Lighting Adjustment)
调整源面部图像的颜色、光照等参数,使其与目标面部环境更加一致。
可以使用图像处理方法,如直方图匹配(Histogram Matching)等。 - 图像融合(Image Blending)
使用 Poisson Image Editing 或其他融合技术平滑过渡,消除接缝,使得换脸效果更加自然。 - 输出结果(Output Image)
最终输出换脸后的图像。
3.检测人脸关键点
def ladmask(img):
"""
使用 MediaPipe 检测人脸关键点。
"""
# 初始化 MediaPipe 的人脸网格检测器,设置静态模式为 False,最多检测 5 张人脸
face_mesh = mp_face.FaceMesh(static_image_mode=False, max_num_faces=5)
# 将输入图像从 BGR 转为 RGB
rgb_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 处理图像以获取人脸关键点
results = face_mesh.process(rgb_img)
# 释放资源
face_mesh.close()
# 用于存储所有检测到的人脸关键点
faces_points = []
if results.multi_face_landmarks:
# 遍历每张检测到的人脸
for landmarks in results.multi_face_landmarks:
# 将每个关键点的坐标转换为图像上的像素位置
points = np.array([
(int(landmark.x * img.shape[1]), int(landmark.y * img.shape[0]))
for landmark in landmarks.landmark
])
faces_points.append(points) # 添加到结果列表
return faces_points # 返回所有检测到的关键点
#这段代码通过 MediaPipe 的 FaceLandmarker 模型检测人脸的关键点,获取其归一化坐标后将其转换为像素坐标。
#返回值是一个 NumPy 数组,包含图像中检测到的人脸关键点的位置,用于后续处理,如特征提取或姿态估计。
注意:以上代码用于检测单个采取到的人脸,如果想用于进行多人人脸换脸,则需改动代码,示例如下:
# 初始化 MediaPipe 的 Face Mesh 模块,用于多人脸关键点检测
face_mesh = mp_face_mesh.FaceMesh(static_image_mode=False, max_num_faces=5)
此处既是更改人脸检测数量的关键,max_num_faces=5,此处为5,意义着可以实现同时进行5个人的换脸
要完成这段代码实现的目标——同时更换多张人脸,需定义和实现以下关键功能模块:
1.掩模生成 (get_face_mask):
根据人脸关键点生成区域掩模。
2.仿射变换 (transformation_from_points):
计算从一个人脸关键点集到另一个人脸关键点集的变换矩阵。
3.仿射变换应用 (get_affine_image):
对图像和掩模执行仿射变换。
4.掩模合并 (get_mask_union):
合并两张掩模,用于泊松融合。
5.泊松融合中心点计算 (get_mask_center_point):
计算掩模的中心点,用于泊松融合
4.换脸仿射
def transformation_from_points(points1, points2):
# 根据两组关键点计算仿射变换矩阵
points1 = np.array(points1, dtype=np.float32) # 转为浮点型数组
points2 = np.array(points2, dtype=np.float32) # 转为浮点型数组
return cv2.estimateAffinePartial2D(points1, points2, method=cv2.LMEDS)[0] # 计算变换矩阵
def get_face_mask(img, landmarks):
# 创建一个与输入图像大小相同的空白蒙版
mask = np.zeros(img.shape[:2], dtype=np.uint8)
# 根据人脸关键点填充一个凸多边形区域(即人脸区域)
cv2.fillConvexPoly(mask, cv2.convexHull(landmarks), 255)
return mask # 返回人脸区域蒙版
def get_mask_center_point(mask):
# 获取蒙版中所有非零像素的坐标
y, x = np.nonzero(mask)
if len(x) == 0 or len(y) == 0:
return None # 如果没有找到非零像素,则返回 None
return (int(np.mean(x)), int(np.mean(y))) # 返回蒙版中像素的平均中心点
要进行换脸,对目标人脸进行识别是必要的
人脸仿射变换的原理:
仿射变换是一种线性变换,能够保持直线、比例和夹角特性。换脸仿射变换的作用是将一张脸的特征对齐到目标脸上。核心过程如下:
a. 确定对应点
根据 Mediapipe 提取的关键点,在源图像和目标图像中选取特定区域(通常是三角形网格)作为基础。
对应点可能包括眼睛角点、鼻尖、嘴角等,用于描述脸部特定区域。
b. 三角形网格的细化
为了实现更精细的换脸效果,可以将整个脸部区域划分成三角形网格。具体方法是:
1.使用 Delaunay 三角剖分将关键点划分为多个三角形。
2.对每个三角形计算仿射变换矩阵并对其进行变换。
5.输入源脸
对目标脸进行替换的源脸的载入
img1=cv2.imread("huge.jpg")
#原图像
points1 = ladmask(img1)
# 调用前面定义的 `ladmask` 函数,检测图片 `img1` 中的人脸关键点。
# 返回值 `points1` 是一个 NumPy 数组,包含人脸的关键点像素坐标。
img1_mask = get_face_mask(img1, points1)
# 调用 `get_face_mask` 函数,生成 `img1` 中人脸的掩模图像。
# 参数 `img1` 是原始图像,`points1` 是对应的人脸关键点坐标。
# 返回值 `img1_mask` 是一个灰度图像,其中人脸区域被填充为白色 (255),背景为黑色 (0)。
landmarks1 = ladmask(img1)
# 再次调用 `ladmask` 函数,重新检测图片 `img1` 中的人脸关键点。
# 返回值 `landmarks1` 是包含检测到的关键点坐标的 NumPy 数组。
# 这里重新调用 `ladmask` 是冗余的,因为 `points1` 已经包含这些信息。
6.图像融合并输出结果
"""
该代码实现从摄像头或视频源获取图像,并对每一帧执行以下任务:
1. 检测人脸关键点。
2. 对输入图像执行仿射变换,以对齐到目标帧的人脸。
3. 生成掩模并进行泊松融合,将仿射变换后的图像无缝融合到目标帧。
4. 实时显示融合结果,并支持按键退出。
"""
while True:
# 读取摄像头当前帧
ret, frame = capture.read()
if not ret:
break # 如果读取失败,退出循环
# 检测当前帧中的人脸关键点
frame_landmarks = ladmask(frame)
if not frame_landmarks:
# 如果没有检测到人脸,直接显示原始帧
cv2.imshow('Face Swap', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break # 按下 'q' 键退出循环
continue
# 遍历当前帧中检测到的每张人脸
for i, target_landmarks in enumerate(frame_landmarks):
# 如果源图像中人脸数少于当前帧,循环使用源图像的人脸
source_index = i % len(source_landmarks)
# 计算源人脸到目标人脸的仿射变换矩阵
M = transformation_from_points(source_landmarks[source_index], target_landmarks)
# 使用仿射变换将源图像人脸和蒙版变换到目标位置
transformed_face = cv2.warpAffine(source_img, M, (frame.shape[1], frame.shape[0]))
transformed_mask = cv2.warpAffine(source_masks[source_index], M, (frame.shape[1], frame.shape[0]))
# 创建当前帧目标人脸的蒙版并获取其中心点
union_mask = get_face_mask(frame, target_landmarks)
center = get_mask_center_point(union_mask)
if center is None or not (0 <= center[0] < frame.shape[1] and 0 <= center[1] < frame.shape[0]):
# 如果蒙版中心点无效,跳过此人脸
continue
# 将蒙版的像素值限制在有效范围内
union_mask = np.clip(union_mask, 0, 255)
# 使用 OpenCV 的无缝克隆将源人脸与当前帧融合
try:
frame = cv2.seamlessClone(
transformed_face, frame, union_mask, center, cv2.NORMAL_CLONE
)
except cv2.error as e:
# 如果融合出错,打印错误信息并跳过
print(f"Skipping blending due to error: {e}")
continue
# 显示处理后的视频帧
cv2.imshow('Face Swap', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break # 按下 'q' 键退出循环
# 释放摄像头资源并关闭所有窗口
capture.release()
cv2.destroyAllWindows()
#实时摄像头或视频文件输入:支持从摄像头捕获实时视频,或者通过修改 cv.VideoCapture 参数读取视频文件。
#人脸处理与仿射变换:检测人脸关键点,生成仿射变换矩阵,实现人脸对齐。
#泊松融合:使用联合掩模和融合点将图像自然地嵌入到目标帧中。
#实时显示:将处理结果通过 OpenCV 窗口实时显示。
三、总结
基于Mediapipe的换脸方法大致涉及以下步骤:
● 面部检测与跟踪:使用Mediapipe模型检测并跟踪人脸。
● 面部特征提取:提取源人脸和目标人脸的特征。
● 面部融合:将源人脸特征融合到目标人脸。
● 渲染输出:生成换脸后的图像或视频。