当前位置: 首页 > news >正文

OpenCV之换脸技术:一场面部识别的奇妙之旅

在这个数字化与智能化并进的时代,图像处理技术日益成为连接现实与虚拟世界的桥梁。其中,换脸技术作为一项颇受欢迎且富有挑战性的应用,不仅让人惊叹于技术的魔力,更在娱乐、影视制作等领域展现了无限可能。今天,我们就来探索如何使用OpenCV这一强大的计算机视觉库,实现基础的换脸效果。

一、前言

OpenCV(Open Source Computer Vision Library)是一个开源的计算机视觉和机器学习软件库,它提供了丰富的图像处理和视频分析功能。通过OpenCV,我们可以轻松地进行面部检测、特征点匹配、图像变换等操作,为实现换脸技术打下坚实基础。

二、技术原理

换脸技术的核心在于将源图像中的人脸区域精确地映射到目标图像上,同时保持面部特征的自然和协调。这通常涉及以下几个关键步骤:

  1. 面部检测:利用预训练的面部检测模型(如Haar特征分类器或深度学习方法)在图像中定位人脸区域。
  2. 特征点匹配:通过面部特征点检测算法(如Dlib的68点或5点特征检测)找到源图像和目标图像中对应的关键点。
  3. 图像变换:使用仿射变换、透视变换或更复杂的非线性变换(如Delaunay三角剖分)将源人脸变形以匹配目标人脸的形状。
  4. 图像融合:将变形后的源人脸与目标图像的背景进行无缝融合,处理边缘,使其看起来自然。
三、实现步骤

下面是一个基于OpenCV的简单换脸示例代码框架,注意,这里假设你已经安装了OpenCV和Dlib库(用于特征点检测)。

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):  """根据关键点生成面部掩模"""  # 创建一个与输入图像大小相同的零矩阵  im = np.zeros(im.shape[:2], dtype=np.float64)  for p in POINTS:  # 对关键点集进行凸包计算  points = cv2.convexHull(keypoints[p])  # 填充凸包区域  cv2.fillConvexPoly(im, points, color=1)  # 将单通道图像转换为三通道图像,以适应OpenCV的显示要求  im = np.array([im, im, im]).transpose((1, 2, 0))  # 对掩模进行高斯模糊处理,以减少边缘的锯齿状  im = cv2.GaussianBlur(im, ksize=(25, 25), sigmaX=0)  return im  def getM(points1, points2):  """计算从points1到points2的仿射变换矩阵"""  # 将点转换为浮点数类型  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):  """检测图像中的面部关键点"""  # 检测面部  rects = detector(im, 1)  # 获取面部关键点  shape = predictor(im, rects[0])  # 将关键点转换为numpy矩阵  s = np.matrix([[p.x, p.y] for p in shape.parts()])  return s  def normalColor(a, b):  """调整b图的颜色值,使其与a图相似"""  # 对a和b进行高斯模糊处理,以减少噪声  aGauss = cv2.GaussianBlur(a, ksize=(111, 111), sigmaX=0)  bGauss = cv2.GaussianBlur(b, ksize=(111, 111), sigmaX=0)  # 计算颜色调整权值  weight = aGauss / bGauss  # 处理无穷大值  where_are_inf = np.isinf(weight)  weight[where_are_inf] = 0  # 返回调整后的b图  return b * weight  # 读取换脸所需的图片  
a = cv2.imread("pyy1.jpg")  # 换脸A图片  
b = cv2.imread("hg.png")  # 换脸B图片  # 初始化面部检测器和关键点预测器  
detector = dlib.get_frontal_face_detector()  
predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")  # 获取A图片和B图片的面部关键点  
aKeyPoints = getKeypoints(a)  
bKeyPoints = getKeypoints(b)  # 复制B图片,以便后续处理不破坏原图  
bOriginal = b.copy()  # 获取A图片和B图片的面部掩模  
aMask = getFaceMask(a, aKeyPoints)  
cv2.imshow("aMask", aMask)  # 显示A图片的面部掩模  
cv2.waitKey()  bMask = getFaceMask(b, bKeyPoints)  # 获取B图片的面部掩模  
cv2.imshow("bMask", bMask)  # 显示B图片的面部掩模  
cv2.waitKey()  # 计算从B图片面部到A图片面部的仿射变换矩阵  
M = getM(aKeyPoints[POINTStuple], bKeyPoints[POINTStuple])  # 使用仿射变换矩阵将B图片的面部掩模变换到A图片上  
bMaskWarp = cv2.warpAffine(bMask, M, dsize=a.shape[:2][::-1],  borderMode=cv2.BORDER_TRANSPARENT,  flags=cv2.WARP_INVERSE_MAP)  
cv2.imshow("bMaskWarp", bMaskWarp)  # 显示变换后的B图片面部掩模  
cv2.waitKey()  # 获取面部区域的最大掩模(A和B的掩模叠加)  
mask = np.max([aMask, bMaskWarp], axis=0)  
cv2.imshow("mask", mask)  # 显示最大掩模  
cv2.waitKey()  # 使用仿射变换矩阵将B图片变换到A图片上  
bWrap = cv2.warpAffine(b, M, dsize=a.shape[:2][::-1],  borderMode=cv2.BORDER_TRANSPARENT,  flags=cv2.WARP_INVERSE_MAP)  
cv2.imshow("bWrap", bWrap)  # 显示变换后的B图片  
cv2.waitKey()  # 调整B图片的颜色,使其与A图片相似  
bcolor = normalColor(a, bWrap)  
cv2.imshow("bcolor", bcolor)  # 显示调整颜色后的B图片  
cv2.waitKey()  # 换脸:在掩模区域使用B图片的颜色,其他区域使用A图片  
out = a * (1.0 - mask) + bcolor * mask  # 显示原始图片和换脸结果  
cv2.imshow("a", a)  # 显示A图片  
cv2.imshow("b", bOriginal)  # 显示原始B图片  
cv2.imshow("out", out / 255)  # 显示换脸结果(注意:这里除以255是为了将像素值归一化到0-1之间,便于显示)  
cv2.waitKey()  
cv2.destroyAllWindows()  # 关闭所有窗口

代码效果:

结语

通过上述步骤,我们利用OpenCV和Dlib实现了一个基础的换脸效果。虽然这只是冰山一角,但它为我们打开了一个充满无限想象的空间。随着技术的不断进步,未来的换脸技术将更加智能化、高效化,为我们的生活带来更多乐趣和可能。如果你对图像处理感兴趣,不妨亲自动手尝试一下,探索更多未知的领域吧!

相关文章:

OpenCV之换脸技术:一场面部识别的奇妙之旅

在这个数字化与智能化并进的时代,图像处理技术日益成为连接现实与虚拟世界的桥梁。其中,换脸技术作为一项颇受欢迎且富有挑战性的应用,不仅让人惊叹于技术的魔力,更在娱乐、影视制作等领域展现了无限可能。今天,我们就…...

Linux学习笔记9 文件系统的基础

一、查看文件组织结构 Linux中一切都是文件。 Linux和Win的文件系统不是一个结构,Linux存在的根目录是所有目录的起点。 所有的存储空间和设备共享一个根目录,不同的磁盘块和分区挂载在其下,成为某个子目录的子目录,甚至设备也挂…...

Android OpenGL粒子特效

在本篇,我们将开启一个新的项目,探索粒子的世界。粒子是一种基本的图形元素,它们通常被表示为一组点。通过巧妙地组合一些基础的物理效果,我们能够创造出许多令人惊叹的视觉效果。想象一下,我们可以模拟一个水滴从喷泉…...

5 -《本地部署开源大模型》在Ubuntu 22.04系统下ChatGLM3-6B高效微调实战

在Ubuntu 22.04系统下ChatGLM3-6B高效微调实战 无论是在单机单卡(一台机器上只有一块GPU)还是单机多卡(一台机器上有多块GPU)的硬件配置上启动ChatGLM3-6B模型,其前置环境配置和项目文件是相同的。如果大家对配置过程还…...

dpkg:错误:另外一个进程已经为dpkg前端锁加锁

一、 问题描述 在新装ubuntu系统时,我们常常会遇见dpkg的错误,dpkg:错误:另外一个进程已经为dpkg前端锁加锁,如下图。 二、问题解决 方法一 先执行sudo rm /var/lib/dpkg/lock-frontend然后再继续安装软件包,如果出现问题dpkg:…...

基于SSM服装定制系统的设计

管理员账户功能包括:系统首页,个人中心,用户管理,服装类型管理,服装信息管理,服装定制管理,留言反馈,系统管理 前台账号功能包括:系统首页,个人中心&#xf…...

RK3588开发笔记-usb3.0 xhci-hcd控制器挂死问题解决

目录 前言 一、问题现象 二、问题分析 三、问题排查 总结 前言 在使用 RK3588 芯片进行开发的过程中,我遇到了 USB 3.0 xHCI-HCD 控制器外接5G通讯模块偶尔挂死的问题。这个问题导致 USB 设备失去响应,且不能恢复,需要重启整个系统才能恢复使用,针对该问题进行大量测试以…...

深入解析TCP/IP协议:网络通信的基石

1. 引言 TCP/IP 协议是现代计算机网络的核心,它为互联网上的设备提供了通信的基础。在网络通信中,TCP/IP 协议栈是无处不在的,无论是个人设备的浏览器请求,还是大型分布式系统的内部通信,都依赖于它的稳定、高效与可靠…...

基于微信小程序的汽车预约维修系统(lw+演示+源码+运行)

基于微信小程序的汽车预约维修系统 摘要 随着信息技术在管理上越来越深入而广泛的应用,管理信息系统的实施在技术上已逐步成熟。本文介绍了基于微信小程序的汽车预约维修系统的开发全过程。通过分析基于微信小程序的汽车预约维修系统管理的不足,创建了…...

wifi、热点密码破解 - python

乐子脚本,有点小慢,试过多线程,系统 wifi 连接太慢了,需要时间确认,多线程的话系统根本反应不过来。 也就可以试试破解别人的热点,一般都是 123456 这样的傻鸟口令 # coding:utf-8 import pywifi from pyw…...

bean的实例化2024年10月17日

跟不上为基础 1.你的java学习路线 2. 3.课程 注解的装配 contoller调用service用的是注解装配...

告别ELK,APO提供基于ClickHouse开箱即用的高效日志方案——APO 0.6.0发布

ELK一直是日志领域的主流产品,但是ElasticSearch的成本很高,查询效果随着数据量的增加越来越慢。业界已经有很多公司,比如滴滴、B站、Uber、Cloudflare都已经使用ClickHose作为ElasticSearch的替代品,都取得了不错的效果&#xff…...

Excel使用技巧:定位Ctrl+G +公式+原位填充 Ctrl+Enter快速填充数据(处理合并单元格)

Excel的正确用法: Excel是个数据库,不要随意合并单元格。 数据输入的时候一定要按照行列输入,中间不要留空,不然就没有关联。 定位CtrlG 公式原位填充 CtrlEnter快速填充数据 如果把合并的单元格 取消合并,只有第一…...

JAVA学习-练习试用Java实现“成绩归类”

问题: 编写程序,输入一批学生的成绩,遇0或负数则输入结束,要求统计并输出优秀(大于85)、通过(60~84)和不及格(小于60)的学生人数。 示例 &#x…...

【Hive】7-拉链表的设计与实现

拉链表的设计与实现 数据同步问题 背景 例如:MySQL中有一张用户表: tb_user,每个用户注册完成以后,就会在用户表中新增该用户的信息,记录该用户的id、手机号码、用户名、性别、地址等信息。 每天都会有用户注册,产生…...

Maxwell 底层原理 详解

Maxwell 是一个 MySQL 数据库的增量数据捕获(CDC, Change Data Capture)工具,它通过读取 MySQL 的 binlog(Binary Log)来捕获数据变化,并将这些变化实时地发送到如 Kafka、Kinesis、RabbitMQ 或其他输出端。…...

使用短效IP池的优势是什么?

短效IP池作为代理IP服务中一种独特的资源管理方式,其应用已经在数据采集、市场分析和网络安全等多个领域中展示出强大的功能。尽管“短效”听起来似乎意味着某种限制,然而在某些特定的应用场景下,短效IP池却提供了无可比拟的优势。本文将详细…...

zynq烧写程序到flash后不运行

🏆本文收录于《全栈Bug调优(实战版)》专栏,主要记录项目实战过程中所遇到的Bug或因后果及提供真实有效的解决方案,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&am…...

JMeter如何设置HTTP代理服务器?

1、 2、添加线程组 3、设置HTTP代理服务器,目标控制器选择“测试计划>线程组” 过滤掉不需要的信息 4、设置电脑手动代理 5、点击启动,在浏览器操作就可以了...

React面试题笔记(一)

一、react基础面试题 1.react中keys的作用是什么? key是是用于追踪哪些列表被修改,被添加或者被移除的辅助标识。 在开发过程中,我们需要保证某个元素的 key 在其同级元素中具有唯一性。在 React Diff 算法中 React 会借助元素的 Key 值来判断该元素是…...

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

Vim 调用外部命令学习笔记

Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板,载入页面后,会显示引导弹窗,适用于引导用户使用页面,点击完成后,会显示下一个引导弹窗,直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...

Leetcode 3576. Transform Array to All Equal Elements

Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到&#xf…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂

蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...

2021-03-15 iview一些问题

1.iview 在使用tree组件时,发现没有set类的方法,只有get,那么要改变tree值,只能遍历treeData,递归修改treeData的checked,发现无法更改,原因在于check模式下,子元素的勾选状态跟父节…...

React19源码系列之 事件插件系统

事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...

【git】把本地更改提交远程新分支feature_g

创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...

C# SqlSugar:依赖注入与仓储模式实践

C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...

使用 SymPy 进行向量和矩阵的高级操作

在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...