#一 什么是人脸交换。
如下图所示,将右边汤唯的脸换成左边鹿晗的脸,就变成啦中间的照片。这就是人脸交换。这个效果通过PS也可以实现,不过这里是完全自动的实现,还是很赞的。这篇文章主要参考[1],作者给出在文章中给出啦代码,不过和上篇人脸合成的一样,给出的代码不是完整的。我这里给出完整的代码:https://github.com/iamwx/faceSwap


注:鹿晗和汤唯的图片来自网络

#二 具体步骤

主要分为两个步骤:人脸对齐(face aligment)和无缝融合(Seamless Cloning,可能翻译的不准确)。其中人脸对齐又分为人脸关键点检测(face landmark detection),计算凸包(convex hull),Delaunay三角剖分(delaunay trangulation), 仿射变换(affine warp)。下面来一点点说。
##1 人脸关键点检测
首先要检测出两张图片上的人脸关键点。直接使用dlib的检测函数,先检测人脸,再检测人脸关键点。

void faceLandmarkDetection(dlib::array2d<unsigned char>& img, shape_predictor sp, std::vector<Point2f>& landmark)
{dlib::frontal_face_detector detector = get_frontal_face_detector();//dlib::pyramid_up(img);std::vector<dlib::rectangle> dets = detector(img);//cout << "Number of faces detected: " << dets.size() << endl;full_object_detection shape = sp(img, dets[0]);//image_window win;//win.clear_overlay();//win.set_image(img);//win.add_overlay(render_face_detections(shape));for (int i = 0; i < shape.num_parts(); ++i){float x=shape.part(i).x();float y=shape.part(i).y(); landmark.push_back(Point2f(x,y));       }}


注:该图来自原作

##2 . 计算凸包
如上图所示一共检测出68个关键点。然而我们只需要人脸边缘的哪些关键点。所以我们先计算这68个关键点的凸包。关于凸包的概念可以参考[2]。

 std::vector<Point2f> hull1;std::vector<Point2f> hull2;std::vector<int> hullIndex;//保存组成凸包的关键点的下标索引。cv::convexHull(points2, hullIndex, false, false);//计算凸包//保存组成凸包的关键点。for(int i = 0; i < hullIndex.size(); i++){hull1.push_back(points1[hullIndex[i]]);hull2.push_back(points2[hullIndex[i]]);}

计算得到的凸包是有那些处在边缘的点组成的。如上图最左边显示。

##3. Delaunay 三角剖份 和 仿射变换

有啦那些脸部边缘的关键点之后,我们对这些关键点进行三角剖份,如上图中间所示。具体的可以参考我的另外一篇博客基于opencv+Dlib的面部合成(Face Morph)。
然后我们在将这左边脸上的这一个个小三角形通过仿射变换投影到右边的脸上,就得到啦右边那副图片。

for(size_t i=0;i<delaunayTri.size();++i){std::vector<Point2f> t1,t2;//存放三角形的顶点correspondens corpd=delaunayTri[i];for(size_t j=0;j<3;++j){t1.push_back(hull1[corpd.index[j]]);t2.push_back(hull2[corpd.index[j]]);}warpTriangle(imgCV1,imgCV1Warped,t1,t2); //进行仿射变换           }
void warpTriangle(Mat &img1, Mat &img2, std::vector<Point2f> &t1, std::vector<Point2f> &t2)
{Rect r1 = boundingRect(t1);Rect r2 = boundingRect(t2);// Offset points by left top corner of the respective rectanglesstd::vector<Point2f> t1Rect, t2Rect;std::vector<Point> t2RectInt;for(int i = 0; i < 3; i++){t1Rect.push_back( Point2f( t1[i].x - r1.x, t1[i].y -  r1.y) );t2Rect.push_back( Point2f( t2[i].x - r2.x, t2[i].y - r2.y) );t2RectInt.push_back( Point(t2[i].x - r2.x, t2[i].y - r2.y) ); // for fillConvexPoly}// Get mask by filling triangleMat mask = Mat::zeros(r2.height, r2.width, CV_32FC3);fillConvexPoly(mask, t2RectInt, Scalar(1.0, 1.0, 1.0), 16, 0);// Apply warpImage to small rectangular patchesMat img1Rect;img1(r1).copyTo(img1Rect);Mat img2Rect = Mat::zeros(r2.height, r2.width, img1Rect.type());applyAffineTransform(img2Rect, img1Rect, t1Rect, t2Rect);multiply(img2Rect,mask, img2Rect);multiply(img2(r2), Scalar(1.0,1.0,1.0) - mask, img2(r2));img2(r2) = img2(r2) + img2Rect;    }void applyAffineTransform(Mat &warpImage, Mat &src, std::vector<Point2f> &srcTri, std::vector<Point2f> &dstTri)
{// Given a pair of triangles, find the affine transform.Mat warpMat = getAffineTransform( srcTri, dstTri );// Apply the Affine Transform just found to the src imagewarpAffine( src, warpImage, warpMat, warpImage.size(), cv::INTER_LINEAR, BORDER_REFLECT_101);
}


注:该图来自原作
##4. 无缝融合

可以从上图最左边看出来,这样直接投影过去的话非常生硬。所以还需要把投影过去的图片src和原来的图片dst进行融合。
这里需要先计算一个脸部的mask,如上图3所示。

 //calculate maskstd::vector<Point> hull8U;for(int i=0; i< hull2.size();++i){Point pt(hull2[i].x,hull2[i].y);hull8U.push_back(pt);}Mat mask = Mat::zeros(imgCV2.rows,imgCV2.cols,imgCV2.depth());fillConvexPoly(mask, &hull8U[0], hull8U.size(), Scalar(255,255,255));

之后再直接调用opencv的seamlessClone函数进行融合就可以啦。使用seamlessClone这个函数需要包含头文件

#include<opencv2/photo.hpp>

seamlessClone的具体定义可以参考opencv的官方文档[3]。在本文中的使用如下:

Rect r = boundingRect(hull2);Point center = (r.tl() +r.br()) / 2;Mat output;imgCV1Warped.convertTo(imgCV1Warped, CV_8UC3);seamlessClone(imgCV1Warped,imgCV2,mask,center,output,NORMAL_CLONE);

经过这些操作之后就得到了最终的图片。

欢迎关注个人公众号!

微信交流群


参考资料:

[1] Face Swap using OpenCV ( C++ / Python )
[2] 凸包_百度百科
[3] seamlessClone opencv documents

基于opencv和Dlib的人脸交换(face swap)相关推荐

  1. 基于OpenCV与Dlib的行人计数开源实现

    基于OpenCV与Dlib的行人计数开源实现 PyImageSearch昨天发布的行人计数的Blog,详述了使用OpenCV和Dlib库中的检测和跟踪算法如何完成该功能.原网址开源代码需要F-Q才能下 ...

  2. 基于Python,dlib实现人脸关键点检测

    @代码实现及安装过程 基于Python,dlib实现人脸关键点检测 dilb 在做人脸检测人脸识别方面用到比较多的.face_recognition就是基于dlib实现的. 这篇文章将使用Python ...

  3. python读取视频流做人脸识别_基于OpenCV和Keras实现人脸识别系列——二、使用OpenCV通过摄像头捕获实时视频并探测人脸、准备人脸数据...

    基于OpenCV和Keras实现人脸识别系列手记: 项目完整代码参见Github仓库. 本篇是上面这一系列手记的第二篇. 在Opencv初接触,图片的基本操作这篇手记中,我介绍了一些图片的基本操作,而 ...

  4. python 视频人脸替换_Python基于OpenCV实现视频的人脸检测

    本文实例为大家分享了基于OpenCV实现视频的人脸检测具体代码,供大家参考,具体内容如下 前提条件 1.摄像头 2.已安装Python和OpenCV3 代码 import cv2 import sys ...

  5. python视频人脸检测_Python基于OpenCV实现视频的人脸检测

    本文实例为大家分享了基于OpenCV实现视频的人脸检测具体代码,供大家参考,具体内容如下 前提条件 1.摄像头 2.已安装Python和OpenCV3 代码 import cv2 import sys ...

  6. 基于opencv和pillow实现人脸识别系统(附demo)

    更多python教程请到友情连接: 菜鸟教程https://www.piaodoo.com 初中毕业读什么技校 http://cntkd.net 茂名一技http://www.enechn.com p ...

  7. 基于OpenCV的视频处理 - 人脸检测

    一个不知名大学生,江湖人称菜狗 original author: jacky Li Email : 3435673055@qq.com  Time of completion:2023.2.7 Las ...

  8. python人脸识别opencv_Python基于Opencv来快速实现人脸识别过程详解(完整版)

    前言 随着人工智能的日益火热,计算机视觉领域发展迅速,尤其在人脸识别或物体检测方向更为广泛,今天就为大家带来最基础的人脸识别基础,从一个个函数开始走进这个奥妙的世界. 首先看一下本实验需要的数据集,为 ...

  9. php配置辨别图片形式,基于OpenCV的PHP图像人脸辨别技术(转载)

    当前位置:我的异常网» 图形/图像 » 基于OpenCV的PHP图像人脸辨别技术(转载) 基于OpenCV的PHP图像人脸辨别技术(转载) www.myexceptions.net  网友分享于:20 ...

最新文章

  1. vue vue-router vuex element-ui axios 的学习笔记(七)完善登录注册
  2. 用贝叶斯来看看抛硬币的概率
  3. 计算机能不能直接识别汇编语言程序,计算机能直接识别执行用汇编语言编写的程序吗...
  4. 北信源管理网页卸载密码_Homebrew: 一行代码实现mac软件管理
  5. 怎样让公式编号不从1开始
  6. java 启动xms_关于java:启动JVM时-Xms和-Xmx参数是什么?
  7. 【转】flex中的labelFunction(combox和dataGrid)
  8. 封装Selenium2Library
  9. 中兴软件笔试 c语言,【中兴通讯员工笔试试题及答案】 - 面试网
  10. H264码流处理详解
  11. 2021年全球起酥油收入大约4171.6百万美元,预计2028年达到5052.7百万美元,2022至2028期间,年复合增长率CAGR为 2.8%
  12. PHP file_get_contents函数详解
  13. IntelliJ IDEA 插件库设置
  14. 开源的网络服务框架:Apache Etch 1.4.0 发布
  15. Windows下mysql数据库的下载、安装、使用(详细)(有后续)
  16. MYSQL----innoDB存储引擎
  17. 孙陶然:企业的愿景、使命和价值观
  18. 惠普战66prog2拆机_惠普战66二代内部做工怎么样?全新惠普战66二代拆机图解评测(含视频教程)...
  19. mapi java_使用 MAPI 实现邮件发送
  20. Mysql第五天 索引

热门文章

  1. 杭电OJ——1290 献给杭电五十周年校庆的礼物
  2. url中传递中文参数时的转码与解码
  3. 【工程数学】笔记2:数学物理方程
  4. 怎样消除公司的信息孤岛
  5. html id claa 命名,Claabiw教师系列.doc
  6. python跑模型是什么意思_django模型是什么意思
  7. 国外程序员整理的 PHP 资源大全
  8. 机器学习数学笔记|大数定理中心极限定理矩估计
  9. C语言大数相乘(整形)
  10. 欢乐狼人服务器维护,欢乐狼人杀6人局怎么玩 欢乐狼人杀6人玩法攻略 如何玩欢乐狼人杀...