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

变脸大师:基于OpenCV与Dlib的人脸换脸技术实现

目录

简介 

重新简介

思路解析

1. 加载人脸检测器和特征点预测模型

2. 读取两张人脸图片

3. 获取人脸的特征点

4. 使用Delaunay三角剖分

5. 仿射变换三角形

6. 三角形变形并复制

7. 脸部轮廓掩模

8. 无缝克隆换脸

9. 缩放图像

10. 显示换脸结果

整体代码

效果展示

准备换脸的图

准备接收换脸的图

结果


a4697c94656a4a51a5c786ef7d6e04d1.jpeg

 

 

简介 

        能让你秒变吴彦祖(或者你的女神,随你挑)的神奇换脸程序!呀哈哈哈哈(博主本人已疯),熬夜秃头(夸张一下)换来的。

        想象一下,早上醒来,对着镜子一照,哎呀妈呀,这不是我一直梦寐以求的那张脸吗?别急着去整容医院,先试试我的换脸神器吧!只需简单几步,你就能在朋友圈里上演一场“大变活人”的好戏,保证让你的朋友们惊掉下巴,直呼内行!

        当然啦,开发这玩意儿可不是闹着玩的。我经历了无数次的代码调试、算法优化,还有那些让人头疼的bug大战。但每当看到换脸效果越来越自然,我就像看到了自己的孩子慢慢长大一样,心里那叫一个美滋滋啊!

        好啦,废话不多说,接下来就让我带你走进这个换脸世界的奇妙之旅吧!记得系好安全带,因为接下来的内容可能会让你的脑洞大开,笑到肚子疼哦!

 

呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃

写得出来上面的话我真的是个正常人吗

 

重新简介

        在现代的图像处理技术中,人脸识别和图像变形已经不再是遥不可及的技术。通过一些简单的算法和工具,我们可以实现很多有趣的效果,比如今天要介绍的“人脸置换”。在这篇文章中,我将向大家展示如何使用OpenCV和Dlib库实现一个有趣的换脸程序,代码不仅能识别人脸,还能通过仿射变换,将一张脸的特征平滑地融合到另一张脸上。that`s ez


f07b903419ed41648d573ff4e2127578.jpeg

思路解析

        实现了两个图像之间的“换脸”功能,使用了Dlib库的人脸检测器、特征点预测器,以及OpenCV的图像处理函数。具体的实现思路如下:

1. 加载人脸检测器和特征点预测模型

 
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor('./model/shape_predictor_68_face_landmarks.dat')

 

        首先,代码加载了Dlib的正面人脸检测器detector,以及68个特征点预测模型predictor,后者会用来提取人脸的关键特征点。

2. 读取两张人脸图片

 
img1 = cv2.imread("./imgs/006.jpg")
img2 = cv2.imread("./imgs/008.jpg")

 

        接着,使用OpenCV的imread函数读取两张图像。img1是要移植脸部特征的图片,img2是脸部特征的目标图片。

3. 获取人脸的特征点

 
def get_landmarks(image):gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)faces = detector(gray)if len(faces) == 0:return Nonelandmarks = predictor(gray, faces[0])return np.array([[p.x, p.y] for p in landmarks.parts()])

 

        这个函数将图像转换为灰度图后,通过detector检测人脸区域。如果检测到人脸,则使用predictor提取人脸的68个特征点,返回一个包含这些特征点坐标的NumPy数组。每个特征点对应面部的特定位置,如眼睛、鼻子、嘴巴等。

4. 使用Delaunay三角剖分

 
def get_triangles(landmarks):rect = cv2.boundingRect(np.array([landmarks]))subdiv = cv2.Subdiv2D(rect)subdiv.insert(landmarks.tolist())triangles = subdiv.getTriangleList()triangles = np.array(triangles, dtype=np.int32)...

 

        该函数通过Delaunay三角剖分算法,将人脸特征点分割为多个三角形。Delaunay三角剖分可以确保生成的三角形不会产生过小的角度,这样更利于仿射变换。

5. 仿射变换三角形

 
def apply_affine_transform(src, src_tri, dst_tri, size):warp_mat = cv2.getAffineTransform(np.float32(src_tri), np.float32(dst_tri))dst = cv2.warpAffine(src, warp_mat, (size[0], size[1]), None, flags=cv2.INTER_LINEAR,borderMode=cv2.BORDER_REFLECT_101)return dst

 

        这个函数实现了将一个三角形从源图像变换到目标图像。通过仿射变换,将src_tri中的三角形变换为dst_tri中的相应三角形,返回变换后的图像。

6. 三角形变形并复制

 
def warp_triangle(img1, img2, t1, t2):r1 = cv2.boundingRect(np.array([t1]))r2 = cv2.boundingRect(np.array([t2]))...

 

  warp_triangle函数使用仿射变换将每个三角形从img1中的位置变形到img2_new_face中的对应位置。这是将源人脸区域贴到目标人脸的核心步骤。

7. 脸部轮廓掩模

 
hull2 = cv2.convexHull(np.array(landmarks2))
mask = np.zeros_like(img2[:, :, 0])
cv2.fillConvexPoly(mask, np.int32(hull2), 255)

 

        在仿射变换之后,通过convexHull构建目标人脸区域的凸包,即覆盖整个脸部的轮廓,并生成一个掩模mask用于后续的无缝克隆。

8. 无缝克隆换脸

 
seamless_img = cv2.seamlessClone(np.uint8(img2_new_face), img2, mask, center, cv2.NORMAL_CLONE)

 

  seamlessClone函数将换脸后的区域与目标图像进行无缝融合。这个函数会避免图像边界产生不自然的效果,从而使换脸看起来更加逼真。

9. 缩放图像

 
scale_factor = 0.9
dim = (int(seamless_img.shape[1] * scale_factor), int(seamless_img.shape[0] * scale_factor))
resized_img = cv2.resize(seamless_img, dim, interpolation=cv2.INTER_AREA)

 

        为了适应显示器大小(你们自己调调,分辨率高的图片就调小一点,分辨率低的就拉满吧),这里设置了一个缩放因子scale_factor(0.9),将最终生成的图像缩小到原来的90%。

10. 显示换脸结果

 
cv2.imshow("Face Swap", resized_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

 

        最后,使用imshow显示处理后的换脸结果,并等待用户按下任意键后关闭窗口。


ae9fd562949b4c1296bfe546ebc1ba32.jpeg

整体代码

import cv2
import numpy as np
import dlib# 加载人脸检测器和特征点预测模型
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor('./model/shape_predictor_68_face_landmarks.dat')# 读取两张人脸图片
img1 = cv2.imread("./imgs/006.jpg")
img2 = cv2.imread("./imgs/002.jpg")# 获取人脸的特征点
def get_landmarks(image):gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)faces = detector(gray)if len(faces) == 0:return Nonelandmarks = predictor(gray, faces[0])return np.array([[p.x, p.y] for p in landmarks.parts()])# 使用Delaunay三角剖分
def get_triangles(landmarks):rect = cv2.boundingRect(np.array([landmarks]))subdiv = cv2.Subdiv2D(rect)subdiv.insert(landmarks.tolist())triangles = subdiv.getTriangleList()triangles = np.array(triangles, dtype=np.int32)indexes = []for t in triangles:pts = []for i in range(3):for j, p in enumerate(landmarks):if (t[i * 2], t[i * 2 + 1]) == (p[0], p[1]):pts.append(j)if len(pts) == 3:indexes.append(pts)return indexes# 仿射变换三角形
def apply_affine_transform(src, src_tri, dst_tri, size):warp_mat = cv2.getAffineTransform(np.float32(src_tri), np.float32(dst_tri))dst = cv2.warpAffine(src, warp_mat, (size[0], size[1]), None, flags=cv2.INTER_LINEAR,borderMode=cv2.BORDER_REFLECT_101)return dst# 变形并复制
def warp_triangle(img1, img2, t1, t2):r1 = cv2.boundingRect(np.array([t1]))r2 = cv2.boundingRect(np.array([t2]))t1_rect = []t2_rect = []t2_rect_int = []for i in range(3):t1_rect.append(((t1[i][0] - r1[0]), (t1[i][1] - r1[1])))t2_rect.append(((t2[i][0] - r2[0]), (t2[i][1] - r2[1])))t2_rect_int.append(((t2[i][0] - r2[0]), (t2[i][1] - r2[1])))img1_rect = img1[r1[1]:r1[1] + r1[3], r1[0]:r1[0] + r1[2]]size = (r2[2], r2[3])img2_rect = apply_affine_transform(img1_rect, t1_rect, t2_rect, size)mask = np.zeros((r2[3], r2[2], 3), dtype=np.float32)cv2.fillConvexPoly(mask, np.int32(t2_rect_int), (1.0, 1.0, 1.0), 16, 0)img2_rect = img2_rect * maskimg2[r2[1]:r2[1] + r2[3], r2[0]:r2[0] + r2[2]] = img2[r2[1]:r2[1] + r2[3], r2[0]:r2[0] + r2[2]] * ((1.0, 1.0, 1.0) - mask)img2[r2[1]:r2[1] + r2[3], r2[0]:r2[0] + r2[2]] = img2[r2[1]:r2[1] + r2[3], r2[0]:r2[0] + r2[2]] + img2_rect# 主程序
landmarks1 = get_landmarks(img1)
landmarks2 = get_landmarks(img2)if landmarks1 is None or landmarks2 is None:print("未能检测到人脸")exit()triangles = get_triangles(landmarks1)# 复制人脸区域
img2_new_face = np.zeros_like(img2)for tri in triangles:t1 = [landmarks1[tri[0]], landmarks1[tri[1]], landmarks1[tri[2]]]t2 = [landmarks2[tri[0]], landmarks2[tri[1]], landmarks2[tri[2]]]warp_triangle(img1, img2_new_face, t1, t2)# 构建脸部轮廓掩模
hull2 = cv2.convexHull(np.array(landmarks2))
mask = np.zeros_like(img2[:, :, 0])
cv2.fillConvexPoly(mask, np.int32(hull2), 255)# 将换脸后的区域融合
r = cv2.boundingRect(np.int32(hull2))  # 使用 np.int32 类型
center = ((r[0] + int(r[2] / 2), r[1] + int(r[3] / 2)))seamless_img = cv2.seamlessClone(np.uint8(img2_new_face), img2, mask, center, cv2.NORMAL_CLONE)# 缩放因子
scale_factor = 0.3# 计算新尺寸
width = int(seamless_img.shape[1] * scale_factor)
height = int(seamless_img.shape[0] * scale_factor)
dim = (width, height)# 缩放图像
resized_img = cv2.resize(seamless_img, dim, interpolation=cv2.INTER_AREA)# 显示换脸效果
cv2.imshow("Face Swap", resized_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

 


效果展示

准备换脸的图

2fa0e1f86bd5466e82cedad5dfc21574.png

准备接收换脸的图

3b1d8f42a33249b2ada923300e39bd6a.png

 

结果

daae93c68bc94e1eac32815920e2dde4.png

 

我正如彭于晏一样的帅气(*^▽^*)

a6635f1defde40d1afc0cd7113ff9fd8.jpeg

 

 

 

 

 

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 掌握Spring Boot数据库集成:用JPA和Hibernate构建高效数据交互与版本控制
  • 【学习笔记】数据结构(六 ②)
  • 如何切换淘宝最新镜像源npm
  • 数字化转型中的企业蓝图构建:基于业务能力建模的全面解读与战略实施指南
  • 基于C语言+SQL Server2008实现(控制台)图书管理系统
  • 编程技巧:SQL 处理超大查询
  • 【machine learning-十-梯度下降-学习率】
  • node.js+Koa框架+MySQL实现注册登录
  • “科学突破奖”获得者连续两篇Nature,成功绘制人类主要激酶底物特异性图谱
  • 字符串函数的使用与模拟(2)——C语言内存函数
  • Leetcode 165. 比较版本号(Medium)
  • 【C++】——多态详解
  • 新电脑工作流搭建记录-前端篇
  • Nginx从入门到入土(三): 静态资源管理与代理服务
  • 腾讯云2024年数字生态大会开发者嘉年华(数据库动手实验)TDSQL-C初体验
  • Angular 4.x 动态创建组件
  • CAP理论的例子讲解
  • ES学习笔记(12)--Symbol
  • github从入门到放弃(1)
  • javascript 哈希表
  • JWT究竟是什么呢?
  • Mac转Windows的拯救指南
  • sublime配置文件
  • 创建一个Struts2项目maven 方式
  • 前端性能优化——回流与重绘
  • 设计模式(12)迭代器模式(讲解+应用)
  • 通过npm或yarn自动生成vue组件
  • 要让cordova项目适配iphoneX + ios11.4,总共要几步?三步
  • 在 Chrome DevTools 中调试 JavaScript 入门
  • 《码出高效》学习笔记与书中错误记录
  • ​LeetCode解法汇总1410. HTML 实体解析器
  • ​决定德拉瓦州地区版图的关键历史事件
  • #QT(QCharts绘制曲线)
  • #QT(TCP网络编程-服务端)
  • $L^p$ 调和函数恒为零
  • (01)ORB-SLAM2源码无死角解析-(56) 闭环线程→计算Sim3:理论推导(1)求解s,t
  • (1)(1.13) SiK无线电高级配置(五)
  • (1/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序
  • (Java数据结构)ArrayList
  • (阿里云在线播放)基于SpringBoot+Vue前后端分离的在线教育平台项目
  • (八十八)VFL语言初步 - 实现布局
  • (大众金融)SQL server面试题(1)-总销售量最少的3个型号的车及其总销售量
  • (函数)颠倒字符串顺序(C语言)
  • (切换多语言)vantUI+vue-i18n进行国际化配置及新增没有的语言包
  • (一)、软硬件全开源智能手表,与手机互联,标配多表盘,功能丰富(ZSWatch-Zephyr)
  • (一)u-boot-nand.bin的下载
  • (幽默漫画)有个程序员老公,是怎样的体验?
  • (原创) cocos2dx使用Curl连接网络(客户端)
  • **CI中自动类加载的用法总结
  • .NET Framework 4.6.2改进了WPF和安全性
  • .net mvc 获取url中controller和action
  • .Net Redis的秒杀Dome和异步执行
  • .Net Web窗口页属性
  • .NET 线程 Thread 进程 Process、线程池 pool、Invoke、begininvoke、异步回调
  • .Net 执行Linux下多行shell命令方法