引言
在当今数字化时代,图像处理技术的发展日新月异。换脸技术作为其中一项极具趣味性和挑战性的应用,吸引了众多开发者和爱好者的目光。OpenCV 作为一款强大的开源计算机视觉库,为我们实现换脸提供了丰富的工具和方法。本文将深入探讨如何使用 OpenCV 进行换脸操作,从原理到实践,一步步带领大家揭开换脸技术的神秘面纱。
一、换脸技术原理
OpenCV换脸的基本原理是通过人脸检测定位源人脸和目标人脸的位置,利用关键点检测(如Dlib的68点模型)标记五官轮廓,然后通过仿射变换将源人脸对齐到目标人脸的几何结构上,最后将换脸区域与目标图像进行颜色和边缘的无缝混合。整个过程结合了几何变形、颜色校正和智能融合技术,实现逼真的换脸效果。换脸技术的核心在于找到两张人脸之间的对应关系,并将一张脸的特征映射到另一张脸上。
二、关键点检测
人脸关键点检测是计算机视觉中的一项重要技术,用于精确定位人脸的五官轮廓和关键特征点(如眼睛、眉毛、鼻子、嘴巴、下巴等)。它在人脸识别、表情分析、换脸(Face Swap)、AR滤镜等应用中发挥核心作用。
本文我们通过Dlib库来实现对人脸的68点检测
import dlib
#构建人脸检测器
detector = dlib.get_frontal_face_detector()
#读取人脸关键点定位模型
predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")
#检测人脸
face = detector(img,0)
#返回68个关键点
landmarks = predictor(img, face)
三、仿射变换
图像的几何变换主要包括:平移,旋转,剪切,仿射,透视等
仿射变换 是一种在几何中广泛使用的线性变换方法,它能够保持图像的 平行性 和 直线性,但可能改变距离和角度。在计算机视觉和图像处理中,仿射变换常用于 图像校正、旋转、缩放、平移、倾斜(剪切) 等操作。
仿射变换简单的来说就是通过3对坐标点从一个二维坐标系变换到另一个二维坐标系
# 计算 3 组对应点(源点 → 目标点)
mat_src = np.float32([[0, 0], [0, h-1], [w-1, 0]])
mat_dst = np.float32([[0, 0], [100, h-100], [w-100, 100]]) # 计算仿射变换矩阵
M = cv2.getAffineTransform(mat_src, mat_dst)
# 仿射变换
dst = cv2.warpAffine(img, M, (w, h))
四、换脸
下面我们通过代码来实现对以下两张图片的换脸处理
首先导入相应的库
import cv2
import dlib
import numpy as np
创建一个列表,保存脸部不同位置68个关键点模型
JAW_POINTS=list(range(0,17))
RIGHT_BROW_POINTS=list(range(17,22))
LEFT_BROW_POINTS=list(range(22,27))
NOSE_POINTS=list(range(27,36))
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_BROW_POINTS+LEFT_EYE_POINTS+RIGHT_EYE_POINTS+NOSE_POINTS+MOUTH_POINTS]POINTStuple=tuple(POINTS)
掩膜函数,生成人脸区域的掩膜
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) #填充凸包,数字在0~1之间 0.# 单通道im构成3通道im(3,行,列),改变形状(行、列、3)适应OpenCVim = np.array([im, im, im]).transpose((1, 2, 0))im = cv2.GaussianBlur(im,(25,25), 0)#这个参数比较重要,需要调整return im
使用dlib检测人脸并提取68个关键点。
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
求出b脸仿射变换到a脸的变换矩阵M
def getM(points1, points2):points1 = points1.astype(np.float64) #int8转换为浮点数类型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 # 除标准差,计算出归一化的结果# 奇异值分解,Singular Value DecompositionU, S, Vt = np.linalg.svd(points1.T * points2)R = (U * Vt).T# 通过U和Vt找到Rreturn np.hstack(((s2/s1)*R, c2.T-(s2/s1)*R*c1.T))
通过高斯模糊,将图像 b
的颜色风格匹配到图像 a
def normalColor(a,b):ksize=(111,111)aGauss=cv2.GaussianBlur(a,ksize,0)bGauss=cv2.GaussianBlur(b,ksize,0)weight=aGauss/bGausswhere_are_inf=np.isinf(weight)weight[where_are_inf]=0return b*weight
主程序
a=cv2.imread('hl2.jpg')
b=cv2.imread('hl1.jpg')#构造脸部位置检测器
detector=dlib.get_frontal_face_detector()
#读取人脸关键点定位模型
predictor=dlib.shape_predictor('shape_predictor_68_face_landmarks.dat')#获取关键点
aKeyPoints=getKeyPoints(a)
bKeyPoints=getKeyPoints(b)#获取人脸掩膜
aMask=getFaceMask(a,aKeyPoints)
cv2.imshow('aMask',aMask)
cv2.waitKey(0)bMask=getFaceMask(b,bKeyPoints)
cv2.imshow('bMask',bMask)
cv2.waitKey(0)#求出b脸仿射变换到a脸的变换矩阵M
M=getM(aKeyPoints[POINTStuple],bKeyPoints[POINTStuple])#将b的脸部(bmask)根据M仿射变换到a上
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(0)#获取脸部掩膜最大值(两个脸掩膜叠加)
mask=np.max([aMask,bMaskWarp],axis=0)
cv2.imshow('mask',mask)
cv2.waitKey(0)#使用仿射矩阵M,将b映射到a
bWarp=cv2.warpAffine(b,M,dsize,borderMode=cv2.BORDER_TRANSPARENT,flags=cv2.WARP_INVERSE_MAP)
cv2.imshow('bWrap',bWarp)
cv2.waitKey(0)
得到换脸结果
#求b图片的仿射到图片a的颜色值,b的颜色值改为a的颜色
bcolor = normalColor(a, bWrap)
cv2.imshow("bcolor",bcolor)
cv2.waitKey()#图片混合
out = a * (1.0 - mask) + bcolor * mask
# 输出换脸结果
cv2.imshow("a",a)
cv2.imshow("b",bOriginal)
cv2.imshow("out",out/255)
cv2.waitKey()
cv2.destroyAllWindows()
最终得到结果如图所示
五、总结
通过以上步骤,我们利用 OpenCV 成功实现了换脸操作。从人脸检测、特征点提取到人脸对齐和图像融合,每一步都蕴含着计算机视觉技术的精妙之处。然而,目前的换脸技术仍存在一些局限性,如在复杂光照条件下、人脸姿态变化较大时可能效果不佳,以及换脸后的图像可能存在边缘不自然等问题。未来,随着深度学习等技术的不断发展,换脸技术有望在准确性和自然度上取得更大的突破,应用领域也将更加广泛,如影视制作、虚拟现实、娱乐等。