java opencv 实现换脸
最近碰到个项目,要求是实现人脸交换,即如下图所示,将右边汤唯的脸换成左边鹿晗的脸,变成中间的照片,就是人脸交换。
网上一般都是基于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 实现换脸相关推荐
- Atitit Java OpenCV 捕获视频
Atitit Java OpenCV 捕获视频 ,打开一段视频或默认的摄像头 有两种方法,一种是在定义类的时候,一种是用open()方法. 一. 读取视频序列 OpenCV提供了一个简便易用的框架以 ...
- java opencv安装路径_Java搭建opencv开发环境
由本菜13历经4天才搭建的好的Java OpenCv开发环境带来 . PS:不保证过程都是必须的,必要的. 反正我自己成功了... 嘿嘿嘿. 官方网站 SourceForge 点击FilesFiles ...
- Java垂直镜像,Java OpenCV实现图像镜像翻转效果
本文实例为大家分享了Java OpenCV实现图像镜像翻转效果的具体代码,供大家参考,具体内容如下 主要使用OpenCV的flip()方法,可以实现图像的垂直.水平以及同时垂直镜像翻转. flip是C ...
- Java OpenCV之Mat类的概述、常用构造方法、常用函数
Java OpenCV之Mat类 概述 头部信息 数据类型 数据部分 Mat对象的构造方法 Mat() Mat(long addr) Mat(int row,int cols,int type) Ma ...
- idea搭建java openCV环境
idea搭建java openCV环境 Opencv安装包下载链接 1.点击下载 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 2.双击运行 3.生成文件夹 4.复制这两个文件 ...
- Java OpenCV 图像处理34 图形图像 分水岭 watershed
Java OpenCV 图像处理34 图形图像 分水岭 watershed package com.xu.opencv;import org.opencv.core.Core; import org. ...
- Java OpenCV 图像处理30 视频分析和对象跟踪 视频读取
Java OpenCV 图像处理30 视频分析和对象跟踪 视频读取 Java OpenCV-4.0.0 图像处理 视频分析和对象跟踪 视频读取 package com.xu.opencv.video; ...
- Java OpenCV 图像处理32.4 视频分析和对象跟踪 切换背景
Java OpenCV 图像处理32.4 视频分析和对象跟踪 切换背景 方法 含义 解释 bitwise_and "与"操作,即对图像(灰度图像或彩色图像均可)每个像素值进行二进制 ...
- JAVA + OPENCV+bgra四通道转bgr三通道
JAVA + OPENCV 对bgra四通道转bgr三通道图片 由于opencv内部方法进行bgra2bgr的转换存在bug,所以需要人工纠正,重写算法进行操作.参考了python转换算法. 具体JA ...
最新文章
- Sonya and Queries CodeForces - 714C
- python下载网络错误_下载失败,出现“网络错误”+40000
- 电脑连接电视方法详解_电脑如何连网?——校园宽带的连接方法(详解版)
- sqlserver 2014 删除主键约束
- 给 EF Core 查询增加 With NoLock
- 一篇文章教会你使用Python中三种简单的函
- linux 缩小链接库体积,两个奇技淫巧,将 Docker 镜像体积减小 99%
- 证明一下拉普拉斯的《概率分析论》观点
- AMD官宣350亿美元收购赛灵思 赛灵思大涨8.56%
- Spring.NET学习笔记13——AOP的概念(基础篇) Level 200
- mysql多索引结构_MySQL-索引结构详解
- Jquery中AJAX的应用
- javascript中object的运算符操作使用
- Linux Centos firewall 防火墙 开启 80 端口配置案例
- 盘点一下使用高德api踩得坑
- juniper防火墙定义策略生效时间
- Qdata模块-python获取关键词百度指数
- 遥控器,未来世界的入口?
- 小程序基于Taro的Canvas写字效果和背景图水印效果
- jsp+ssm计算机毕业设计中青年健康管理监测系统【附源码】