- 人脸检测:使用人脸检测算法,如Haar级联分类器或基于深度学习的人脸检测器,从源图像和测出人脸区域。这是实现AI换脸的第一步,也是后续步骤的基础。
- 特征点检测:通过关键点检测算法,如基于深度学习的面部特征点检测器,从源图像和目标图脸的特征点。这些特征点通常包括眼睛、鼻子、嘴巴等关键部位的位置信息。
- 特征点匹配:对源图像和目标图像中提取的特征点进行匹配,找出相应的对应点对。这一步是》像中的脸部特征能够准确地映射到目标图像上。
- 人脸对齐:通过仿射变换或透视变换,将源图像中的人脸区域对齐到目标图像中的人脸区域。这使源图像中的脸部特征与目标图像中的脸部形状保持致。
- 颜色校正:根据目标图像中的颜色信息,对合成图像中的颜色进行校正,使合成图像更加逼真自是为了确保合成图像在颜色上与目标图像保持一致,避免出现明显的色差。
- 合成图像生成:通过图像融合算法,将源图像中的人脸特征融合到目标图像中,生成最终的合成步是A换脸技术的核心,它决定了合成图像的质量和逼真程度。
数据预处理 特征点检测精度
最后进行图像融合算法
import mediapipe as mp导入MediaPipe库
from mediapipe.tasks从MediaPipe的任务模块中导入Python接口
mediapi import python
from进一步从MediaPipe的任务模块中的Python接口中导入视觉子模块 mediapipe.tasks.python import vision
import cv2导入OpenCV库
import numpy as np导入NumPy库13:01def ladmask(img):
# 将输入图像转换为MediaPipe的Image对象,指定图像格式为SRGB
mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=img)
# 设置基础选项,加载面部标志模型
base_options = python.BaseOptions(model_asset_path='face_landmarker.task')
# 配置面部标志检测器选项,包括输出面部混合形状和面部变换矩阵,并限制检测到的面部数量为1
options = vision.FaceLandmarkerOptions(base_options=base_options,
output_face_blendshapes=True,
output_facial_transformation_matrixes=True,
num_faces=1)
# 创建面部标志检测器
detector = vision.FaceLandmarker.create_from_options(options)
# 使用检测器对图像进行面部标志检测
detection_result = detector.detect(mp_image)
# 初始化空列表用于存储x坐标、y坐标和所有点
x = []
y = []
points = []
# 遍历检测到的面部标志点,将其归一化坐标转换为实际像素坐标,并添加到points列表中
for i in range(0, len(detection_result.face_landmarks[0])):
points.append([int(detection_result.face_landmarks[0][i].x * img.shape[1]), int(detection_result.face_landmarks[0][i].y * img.shape[0])])
# 将points列表转换为NumPy数组
points = np.array(points)
# 返回包含所有面部标志点的NumPy数组
return points13:01def transformation_from_points(points1, points2):
'''0 - 先确定是float数据类型 '''
points1 = points1.astype(numpy.float64) # 确保输入的点集为浮点数类型,以便进行精确计算
points2 = points2.astype(numpy.float64) # 同上
'''1 - 消除平移的影响 '''
c1 = numpy.mean(points1, axis=0) # 计算第一个点集的质心(均值)
c2 = numpy.mean(points2, axis=0) # 计算第二个点集的质心(均值)
points1 -= c1 # 将第一个点集平移到原点
points2 -= c2 # 将第二个点集平移到原点
'''2 - 消除缩放的影响 '''
s1 = numpy.std(points1) # 计算第一个点集的标准差(尺度)
s2 = numpy.std(points2) # 计算第二个点集的标准差(尺度)
points1 /= s1 # 将第一个点集标准化到单位尺度
points2 /= s2 # 将第二个点集标准化到单位尺度
'''3 - 计算矩阵M=BA^T;对矩阵M进行SVD分解;计算得到R '''
# ||RA-B||; M=BA^T
A = points1.T # 转置第一个点集,使其成为2xN矩阵
B = points2.T # 转置第二个点集,使其成为2xN矩阵
M = np.dot(B, A.T) # 计算矩阵M=BA^T
U, S, Vt = numpy.linalg.svd(M) # 对矩阵M进行奇异值分解(SVD)
R = np.dot(U, Vt) # 计算旋转矩阵R
'''4 - 构建仿射变换矩阵 '''
s = s2/s1 # 计算尺度因子
sR = s*R # 应用尺度因子到旋转矩阵R
c1 = c1.reshape(2,1) # 将质心c1重塑为列向量
c2 = c2.reshape(2,1) # 将质心c2重塑为列向量
T = c2 - np.dot(sR,c1) # 计算平移向量T,使得模板人脸的中心位置减去需要对齐的中心位置(经过旋转和缩放之后)
trans_mat = numpy.hstack([sR,T]) # 构建最终的仿射变换矩阵,由旋转和平移部分组成
return trans_mat # 返回仿射变换矩阵13:02def get_affine_image(image1, image2, face_landmarks1, face_landmarks2, M):
"""
获取图片1仿射变换后的图片
:param image1: 图片1, 要进行仿射变换的图片
:param image2: 图片2, 只要用来获取图片大小,生成与之大小相同的仿射变换图片
:param face_landmarks1: 图片1的人脸特征点
:param face_landmarks2: 图片2的人脸特征点
:return: 仿射变换后的图片
"""
three_p oints_index = [18, 8, 25] # 定义用于计算仿射变换的三个点的索引
M = M # 使用传入的变换矩阵M
#M=cv2.getAffineTransform(face_landmarks1[three_points_index].astype(np.float32),face_landmarks2[three_points_index].astype(np.float32))
dsize = (image2.shape[1], image2.shape[0]) # 获取image2的尺寸,作为仿射变换后的图像尺寸
affine_image = cv2.warpAffine(image1, M, dsize) # 对image1应用仿射变换,得到仿射变换后的图像
return affine_image.astype(np.uint8) # 将结果转换为无符号8位整数类型并返回13:03def get_face_mask(img, face_landmarks):
"""
获取人脸掩模
:param img: 输入图像
:param face_landmarks: 68个特征点
:return: mask, 掩模图片
"""
# 将输入图像转换为灰度图
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 获取图像的尺寸(高度和宽度)
image_size = (img.shape[0], img.shape[1])
# 创建一个与灰度图像大小相同的全零矩阵,用于存储掩模
mask = np.zeros_like(img_gray)
# 使用凸包算法找到特征点的凸包
points = cv2.convexHull(face_landmarks)
# 在掩模上填充凸包区域,颜色为白色(255)
cv2.fillConvexPoly(mask, points, color=255)
# 返回生成的掩模
return mask13:03def get_mask_union(mask1, mask2):
"""
获取两个掩模掩盖部分的并集
:param mask1: mask_image, 掩模1
:param mask2: mask_image, 掩模2
:return: 两个掩模掩盖部分的并集
"""
mask = np.min([mask1, mask2], axis=0) # 计算两个掩模的逐元素最小值,得到掩盖部分的并集
mask = ((cv2.blur(mask, (3, 3)) == 255) * 255).astype(np.uint8) # 对掩盖部分进行模糊处理,缩小掩模大小,并将结果转换为无符号8位整数类型
mask = cv2.blur(mask, (5, 5))#.astype(np.uint8) # 再次对掩模进行模糊处理,进一步平滑边缘
return mask # 返回最终生成的掩模13:03def get_mask_center_point(image_mask):
"""
获取掩模的中心点坐标
:param image_mask: 掩模图片
:return: 掩模中心
"""
# 使用np.argwhere找到所有非零像素的索引,这些索引表示掩模中被掩盖的部分
image_mask_index = np.argwhere(image_mask > 0)
# 计算掩模边界的最小和最大x, y坐标
miny, minx = np.min(image_mask_index, axis=0)
maxy, maxx = np.max(image_mask_index, axis=0)
# 计算中心点的坐标,通过取最小和最大坐标的平均值
center_point = ((maxx + minx) // 2, (maxy + miny) // 2)
# 返回计算出的中心点坐标
return center_point13:04import cv2 as cv
3487






