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

embedded/2025/3/6 4:38:20/

文章目录

    • dlib换脸介绍
    • 仿射变换
      • 在 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 换脸中的应用

在这里插入图片描述

图片:在这里插入图片描述

仿射变换代码:

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)

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

结果:
在这里插入图片描述

换脸操作

对下方图片进行换脸操作:
在这里插入图片描述
在这里插入图片描述
实现代码:

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])# 填充凸包区域为 1cv2.fillConvexPoly(im, points, color=1)# 将单通道掩码扩展为三通道im = np.array([im, im, im]).transpose((1, 2, 0))# 对掩码进行高斯模糊处理im = cv2.GaussianBlur(im, (25, 25), 0)return imdef 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 -= c1points2 -= c2# 计算关键点的标准差s1 = np.std(points1)s2 = np.std(points2)# 归一化关键点points1 /= s1points2 /= s2# 进行奇异值分解U, S, Vt = np.linalg.svd(points1.T * points2)# 计算旋转矩阵R = (U * Vt).Treturn 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 sdef 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 / bGaussreturn 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()

结果:

在这里插入图片描述
可通过结果看出换脸成功。


http://www.ppmy.cn/embedded/170392.html

相关文章

蓝陵科技:以“数字底座”之力,全面布局影视行业工业化

“在数字技术与文化产业深度融合的今天,海南蓝陵数字科技有限公司(以下简称蓝陵科技)凭借其卓越的渲染实力和前瞻性的战略布局,正逐步成为推动中国影视行业工业化进程的重要力量。2025年2月,蓝陵科技不仅获得了陵水融媒…

几道考研数学题求解

函数性质问题 【题目】 已知函数 f ( x , y ) x 3 y 3 − ( x y ) 2 3 f(x, y) x^3 y^3 - (xy)^2 3 f(x,y)x3y3−(xy)23。设 T T T 为曲面 z f ( x , y ) z f(x, y) zf(x,y) 在点 ( 1 , 1 , 1 ) (1,1,1) (1,1,1) 处的切平面, D D D 为 T T T 与坐标…

如何使用Python和SQLAlchemy结合外键映射来获取其他表中的数据

当我们已经了解SQLAlchemy的基础,比如定义模型和基本查询,但对外键和关联查询不太熟悉。他们的实际需求可能是想通过外键关联两个表,并且在查询一个表时能够方便地获取关联表的数据,比如查询用户时同时获取他们的订单信息。 接下…

磐石云AXB小号平台——安全与隐私的守护者

在当今信息化时代,隐私和安全问题日益受到关注。无论是个人用户还是企业用户,都希望在通信过程中能够确保数据的安全性和隐私性。磐石云AXB小号平台正是为此而生,凭借其先进的加密技术,为用户提供了坚不可摧的安全保障。 安全可靠…

橙心同步助手更新,,支持博客园、头条和语雀

文章同步助手v1.5版本开发完成了,新增了博客园、头条和语雀同步支持。 使用方式和前几个版本一致,都需要先登录上对应平台的账号,再进行同步。 语雀因为可以有不同的知识库,所以同步的时候需要指定对应的知识库,才能…

SslConnection::SslConnection()详解

一、🔍 SslConnection::SslConnection() 详解 这个构造函数的主要作用是: 创建 SSL 对象创建 BIO(I/O 缓冲区)初始化 SSL 服务器模式绑定回调函数(onRead() 处理接收数据) 📌 1. 初始化 SSL 相…

Easy Trans Spring Boot Starter ---Spring系列的字段翻译库

Easy Trans Spring Boot Starter 使用文档 1. 简介 easy-trans-spring-boot-starter 是一个基于 Spring Boot 的库,用于简化数据翻译和转换操作。它可以帮助你将数据库中的枚举值、状态码等转换为用户友好的文本,或者将一种数据格式转换为另一种格式。…

react 19版中路由react-router-dom v7版的使用

路由的安装: npm install react-router-dom在src目录下建一个router文件夹 在router文件夹里面建一个index.tsx index.tsx内容: import React from react; import {BrowserRouter as Router,Routes,Route,Link } from react-router-dom; import ManuLi…