《深度学习》dlib 人脸应用实例 仿射变换 换脸术
目录
一、仿射变换
1、什么是仿射变换
2、原理
3、图像的仿射变换
1)图像的几何变换主要包括
2)图像的几何变换主要分为
1、刚性变换:
2、仿射变换
3、透视变换
3)常见仿射变换
二、案例实现
1、定义关键点索引
2、定义函数用于获取脸部掩膜
3、定义函数求变换矩阵
4、定义求68关键点函数
5、定义函数修改图片颜色
6、主函数
运行结果:
一、仿射变换
1、什么是仿射变换
仿射变换(Affine Transformation)是指在向量空间中进行一次线性变换(乘以一个矩阵)和一次平移(加上一个向量),变换到另一个向量空间的过程,即对图像进行形状、大小和方位的变换。
2、原理
仿射变换代表的是两幅图之间的映射关系,仿射变换矩阵为2x3的矩阵,如下图中的矩阵M,其中的B起着平移的作用,而A中的对角线决定缩放,反对角线决定旋转或错切。

原像素点坐标(x,y),则矩阵仿射变换基本算法原理:

所以仿射变换是一种二维坐标(x,y)到二维坐标(u,v)之间的线性变换,其数学表达式如下:

这个矩阵是2×3的,但是这会改变原始图像的维度,为此,增加一个维度,构造齐次变换矩阵3×3

这就保持了图像的‘平直性’和‘平行性’。 平直性:直线、圆弧不变。平行性:平行关系不变,直线相对位置不变,但是夹角可能会改变。
3、图像的仿射变换
1)图像的几何变换主要包括
平移、旋转、缩放、剪切、仿射、透视等。
2)图像的几何变换主要分为
刚性变换、仿射变换和透视变换(投影变换)
1、刚性变换:
平移+旋转 相似变换:缩放+剪切
2、仿射变换
从一个二维坐标系变换到另一个二维坐标系,属于线性变换。通过已知3对坐标点可以求得变换矩阵
3、透视变换
从一个二维坐标系变换到一个三维坐标系,属于非线性变换。通过已知4对坐标点可以求得变换矩阵。

3)常见仿射变换
常见的仿射变换包括平移(Translation)、缩放(Scaling)、旋转(Rotation)、错切(Shearing)和镜像(Flipping)等
平移:沿着x和y轴的平移使图像的位置发生改变。
缩放:沿着x和y轴的缩放使图像的大小发生改变。
旋转:绕图像中心点进行旋转,使图像按某个角度进行旋转。
错切:使图像在某个方向上产生倾斜。
镜像:沿着x或y轴进行镜像翻转,使图像左右或上下对称。
二、案例实现

1、定义关键点索引
# 定义关键点索引
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) # 元组内存放关键点集的列表
2、定义函数用于获取脸部掩膜
def getFaceMask(im,keyPoints): # 传入图像和关键点矩阵,根据关键点获取脸部掩膜im = np.zeros(im.shape[:2],dtype=np.float64) # 生成一个和图像大小一致的0矩阵,类型为浮点型for p in POINTS: # 遍历每一个关键点的索引号points = cv2.convexHull(keyPoints[p]) # 调用凸包函数,获取凸包,即最小凸多边形,返回凸包边界信息cv2.fillConvexPoly(im,points,color=1) # 在掩膜im上填充凸包points,color=1表示填充蓝色# 单通道im构成3通道im(3,行,列),改变形状(行、列、3)适应0penCVim = np.array([im,im,im]).transpose((1,2,0)) # 将掩膜升高一个维度然后转换第一个维度和第三个维度的位置,表示为宽、高、3通道im = cv2.GaussianBlur(im,ksize=(25,25),sigmaX=0) # 使用高斯模糊对im掩膜进行处理,高斯核大小为25*25,0表示指定高斯核在x方向的标准差,设置为0表示自动计算一个合适的值return im # 返回处理完的掩膜图像
3、定义函数求变换矩阵
def getM(points1, points2):points1 = points1.astype(np.float64) # int8转换为浮点数类型points2 = points2.astype(np.float64) # 转换为浮点数类型c1 = np.mean(points1,axis=0) # 计算均值,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) # points1.T * points2计算协方差矩阵,使用np.linalg.svd对协方差矩阵进行奇异值分解,返回三个矩阵R = (U * Vt).T # 通过U和Vt计算旋转矩阵R,U * Vt将points1对其到points2,T为转置return np.hstack(((s2 / s1) * R,c2.T-(s2 / s1) * R * c1.T)) # 返回a仿射到b的变换矩阵# 计算一个刚体变换矩阵,该矩阵可以将一个点集(points1)经过旋转和平移后对齐到另一个点集(points2),同时考虑了可能的尺度差异。
4、定义求68关键点函数
def getKeyPoints(im): # 接收到一张图片,获取图片中人脸的关键点rects = detector(im,1) # 调用人脸检测器,获取人脸方框位置,返回数组类型,其中存放方框左上角坐标和右下角坐标shape = predictor(im,rects[0]) # 调用预训练模型,获取人脸的68个关键点s = np.matrix([[p.x,p.y] for p in shape.parts()]) # shape.parts()获取关键点的迭代器,遍历出来每一个关键点,将遍历出来的关键点存入列表,然后使用np.matrix创建一个二维矩阵return s # 返回68个关键点的坐标矩阵
5、定义函数修改图片颜色
def normalColor(a, b):ksize = (111,111) # 非常大的核,用于进行高斯模糊的核,去噪等运算时为11就比较大了aGauss = cv2.GaussianBlur(a,ksize,0) # 对a进行高斯滤波bGauss = cv2.GaussianBlur(b,ksize,0) # 对b进行高斯滤波weight = aGauss/bGauss # 计算目标图像调整颜色的权重值,存在0除警告,可忽略。where_are_inf = np.isinf(weight) # 检查weight中是否有无穷大的值weight[where_are_inf] = 0 # 如果有将其更改为0return b * weight # 将权重应用于图像b,以调整其颜色
6、主函数
a = cv2.imread("pyy1.png") # 待换脸的A图片
b = cv2.imread("zly1.png")detector = dlib.get_frontal_face_detector() # 构造脸部位置检测器
predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat") # 读取人脸68关键点检测器的预处理模型aKeyPoints = getKeyPoints(a) # 将图片传入函数,获取A图片的68个关键点坐标矩阵
bKeyPoints = getKeyPoints(b)bOriginal = b.copy() # 不对原来的图片b进行破坏和修改aMask = getFaceMask(a,aKeyPoints) # 获取图片A的人脸掩膜
cv2.imshow("aMask",aMask) # 展示掩膜
cv2.waitKey()bMask = getFaceMask(b,bKeyPoints) # 获取图片B的人脸掩膜
cv2.imshow("bMask", bMask)
cv2.waitKey()"""求出b脸仿射变换到a脸的变换矩阵M"""
M = getM(aKeyPoints[POINTStuple],bKeyPoints[POINTStuple]) # 传入a脸关键点坐标和b脸关键点坐标,获取a仿射到b的变换矩阵"""将b的脸部(bmask)根据M仿射变换到a上"""
dsize = a.shape[:2][::-1] # 获取原图高宽,然后倒序
# 目标输出与图像a大小一致
# 需要注意,shape是(行、列),warpAffine参数dsize是(列、行)
# 使用a.shape[:2][::-1],获取a的(列、行)# 函数warpAffine(src,M,dsize,dst=None, flags=None, borderMode=None, borderValue=None)
# src:输入图像
# M:运算矩阵,2行3列的,
# dsize:运算后矩阵的大小,也就是输出图片的尺寸
# dst:输出图像
# flags:插值方法的组合,与resize函数中的插值一样,可以查看cv2.resize
# borderMode:边界模式,BORDER_TRANSPARENT表示边界透明
# borderValue:在恒定边框的情况下使用的borderValue值;默认情况下,它是0
bMaskWarp = cv2.warpAffine(bMask,M,dsize,borderMode=cv2.BORDER_TRANSPARENT,flags=cv2.WARP_INVERSE_MAP)
# 返回变换后的掩膜图像,包含了根据变换矩阵M和指定大小dsize对bMask进行仿射变换的结果。cv2.imshow("bMaskWarp",bMaskWarp)
cv2.waitKey()"""获取脸部最大值(两个脸模板叠加)"""
mask = np.max([aMask,bMaskWarp],axis=0) # a将掩膜图像与b变换后的掩膜图像数值转变为数组类型,然后沿行方向求最大值
cv2.imshow("mask",mask)
cv2.waitKey()"""使用仿射矩阵M,将b映射到a"""
# 计算b图片经过变换矩阵进行仿射变换处理后的图像
bWrap = cv2.warpAffine(b,M, dsize,borderMode=cv2.BORDER_TRANSPARENT,flags=cv2.WARP_INVERSE_MAP)
cv2.imshow("bWrap",bWrap)
cv2.waitKey()# 求b图片仿射到图片a的颜色值,b的颜色值改为a的颜色
bcolor = normalColor(a,bWrap)
cv2.imshow("bcolor",bcolor)
cv2.waitKey()# 换脸(mask区域用bcolor,非mask区城用a)
out = a*(1.0-mask) + bcolor * mask# 输出原始人脸、换脸结果
cv2.imshow("a",a)
cv2.imshow("b" ,bOriginal)
cv2.imshow("out", out/255)
cv2.waitKey()
cv2.destroyAllWindows()
运行结果:

相关文章:
《深度学习》dlib 人脸应用实例 仿射变换 换脸术
目录 一、仿射变换 1、什么是仿射变换 2、原理 3、图像的仿射变换 1)图像的几何变换主要包括 2)图像的几何变换主要分为 1、刚性变换: 2、仿射变换 3、透视变换 3)常见仿射变换 二、案例实现 1、定义关键点索引 2、定…...
springboot044美容院管理系统(论文+源码)_kaic
本科毕业设计论文 题目:美容院管理系统设计与实现 系 别: XX系(全称) 专 业: 软件工程 班 级: 软件工程15201 学生姓名: 学生学号: 指导教师: 导师…...
大数据新视界 --大数据大厂之数据脱敏技术在大数据中的应用与挑战
💖💖💖亲爱的朋友们,热烈欢迎你们来到 青云交的博客!能与你们在此邂逅,我满心欢喜,深感无比荣幸。在这个瞬息万变的时代,我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…...
Erric Gamma 关于resuable code的采访
采访地址 The risk of speculating Bill Venners: The GoF book says, “The key to maximizing reuse lies in anticipating new requirements and changes to existing requirements, and in designing your systems so they can evolve accordingly. To design a system s…...
【Ubuntu18.04命令行code打不开】可能的解决方法
目录 问题:命令行code打不开文件尝试① kimi是这么说的② sudo apt-get install apparmor apparmor_utils③ 在混沌的操作完以上一通后,sudo apt-get install snapd 我试了将近一个小时 : ( so depressed 我只是想用vscode打开个文件夹,我甚至…...
大数据毕业设计基于springboot+Hadoop实现的豆瓣电子图书推荐系统
文章目录 前言项目介绍技术介绍功能介绍核心代码数据库参考 系统效果图 前言 文章底部名片,获取项目的完整演示视频,免费解答技术疑问 项目介绍 随着开数字化阅读的普及,豆瓣电子图书推荐系统应运而生,旨在为用户提供个性化的阅…...
【进阶OpenCV】 (15)-- 人脸识别 -- EigenFaces算法
文章目录 EigenFaces算法一、算法原理二、算法流程三、算法特点四、代码步骤1. 图像预处理2. 创建Eigenfaces人脸识别器3. 训练模型4. 预测图像 总结 EigenFaces算法 EigenFaces算法是一种基于主成分分析(PCA)的人脸识别方法,其核心思想是通…...
无人机封闭空间建图检测系统技术详解
无人机封闭空间建图检测系统技术是一种集成了多种传感器、智能算法与控制系统的高级技术,旨在实现无人机在复杂封闭环境下的自主导航、精确建图与高效检测。以下是对该技术的详细解析: 一、技术概述 无人机封闭空间建图检测系统通过集成激光雷达(LiDAR…...
webpack自定义插件 ChangeScriptSrcPlugin
插件文件 class ChangeScriptSrcPlugin {apply(compiler) {const pluginName "ChangeScriptSrcPlugin";compiler.hooks.compilation.tap(pluginName, (compilation, callback) > {compilation.hooks.htmlWebpackPluginAlterAssetTags.tapAsync(pluginName,(html…...
win11 笔记本指纹驱动正常就是使用不了
去微信官网下载最新的win11重装系统后,发现笔记本上的指纹登录失效了,网上搜索各种试过不行,新版本与旧的win11有微出入,那些方法都不行。系统版本如下: 操作前确保: 1. win11能升级,默认有试用…...
表的约束
约束就是设定条条框框,保证被约束目标符合约束条件。表的约束就是对表内列数据的约束,保证列数据的正确性。 default 设置列数据的默认值。当用户添加记录的时候没有初始化该列的数据,就是使用默认值初始化。 如果创建表的时候没有设置默认…...
jmeter是怎么用的,怎么设计接口测试用例的
如何使用: Apache JMeter 是一款广泛使用的开源性能测试工具,用于对各种服务(如 Web 应用、数据库、FTP、WEB 服务等)进行负载测试和性能测试。以下是如何使用 JMeter 的基本步骤。 1. 安装 JMeter 下载 JMeter:访问…...
数据挖掘示例
案例背景,有公司进行橡胶玩具的生产,一共生产两种产品,分别为橡皮鱼和橡皮鸭。 已知条件为: 1、公司的橡胶原材料能够生产500只橡皮鸭或者400条橡皮鱼。 2、生产效率为公司产量不会高于400只橡皮鸭和300条橡皮鱼。 3、每只橡皮…...
【PHP】在ThinkPHP6中Swoole与FPM的简单性能测试对比
一、前言 本文主要测试在ThinkPHP 6框架中,使用Swoole扩展库与使用PHP-FPM两者的HTTP并发性能差距,测试方法较简单,仅供参考。 二、测试环境 系统:Ubuntu 22.04 PHP版本:7.4.33 Swoole版本:4.8.13 ThinkPHP版本:6.1.5 ThinkPHP-Swoole扩展库版本:3.1.4 测试工具:A…...
论文写作学习--POLYGCL
POLYGCL: GRAPH CONTRASTIVE LEARNING VIA LEARNABLE SPECTRAL POLYNOMIAL filters 今天由于开了一天的积极分子会议以及要写汇报,因此,只来得及学一会。 摘要 第一步,设定背景 Recently, Graph Contrastive Learning (GCL) has achieved …...
【高等数学】无穷级数
0. 了解 无穷级数是指将无穷多个数按照一定的规律相加起来的表达式。 打个比方,就像你有一个无穷长的梯子,每一级梯子代表一个数。把这些数一个一个加起来,就形成了无穷级数。 比如常见的等比级数,这里是首项,是公比。如果,这个等比级数是收敛的,也就是它的和是一个有限…...
计算机网络——无连接传输UDP
UDP用于流媒体应用和事务性应用(一次往返搞定的应用)...
DS几大常见排序讲解和实现(下)(15)
文章目录 前言一、快排的思想二、Hoare版基本思路代码实现 三、挖坑法基本思路代码实现 四、双指针法基本思想代码实现 五、三数取中六、小区间优化七、三路划分八、自省排序总结 前言 其实下篇就单独讲个快速排序 你可能会想这是什么神通,竟然能单独开一篇来讲…...
电脑视频剪辑大比拼,谁更胜一筹?
随着短视频的火爆,越来越多的人开始尝试自己动手制作视频,无论是记录生活点滴还是创作个性短片,一款好用的视频剪辑软件是必不可少的。今天,我们就从短视频运营的角度,来聊聊几款热门的电脑视频剪辑软件,看…...
计算机毕业设计 基于Web的景区管理系统的设计与实现 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试
🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点…...
树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频
使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...
Day131 | 灵神 | 回溯算法 | 子集型 子集
Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣(LeetCode) 思路: 笔者写过很多次这道题了,不想写题解了,大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...
条件运算符
C中的三目运算符(也称条件运算符,英文:ternary operator)是一种简洁的条件选择语句,语法如下: 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true,则整个表达式的结果为“表达式1”…...
根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:
根据万维钢精英日课6的内容,使用AI(2025)可以参考以下方法: 四个洞见 模型已经比人聪明:以ChatGPT o3为代表的AI非常强大,能运用高级理论解释道理、引用最新学术论文,生成对顶尖科学家都有用的…...
Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)
参考官方文档:https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java(供 Kotlin 使用) 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...
【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)
1.获取 authorizationCode: 2.利用 authorizationCode 获取 accessToken:文档中心 3.获取手机:文档中心 4.获取昵称头像:文档中心 首先创建 request 若要获取手机号,scope必填 phone,permissions 必填 …...
Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
文章目录 Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket(服务端和客户端都要)2. 绑定本地地址和端口&#x…...
人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...
【p2p、分布式,区块链笔记 MESH】Bluetooth蓝牙通信 BLE Mesh协议的拓扑结构 定向转发机制
目录 节点的功能承载层(GATT/Adv)局限性: 拓扑关系定向转发机制定向转发意义 CG 节点的功能 节点的功能由节点支持的特性和功能决定。所有节点都能够发送和接收网格消息。节点还可以选择支持一个或多个附加功能,如 Configuration …...
怎么开发一个网络协议模块(C语言框架)之(六) ——通用对象池总结(核心)
+---------------------------+ | operEntryTbl[] | ← 操作对象池 (对象数组) +---------------------------+ | 0 | 1 | 2 | ... | N-1 | +---------------------------+↓ 初始化时全部加入 +------------------------+ +-------------------------+ | …...
