《OpenCV》—— dlib(换脸操作)
文章目录
- 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()
结果:

可通过结果看出换脸成功。
相关文章:
《OpenCV》—— dlib(换脸操作)
文章目录 dlib换脸介绍仿射变换在 dlib 换脸中的应用 换脸操作 dlib换脸介绍 dlib 换脸是基于 dlib 库实现的一种人脸替换技术,以下是关于它的详细介绍: 原理 人脸检测:dlib 库中包含先进的人脸检测器,如基于 HOG(方向…...
修改Flutter项目使用的JAVA版本
使用Android studio开发Flutter过程中,会默认使用Android studio自带的JDK。因为新版Android studio中的JDK版本过高,导致项目编译时总是无法完成,报【 unsupported class file major version 65】错误,如下: 解决这个…...
虚拟dom的diff中的双端比较算法
双端比较算法是Vue中用于高效比较新旧VNode子节点的一种策略。该算法的核心思想是,通过从新旧VNode子节点的两端开始比较,逐步向中间靠拢,以找到最小的差异并据此更新DOM。以下是双端比较算法的大致流程: 初始化指针&…...
# 如何确认elementary os (linux)使用的是Wayland而不是x11?
如何确认elementary os (linux)使用的是Wayland而不是x11? 文章目录 如何确认elementary os (linux)使用的是Wayland而不是x11?**方法 1:使用 loginctl 命令(systemd 系统࿰…...
VMware安装Windows server 2016
1、新建虚拟机,选择自定义模式 2、选择兼容性 4、命名虚拟机 5、固件类型 EFI 虚拟磁盘类型,不同电脑推荐的类型不同,用默认的就行 删除声卡和打印机 检查网络配置 选择本地的Windows server 2016的系统镜像,系统镜像可以去Window…...
K8s 1.27.1 实战系列(十)PV PVC
一、核心概念与关系 1、PV(Persistent Volume) PV 是集群中的持久化存储资源,由管理员预先创建并配置,独立于 Pod 生命周期。它抽象了底层存储(如 NFS、云存储等),定义存储容量、访问模式(如 ReadWriteOnce)、回收策略(Retain/Delete/Recycle)等属性。例如,一…...
HippoRAG 2 原理精读
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 整体流程离线索引阶段在线检索和问答阶段 总结 整体流程 从上图可以看出,整个流程分为两个阶段 1、离线索引阶段 2、在线检索和问答阶段 离线索引阶段…...
三:FFMPEG拉流读取模块的讲解
FFMPEG拉流读取模块在远程监控项目最核心的作用是读取UVC摄像头传输的H264码流,并对其码流进行帧的提取,提取完成之后则把数据传输到VDEC解码模块进行解码。而在我们这个项目中,UVC推流的功能由FFMPEG的命令完成。 FFMPEG拉流读取模块的API…...
linux makefile tutorial
一个makefile的教程,几个小时就能看完,对makefile有个总体加细节的系统了解,非常不错: Learn Makefiles With the tastiest examples 中文翻译版: 起步 - Makefile 教程 (gavinliu6.github.io) gcc官网手册&#x…...
【从零开始学习计算机科学】操作系统(五)处理器调度
【从零开始学习计算机科学】操作系统(五)处理器调度 处理器调度一些简单的短程调度算法的思路先来先服务(First-Come-First-Served,FCFS)优先级调度及其变种最短作业优先调度算法(SJF)--非抢占式最短作业优先调度算法(SJF)--抢占式最高响应比优先调度算法轮转调度算法…...
视觉图像处理
在MATLAB中进行视觉图像处理仿真通常涉及图像增强、滤波、分割、特征提取等操作。以下是一个分步指南和示例代码,帮助您快速入门: 1. MATLAB图像处理基础步骤 1.1 读取和显示图像 % 读取图像(替换为实际文件路径) img = imread(lena.jpg); % 显示原图 figure; subplot(2…...
从零开始设计一个完整的网站:HTML、CSS、PHP、MySQL 和 JavaScript 实战教程
前言 本文将从实战角度出发,带你一步步设计一个完整的网站。我们将从 静态网页 开始,然后加入 动态功能(使用 PHP),连接 数据库,最后加入 JavaScript 实现交互功能。通过这个教程,你将掌握一个…...
JavaScript(Web APIs)
这个阶段两天也能看完 目录 壹_DOM-获取元素 00、获取DOM元素(根据CS选择器来获取DOM元素) 01、修改元素内容 02、修改CSS 03、H5自定义属性 04、定时器 贰_DOM-事件基础 00、事件监听 01、事件类型 02、事件对象 03、环境对象 04、回调函数 叁_DOM-事…...
Global top sap abap 和deepseek对话,测试其abap推理能力
我提交给deepseek一段代码 FUNCTION zXXX_hr_pafm_pannnn_up. *"---------------------------------------------------------------------- *"*"Local Interface: *" IMPORTING *" VALUE(IS_PRELP) TYPE PRELP OPTIONAL *" VALUE(IV…...
Android DUKPT - 3DES
一、DUKPT概述 DUKPT 即Derived Unique Key Per Transaction(每个事务的派生唯一密钥)。ANSI X9.24规范定义的密钥管理体系,主要用于对称密钥加密场景(如MAC、PIN等敏感数据保护)。通过动态生成唯一交易密钥ÿ…...
机器学习数学基础:45.多重响应分析
多重响应分析超详细教程:手把手教你分析多选题数据 一、深入理解多重响应分析的背景 问卷调查中,问题分为单选题与多选题: 单选题:如“你的性别?1.男 2.女”,答题者仅选一个选项,分析时直接统…...
《苍穹外卖》SpringBoot后端开发项目核心知识点与常见问题整理(DAY1 to DAY3)
目录 一、在本地部署并启动Nginx服务1. 解压Nginx压缩包2. 启动Nginx服务3. 验证Nginx是否启动成功: 二、导入接口文档1. 黑马程序员提供的YApi平台2. YApi Pro平台3. 推荐工具:Apifox 三、Swagger1. 常用注解1.1 Api与ApiModel1.2 ApiModelProperty与Ap…...
企业安全—对数据和资产进行识别和分类
0x00 前言 针对数据和资产的保护刻不容缓,这个是每一个做企业安全建设不容放过的一环,那么在识别数据和资产已经对这些数据分类就是必须要了解和掌握的内容。 这里不仅是针对商业机密,还有用户数据,前者在于保护公司利益&#x…...
QT系列教程(20) Qt 项目视图便捷类
视频连接 https://www.bilibili.com/video/BV1XY41127t3/?vd_source8be9e83424c2ed2c9b2a3ed1d01385e9 Qt项目视图便捷类 Qt项目视图提供了一些便捷类,包括QListWidget, QTableWidget, QTreeWidget等。我们分别介绍这几个便捷类。 我们先创建一个Qt …...
Spring Boot 调用DeepSeek API的详细教程
目录 前置准备步骤1:创建Spring Boot项目步骤2:配置API参数步骤3:创建请求/响应DTO步骤4:实现API客户端步骤5:创建控制器步骤6:异常处理步骤7:测试验证单元测试示例Postman测试请求 常见问题排查…...
动态扩缩容引发的JVM堆内存震荡:从原理到实践的GC调优指南
目录 一、典型案例:系统发布后的GC雪崩事件 (一)故障现象 1. 刚刚启动时 GC 次数较多 2. 堆内存锯齿状波动 3. GC日志特征:Allocation Failure (二)问题定位 二、原理深度解析:JVM内存弹…...
AI智能眼镜主控芯片:技术演进与产业生态的深度解析
一、AI智能眼镜的技术挑战与主控芯片核心诉求 AI智能眼镜作为XR(扩展现实)技术的代表产品,其核心矛盾在于性能、功耗与体积的三角平衡。主控芯片作为设备的“大脑”,需在有限空间内实现复杂计算、多模态交互与全天候续航…...
微服务拆分-远程调用
我们在查询购物车列表的时候,它有一个需求,就是不仅仅要查出购物车当中的这些商品信息,同时还要去查到购物车当中这些商品的最新的价格和状态信息,跟购物车当中的快照进行一个对比,从而去提醒用户。 现在我们已经做了服…...
[网络爬虫] 动态网页抓取 — Selenium 介绍 环境配置
🌟想系统化学习爬虫技术?看看这个:[数据抓取] Python 网络爬虫 - 学习手册-CSDN博客 0x01:Selenium 工具介绍 Selenium 是一个开源的便携式自动化测试工具。它最初是为网站自动化测试而开发的,类似于我们玩游戏用的按…...
【RAGFlow】windows本地pycharm运行
原因 由于官方只提供了docker部署,基于开源代码需要实现自己内部得逻辑,所以需要本地pycharm能访问,且docker运行依赖得其余组件,均需要使用开发服务器得配置。 修改过程 安装python 项目依赖于Python 版本:>3.1…...
git子仓库管理的两种方式
在团队协作中选择使用 Git Submodule 还是 Git Subtree 取决于项目的需求和团队的工作方式。以下是两者的对比和适用场景分析,帮助你做出选择: Git Submodule 优点 独立性高 子模块是一个独立的仓库,拥有自己的提交历史和分支。这使得子模…...
树莓派5首次开机保姆级教程(无显示器通过VNC连接树莓派桌面)
第一次开机详细步骤 步骤一:树莓派系统烧录1 搜索打开烧录软件“Raspberry Pi Imager”2 选择合适的设备、系统、SD卡3 烧录配置选项 步骤二:SSH远程树莓派1 树莓派插电2 网络连接(有线或无线)3 确定树莓派IP地址 步骤三ÿ…...
html-表格标签
一、表格标签 1. 表格的主要作用 表格主要用于显示、展示数据,因为它可以让数据显示的非常的规整,可读性非常好。特别是后台展示数据 的时候,能够熟练运用表格就显得很重要。一个清爽简约的表格能够把繁杂的数据表现得很有条理。 总…...
大模型安全新范式:DeepSeek一体机内容安全卫士发布
2月以来,DeepSeek一体机几乎成为了政企市场AI消费的最强热点。 通过一体机的方式能够缩短大模型部署周期,深度结合业务场景,降低中小企业对于大模型的使用门槛。据不完全统计,已约有超过60家企业基于DeepSeek推出一体机产品。 但…...
数据分析绘制随时间顺序变化图加入线性趋势线——numpy库的polyfit计算一次多项式拟合
import pandas as pd import numpy as np import matplotlib.pyplot as plt# 导入数据 data pd.read_csv(rC:\Users\11712\notebooktrain1.csv)# 假设数据包含 date_time 和 speed 列 data[date_time] pd.to_datetime(data[date_time]) # 确保时间列是 datetime 类型 data.s…...



