最近碰到个项目,要求是实现人脸交换,即如下图所示,将右边汤唯的脸换成左边鹿晗的脸,变成中间的照片,就是人脸交换。

网上一般都是基于opencv和Dlib来实现,且多为c++或python语言,或app,我要用java语言来实现,且为web版本,于是就开始了漫长的资料查找筛选和代码理解、修改过程。

这篇文章主要参考[http://blog.csdn.net/wangxing233/article/details/51771265],作者给出在文章中给出了c++的源码,以及具体步骤和讲解,这篇文章改为java版本并提供源码[https://github.com/ttshen1029/faceswap],主要参考了c++版的代码、java opencv的api,下面是具体步骤。

1. 人脸关键点检测

由于没有找到java版的dlib库,就用百度人脸检测替代了,百度人脸检测接口返回人脸关键点72个,不影响后面的凸包计算和三角剖分。

2. 计算凸包

// 计算凸包
Mat imgCV1Warped = imgCV2.clone();
imgCV1.convertTo(imgCV1, CvType.CV_32F);
imgCV1Warped.convertTo(imgCV1Warped, CvType.CV_32F);MatOfInt hullIndex = new MatOfInt();
Imgproc.convexHull(new MatOfPoint(points2), hullIndex, true);
int[] hullIndexArray = hullIndex.toArray();
int hullIndexLen = hullIndexArray.length;
List<Point> hull1 = new LinkedList<>();
List<Point> hull2 = new LinkedList<>();// 保存组成凸包的关键点
for (int i = 0; i < hullIndexLen; i++) {hull1.add(points1[hullIndexArray[i]]);hull2.add(points2[hullIndexArray[i]]);
}


注:该图来自原作

3. 三角剖分和变换仿射

就有如上图中间图片所示(是否可以直接提取凸包包围的整个区域,然后变换角度仿射到目标图片上,需要再研究)。这个部分在调试的过程中遇到了挺多的坑,就分步解析记录下,以免自己忘记。

// 获取Delaunay三角形的列表
public static List<Correspondens> delaunayTriangulation(List<Point> hull, Rect rect) {Subdiv2D subdiv = new Subdiv2D(rect);for(int it = 0; it < hull.size(); it++) {subdiv.insert(hull.get(it));}MatOfFloat6 triangles = new MatOfFloat6();subdiv.getTriangleList(triangles);int cnt = triangles.rows();float buff[] = new float[cnt*6];triangles.get(0, 0, buff);List<Correspondens> delaunayTri = new LinkedList<>();for(int i = 0; i < cnt; ++i) {List<Point> points = new LinkedList<>();points.add(new Point(buff[6*i+0], buff[6*i+1]));points.add(new Point(buff[6*i+2], buff[6*i+3]));points.add(new Point(buff[6*i+4], buff[6*i+5]));Correspondens ind = new Correspondens();if (rect.contains(points.get(0)) && rect.contains(points.get(1)) && rect.contains(points.get(2))) {int count = 0;for (int j = 0; j < 3; j++) {for (int k = 0; k < hull.size(); k++) {if (Math.abs(points.get(j).x - hull.get(k).x) < 1.0 && Math.abs(points.get(j).y - hull.get(k).y) < 1.0) {ind.add(k);count++;}}}if (count == 3)delaunayTri.add(ind);}}return delaunayTri;}
public static Mat warpTriangle(Mat img1, Mat img2, MatOfPoint t1, MatOfPoint t2) {Rect r1 = Imgproc.boundingRect(t1);Rect r2 = Imgproc.boundingRect(t2);Point[] t1Points = t1.toArray();Point[] t2Points = t2.toArray();List<Point> t1Rect = new LinkedList<>();List<Point> t2Rect = new LinkedList<>();List<Point> t2RectInt = new LinkedList<>();for (int i = 0; i < 3; i++) {t1Rect.add(new Point(t1Points[i].x - r1.x, t1Points[i].y - r1.y));t2Rect.add(new Point(t2Points[i].x - r2.x, t2Points[i].y - r2.y));t2RectInt.add(new Point(t2Points[i].x - r2.x, t2Points[i].y - r2.y));}// mask 包含目标图片三个凸点的黑色矩形Mat mask = Mat.zeros(r2.height, r2.width, CvType.CV_32FC3);Imgproc.fillConvexPoly(mask, list2MP(t2RectInt), new Scalar(1.0, 1.0, 1.0), 16, 0);// [图1]--   maskMat img1Rect = new Mat(); img1.submat(r1).copyTo(img1Rect);// img2Rect 原始图片适应mask大小并调整位置的图片Mat img2Rect = Mat.zeros(r2.height, r2.width, img1Rect.type());// [图2]--   img2Rectimg2Rect = applyAffineTransform(img2Rect, img1Rect, t1Rect, t2Rect);// [图3]--   img2RectCore.multiply(img2Rect, mask, img2Rect); // img2Rect在mask三个点之间的图片// [图4]--   img2RectMat dst = new Mat();Core.subtract(mask, new Scalar(1.0, 1.0, 1.0), dst);Core.multiply(img2.submat(r2), dst, img2.submat(r2));Core.absdiff(img2.submat(r2), img2Rect, img2.submat(r2));// [图5]--   img2return img2;}


图1

图2

图3

图4

图5
所有的三角形区域都如此处理之后,整张脸就扣到目标图上了。

4. 无缝融合

这个步骤很简单,就一个方法:

Photo.seamlessClone(imgCV1Warped, imgCV2, mask, center, output, Photo.NORMAL_CLONE);

5.备注

1、必须有下面代码来保证加载opencv库:

static {System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}

2、然而,java web项目下直接这么写不行会报错找不到库。于是我是这样处理的:
添加User Libraries,并添加到build path:

然后使用@PostConstruct语法糖,在Spring容器启动的时候去执行加载opencv动态库的操作【参考文章:http://blog.csdn.net/chenxiao_ji/article/details/52807561】

private void addDirToPath(String s) {try {  //获取系统path变量对象  Field field=ClassLoader.class.getDeclaredField("sys_paths");  //设置此变量对象可访问  field.setAccessible(true);  //获取此变量对象的值  String[] path=(String[])field.get(null);  //创建字符串数组,在原来的数组长度上增加一个,用于存放增加的目录  String[] tem=new String[path.length+1];  //将原来的path变量复制到tem中  System.arraycopy(path,0,tem,0,path.length);  //将增加的目录存入新的变量数组中  tem[path.length]=s;  //将增加目录后的数组赋给path变量对象  field.set(null,tem);  } catch (Exception e) {  e.printStackTrace();  }
}@PostConstruct
public void init(){  //获取存放dll文件的绝对路径  String path = System.getProperty("webapp.test.root");  System.out.println("######## 路径为 : " + path + " ########");  //将此目录添加到系统环境变量中  addDirToPath(path); //加载相应的dll文件,注意要将'\'替换为'/'  System.load(path.replaceAll("\\\\","/")+"/opencv_java330.dll");  System.loadLibrary(Core.NATIVE_LIBRARY_NAME);  System.out.println("######## Opencv加载完毕 ########");
}  

3.在运行过程中发现还是报找不到类的错误,就把opencv.jar解压了直接放在项目目录中来解决这个问题。

java opencv 实现换脸相关推荐

  1. Atitit Java OpenCV 捕获视频

    Atitit Java  OpenCV 捕获视频 ,打开一段视频或默认的摄像头 有两种方法,一种是在定义类的时候,一种是用open()方法. 一. 读取视频序列 OpenCV提供了一个简便易用的框架以 ...

  2. java opencv安装路径_Java搭建opencv开发环境

    由本菜13历经4天才搭建的好的Java OpenCv开发环境带来 . PS:不保证过程都是必须的,必要的. 反正我自己成功了... 嘿嘿嘿. 官方网站 SourceForge 点击FilesFiles ...

  3. Java垂直镜像,Java OpenCV实现图像镜像翻转效果

    本文实例为大家分享了Java OpenCV实现图像镜像翻转效果的具体代码,供大家参考,具体内容如下 主要使用OpenCV的flip()方法,可以实现图像的垂直.水平以及同时垂直镜像翻转. flip是C ...

  4. Java OpenCV之Mat类的概述、常用构造方法、常用函数

    Java OpenCV之Mat类 概述 头部信息 数据类型 数据部分 Mat对象的构造方法 Mat() Mat(long addr) Mat(int row,int cols,int type) Ma ...

  5. idea搭建java openCV环境

    idea搭建java openCV环境 Opencv安装包下载链接 1.点击下载 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 2.双击运行 3.生成文件夹 4.复制这两个文件 ...

  6. Java OpenCV 图像处理34 图形图像 分水岭 watershed

    Java OpenCV 图像处理34 图形图像 分水岭 watershed package com.xu.opencv;import org.opencv.core.Core; import org. ...

  7. Java OpenCV 图像处理30 视频分析和对象跟踪 视频读取

    Java OpenCV 图像处理30 视频分析和对象跟踪 视频读取 Java OpenCV-4.0.0 图像处理 视频分析和对象跟踪 视频读取 package com.xu.opencv.video; ...

  8. Java OpenCV 图像处理32.4 视频分析和对象跟踪 切换背景

    Java OpenCV 图像处理32.4 视频分析和对象跟踪 切换背景 方法 含义 解释 bitwise_and "与"操作,即对图像(灰度图像或彩色图像均可)每个像素值进行二进制 ...

  9. JAVA + OPENCV+bgra四通道转bgr三通道

    JAVA + OPENCV 对bgra四通道转bgr三通道图片 由于opencv内部方法进行bgra2bgr的转换存在bug,所以需要人工纠正,重写算法进行操作.参考了python转换算法. 具体JA ...

最新文章

  1. Sonya and Queries CodeForces - 714C
  2. python下载网络错误_下载失败,出现“网络错误”+40000
  3. 电脑连接电视方法详解_电脑如何连网?——校园宽带的连接方法(详解版)
  4. sqlserver 2014 删除主键约束
  5. 给 EF Core 查询增加 With NoLock
  6. 一篇文章教会你使用Python中三种简单的函
  7. linux 缩小链接库体积,两个奇技淫巧,将 Docker 镜像体积减小 99%
  8. 证明一下拉普拉斯的《概率分析论》观点
  9. AMD官宣350亿美元收购赛灵思 赛灵思大涨8.56%
  10. Spring.NET学习笔记13——AOP的概念(基础篇) Level 200
  11. mysql多索引结构_MySQL-索引结构详解
  12. Jquery中AJAX的应用
  13. javascript中object的运算符操作使用
  14. Linux Centos firewall 防火墙 开启 80 端口配置案例
  15. 盘点一下使用高德api踩得坑
  16. juniper防火墙定义策略生效时间
  17. Qdata模块-python获取关键词百度指数
  18. 遥控器,未来世界的入口?
  19. 小程序基于Taro的Canvas写字效果和背景图水印效果
  20. jsp+ssm计算机毕业设计中青年健康管理监测系统【附源码】

热门文章

  1. 最短哈密顿环 退火_【CH0103】最短哈密顿路径
  2. Elasticsearch搜索引擎一些参数含义和用法
  3. C语言获取键盘方向键的键值
  4. RPA智能客服机器人,电商的好伙伴
  5. python+selenium设置chrome代理的方式
  6. 冶金物理化学复习 --- 湿法分离提纯过程
  7. 广告过滤算法实现及优化
  8. 中国人保为嘉德奥通承保产品责任险,为消费者保驾护航!
  9. CPU架构解析:ARM和x86大比拼
  10. html复习第七天 京东首页布局完成