换脸系列——眼鼻口替换

前言

想着整理一下换脸相关的技术方法,免得以后忘记了,最近脑袋越来越不好使了。应该会包含三个系列: 仅换眼口鼻;换整个面部;3D换脸

先看看2D换脸吧,网上已经有现成的教程了,这里拿过来整理一下,做个记录。

国际惯例,参考博客:

Switching Eds: Face swapping with Python, dlib, and OpenCV

opencv人脸关键点检测模型

这里面一般涉及到脸与脸之间的映射变换矩阵,这里记录一个opencv中的函数findHomography,用于找到多个二维点之间的最优变换矩阵。

流程

整个换脸包含四步:

  • 检测人脸关键点
  • 旋转、缩放、平移第二张图片,使其与第一张图片的人脸对齐
  • 调整第二张图片的色彩平衡,使其与第一张图片对应
  • 将两张图片融合

先加载一些必要的库

import cv2
import numpy as np
import matplotlib.pyplot as plt

检测人脸关键点

使用dlib库检测人脸关键点,不过由于dlib的安装貌似必须有cmake和c++编译器,个人电脑暂时没有,所以使用opencv的人脸关键点检测算法

先去这里下载人脸框检测模型haarcascade_frontalface_alt2.xml,这里下载人脸关键点检测模型LBF.model,然后预加载模型:

cas = cv2.CascadeClassifier('./model/haarcascade_frontalface_alt2.xml')
obj = cv2.face.createFacemarkLBF()
obj.loadModel('./model/lbfmodel.yaml')

检测人脸关键点:

def detect_facepoint(img):img_gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)faces = cas.detectMultiScale(img_gray,2,3,0,(30,30))landmarks = obj.fit(img_gray,faces)assert landmarks[0],'no face detected'if(len(landmarks[1])>1):print('multi face detected,use the first')return faces[0],np.squeeze(landmarks[1][0])

画人脸关键点

def draw_kps(img,face_box,kps):img_show = img.copy()cv2.rectangle(img_show,(face_box[0],face_box[1]),(face_box[0]+face_box[2],face_box[1]+face_box[3]),(0,255,0),3)for i in range(kps.shape[0]):cv2.circle(img_show,(kps[i,0],kps[i,1]),2,(0,255,0),-1)img_show = cv2.cvtColor(img_show,cv2.COLOR_BGR2RGB)return img_show

测试一波

在这里插入图片描述

人脸对齐

有关键点以后,需要将两个关键点的大小形状变换地尽可能接近才能进行换脸,主要涉及到旋转、平移、缩放。假设定义如下变量:旋转R、平移T、缩放S,第一个人脸关键点是qi(i=1⋯68)q_i(i=1\cdots 68)qi(i=168),第二个人脸关键点是pi(i=1⋯68)p_i(i=1\cdots 68)pi(i=168),那么我们需要最小化
∑i=168∥sRpiT+T−qiT∥2\sum_{i=1}^{68} \parallel sRp_i^T+T-q_i^T \parallel ^2 i=168sRpiT+TqiT2
也就是关键点ppp经过旋转、缩放、平移以后,要和关键点qqq相近

以下对齐顺序是将第二个图像与第一个图像对齐

方法1:SVD分解+仿射变换(推荐)

这里原文提到了一个Ordinary Procrustes Analysis的算法,专门用于提取这个旋转矩阵的,用的SVD分解方法:

def transformation_from_points(points1, points2):points1 = points1.copy()points2 = points2.copy()points1 = points1.astype(np.float64)points2 = points2.astype(np.float64)c1 = np.mean(points1, axis=0)c2 = np.mean(points2, axis=0)points1 -= c1points2 -= c2s1 = np.std(points1)s2 = np.std(points2)points1 /= s1points2 /= s2U, S, Vt = np.linalg.svd(np.dot(points1.T , points2))R = (np.dot(U , Vt)).T return np.vstack([np.hstack(((s2 / s1) * R,np.array([c2.T - np.dot((s2 / s1) * R , c1.T)]).T )),np.array([0., 0., 1.])])

得到变换矩阵以后,就可以使用opencvwarpAffine进行仿射变换

def wrap_im(im,M,dshape):output_im = np.zeros(dshape,dtype=im.dtype)cv2.warpAffine(im,M[:2],(dshape[1],dshape[0]),dst=output_im,borderMode=cv2.BORDER_TRANSPARENT,flags=cv2.WARP_INVERSE_MAP)return output_im

两个函数合起来作为一个调用方法

def align_img1(img1,img2,landmarks1,landmarks2):trans_mat = transformation_from_points(landmarks1, landmarks2)img2_align = wrap_im(img2,trans_mat,img1.shape)return img2_align

额外加一个比较好的SVD理论证明

方法2:透视变换(不推荐)

这篇文章提到的一个方法,直接用opencvfindHomography找到变换矩阵,再用warpPerspective做透视变换。函数如下:

def align_img2(img1,img2,landmarks1,landmarks2):trans_mat,mask = cv2.findHomography(landmarks2, landmarks1, cv2.RANSAC,5.0)img2_align = cv2.warpPerspective(img2.copy(),trans_mat,(img1.shape[1],img1.shape[0]))return img2_align

对比

分别调用对比看看

img2_align = align_img1(img1,img2,face_kps1,face_kps2)
plt.figure(figsize=[8,8])
plt.subplot(131)
plt.imshow(cv2.cvtColor(img1.copy(),cv2.COLOR_BGR2RGB))
plt.subplot(132)
plt.imshow(cv2.cvtColor(img2_align.copy(),cv2.COLOR_BGR2RGB))
img2_align2 = align_img2(img1,img2,face_kps1,face_kps2)
plt.subplot(133)
plt.imshow(cv2.cvtColor(img2_align2.copy(),cv2.COLOR_BGR2RGB))

在这里插入图片描述

这么一看,咱们还是用方法1的仿射变换吧,咳咳。透视变换功能过于强大。

获取被替换部分的掩膜

按照参考博客和本博客的目的,只替换眼、鼻、口

LEFT_EYE_POINTS = list(range(42, 48))
RIGHT_EYE_POINTS = list(range(36, 42))
LEFT_BROW_POINTS = list(range(22, 27))
RIGHT_BROW_POINTS = list(range(17, 22))
NOSE_POINTS = list(range(27, 35))
MOUTH_POINTS = list(range(48, 61))
OVERLAY_POINTS = [LEFT_EYE_POINTS + RIGHT_EYE_POINTS + LEFT_BROW_POINTS + RIGHT_BROW_POINTS,NOSE_POINTS + MOUTH_POINTS,
]
FEATHER_AMOUNT = 11def draw_convex_hull(im, points, color):points = cv2.convexHull(points)cv2.fillConvexPoly(im, points, color=color)def get_face_mask(im, landmarks):im = np.zeros(im.shape[:2], dtype=np.float64)#双眼的外接多边形、鼻和嘴的多边形,作为掩膜for group in OVERLAY_POINTS:draw_convex_hull(im,landmarks[group],color=1)im = np.array([im, im, im]).transpose((1, 2, 0))im = (cv2.GaussianBlur(im, (FEATHER_AMOUNT, FEATHER_AMOUNT), 0) > 0) * 1.0im = cv2.GaussianBlur(im, (FEATHER_AMOUNT, FEATHER_AMOUNT), 0)return im

提取完mask,当然需要将mask也按照仿射变换的方法进行校正

mask = get_face_mask(img2, face_kps2)
warped_mask = align_img1(img1, mask,face_kps1,face_kps2)
combined_mask = np.max([get_face_mask(img1, face_kps1), warped_mask],axis=0)

可视化

plt.imshow(np.multiply(img2_align,combined_mask).astype('uint8')[...,::-1])

在这里插入图片描述

颜色校正

直接贴图,肯定会由于颖宝和老干部的肤色不搭,会有非常明显的贴图边缘痕迹。

output_im = img1 * (1.0 - combined_mask) + img2_align * combined_mask

在这里插入图片描述

很明显,mask的边缘部分,有非常明显的颜色差

所以需要矫正一波颜色,在后续博客中,会提供其它颜色校正方法。

COLOUR_CORRECT_BLUR_FRAC = 0.6
LEFT_EYE_POINTS = list(range(42, 48))
RIGHT_EYE_POINTS = list(range(36, 42))def correct_colours(im1, im2, landmarks1):blur_amount = COLOUR_CORRECT_BLUR_FRAC * np.linalg.norm(np.mean(landmarks1[LEFT_EYE_POINTS], axis=0) -np.mean(landmarks1[RIGHT_EYE_POINTS], axis=0))blur_amount = int(blur_amount)if blur_amount % 2 == 0:blur_amount += 1im1_blur = cv2.GaussianBlur(im1, (blur_amount, blur_amount), 0)im2_blur = cv2.GaussianBlur(im2, (blur_amount, blur_amount), 0)# Avoid divide-by-zero errors.im2_blur += (128 * (im2_blur <= 1.0)).astype(im2_blur.dtype)return (im2.astype(np.float64) * im1_blur.astype(np.float64) /im2_blur.astype(np.float64))

矫正完毕以后:

img2_correct = correct_colours(img1,img2_align,face_kps1)
plt.imshow(cv2.cvtColor(img2_correct.copy().astype('uint8'),cv2.COLOR_BGR2RGB))
plt.axis('off')

在这里插入图片描述

再来瞅瞅结果:

output_im = img1 * (1.0 - combined_mask) + img2_correct * combined_mask

在这里插入图片描述

好了,边缘基本没有贴图痕迹了,但是整体效果。。。。。。。。

打扰了~~

后面用其它算法优化

后记

简单的记录一下只替换五官的情况下,整个换脸算法的流程。

博客代码:

链接:https://pan.baidu.com/s/1a-TNuIVslJiOTWgKse58Ag
提取码:kabv

本文已经同步到微信公众号中,公众号与本博客将持续同步更新运动捕捉、机器学习、深度学习、计算机视觉算法,敬请关注
在这里插入图片描述

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

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

相关文章

换脸系列——整脸替换

前言 前面介绍了仅替换五官的方法&#xff0c;这里介绍整张脸的方法。 国际惯例&#xff0c;参考博客&#xff1a; [图形算法]Delaunay三角剖分算法 维诺图&#xff08;Voronoi Diagram&#xff09;分析与实现 Delaunay Triangulation and Voronoi Diagram using OpenCV (…

3D人脸重建——PRNet网络输出的理解

前言 之前有款换脸软件不是叫ZAO么&#xff0c;分析了一下&#xff0c;它的实现原理绝对是3D人脸重建&#xff0c;而非deepfake方法&#xff0c;找了一篇3D重建的论文和源码看看。这里对源码中的部分函数做了自己的理解和改写。 国际惯例&#xff0c;参考博客&#xff1a; 什…

tensorflow官方posenet模型解析

前言 tensorflow官方有个姿态估计项目&#xff0c;这个输入和openpose还有点不一样&#xff0c;这里写个单人情况下的模型输出解析方案。 国际惯例&#xff0c;参考博客&#xff1a; 博客: 使用 TensorFlow.js 在浏览器端上实现实时人体姿势检测 tensorflow中posnet的IOS代…

tensorflow2安装时候的一个dll找不到的错误

电脑环境&#xff1a; vs2015python3.7.6&#xff0c;使用anaconda安装的CUDA 10.1cuDnn 7.6.5tensorflow2.1.0 错误内容 File "C:\Users\zb116\anaconda3\lib\imp.py", line 242, in load_modulereturn load_dynamic(name, filename, file)File "C:\Users\z…

PCA、SVD、ZCA白化理论与实现

简介 在UFLDL中介绍了主成分分析这一块的知识&#xff0c;而且当时学机器学习的时候&#xff0c;老师是将PCA和SVD联系起来将的&#xff0c;同时UFLDL也讲到了使用PCA做数据白化whitening处理&#xff0c;这个词经常在论文里面看到。 国际惯例&#xff0c;参考博客&#xff1…

OpenCV使用Tensorflow2-Keras模型

前言 最近工作上需要在C上快速集成Tensorflow/Keras训练好的模型&#xff0c;做算法验证。首先想到的就是opencv里面的dnn模块了&#xff0c;但是它需要的格式文件比较郁闷&#xff0c;是pb格式的模型&#xff0c;但是keras通常保存的是h5文件&#xff0c;查阅了很多资料&…

3D人脸表情驱动——基于eos库

前言 之前出过三篇换脸的博文&#xff0c;遇到一个问题是表情那一块不好处理&#xff0c;可行方法是直接基于2D人脸关键点做网格变形&#xff0c;强行将表情矫正到目标人脸&#xff0c;还有就是使用PRNet的思想&#xff0c;使用目标人脸的顶点模型配合源人脸的纹理&#xff0c…

3D姿态估计——ThreeDPose项目简单易用的模型解析

前言 之前写过tensorflow官方的posenet模型解析&#xff0c;用起来比较简单&#xff0c;但是缺点是只有2D关键点&#xff0c;本着易用性的原则&#xff0c;当然要再来个简单易用的3D姿态估计。偶然看见了ThreeDPose的项目&#xff0c;感觉很强大的&#xff0c;所以把模型扒下来…

简易的素描图片转换流程与实现

前言 之前经常在网上看到用PS实现真实图片到素描图片的转换&#xff0c;但是流程都大同小异&#xff0c;身为一只程序猿&#xff0c;必须来个一键转化额。 国际惯例&#xff0c;参考博客&#xff1a; Photoshop基础教程&#xff1a;混合模式原理篇 颜色减淡的原理讲解以及应…

一个简单好用的磨皮祛斑算法理论和python实现

前言 最近看了一个磨皮算法祛斑感觉效果不错&#xff0c;效果图看文末就行&#xff0c;个人觉得效果非常不错滴。 国际惯例&#xff0c;参考博客&#xff1a; 磨皮算法的源码&#xff1a;YUCIHighPassSkinSmoothing How To Smooth And Soften Skin With Photoshop 图像算法…

OpenVINO——配置与道路分割案例

前言 最近看到了一个深度学习库OpenVINO&#xff0c;专门用于Intel硬件上部署深度学习模型&#xff0c;其内置了非常非常多使用的预训练模型&#xff0c;比如道路分割、人脸提取、3D姿态估计等等。但是配置和调用有点小恶心&#xff0c;这里以道路分割为例&#xff0c;展示如何…

图像颜色迁移《color transfer between images》

前言 前段时间&#xff0c;在深度学习领域不是有个比较火的方向叫风格迁移的嘛&#xff0c;对于我这种不喜欢深度学习那种不稳定结果的人来说&#xff0c;还是想看看传统图像处理领域有什么类似的技术&#xff0c;发现了一个颜色迁移的算法&#xff0c;很久前的论文了。 国际…

ColorSpace颜色空间简介

前言 如果看过之前的介绍的图像颜色迁移《color transfer between images》和颜色协调模型Color Harmoniztion就会发现&#xff0c;大部分图像处理算法虽然输入输出是RGB像素值&#xff0c;但是中间进行算法处理时很少直接更改RGB值&#xff0c;而是转换到其它空间&#xff0c…

Ogre共享骨骼与两种骨骼驱动方法

前言 最近业务中用到Ogre做基于3D关键点虚拟角色骨骼驱动&#xff0c;但是遇到两个问题&#xff1a; 身体、头、眼睛、衣服等mesh的骨骼是分开的&#xff0c;但是骨骼结构都是一样的&#xff0c;需要设置共享骨骼驱动的时候可以直接修改骨骼旋转量&#xff0c;或者将旋转量存…

仿射变换和透视变换

前言 在前面做换脸的博客中提到了使用仿射变换和透视变换将两张不同的人脸基于关键点进行对齐&#xff0c;保证一张人脸贴到另一张人脸时&#xff0c;大小完全一致&#xff1b;所以有必要理解一下这两个概念的区别&#xff0c;由于以实用性为目的&#xff0c;所以所有的图像算…

obj格式解析

前言 最近处理一些网格渲染的时候&#xff0c;需要解析Obj文件&#xff0c;从Free3D上随便找了个免费的人体obj模型解析测试一波 国际惯例&#xff0c;参考博客&#xff1a; 本文所使用的从Free3D下载的模型 .obj文件格式与.mtl文件格式 详解3D中的obj文件格式 3D中OBJ文…

Flask服务部署与简单内网穿透

前言 最近学习部署的时候&#xff0c;想到深度学习里面通常用的部署方法是flask做服务端&#xff0c;然后使用nginx做负载均衡&#xff0c;貌似也能做内网穿透。不过我不太懂负载均衡&#xff0c;只想利用本地电脑搭建一个简单的服务器&#xff0c;实现外部调用API服务的功能。…

OpenCV学习——轮廓检测

前言 轮廓检测是传统视觉中非常常用的功能&#xff0c;这里简单记录一下opencv中的轮廓检测算法使用方法&#xff0c;至于理论&#xff0c;后续有机会再去细品。 国际惯例&#xff1a; OpenCV官方的轮廓检测教程python版 OpenCV中的二值化方法教程 OpenCV轮廓层级官方文档…

RBF神经网络理论与实现

前言 最近发现有挺多人喜欢径向基函数(Radial Basis Function,RBF)神经网络&#xff0c;其实它就是将RBF作为神经网络层间的一种连接方式而已。这里做一个简单的描述和找了个代码解读。 之前也写过一篇&#xff0c;不过排版不好看&#xff0c;可以戳这里跳转 国际惯例&#x…

基于python和unity交互的卡通角色肢体和表情驱动(深度学习)

前言 最近看到了好多卡通角色的肢体驱动的东东&#xff0c;感觉是时候发挥一下读研时候学的东西了&#xff0c;而且虽然现在不炼丹了&#xff0c;但是还是得保持吃丹的技能。这个项目找了很多很多代码进行测试&#xff0c;最终集成了一个3D姿态估计和人脸关键点提取的代码。 …