文章目录
使用OpenCV与dlib进行换脸是一个复杂但有趣的过程,它涉及人脸检测、特征点提取、图像变换和融合等多个步骤。以下是一个基于OpenCV和dlib实现换脸技术的概述:
一、环境准备
首先,需要安装OpenCV和dlib库。这两个库都是Python中常用的图像处理库,其中OpenCV提供了丰富的图像处理函数,而dlib则专注于机器学习和图像处理中的高级算法。
二、基本原理
OpenCV与dlib在换脸技术中的应用,主要基于计算机视觉和图像处理的相关原理。以下是对这一过程中涉及的关键知识与原理的详细解释:
1.人脸检测
- 原理:使用预训练的模型(如dlib的正面人脸检测器)在图像中检测人脸区域。
OpenCV与dlib的作用:
- OpenCV提供图像读取、预处理和显示等功能。
- dlib则专注于人脸检测,提供高效且准确的人脸检测器。
2.特征点提取
- 原理:在检测到的人脸区域内,使用特征点提取算法(如dlib的68点特征提取模型)提取关键特征点。
这些特征点通常位于人脸的眼角、嘴角、鼻尖等显著位置,用于后续的人脸对齐和图像变换。
OpenCV与dlib的作用:
- OpenCV提供图像处理函数,如灰度转换、边缘检测等,为特征点提取做准备。
- dlib的68点特征提取模型能够准确地提取人脸特征点,为后续的图像变换提供基础。
3.图像变换与对齐
- 原理:使用仿射变换或透视变换等图像变换算法,将源人脸的特征点区域变换到目标人脸的相应位置。
仿射变换可以保持图像的直线性和平行性,适用于人脸的初步对齐。
透视变换则能够处理更复杂的图像变换,如人脸的旋转和缩放。
OpenCV与dlib的作用:
- OpenCV提供仿射变换和透视变换等图像变换函数。
- dlib的特征点提取为图像变换提供了必要的输入信息。
三、基本步骤
1.加载人脸检测器和特征点预测模型
- 使用dlib加载正面人脸检测器和68个特征点预测模型。这些模型用于检测图像中的人脸并提取关键特征点。
2.读取人脸图片
- 使用OpenCV的imread函数读取两张要进行换脸的图片。
3.获取人脸的特征点
- 将图像转换为灰度图,并使用dlib的人脸检测器检测人脸区域。然后,使用特征点预测模型提取人脸的68个特征点。这些特征点对应面部的特定位置,如眼睛、鼻子、嘴巴等。
仿射变换三角形 - 对每个三角形进行仿射变换,将其从源图像(即要移植脸部特征的图片)变换到目标图像(即脸部特征的目标图片)中的相应位置。这是实现换脸的关键步骤之一。
4.脸部轮廓掩模
- 在仿射变换之后,使用凸包算法构建目标人脸区域的凸包,并生成一个掩模(mask)。这个掩模用于后续的无缝克隆步骤中,以确保换脸后的图像看起来更加自然。
四、代码实现
1.导入库
import cv2
import dlib
import numpy as np
cv2:OpenCV库,用于图像处理和计算机视觉任务。
dlib:一个包含机器学习算法和工具的库,这里用于面部特征点检测。
numpy:一个用于科学计算的库,提供了大量的数学函数和操作数组的工具。
2.定义面部特征点
JAM_POINTS = list(range(0, 17))
RIGHT_BROW_POINTS = list(range(17, 22))
LEFT_BROW_POINTS = list(range(22, 27))
NOSE_POINT = list(range(27, 35))
RIGHT_EYE_POINTS = list(range(36, 42))
LEFT_EYE_POINTS = list(range(42, 48))
MOUTH_POINTS = list(range(48, 61))
FACE_POINTS = list(range(17, 68))
# 关键点集
POINTS = [LEFT_BROW_POINTS + RIGHT_EYE_POINTS + LEFT_EYE_POINTS + RIGHT_BROW_POINTS + NOSE_POINT + MOUTH_POINTS]
# 处理元组,后续使用方便
POINTStuple = tuple(POINTS)
定义多个面部特征点的集合,如眉毛、眼睛、鼻子、嘴巴等。
POINTS变量包含了用于生成面部掩膜的特征点集合。
3.定义特征函数
(1)getFaceMask函数
def getFaceMask(im, keyPoints): # 根据关键点获取掩膜
im = np.zeros(im.shape[:2], dtype=np.float64)
for p in POINTS:
points = cv2.convexHull(keyPoints[p]) # 获取凸包
cv2.fillConvexPoly(im, points, color=1) # 填充凸包,数字在1~2之间
# 单通道im构成3通道im(3,行,列),改变形状(行、列、3)适应0penCV
im = np.array([im, im, im]).transpose((1, 2, 0))
im = cv2.GaussianBlur(im, (33, 33), 0)
return im
- 根据给定的面部特征点生成一个面部掩膜。
- 使用cv2.convexHull获取特征点的凸包,然后用cv2.fillConvexPoly填充凸包,生成掩膜。
- 对掩膜进行高斯模糊处理,以减少边缘的锯齿效应。
(2)getM函数
def getM(points1, points2):
points1 = points1.astype(np.float64) # int转换为浮点数
points2 = points2.astype(np.float64)
c1 = np.mean(points1, axis=0) #归一化:(数值-均值)/标准差
c2 = np.mean(points2, axis=0)
points1 -= c1 # 减去均值
points2 -= c2
s1 = np.std(points1) # 方差计算标准差
s2 = np.std(points2)
points1 /= s1 # 除标准差,计算归一化结果
points2 /= s2
# 奇异值分解
U, S, Vt = np.linalg.svd(points1.T * points2)
R = (U * Vt).T # 通过U和Vt找到R
return np.hstack(((s2 / s1) * R, c2.T - (s2 / s1) * R * c1.T))
- 计算两个面部特征点集合之间的仿射变换矩阵。
- 首先对特征点进行归一化处理,然后使用奇异值分解(SVD)找到最佳仿射变换矩阵。
(3)getKeyPoints函数
def getKeyPoints(im): # 获取关键点
rects = detector(im, 1) # 获取人脸方框的位置
shape = predictor(im, rects[0])
s = np.matrix([[p.x, p.y] for p in shape.parts()])
return s
- 使用dlib的面部检测器和特征点预测器获取图像的面部特征点。
(4)normalColor函数
def normalColor(a, b):
ksize = (155, 155)
aGauss = cv2.GaussianBlur(a, ksize, 0) # 对a进行高斯滤波
bGauss = cv2.GaussianBlur(b, ksize, 0)
weight = aGauss / bGauss # 计算目标图像调整颜色的权重值,存在0除警告,可忽略。
where_are_inf = np.isinf(weight)
weight[where_are_inf] = 0
return b * weight
- 调整图像的颜色,使其与目标图像的颜色相匹配。
- 通过计算两个图像的高斯模糊版本之间的比例来实现。
4.数据预处理
a = cv2.imread('xz.png')
b = cv2.imread('yy.png')
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat')
aKeyPoints = getKeyPoints(a) # 获取关键点
bKetPoints = getKeyPoints(b)
bOriginal = b.copy()
aMask = getFaceMask(a, aKeyPoints) # 获取掩膜
cv2.imshow('aMask', aMask)
cv2.waitKey()
bMask = getFaceMask(b, bKetPoints)
cv2.imshow('bMask', bMask)
- 使用cv2.imread读取两张图像。
- 初始化dlib的面部检测器和68点特征点预测器。
- 对两张图像分别获取面部特征点,并生成对应的面部掩膜。
5.仿射变换
M = getM(aKeyPoints[POINTStuple], bKetPoints[POINTStuple])
dsize = a.shape[:2][::-1]
bMaskWarp = cv2.warpAffine(bMask, M, dsize, borderMode=cv2.BORDER_TRANSPARENT, flags=cv2.WARP_INVERSE_MAP)
cv2.imshow('bMaskWarp', bMaskWarp)
cv2.waitKey()
mask = np.max([aMask, bMaskWarp], axis=0)
cv2.imshow('mask', mask)
cv2.waitKey()
- 使用getM函数计算从图像b到图像a的仿射变换矩阵。
- 使用cv2.warpAffine函数将图像b的面部(包括掩膜)根据仿射变换矩阵变换到图像a的坐标系中。
6.生成并显示图像
bWarp = cv2.warpAffine(b, M, dsize, borderMode=cv2.BORDER_TRANSPARENT, flags=cv2.WARP_INVERSE_MAP)
cv2.imshow('bWarp', bWarp)
cv2.waitKey()
# 求b图片的仿射到图片a的颜色值,b的颜色值改为a的颜色
bbolor = normalColor(a, bWarp)
cv2.imshow('bcolor', bbolor)
cv2.waitKey()
out = a * (1.0 - mask) + bbolor * mask
cv2.imshow('a', a)
cv2.imshow('b', b)
cv2.imshow('out', out/255)
cv2.waitKey()
cv2.destroyAllWindows()
- 使用掩膜将变换后的图像b的颜色与图像a进行融合。
- 在掩膜区域使用图像b的颜色,在非掩膜区域使用图像a的颜色。
- 显示原始图像、变换后的图像和最终的换脸结果。
五、总结
使用OpenCV和dlib进行换脸是一个具有挑战性的任务,但它也是一个非常有趣和实用的应用。这里我们通过定义一系列特征函数,来对两张人图片进行操作,并将最后获得的结果进行融合展示,为大家展示了OpenCV与dlib库的联合使用。
我们可以通过修改高斯核函数的核大小来实现高质量的换脸效果。
注意:
核的宽度和高度都必须大于0。
核的宽度和高度都必须是奇数。