《OpenCV》—— dlib(换脸操作)

文章目录

dlib换脸介绍

  • dlib 换脸是基于 dlib 库实现的一种人脸替换技术,以下是关于它的详细介绍:
    • 原理
      • 人脸检测:dlib 库中包含先进的人脸检测器,如基于 HOG(方向梯度直方图)特征和线性分类器的方法,能够在图像或视频帧中快速定位人脸的位置,确定人脸的边界框。
      • 关键点检测:利用 dlib 预训练的形状预测模型,如shape_predictor_68_face_landmarks.dat,可以精准定位出人脸的 68 个关键点,这些点分布在眼睛、眉毛、鼻子、嘴巴、下巴等部位,精确描述了人脸的形状和表情。
      • 图像变换与融合:通过计算源人脸和目标人脸关键点之间的变换关系,如仿射变换、Delaunay 三角剖分等,将源人脸的形状和姿态调整到与目标人脸匹配。然后,采用图像融合技术,将调整后的源人脸与目标图像进行融合,使换脸效果更加自然。
    • 实现步骤
      • 安装相关库:主要安装 dlib 库,还可能需要 opencv、numpy 等库配合。在 Python 中可以使用pip install dlib等命令安装。
      • 人脸检测与关键点提取:使用 dlib 的人脸检测器和形状预测器,加载图像或视频帧,检测其中的人脸并提取关键点。
      • 计算变换关系:根据源人脸和目标人脸的关键点,计算出两者之间的变换矩阵,确定如何将源人脸变换到目标人脸的位置和姿态。
      • 人脸替换:将源人脸按照计算出的变换关系进行变形和调整,然后将其融合到目标图像的对应位置上。
      • 后处理:对换脸后的图像进行一些后处理操作,如调整颜色、亮度、对比度等,使换脸效果更加自然逼真。
    • 优缺点
      • 优点:具有较高的人脸检测和关键点定位精度,能够处理不同姿态、表情和光照条件下的人脸;提供了丰富的函数和工具,方便开发者进行二次开发和集成;在处理速度上相对较快,能够满足一些实时性要求不高的应用场景。
      • 缺点:对图像和视频的质量要求较高,如果输入的图像分辨率低、模糊或存在遮挡,可能会影响换脸效果;对于复杂的场景和特殊的光照条件,换脸效果可能不够自然;在实时性要求较高的场景中,如实时视频通话换脸,可能需要进一步优化才能满足性能要求。

仿射变换

仿射变换(Affine Transformation)是一种在二维或三维空间中广泛应用的线性变换,它能够保持图像的 "平直性" 和 "平行性",在计算机视觉、图形学等领域有诸多应用,比如在 dlib 换脸中就用于调整源人脸的形状和姿态以匹配目标人脸。

在 dlib 换脸中的应用

图片:

仿射变换代码:

python 复制代码
import cv2
import numpy as np

# 读取图像,这里读取的图像文件名为 'c_luo.png'
# cv2.imread 函数用于从指定路径读取图像,返回一个表示图像的 NumPy 数组
img = cv2.imread('c_luo.png')

# 获取图像的高度和宽度
# img.shape 返回一个包含图像维度信息的元组,元组的前两个元素分别是图像的高度和宽度
height, width = img.shape[:2]

# 定义源图像的三个关键点
# 这里使用 np.float32 创建一个 3x2 的浮点型 NumPy 数组
# 三个点分别是图像的左上角 (0, 0)、左下角 (0, height - 1) 和右上角 (width - 1, 0)
mat_src = np.float32([[0, 0], [0, height - 1], [width - 1, 0]])

# 定义目标图像对应的三个关键点
# 同样是一个 3x2 的浮点型 NumPy 数组
# 这里的三个点是经过变换后源图像三个关键点对应的目标位置
mat_dst = np.float32([[0, 0], [100, height - 100], [width - 100, 100]])

# 计算仿射变换矩阵
# cv2.getAffineTransform 函数根据源图像和目标图像的三个对应关键点,计算仿射变换矩阵
# 该矩阵用于描述从源图像到目标图像的仿射变换关系
M = cv2.getAffineTransform(mat_src, mat_dst)

# 应用仿射变换
# cv2.warpAffine 函数将仿射变换应用到输入图像 img 上
# M 是前面计算得到的仿射变换矩阵
# (width, height) 是输出图像的大小
dst = cv2.warpAffine(img, M, (width, height))

# 将原始图像和经过仿射变换后的图像水平拼接在一起
# np.hstack 函数用于将多个数组水平堆叠,方便同时显示原始图像和变换后的图像
imgs = np.hstack([img, dst])

# 创建一个可调整大小的窗口
# cv2.namedWindow 函数用于创建一个指定名称的窗口,cv2.WINDOW_NORMAL 表示窗口可以调整大小
cv2.namedWindow('imgs', cv2.WINDOW_NORMAL)

# 在创建的窗口中显示拼接后的图像
cv2.imshow('imgs', imgs)

# 等待用户按下任意按键
# cv2.waitKey(0) 表示无限期等待用户按键,按键后程序继续执行
cv2.waitKey(0)

这段代码的主要功能是对一张图像进行仿射变换,并将原始图像和变换后的图像拼接在一起显示。通过指定源图像和目标图像的三个对应关键点,计算出仿射变换矩阵,然后将该变换应用到图像上。

结果:

换脸操作

对下方图片进行换脸操作:


实现代码:

python 复制代码
import cv2
import dlib
import numpy as np

# 定义人脸关键点的索引范围
# 下巴关键点索引
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, 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_POINTS + MOUTH_POINTS]
# 将关键点集合转换为元组
POINTStuple = tuple(POINTS)

def getFaceMask(im, keyPoints):
    """
    生成人脸掩码
    :param im: 输入图像
    :param keyPoints: 人脸关键点
    :return: 人脸掩码图像
    """
    # 创建一个与输入图像高度和宽度相同的零矩阵
    im = np.zeros(im.shape[:2], dtype=np.float64)
    for p in POINTS:
        # 计算关键点的凸包
        points = cv2.convexHull(keyPoints[p])
        # 填充凸包区域为 1
        cv2.fillConvexPoly(im, points, color=1)
    # 将单通道掩码扩展为三通道
    im = np.array([im, im, im]).transpose((1, 2, 0))
    # 对掩码进行高斯模糊处理
    im = cv2.GaussianBlur(im, (25, 25), 0)
    return im

def getM(points1, points2):
    """
    计算仿射变换矩阵
    :param points1: 源图像的关键点
    :param points2: 目标图像的关键点
    :return: 仿射变换矩阵
    """
    points1 = points1.astype(np.float64)
    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
    return np.hstack(((s2 / s1) * R, c2.T - (s2 / s1) * R * c1.T))

def getKeyPoints(im):
    """
    检测图像中的人脸关键点
    :param im: 输入图像
    :return: 人脸关键点矩阵
    """
    # 使用 dlib 的人脸检测器检测人脸
    rects = detector(im, 1)
    # 使用 dlib 的形状预测器预测关键点
    shape = predictor(im, rects[0])
    # 将关键点转换为矩阵形式
    s = np.matrix([[p.x, p.y] for p in shape.parts()])
    return s

def normalColor(a, b):
    """
    颜色校正
    :param a: 源图像
    :param b: 目标图像
    :return: 颜色校正后的目标图像
    """
    # 定义高斯核大小
    ksize = (71, 71)
    # 对源图像和目标图像进行高斯模糊
    aGauss = cv2.GaussianBlur(a, ksize, 0)
    bGauss = cv2.GaussianBlur(b, ksize, 0)
    # 避免除以零
    bGauss = np.where(bGauss == 0, 1e-8, bGauss)
    # 计算权重
    weight = aGauss / bGauss
    return b * weight

# 读取源图像和目标图像
a = cv2.imread('c_luo.png')
b = cv2.imread('mu_ba_pei.png')

# 初始化 dlib 的人脸检测器
detector = dlib.get_frontal_face_detector()
# 初始化 dlib 的形状预测器,需要提前下载 shape_predictor_68_face_landmarks.dat 文件
predictor = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat')

# 检测源图像和目标图像的人脸关键点
aKeyPoints = getKeyPoints(a)
bKeyPoints = getKeyPoints(b)

# 保存目标图像的原始副本
bOriginal = b.copy()

# 生成源图像的人脸掩码
aMask = getFaceMask(a, aKeyPoints)
# 显示源图像的人脸掩码
cv2.imshow('aMask', aMask)
cv2.waitKey()

# 生成目标图像的人脸掩码(使用源图像的关键点)
bMask = getFaceMask(b, aKeyPoints)
# 显示目标图像的人脸掩码
cv2.imshow('bMask', bMask)
cv2.waitKey()

# 计算仿射变换矩阵
M = getM(aKeyPoints[POINTStuple], bKeyPoints[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()

# 对目标图像进行仿射变换
bWrap = cv2.warpAffine(b, M, dsize,
                       borderMode=cv2.BORDER_TRANSPARENT,
                       flags=cv2.WARP_INVERSE_MAP)
# 显示变换后的目标图像
cv2.imshow('bWrap', bWrap)
cv2.waitKey()

# 对变换后的目标图像进行颜色校正
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()
# 关闭所有 OpenCV 窗口
cv2.destroyAllWindows()

结果:

可通过结果看出换脸成功。

相关推荐
小椿_1 小时前
探索AIGC未来:通义万相2.1与蓝耘智算平台的完美结合释放AI生产力
人工智能·aigc
小赖同学啊1 小时前
PyTorch 中实现模型训练看板实时监控训练过程中的关键指标
人工智能·pytorch·python
CASAIM1 小时前
CASAIM与承光电子达成深度合作,三维扫描逆向建模技术助力车灯设计与制造向数字化与智能化转型
大数据·人工智能·制造
CodeJourney.1 小时前
DeepSeek赋能Power BI:开启智能化数据分析新时代
数据库·人工智能·算法
Liudef061 小时前
Stable Diffusion模型Pony系列模型深度解析
人工智能·ai作画·stable diffusion·人工智能作画
weixin_468466851 小时前
C++、C#、python调用OpenCV进行图像处理耗时对比
c++·图像处理·python·opencv·c#·机器视觉·opencvsharp
好多渔鱼好多2 小时前
【大模型学习】第八章 深入理解机器学习技术细节
人工智能·机器学习·ai·大模型
Y1nhl2 小时前
数据挖掘校招面经二
人工智能·python·深度学习·算法·机器学习·数据挖掘
BRUCE_WUANG2 小时前
【不是广告】华为昇腾的一小步,Pytorch的一大步
人工智能·pytorch·华为
幸福回头2 小时前
vLLM代码推理Qwen2-VL多模态
人工智能·llm·qwen