【换脸详细教程】手把手教你进行AI换脸:换脸流程及源码详解

目录

  • 1. 换脸基本原理
  • 2 人脸检测及可视化
  • 3. 人脸轮廓点检测及可视化
  • 4. 人脸图像变换--仿射变换
  • 5. 生成遮罩并直接替换人脸
  • 6. 人脸颜色校正

最近AI换脸貌似比较火爆,就稍微研究了一下相关了内容。AI换脸是一个娱乐性比较强的应用,这种错位感让人觉得非常有趣。很多人都以为这是什么黑科技,但这里想告诉大家,AI换脸其实很简单。只需要你会一点Python基础,就可以实现自己的AI换脸程序。

本文将详细介绍图片换脸的基本原理以及实际的换脸过程。感兴趣的小伙伴可以一起试试哦~所有内容均已经打包好,获取方式如下:

关注GZH:阿旭算法与机器学习,回复:“换脸”即可获取本文源码、测试文件等内容,欢迎共同学习交流

1. 换脸基本原理

为了做人脸相关的工作,首先我们需要获得人脸的轮廓点。通常轮廓点检测也被称为 Face Alignment。本文的AI换脸实现就是建立在68个人脸轮廓点(Landmarks)的基础之上。而 Face Alignment 必须依赖一个人脸检测器提供人脸位置的矩形框,即 Face Detection。也就是说,本文所介绍的换脸或者其他精细化人脸操作的基础是 Face Detection + Face Alignment。但是,这两个技术并不需要我们亲自开发,dlib已经提供了效果不错、使用简便的第三方库。

AI换脸技术路线图的主要步骤如下:
在这里插入图片描述

我们想要将源图像B中的人脸放置到目标图像A中,这就需要将B中的人脸变换到A中人脸所在位置,然后截取对应区域并替换。如上图所示,这一步骤可以由OpenCV支持完成。最后,由于两张图像的人脸可能存在光照、色相等方面的不一致,为了替换后更加逼真,所以需要对图像B的颜色进行一些调整。

2 人脸检测及可视化

目标检测(Object Detection)一直都是计算机视觉应用的基础,这也是每次出现新的目标检测算法总能引起很大轰动的原因。完成人脸相关的任务,那自然就是需要人脸检测了。dlib提供了一个人脸检测器,可以使用如下方式获取:
face_detector = dlib.get_frontal_face_detector()
对输入的两幅带有人脸的图像进行 Face Detection,就可以获得其中人脸位置的矩形框,代码如下:

# coding:utf-8
import os, sys
import numpy as np
import cv2, dlibimg1_path = 'imgs/2.jpg'
img2_path = 'imgs/1.jpg'face_detector = dlib.get_frontal_face_detector()
img1 = cv2.imread(img1_path, cv2.IMREAD_COLOR)
img2 = cv2.imread(img2_path, cv2.IMREAD_COLOR)
face1 = face_detector(img1, 1)
face2 = face_detector(img2, 1)
print(face1, face2)

输出结果:

rectangles[[(171, 201) (438, 468)]] rectangles[[(171, 231) (438, 498)]]

fig = plt.figure(figsize=(9.6, 5.4))
plt.subplot(1,2,1)
plt.imshow(img1)
rect = face1[0]
plt.plot([rect.left(), rect.right(), rect.right(), rect.left(), rect.left()],[rect.top(), rect.top(), rect.bottom(), rect.bottom(), rect.top()])
plt.subplot(1,2,2)
plt.imshow(img2)
rect = face2[0]
plt.plot([rect.left(), rect.right(), rect.right(), rect.left(), rect.left()],[rect.top(), rect.top(), rect.bottom(), rect.bottom(), rect.top()])
plt.show()

在这里插入图片描述

3. 人脸轮廓点检测及可视化

此处直接使用dlib提供的模型文件shape_predictor_68_face_landmarks。下载解压之后,就可以使用以下代码载入
landmark_detector = dlib.shape_predictor(LANDMARK_MODEL_PATH)
关于dlib.shape_predictor更多的使用方法,可以访问官网的检测说明。对输入的这两幅图像检测人脸轮廓点的代码及检测效果如下:

LANDMARK_MODEL_PATH = os.path.expanduser('models/shape_predictor_68_face_landmarks.dat')
landmark_detector = dlib.shape_predictor(LANDMARK_MODEL_PATH)
pts1 = landmark_detector(img1, face1[0])
pts1 = np.array([[pt.x,pt.y] for pt in pts1.parts()])
pts2 = landmark_detector(img2, face2[0])
pts2 = np.array([[pt.x,pt.y] for pt in pts2.parts()])
fig = plt.figure(figsize=(9.6, 5.4))
plt.subplot(1,2,1)
plt.imshow(img1)
rect = face1[0]
plt.plot([rect.left(), rect.right(), rect.right(), rect.left(), rect.left()],[rect.top(), rect.top(), rect.bottom(), rect.bottom(), rect.top()])
plt.scatter(np.squeeze(pts1[:, 0]), np.squeeze(pts1[:, 1]), linewidths=1)
plt.subplot(1,2,2)
plt.imshow(img2)
rect = face2[0]
plt.plot([rect.left(), rect.right(), rect.right(), rect.left(), rect.left()],[rect.top(), rect.top(), rect.bottom(), rect.bottom(), rect.top()])
plt.scatter(np.squeeze(pts2[:, 0]), np.squeeze(pts2[:, 1]), linewidths=1)
plt.show()

在这里插入图片描述

4. 人脸图像变换–仿射变换

现在我们需要将上图中右边的人脸放到左边图像的人脸处,但很显然,两张人脸的大小和位置都不一样。无法直接操作,所以需要进行图像变换,才能将第二幅图中的人脸投影到第一幅中人脸的位置。

现在,我们已经得到了两张人脸上的landmark坐标,那么求解变换就不是一个很难的问题。第一,我们可以在这68个点中选择三个稳定的点构成3个匹配点对,然后利用OpenCV中的接口求解一个仿射变换矩阵。当然,这种方式会导致对轮廓点的利用率很低,而且结果常常不是太稳定。第二,我们可以假设一个特殊的仿射变换 —— 相似变换可以达到目的,那么就能够利用这68个点对完成普氏分析。其流程是,先计算缩放因子,然后求解一个旋转变换,最后再求解平移变换,这样就可以构成一个完整的相似变换,变换的代码与结果如下:

def compute_affine_param(pts1, pts2):pts1, pts2 = np.mat(pts1), np.mat(pts2)pts1, pts2 = pts1.astype(np.float64), pts2.astype(np.float64)# centrallizecenter1 = np.mean(pts1, axis=0)center2 = np.mean(pts2, axis=0)pts1 -= center1pts2 -= center2print()# normalizestd1 = np.std(pts1)std2 = np.std(pts2)pts1 /= std1pts2 /= std2# compute rotation param by svdU, S, V = np.linalg.svd(pts1.transpose() * pts2)R = (U * V).transpose()# concat affine tranformation matrixtmp = (std2/std1)*RT = center2.transpose() - tmp*(center1.transpose())
#     print(T, tmp, T.shape, tmp.shape, center1.shape, center2.shape)affine = np.vstack([np.hstack([tmp, T]), np.matrix([0.0, 0.0, 1.0])])return affine
affine_params = compute_affine_param(pts1, pts2)# 仿射变换
w_img2 = np.zeros(img1.shape, dtype=img2.dtype)
cv2.warpAffine(img2, affine_params[:2], (w_img2.shape[1], w_img2.shape[0]), dst=w_img2, flags=cv2.WARP_INVERSE_MAP, borderMode=cv2.BORDER_TRANSPARENT)fig = plt.figure(figsize=(9.6, 5.4))
plt.subplot(1,2,1)
plt.imshow(img2)
plt.subplot(1,2,2)
plt.imshow(w_img2)

在这里插入图片描述

5. 生成遮罩并直接替换人脸

可以选择人脸的一些部位轮廓点生成遮罩,这里选择两只眼睛、眉毛、鼻子和嘴巴这几部分组成一个遮罩的区域。生成遮罩的算法可以直接调用OpenCV中的cv2.fillConvexPoly()函数完成。在两张原始图像上直接生成的遮罩思的代码与效果如下:

JAW_IDX = list(range(0, 17))
FACE_IDX = list(range(17, 68))
RIGHT_BROW_IDX = list(range(17, 22))
LEFT_BROW_IDX = list(range(22, 27))
NOSE_IDX = list(range(27, 35))
RIGHT_EYE_IDX = list(range(36, 42))
LEFT_EYE_IDX = list(range(42, 48))
MOUTH_IDX = list(range(48, 61))COVER_IDX = [LEFT_BROW_IDX + RIGHT_EYE_IDX + LEFT_EYE_IDX+ RIGHT_BROW_IDX + NOSE_IDX + MOUTH_IDX]
def get_mask(img_shape, pts):img = np.zeros(img_shape[:2], dtype=np.float64)for idx in COVER_IDX:cv2.fillConvexPoly(img, cv2.convexHull(pts[idx]), color=1)img = np.array([img, img, img]).transpose([1,2,0])img = (cv2.GaussianBlur(img, (13, 13), 0) > 0) * 1.0img = cv2.GaussianBlur(img, (13, 13), 0)return imgmask1 = get_mask(img1.shape, pts1)
mask2 = get_mask(img2.shape, pts2)
fig = plt.figure(figsize=(9.6, 5.4))
plt.subplot(1,2,1)
plt.imshow(mask1)
plt.subplot(1,2,2)
plt.imshow(mask2)
plt.show()

在这里插入图片描述
根据这两个遮罩,可以查看一下图像A和图像B中选中的区域:

fig = plt.figure(figsize=(9.6, 5.4))
plt.subplot(1,2,1)
plt.imshow((mask1*img1).astype(np.uint8))
plt.subplot(1,2,2)
plt.imshow((mask2*img2).astype(np.uint8))
plt.show()

在这里插入图片描述
从上图中可以看出,这就是我们需要替换的区域,但是很明显,这样的遮罩是不能够直接使用的,因为人脸的位置没有对齐。所以需要对源人脸图像B的遮罩作上面已经求解的相似变换。然后对比图像A和warp图像B的遮罩,取并集形成一个统一的遮罩,这样就能保证遮罩操作的区域是在两幅图像中相同的位置。
目标图像A的遮罩与统一遮罩的对比效果图如下:

w_mask2 = np.zeros(img1.shape, dtype=img2.dtype)
cv2.warpAffine(mask2, affine_params[:2], (w_img2.shape[1], w_img2.shape[0]),dst=w_img2, flags=cv2.WARP_INVERSE_MAP, borderMode=cv2.BORDER_TRANSPARENT)
mix_mask = np.max([mask1, w_mask2], axis=0)
fig = plt.figure(figsize=(9.6, 5.4))
plt.subplot(1,2,1)
plt.imshow(mask1)
plt.subplot(1,2,2)
plt.imshow(mix_mask)
plt.show()

在这里插入图片描述
显示统一的遮罩区域:

fig = plt.figure(figsize=(9.6, 5.4))
plt.subplot(1,2,1)
plt.imshow((mix_mask*img1).astype(np.uint8))
plt.subplot(1,2,2)
plt.imshow((mix_mask*w_img2).astype(np.uint8))
plt.show()

在这里插入图片描述
直接替换的效果图如下:

rough_dst_img = img1 * (1.0 - mix_mask) + w_img2 * mix_mask
fig = plt.figure(figsize=(9.6, 5.4))
plt.subplot(1,2,1)
plt.imshow(img1)
plt.subplot(1,2,2)
plt.imshow(rough_dst_img.astype(np.uint8))
plt.show()

在这里插入图片描述
从结果可以发现:人脸确实给替换上了,但是脸色就太诡异了。这种状况很常见,因为很难保证任意的两个人脸图像的光照、色相以及对比度等状况完全一致。因此需要对人脸图像B进行颜色调整。

6. 人脸颜色校正

这里使用一种比较简单的颜色平衡方法。矫正图像B的颜色之后,效果如下:

def adjust_color(img1, img2, pts, blur_factor=0.5):mean_le = np.mean(pts[LEFT_EYE_IDX], axis=0)mean_re = np.mean(pts[RIGHT_EYE_IDX], axis=0)blur_degree = int(blur_factor * np.linalg.norm(mean_le - mean_re))print(blur_degree)if blur_degree % 2 == 0:blur_degree += 1blur_img1 = cv2.GaussianBlur(img1, (blur_degree, blur_degree), 0)blur_img2 = cv2.GaussianBlur(img2, (blur_degree, blur_degree), 0)blur_img1 = blur_img1.astype(np.float64)blur_img2 = blur_img2.astype(np.float64)blur_img2 += (blur_img2 <= 1.0) * 128img2 = img2.astype(np.float64)return img2 * blur_img1 / blur_img2adjust_img = adjust_color(img1, w_img2, pts1, blur_factor=0.5)
fig = plt.figure(figsize=(9.6, 5.4))
plt.subplot(1,2,1)
plt.imshow(img1)
plt.subplot(1,2,2)
plt.imshow(adjust_img.astype(np.uint8))
plt.show()

在这里插入图片描述

对颜色矫正之后的图像B使用已经生成的统一遮罩,并得到最终的换脸结果:

dst_img = img1 * (1.0 - mix_mask) + adjust_img * mix_mask
fig = plt.figure(figsize=(9.6, 5.4))
plt.subplot(1,2,1)
plt.imshow(img1)
plt.subplot(1,2,2)
plt.imshow(dst_img.astype(np.uint8))
plt.show()

在这里插入图片描述
上面换脸的结果整体看起来只能说勉强凑活,比没进行颜色矫正后的结果好了一点。但是还是需要说明,这里使用的颜色平衡的方式仍然非常粗糙。如果想要让融合之后的效果更加逼真,可能需要考虑在这方面花费更多的功夫。

本文所有内容均已经打包好,获取方式如下:

关注下方GZH:【阿旭算法与机器学习】,回复:【换脸】
即可获取本文源码、测试文件等内容,欢迎共同学习交流

如果文章对你有帮助,感谢点赞+关注!
好啦,以上便是换脸的整个流程,小伙伴们一起实操起来吧,欢迎共同学习交流~
小伙伴们有什么想法欢迎评论区留言交流!

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

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

相关文章

faceswap换脸程序安装及使用

faceswap简介 faceswap是一个开源的视频换脸软件&#xff0c;基本需要三个步骤&#xff0c;1.将视频切割成图片&#xff0c;并进行人脸提取 2.样本训练 3.换脸 faceswap安装&#xff08;windows环境&#xff09; 依赖安装 anaconda 需要先安装conda&#xff0c;用于python的…

分享几个我试过的AI工具,也可以在写论文时使用

现在AI工具越来越火了&#xff0c;特别是Chatgpt兴起以后。分享几个我平常会用的AI工具希望大家也有用到 1. Chatgpt与davinci Chatgpt相信大家比较熟悉了&#xff0c;但实际上OpenAI还有一个基于GPT-3的模型&#xff0c;这里我推荐一个可以代替chatgpt的工具, davinci-003。是…

国内企业怎么做好海外市场营销? 海外市场营销推广的全过程

如果您在中国不了解海外市场&#xff0c;但有国际推广需求&#xff0c;那么本文可以帮助您了解海外市场的营销理 念与方法&#xff0c;并帮助您实施全面的营销计划&#xff1a; 首先让我们从市场调查开始。 一、海外市场调研 如果你不熟悉当地的情况&#xff0c;开始贸易推…

【课件】Python调用OpenAI API实现ChatGPT多轮对话

Python调用openai API实现多轮对话 如何实现多轮对话&#xff1f; gpt-3.5-turbo 模型调用方法 openai.ChatCompletion.create 里传入的 message 是一个列表&#xff0c;列表里每个元素是字典&#xff0c;包含了角色和内容&#xff0c;我们只需将每轮对话都存储起来&#xff0c…

使用OpenAI创建对话式聊天机器人

引言 在当今的技术世界中&#xff0c;人工智能&#xff08;AI&#xff09;的发展迅猛&#xff0c;为我们带来了许多令人兴奋的创新。其中&#xff0c;自然语言处理&#xff08;NLP&#xff09;领域的进展使得开发对话式聊天机器人成为可能。OpenAI是一家领先的人工智能研究实验…

人工智能数学基础--概率与统计8:一个很有意思的下棋输赢概率问题

一、问题 甲、乙二人下象棋&#xff0c;每局甲胜的概率为a&#xff0c;乙胜的概率为b。为简化问题&#xff0c;设没有和局的情况&#xff0c;这意味着ab1。设想甲的棋艺高于乙&#xff0c;即a>b。考虑到这个情况&#xff0c;他们商定最终胜负的规则如下&#xff1a; 到什么…

2018年数学与计算机大事件:18岁少年大放异彩!ABC猜想证明被推翻?

【导读】2018年数学和计算机科学领域发生了哪些重大事件&#xff1f;量子霸权并未实现&#xff0c;年轻的菲尔兹奖得主质疑日本数学家望月新一对ABC猜想的证明。还有18岁的少年、苦读8年不毕业的女研究生&#xff0c;以及退休软件工程师和抗衰老组织联合创始人&#xff0c;都在…

里程碑式的数学证明,攻破著名Erdős猜想中关键障碍

大数据文摘出品 来源&#xff1a;wired 编译&#xff1a;Canary、Andy 最近&#xff0c;两名数学家解决了一个关于整数相加性质最著名猜想中的第一部分。该猜想由匈牙利传奇数学家Paul Erdős于60多年前提出&#xff0c;一个无限整数序列在何时一定会包含至少有三个等差数的模式…

6174猜想的证明 Python

什么是6174猜想 1955年&#xff0c;卡普耶卡(D.R.Kaprekar)研究了对四位数的一种变换&#xff1a;任给出四位数k0,用它的四个数字由大到小重新排列成一个四位数m,再减去它的反序数rev(m),得出数k1m-rev(m),然后&#xff0c;继续对k1重复上述变换&#xff0c;得数k2.如此进行下去…

程序员的数学课04 万物可数学,经典公式是如何在生活中应用的?

在我们的生活和工作中&#xff0c;有大量的数学应用场景&#xff0c;一些简单的经典公式会在我们的生活中被反复验证、体现。对于经典公式的理解&#xff0c;能增强你的数据 sense&#xff0c;更能帮助你在遇到问题时&#xff0c;迅速找到解决思路。 这一课时我将列举四个脑洞…

评审8年终获发表,数学天才望月新一证明abc猜想,全球只有十几个数学家读懂但争议未消...

晓查 发自 凹非寺 量子位 报道 | 公众号 QbitAI abc猜想&#xff0c;数学界悬而未决的重要猜想&#xff0c;它的证明过程经过8年的同行评审&#xff0c;终于要在期刊上发表了。 论文作者是日本的天才数学家望月新一&#xff0c;他33岁起就在京都大学担任数学教授。 这一次望月新…

C++角谷猜想

题目描述&#xff1a; 请编写一个程序&#xff0c;验证角谷猜想。所谓角谷猜想是&#xff1a;“对于任意大于1的自然数n&#xff0c;若n为奇数&#xff0c;则将n变为3*n1&#xff0c;否则将n变为n的一半。”经过若干次这样的变化&#xff0c;一定会使n变为1。 输入格式&#…

某安网别逆向,一不小心就......

​ 大家好&#xff0c;我是TheWeiJun&#xff0c;欢迎来到我的公众号。在现代互联网中&#xff0c;cookie成为了网站管理的重要工具。某些网站会对cookie进行加密&#xff0c;以加强数据的安全性和保密性。然而&#xff0c;逆向加密算法并不是一件简单的事情。本文将探讨如何逆…

群晖docker实现IPV6访问

概述&#xff1a; 群晖docker默认没有没有开启ipv6&#xff0c;需要修改docker的配置文件。 修改过程&#xff1a; 一、首先确认自己的网络已经支持IPV6&#xff08;需要光猫及路由器支持ipv6&#xff09; 1.登陆www.test-ipv6.com查看是否已经接入IPV6 2.登陆自己的群晖查…

群晖传文件到服务器,文件上传到群晖服务器

文件上传到群晖服务器 内容精选 换一换 监控数据上报功能可以将系统中采集到的监控数据写入到文本文件&#xff0c;并以FTP或SFTP的形式上传到指定的服务器中。使用该功能前&#xff0c;管理员需要在FusionInsight Manager页面进行相关配置。“监控数据上传”默认为不启用&…

群晖硬盘警告修复

群晖硬盘警告修复 为了更好的浏览体验&#xff0c;欢迎光顾勤奋的凯尔森同学个人博客http://www.huerpu.cc:7000 我的群晖每次被我拿出来的时候&#xff0c;由于震动等原因&#xff0c;会时不时出现硬盘警告的提示&#xff0c;从而导致无法使用&#xff0c;现在我们来屏蔽一下…

安装群晖显示服务器忙,云服务器安装群晖

云服务器安装群晖 内容精选 换一换 华为云帮助中心,为用户提供产品简介、价格说明、购买指南、用户指南、API参考、最佳实践、常见问题、视频帮助等技术文档,帮助您快速上手使用华为云服务。 登录Windows操作系统的弹性云服务器时,需使用密码方式登录。因此,用户需先根据创…

群晖php mysql网站博客,群晖建博客详细教程

群晖建博客详细教程 2017-03-01 14:23:01 36点赞 419收藏 44评论 小编注:此篇文章来自即可瓜分10万金币,周边好礼达标就有,邀新任务奖励无上限,点击查看活动详情 群晖NAS功能强大,除了大家都知道的存储文件,代替各种公有云以外,还可以当成一个网站服务器来使用。配合DDN…

群晖web文件服务器docker,群晖docker搭建数据库服务器

群晖docker搭建数据库服务器 内容精选 换一换 本手册基于华为云关系型数据库实践所编写,用于指导您完成相关设置,购买更符合业务的数据库实例。 创建一个外部服务器。外部服务器是存储OBS服务器信息或其他同构集群信息的载体。默认只有系统管理员才可以创建外部服务器,否则需…

群晖系统ftp服务器,群晖对接云服务器ftp

群晖对接云服务器ftp 内容精选 换一换 用户可以在MRS Manager界面上配置监控指标数据对接参数,使集群内各监控指标数据通过FTP或SFTP协议保存到指定的FTP服务器,与第三方系统进行对接。FTP协议未加密数据可能存在安全风险,建议使用SFTP。MRS Manager支持采集当前管理的集群内…