融合流程

  1. 检测面部标记
  2. 旋转、缩放和转换第二张图像,使之与第一张图像相适应
  3. 调整第二张图像的色彩平衡,使之与第一个相匹配
  4. 把第二张图像的特性混合在第一张图像中

实现流程

找到人脸矩阵

使用dlib提取面部标记

用Dlib实现了论文One Millisecond Face Alignment with an Ensemble of Regression Trees中的算法

( http://www.csc.kth.se/~vahidk/papers/KazemiCVPR14.pdf ,作者为Vahid Kazemi 和Josephine Sullivan) 。
算法本身非常复杂,但dlib接口使用起来非常简单

人脸矩阵是由68个像素点组成,根据对应的像素的对应相应的五官
1-16代码脸型
17-21 22-26 分别代表眉毛
27-35代表鼻子
36-41 42-47 代码眼睛
48-68 带表嘴巴
 get_landmarks()函数将一个图像转化成numpy数组,并返回一个68 x2元素矩阵,输入图像的每个特征点对应每行的一个x,y坐标。

特征提取器(predictor)要一个粗糙的边界框作为算法输入,由传统的能返回一个矩形列表的人脸检测器(detector)提供,其每个矩形列表在图像中对应一个脸。
为了构建特征提取器,预训练模型必不可少,相关模型可从dlib sourceforge库下载
( http://sourceforge.net/projects/dclib/files/dlib/v18.10/shape_predictor_68_face_landmarks.dat.bz2 )。

调整脸部图像对齐

原理

 现在我们已经有了两个标记矩阵,每行有一组坐标对应一个特定的面部特征(如第30行给出的鼻子的坐标)。我们现在要搞清楚如何旋转、翻译和规模化第一个向量,使它们尽可能适合第二个向量的点。想法是,可以用相同的变换在第一个图像上覆盖第二个图像。

实现步骤

代码分别实现了下面几步:

1.将输入矩阵转换为浮点数。这是之后步骤的必要条件。
2.每一个点集减去它的矩心。一旦为这两个新的点集找到了一个最佳的缩放和旋转方法,这两个矩心c1和c2就可以用来找到完整的解决方案。
3.同样,每一个点集除以它的标准偏差。这消除了问题的组件缩放偏差。
4.使用Singular Value Decomposition计算旋转部分。可以在维基百科上看到关于解决正交普氏问题的细节( https://en.wikipedia.org/wiki/Orthogonal_Procrustes_problem )。

5.利用透视变换矩阵( https://en.wikipedia.org/wiki/Transformation_matrix#Affine_transformations )返回完整的转化。

源代码

#include <string.h>
#include <jni.h>
#include <android/log.h>
#include <string>
#include <stdio.h>
#include <vector>
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>

#include <dlib/image_processing/frontal_face_detector.h>
#include <dlib/image_processing/render_face_detections.h>
#include <dlib/image_loader/load_image.h>
#include <dlib/image_processing.h>
#include <dlib/opencv/cv_image.h>
#include <dlib/gui_widgets.h>
#include <dlib/image_io.h>
#include <dlib/opencv.h>
#include <iostream>

using namespace dlib;
using namespace std;
using namespace cv;

#define LOG_TAG "People_Det-JNI"
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)

/*
#ifdef __cplusplus
extern "C" {
#endif
*/

static dlib::shape_predictor msp;
static frontal_face_detector detector;
static dlib::full_object_detection FACE_MARK1,FACE_MARK2;
static Mat wrap_dst, src_img2, mask;

dlib::full_object_detection detectface(dlib::cv_image<dlib::bgr_pixel> mat)
{
dlib::full_object_detection mark;
std::vector<dlib::rectangle> mRets;
mRets = detector(mat);
if(mRets.size() > 0)
mark = msp(mat, mRets[0]);
return mark;
}

jboolean facesRotate_UseData(JNIEnv* env, jobject thiz,
jintArray face1,jint w1,jint h1,
jintArray face2,jint w2,jint h2)
{
jint *cbuf1;
jint *cbuf2;
cbuf1 = env->GetIntArrayElements(face1, JNI_FALSE);
cbuf2 = env->GetIntArrayElements(face2, JNI_FALSE);
if(cbuf1 == NULL || cbuf1 == NULL){
return JNI_FALSE;
}

Mat src_img(h1, w1, CV_8UC4, (unsigned char*)cbuf1);
Mat src_img1(h2, w2, CV_8UC4, (unsigned char*)cbuf2);
cvtColor(src_img,src_img,CV_RGBA2RGB);
cvtColor(src_img1,src_img1,CV_RGBA2RGB);

if(!src_img.data || !src_img1.data)
LOGD("data error");

dlib::cv_image<dlib::bgr_pixel> img(src_img);
dlib::cv_image<dlib::bgr_pixel> img1(src_img1);
FACE_MARK1 = detectface(img);
FACE_MARK2 = detectface(img1);

std::vector<cv::Point2f> srcTri;
std::vector<cv::Point2f> dstTri;

for(int i = 0;i < 68;i++) {
srcTri.push_back(cv::Point2f(FACE_MARK1.part(i).x(),FACE_MARK1.part(i).y()));
dstTri.push_back(cv::Point2f(FACE_MARK2.part(i).x(),FACE_MARK2.part(i).y()));
}
cv::Mat warp_mat;
warp_mat = cv::findHomography(srcTri,dstTri,0);
//the faceLine's size is 68,including all face data,such as:
//the index 0 - 16:jaw data
//the index 17 - 68:face data
//the index 17 - 21:right brow data
//the index 22 - 26:left brow data
//the index 27 - 34:nose data
//the index 36 - 41:right eye data
//the index 42 - 47:left eye data
//the index 48 - 60:mouth data

src_img2 = src_img1;
cv::warpPerspective(src_img,wrap_dst,warp_mat,src_img1.size());
dlib::cv_image<dlib::bgr_pixel> img3(wrap_dst);
FACE_MARK1 = detectface(img3);
Mat new_img = Mat::zeros(src_img1.size(),CV_8UC3);
std::vector<image_window::overlay_line> faceLine;
faceLine = render_face_detections(FACE_MARK1);
for(int i = 17 ;i < 61; i++){
cv::Point p1(faceLine[i].p1.x(),faceLine[i].p1.y());
cv::Point p2(faceLine[i].p2.x(),faceLine[i].p2.y());
cv::line(new_img,p1,p2,cv::Scalar(255,255,255),1,cv::LINE_8);
}
imwrite("/data/data/com.example.faceswapdemo/files/newimg.png",new_img);
int blur_amount = faceLine[41].p1.x() - faceLine[47].p1.x();
int blur_mask = blur_amount;
    if (blur_mask % 2 == 0) {
    blur_mask += 1;

}
    LOGD("blur_mask%d-------",blur_mask);
GaussianBlur(new_img,mask,Size(blur_mask,blur_mask),0,0);

GaussianBlur(mask,mask,Size(blur_mask,blur_mask),0,0);
// FileStorage fs("/data/data/com.example.faceswapdemo/files/face.xml",FileStorage::WRITE);
// fs << "mask" << mask;
blur_amount = blur_amount*3/5;
    if (blur_amount % 2 == 0) {
        blur_amount += 1;
    }
    LOGD("blur_amount%d-------",blur_amount);
    Mat blur0 = Mat(wrap_dst.size(), wrap_dst.type());
    GaussianBlur(wrap_dst, blur0, Size( blur_amount, blur_amount ), 0, 0 );
    Mat blur1= Mat(src_img1.size(), src_img1.type());
    GaussianBlur(src_img1, blur1, Size( blur_amount, blur_amount ), 0, 0 );
    Mat out = Mat::zeros( blur1.size(), blur1.type());
for (int y = 0; y < blur1.rows; y++) {
for (int x = 0; x < blur1.cols; x++) {
for (int c = 0; c < 3; c++) {
if ((blur0.at<Vec3b>(y, x)[c]) > 0) {

out.at<Vec3b>(y, x)[c] = saturate_cast<uchar>(
(wrap_dst.at<Vec3b>(y, x)[c])
* (blur1.at<Vec3b>(y, x)[c])
/ (blur0.at<Vec3b>(y, x)[c]));
} else {
out.at<Vec3b>(y, x)[c] = saturate_cast<uchar>(
(wrap_dst.at<Vec3b>(y, x)[c])
* (blur1.at<Vec3b>(y, x)[c]));
}
}
}
}

for(int y = 0; y < src_img1.rows; y++ )
        {
            for(int x = 0; x < src_img1.cols; x++ )
            {
            if((mask.at<Vec3b>(y,x)[0]) > 0) {
                    for(int c = 0; c < 3; c++ )
                    {
                    src_img1.at<Vec3b>(y,x)[c]= (out.at<Vec3b>(y,x)[c]);
                    }
            }

}
        }
//    src_img1 += 128 * (src_img1 <= 1.0);
// for(int i = 0; i < faceLine1.size(); i++) {
// //mark the all face data
// cv::Point p1(faceLine1[i].p1.x(),faceLine[i].p1.y());
// cv::Point p2(faceLine1[i].p2.x(),faceLine[i].p2.y());
// cv::line(src_img,p1,p2,cv::Scalar(0,255,0),1,cv::LINE_8);
// }
//
// for(int i = 0; i < faceLine2.size(); i++) {
// //mark the all face data
// cv::Point p1(faceLine2[i].p1.x(),faceLine[i].p1.y());
// cv::Point p2(faceLine2[i].p2.x(),faceLine[i].p2.y());
// cv::line(src_img1,p1,p2,cv::Scalar(0,255,0),1,cv::LINE_8);
// }
//Size dsize = Size(src_img1.cols,src_img1.rows);
//Mat image2 = Mat(dsize,wrap_dst.type());
//LOGD("img1 w:%d h:%d",src_img1.cols,src_img1.rows);
//resize(wrap_dst, image2,dsize);
//LOGD("dst w:%d h:%d",image2.cols,image2.rows);
imwrite("/data/data/com.example.faceswapdemo/files/data.jpg",src_img1);
env->ReleaseIntArrayElements(face1, cbuf1, 0);
env->ReleaseIntArrayElements(face2, cbuf2, 0);
return JNI_TRUE;
}

jboolean studytrain()
{
dlib::deserialize("/data/data/com.example.faceswapdemo/files/shape_predictor_68_face_landmarks.dat") >> msp;
detector = get_frontal_face_detector();
return JNI_TRUE;
}

jboolean facecheck(JNIEnv* env, jobject thiz,
jintArray face1,jint w1,jint h1,
jintArray face2,jint w2,jint h2)
{

return facesRotate_UseData(env,thiz,face1,w1,h1,face2,w2,h2);
}

jboolean removefaceswap()
{
return JNI_TRUE;
}

void startfaceswap()
{

}

void endfaceswap()
{

}

void facemapping()
{

}

void facefusion()
{

}

void faceswap()
{

}

void setfaceswapcallback()
{

}

/*jintArray facesRotate_UsePath(JNIEnv* env, jobject thiz,jstring path1,jstring path2)
{

LOGD("in det");
frontal_face_detector detector = get_frontal_face_detector();
char * str1;
str1=(char*)env->GetStringUTFChars(path1,NULL);
char * str2;
str2=(char*)env->GetStringUTFChars(path2,NULL);
LOGD("load picture");
Mat src_img = imread(str1,1);
Mat src_img1 = imread(str2,1);
LOGD("%d-----img1-----%d",src_img.cols,src_img.rows);
LOGD("%d-----img1-----%d",src_img1.cols,src_img1.rows);
LOGD("opencv img to dlib img");
if(!src_img.data || !src_img1.data)
LOGD("data error");
dlib::cv_image<dlib::bgr_pixel> img(src_img);
dlib::cv_image<dlib::bgr_pixel> img1(src_img1);

LOGD("start detector");
    std::vector<dlib::rectangle> mRets;
    std::vector<dlib::rectangle> mRets1;
    double t = (double)getTickCount();
mRets = detector(img);
mRets1 = detector(img1);
t = ((double)getTickCount()-t)/getTickFrequency();
dlib::shape_predictor msp;
LOGD("detect use %lf s",t);

t = (double)getTickCount();
dlib::deserialize("/data/data/com.example.faceswapdemo/files/shape_predictor_68_face_landmarks.dat") >> msp;
t = ((double)getTickCount()-t)/getTickFrequency();
LOGD("load local mark use %lf s",t);

LOGD("get face mark from the picture");
t = (double)getTickCount();
dlib::full_object_detection shape1,shape2;
if (mRets.size() != 0) {
for (unsigned long j = 0; j < mRets.size(); ++j)
shape1 = msp(img, mRets[j]);
}
if (mRets1.size() != 0) {
for (unsigned long j = 0; j < mRets1.size(); ++j)
shape2 = msp(img1, mRets1[j]);
}
t = ((double)getTickCount()-t)/getTickFrequency();
LOGD("--get face mark use %lf s---",t);

LOGD("--use face mark to get mapping matrix---");
t = (double)getTickCount();
std::vector<cv::Point2f> srcTri;
std::vector<cv::Point2f> dstTri;
for(int i = 0;i < 16;i++) {
srcTri.push_back(cv::Point2f(shape1.part(i).x(),shape1.part(i).y()));
dstTri.push_back(cv::Point2f(shape2.part(i).x(),shape2.part(i).y()));
}
LOGD("--got special points---");
cv::Mat warp_mat;
warp_mat = cv::findHomography(srcTri,dstTri,0);
LOGD("%d---get change matrix-----%d",warp_mat.cols,warp_mat.rows);
uchar* p1;
for(int i = 0;i < 3;i ++) {
   p1 = warp_mat.ptr<uchar>(i);
   for(int j = 0;j < 3;j ++)
    LOGD("matrix-----%d",(int)p1[j]);
    }
//the faceLine's size is 68,including all face data,such as:
//the index 0 - 16:jaw data
//the index 17 - 68:face data
//the index 17 - 21:right brow data
//the index 22 - 26:left brow data
//the index 27 - 34:nose data
//the index 36 - 41:right eye data
//the index 42 - 47:left eye data
//the index 48 - 60:mouth data

LOGD("use the mapping matrix to change the picture");
Mat wrap_dst;
cv::warpPerspective(src_img,wrap_dst,warp_mat,src_img.size());
t = ((double)getTickCount()-t)/getTickFrequency();
LOGD("change picture use %lf s",t);
int size = wrap_dst.cols * wrap_dst.rows;
jintArray result = env->NewIntArray(size);
uchar* p;
p = wrap_dst.ptr(0);
cv::imwrite("/data/data/com.example.faceswapdemo/files/face_wrap1.png", wrap_dst);
env->SetIntArrayRegion(result, 0, size, (int*)p);
env->ReleaseStringUTFChars(path1, str1);
env->ReleaseStringUTFChars(path2, str2);
return result;
}*/

void testmat(jlong addr1,jlong addr2)
{
Mat& src = *(Mat*)addr1;
Mat& src1 = *(Mat*)addr2;
// Mat src1 = Mat(*((Mat*)addr2));
cvtColor(src1,src1,CV_RGBA2RGB);
cvtColor(src,src,CV_BGR2GRAY);
}

static JNINativeMethod gMethods[] = {
    {"facecheck", "([III[III)Z", (void*)facecheck},
    {"studytrain","()Z",(void*) studytrain},
//    {"faceRotateUsePath", "(Ljava/lang/String;Ljava/lang/String;)[I", (void*)facesRotate_UsePath},
    //    {"testMat","(JJ)V",(void*) testmat},
    // {"faceRotate", "()I", (void*)facesRotate},
};

/*
* 为某一个类注册本地方法
*/
static int registerNativeMethods(JNIEnv* env
        , const char* className
        , JNINativeMethod* gMethods, int numMethods) {
    jclass clazz;
    clazz = env->FindClass(className);
    if (clazz == NULL) {
        return JNI_FALSE;
    }
    if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
        return JNI_FALSE;
    }

return JNI_TRUE;
}

/*
* 为所有类注册本地方法
*/
static int registerNatives(JNIEnv* env) {
    const char* kClassName = "com/example/faceswapdemo/FaceUtils";//指定要注册的类
    return registerNativeMethods(env, kClassName, gMethods,
    sizeof(gMethods)/sizeof(gMethods[0]));
}

jint JNIEXPORT JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
{
    LOGE("JNI On Load");
    JNIEnv* env = NULL;
    jint result = -1;

if (vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) {
        LOGE("GetEnv failed!");
        return result;
    }
    LOGE("start register!");
    if (!registerNatives(env)) {//注册
    LOGE("register failed!");
        return result;
    }
    return JNI_VERSION_1_4;
}

/*
#ifdef __cplusplus
}
#endif
*/

opencv 与dlib 结合实现人脸融合相关推荐

  1. Opencv与dlib联合进行人脸关键点检测与识别

    前言 依赖库:opencv 2.4.9 /dlib 19.0/libfacedetection 本篇不记录如何配置,重点在实现上.使用libfacedetection实现人脸区域检测,联合dlib标记 ...

  2. Unity使用OpenCV插件实现人脸融合 —— 换脸换装示例

    Unity使用OpenCV插件实现人脸融合 案例说明 Unity版本以及必备插件 快速上手 核心(黑心)方法(脚本): 结束 案例说明 本章节针对部分网友提出的看不懂源码,拿到相关资料后这也报错,那也 ...

  3. 实战:使用OpenCV+Python+dlib为人脸生成口罩

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达本文转自|AI算法与图像处理 本文使用OpenCV dlib库生成口 ...

  4. OpenCV vs Dlib 人脸检测比较分析

    点击我爱计算机视觉标星,更快获取CVML新技术 人脸检测是计算机视觉最典型的应用之一,早期OpenCV的logo就是Haar人脸检测的示意图. 很多人的第一个OpenCV学习目标就是跑通Haar级联人 ...

  5. opencv联合dlib人脸检测例子二(加快检测)

    本篇博客是在opencv联合dlib人脸检测例子的基础上改进了下,加快检测流程 观察了下,opencv利用haar级联分类器检测人脸区域的速度要稍快于dlib的frontal_face_detecto ...

  6. opencv联合dlib视频人脸识别例子

    本篇文章是在上一篇文章opencv联合dlib人脸识别例子 的基础上做了一个实时视频人脸识别功能. 原理是利用opencv实时提取视频中的视频流,然后进入人脸检测步骤,步骤类似上篇文章. 本篇文章中的 ...

  7. OpenCV基于dlib进行人脸关键点检测(摄像头)

    1. dlib.get_frontal_face_detector()获取人脸检测器 2. dlib.shape_predictor()预测人脸关键点 人脸关键点模型,下载地址: http://dli ...

  8. python+opencv+百度智能云 人脸识别——人脸融合

    一.设计思路 1.导入模块 import base64 import json import requests base64模块:由于某些系统中只能使用ASCII字符.Base64就是用来将非ASCI ...

  9. 「云毕业照」刷爆朋友圈!AI人脸融合技术谁家强?

    点击上方"3D视觉工坊",选择"星标" 干货第一时间送达 2020,特殊的毕业季,需要特殊的纪念. 之前看过日本东京的BBT大学使用的「Newme」机器人代替学 ...

最新文章

  1. java监控多个线程的实现
  2. 5分钟,看尽芯片和摩尔定律“你追我赶”的抗衡50年
  3. oracle 修索引改空间_记一次Oracle分区表全局索引重修的历程
  4. 使用匿名函数和内嵌函数处理多变量传递问题
  5. 【模拟】Codeforces 710C Magic Odd Square
  6. ASP.NET Core quot;完整发布,自带运行时quot; 到jexus
  7. qnap nas web php,如何在QNAP NAS上建立并使用 iSCSI Target
  8. 程序员的职业生涯之我见
  9. 洛谷 P1063 能量项链 区间dp
  10. 自动清理归档日志_LGWR 日志写入进程
  11. python创建类和类方法
  12. 走进C++程序世界-----函数相关(全局变量)
  13. php 实现联想式 搜索,php实现联想搜索,你会吗?_后端开发
  14. python源码中的学习笔记_第11章_模块与包
  15. android+显示ui布局,[Android ]UI布局 (线性布局+相对布局)
  16. android手机 windows7,windows7手机版系统下载
  17. C4D建模宝典R20笔记
  18. 通达信标记符号_通达信添加标记符号
  19. 如何调试 chrome插件
  20. swift 设置启动页

热门文章

  1. MIPI-CSI-2协议
  2. 世界顶级杀毒软件排行(Toptennews)
  3. STM32 C++编程系列二:STM32 C++代码封装初探
  4. 未分配利润与利润表不一致_为什么本年利润与利润表不一致
  5. mac电脑如何打包dmg安装包文件
  6. 爬虫逆向基础,认识 SM1-SM9、ZUC 国密算法
  7. PID算法详细解析——基于单片机
  8. CodeGear Releases 3rdrail
  9. 未来教育计算机二级为什么分数很低,为什么计算机二级office通过率不足30%很多大学童鞋生依然在考...
  10. 什么是放大器和宽带放大器及其性能概述