Android 基于 dlib 和 opencv 实现换脸(不需要依赖第三方关键点检测)

支持技术分享,转载或复制,请指出文章来源 此博客作者为Jack__0023

感谢(一些理论和关键代码的来源链接)

dlib 模块组来源
变脸部分关键代码的来源

效果如下

将中间红色女性脸(以下简称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
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值