支持技术分享,转载或复制,请指出文章来源 此博客作者为Jack__0023
感谢(一些理论和关键代码的来源链接)
效果如下
将中间红色女性脸(以下简称C脸)换到第一张图的男性图片(以下简称D脸),换脸结果就是第三张图片(以下简称E脸)

1、简介
简单总结,一共 四个 步骤
这些理论掺杂了我个人看法,你可以去网上看对应的理论也是可以的
1-1、我使用 dlib 检测模块检测出 人脸的 68个关键点(这也就意味着我不用以后去担心使用第三方,他会不会收费的问题),
1-2、然后通过这68个关键点计算凸包并且保存凸包点(这一块很重要,没有这一块,就算执行了1-3和1-4步骤,你也会发现换脸的结果容易变成一些很糟糕的结果,例如两张半脸在一起);
1-3、然后在进行三角剖分和仿射变换(这块是将C脸切分成一个个三角形的然后通过关键点坐标覆盖到D脸上);
1-4、最后一步就是 无缝融合(这一步的作用是进行将仿射变换的脸与周围颜色的融合,避免看起来觉得很怪异不真实)。
2、注意事项(图片分辨率)
对于换脸的图片分辨率,你最好选取比较大一点的,并且双方的分辨率最好是相近的,目的是避免下采样效果严重,不然到了 三角剖分和仿射变换 的阶段,你会发现有一些小黑线,具体可以是例如 (1080*1920)这样的尺寸。
3、准备工具
3-1、Opencv 环境
如果没有安装的过的请参考我这篇博客,里面有介绍 Opencv 人实现人脸识别检测
3-1、dlib 模块组
这个我找了蛮久的,if 你觉得我写的不错,你积分又充足(只要两分),你点击这里下载,else 情况,你可以去我推荐的github里面dlib那个连接里面下载,一样的东西来的。
还需要 文件 shape_predictor_68_face_landmarks.dat,你可以dlib官网下载,这个是 68 个人脸关键点的检查文件
3、代码区(这次直接上代码吧,不分开了)
这里面的代码是我另外开的测试项目,所以包含了很多我的猜想和测试内容,比较乱,后面我如果有时间会整理一下这块然后再发上来,一些类缺失import 是被我删除了,但是是个性化类,你们重写一下即可。
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import com.tzutalin.dlib.Constants;
import com.tzutalin.dlib.FaceDet;
import com.tzutalin.dlib.VisionDetRet;
import org.opencv.android.OpenCVLoader;
import org.opencv.android.Utils;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfFloat6;
import org.opencv.core.MatOfInt;
import org.opencv.core.MatOfPoint;
import org.opencv.core.MatOfPoint2f;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.imgproc.Subdiv2D;
import org.opencv.photo.Photo;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class FaceOffActivity extends Activity {
private final String TAG = "yaoxumin33";
private FaceDet mFaceDet;
List<VisionDetRet> results;
private Paint mFaceLandmardkPaint;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MgThread.exceute(new Runnable() {
@Override
public void run() {
FileUtils.delAllFile(FileConts.PATH_CUT_IMAGE);
FileUtils.exitOrCreatePath(FileConts.PATH_CUT_IMAGE);
}
});
}
@SuppressLint("ResourceType")
@Override
public void onResume() {
super.onResume();
if (!OpenCVLoader.initDebug()) {
Log.e("yaoxumin33", "OpenCV init error");
} else {
Log.d("yaoxumin33", "OpenCV library found inside package. Using it!");
}
initSetting();
//读取第一张图片资源,并获取人脸关键点
Resources r = getApplicationContext().getResources();
InputStream is = r.openRawResource(R.drawable.test8);
BitmapDrawable bmpDraw = new BitmapDrawable(is);
Bitmap bitmap = bmpDraw.getBitmap();
//获取人脸关键点,大概应该是68个
List<Point> points1 = checkFace(bitmap);
//读取第二张图片资源,获取人脸关键点
is = r.openRawResource(R.drawable.test7);
bmpDraw = new BitmapDrawable(is);
Bitmap bitmap2 = bmpDraw.getBitmap();
//获取人脸关键点
List<Point> points2 = checkFace(bitmap2);
//转换成 Mat 方便计算
Mat mat1 = new Mat();
Utils.bitmapToMat(bitmap, mat1, true);
Mat mat2 = new Mat();
Utils.bitmapToMat(bitmap2, mat2, true);
Imgproc.cvtColor(mat1, mat1, Imgproc.COLOR_RGBA2BGR);
Imgproc.cvtColor(mat2, mat2, Imgproc.COLOR_RGBA2BGR);
Log.d("yaoxumin33", "CV_8UC3 : " + CvType.CV_8UC3 + ",CvType.CV_8UC1 : " + CvType.CV_8UC1);
Log.d("yaoxumin33", "mat1 : " + mat1.type());
// Imgproc.GaussianBlur(mat1, mat1, new Size(3, 3), 0, 0);
// Imgproc.bilateralFilter(mat1, mat3, 3, 50, 50);
// Imgproc.GaussianBlur(mat3, mat3, new Size(3, 3), 0, 0);
/*Mat mat3 = beautiful(mat1);
Mat dest = new Mat(new Size(mat3.cols(), mat1.rows()), mat1.type());
Mat temp2 = dest.colRange(0, dest.cols());
mat3.copyTo(temp2);
String filename = FileConts.PATH_CUT_IMAGE + "dest.jpg";
Imgcodecs.imwrite(filename, dest);
Log.d("yaoxumin33", "dest");*/
/*filename = FileConts.PATH_CUT_IMAGE + "done-1.jpg";
Imgcodecs.imwrite(filename, brightness(dest, 1.0f, 98));
Log.d("yaoxu

&spm=1001.2101.3001.5002&articleId=103956608&d=1&t=3&u=c2b6fdf87a504e21ac3c6637d89be210)
2429

被折叠的 条评论
为什么被折叠?



