《深度学习》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()
        运行结果:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/56945.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OpenHarmony 入门——ArkUI 自定义组件内同步的装饰器@State小结(二)

文章大纲 引言一、组件内状态装饰器State1、初始化2、使用规则3、变量的传递/访问规则说明4、支持的观察变化的场景5、State 变量的值初始化和更新机制6、State支持联合类型实例 引言 前一篇文章OpenHarmony 入门——ArkUI 自定义组件之间的状态装饰器小结(一&…

100多种【基于YOLOv8/v10/v11的目标检测系统】目录(python+pyside6界面+系统源码+可训练的数据集+也完成的训练模型)

待更新(持续更新),早关注,不迷路............................................................................... 基于YOLOv8的车辆行人实时检测系统基于YOLOv10的车辆行人实时检测系统基于YOLOv11的车辆行人实时检测系统基于YOLOv8的农…

如何在UE5中创建加载屏幕(开场动画)?

第一步: 首先在虚幻商城安装好Async Loading Screen,并且在项目的插件中勾选好。 第二步: 确保准备好所需要的素材: 1)开头的动画视频 2)关卡加载图片 3)准备至少两个关卡 第三步&#xff1a…

PythonExcel批量pingIP地址

问题: 作为一个电气工程师(PLC),当设备掉线的时候,需要用ping工具来检查网线物理层是否可靠连接,当项目体量过大时,就不能一个手动输入命令了。 解决方案一: 使用CMD命令 for /L %…

二百六十八、Kettle——同步ClickHouse清洗数据到Hive的DWD层静态分区表中(每天一次)

一、目的 实时数仓用的是ClickHouse,为了避免Hive还要清洗数据,因此就直接把ClickHouse中清洗数据同步到Hive中就行 二、所需工具 ClickHouse:clickhouse-client-21.9.5.16 Kettle:kettle9.2 Hadoop:hadoop-3.1.3…

视频网站开发:Spring Boot框架的高效实现

5 系统实现 5.1用户信息管理 管理员管理用户信息,可以添加,修改,删除用户信息信息。下图就是用户信息管理页面。 图5.1 用户信息管理页面 5.2 视频分享管理 管理员管理视频分享,可以添加,修改,删除视频分…

linux线程 | 同步与互斥 | 全解析信号量、环形生产消费者模型

前言: 本节内容讲述linux下的线程的信号量, 我们在之前进程间通信那里学习过一部分信号量, 但是那个是systemV版本的信号量,是以进程间通信的视角谈的。 但是本篇内容会以线程的视角谈一谈信号量。 ps:本篇内容建议学习了生产者消…

Qml-Item的Id生效范围

Qml-Item的Id生效范围 前置声明 本实例在Qt6.5版本中做的验证同一个qml文件中,id是唯一的,即不同有两个相同id 的Item;当前qml文件中声明的id在当前文件中有效(即如果其它组件中传入的id,与当前qml文件中id 相同,当前…

国庆旅游高峰期,如何利用可视化报表来展现景区、游客及消费数据

国庆黄金周,作为国内旅游市场的年度盛宴,总是吸引着无数游客的目光。今年,随着旅游市场的强劲复苏,各大景区又再次迎来游客流量的高峰。全国国内出游7.65亿人次,同比增长5.9%,国内游客出游总花费7008.17亿元…

Java | Leetcode Java题解之第485题最大连续1的个数

题目&#xff1a; 题解&#xff1a; class Solution {public int findMaxConsecutiveOnes(int[] nums) {int maxCount 0, count 0;int n nums.length;for (int i 0; i < n; i) {if (nums[i] 1) {count;} else {maxCount Math.max(maxCount, count);count 0;}}maxCou…

一起搭WPF架构之livechart的MVVM使用介绍

一起搭WPF架构之livechart使用介绍 前言ModelViewModelView界面设计界面后端 效果总结 前言 简单的架构搭建已经快接近尾声了&#xff0c;考虑设计使用图表的形式将SQLite数据库中的数据展示出来。前期已经介绍了livechart的安装&#xff0c;今天就详细介绍一下livechart的使用…

前三章例题【现代控制理论】

【现代控制理论-状态空间方程能观性分解】https://www.bilibili.com/video/BV1KU4y1N7jV?p17&vd_source3cc3c07b09206097d0d8b0aefdf07958

如何下载3GPP协议?

一、进入3GPP网页 https://www.3gpp.org/ 二、点击“Specifications &Technologies” 三、点击“FTP Server” 网址&#xff1a; https://www.3gpp.org/specifications-technologies 四、找到“latest”&#xff0c;查看最新版 网址&#xff1a; https://www.3gpp.org/ftp…

【jQuery】jQuery 处理 Ajax 以及解决跨域问题的方式

文章目录 HTTP原生创建 AjaxjQuery 处理 Ajax$.ajax()$().load()$.get()$.post() 跨域CORSJSONPiframeweb sockets HTTP 超文本传输协议&#xff08;HTTP&#xff0c;HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议。设计 HTTP 最初的目的是为了提供一种发…

计算机网络易混知识点

1.以太网采用曼彻斯特编码&#xff1b;以太网帧最短为64B&#xff0c;其中14个B首部(目的MAC-6B&#xff0c;源MAC-6B&#xff0c;类型-2B)4B尾部 2.OSI协议中&#xff0c;每一层为上一层提供服务&#xff0c;为下一层提供接口 3.帧序号的比特数表示的是发送窗口的大小&#…

LabVIEW提高开发效率技巧----离线调试

离线调试是LabVIEW开发中一项重要的技巧&#xff0c;通过使用Simulate Signal Express VI生成虚拟数据&#xff0c;开发者能够有效减少对实际硬件的依赖&#xff0c;加速开发过程。这种方法不仅可以提高开发效率&#xff0c;还能降低成本&#xff0c;增强系统的灵活性。 ​ 离…

从零开始使用最新版Paddle【PaddleOCR系列】——第二部分:自建数据集 + 模型微调训练

目录 一、自建数据集 1.官方数据集格式参考 2.自建数据集txt文件编写代码 3.数据集检验 二、模型训练 1.模型配置yaml文件 2.命令行指令训练 在上一篇文章中&#xff0c;构建好了paddleOCR 运行必需的环境&#xff0c;并通过在线下载的方式&#xff0c;使用官方训练好的模型进…

OpenCV图像处理——查找线条的转折点

问题描述 图像中有一条线&#xff0c;如何判断这条线的转折点&#xff1f; 比如下面一张图&#xff1a; 目的是找到图中的三个转折点。 要在图像中检测线的转折点&#xff0c;可以通过分析线的几何形状来完成。这通常需要首先提取线的轮廓&#xff0c;然后根据曲率、角度变化…

D42【python 接口自动化学习】- python基础之函数

day42 高阶函数 学习日期&#xff1a;20241019 学习目标&#xff1a;函数&#xfe63;- 55 高阶函数&#xff1a;函数对象与函数调用的用法区别 学习笔记&#xff1a; 函数对象和函数调用 # 函数对象和函数调用 def foo():print(foo display)# 函数对象 a foo print(a) # &…

JavaWeb Servlet--09深入:注册系统05---动态搜索栏

动态搜索栏 分析&#xff1a;在显示用户信息的表单里有一个下拉框选择用户的信息&#xff0c;一个文本框进行输入&#xff0c;一个按钮就行搜索&#xff0c;在下拉框选择了性别或许姓名的某一个包含字就会返回所有满足的用户。在controller层进行接收选择的搜索条件&#xff0…