依赖项目地址 :
https://github.com/seetaface/SeetaFaceEngine
https://github.com/sarxos/webcam-capture
项目结构
dll 文件下载:链接:https://pan.baidu.com/s/1jCnrWb4a5V0FINZiT55KKg 密码:hzv7
model下载:https://github.com/seetaface/SeetaFaceEngine/tree/master/FaceAlignment/model
https://github.com/seetaface/SeetaFaceEngine/tree/master/FaceDetection/model
项目下载链接:https://pan.baidu.com/s/1AIRahXcd5X5gf93PHqKm1A 密码:gmg0
PS:需要将clib里的dll加到lib中(eclipse自行百度)
基于seetaface实现人脸识别
包括人脸检测和人脸识别功能
- 人脸检测,获取人脸的位置(x,y,w,h)
- 人脸识别,比较两个图片中的人脸相似度
基于webcam-capture实现本地摄像头调用
完成以下功能
- 打开摄像头,实时检测人脸位置,将人脸框出来
- 保存模板
- 后台将摄像头中的人脸与模板进行比较,相似度大于一定值则认为是同一个人
项目主要代码
com.vvyun.service.FaceRecognitionService.java
package com.vvyun.service;
/**
* 人脸匹配相关服务
*
* @author wangyf
* @version 0.1
*/
public class FaceRecognitionService {
// 加载依赖库
static {
System.loadLibrary("opencv_core2413");
System.loadLibrary("opencv_highgui2413");
System.loadLibrary("opencv_imgproc2413");
System.loadLibrary("FaceAlignment");
System.loadLibrary("FaceDetection");
System.loadLibrary("FaceIdentification");
System.loadLibrary("faceservice");
}
/**
* 获取人脸位置
* @param imagebase64 图片base64编码
* @return int[4] x,y ,width,higth
*/
public native static int[] getfacepoint(String imagebase64);
/**
* 获取相似度
*
* @param befimagebase64 模板图片base64编码
* @param aftimagebase64 待比较图片base64编码
* @return face Similarity value
*/
public native static float getSimilarity(String befimagebase64, String aftimagebase64);
}
使用jni 将FaceRecognitionService.java生成com_vvyun_service_FaceRecognitionService.h,在项目路径下运行
javah -jni com.vvyun.service.FaceRecognitionService
使用C++实现com_vvyun_service_FaceRecognitionService.h
PS:人脸识别等服务主要代码(C++)(本人已在win10下编译成了dll:faceservice.dll,可直接调用,使用方式见上文 FaceRecognitionService.java)
vs2017 搭建SeetaFaceEngine工程参考链接:
https://github.com/seetaface/SeetaFaceEngine/blob/master/SeetaFace_config.docx
按照SeetaFace_config.docx文档搭建faceservice工程,建dll项目,添加依赖(类似文档里的 4 Console application(Identification)工程文件配置
),添加jni.h等
com_vvyun_service_FaceRecognitionService.h上文已生成
myface.cpp
#include "com_vvyun_service_FaceRecognitionService.h"
#include<iostream>
using namespace std;
#ifdef _WIN32
#pragma once
#include <opencv2/core/version.hpp>
#define CV_VERSION_ID CVAUX_STR(CV_MAJOR_VERSION) CVAUX_STR(CV_MINOR_VERSION) \
CVAUX_STR(CV_SUBMINOR_VERSION)
#ifdef _DEBUG
#define cvLIB(name) "opencv_" name CV_VERSION_ID "d"
#else
#define cvLIB(name) "opencv_" name CV_VERSION_ID
#endif //_DEBUG
#pragma comment( lib, cvLIB("core") )
#pragma comment( lib, cvLIB("imgproc") )
#pragma comment( lib, cvLIB("highgui") )
#endif //_WIN32
#if defined(__unix__) || defined(__APPLE__)
#ifndef fopen_s
#define fopen_s(pFile,filename,mode) ((*(pFile))=fopen((filename),(mode)))==NULL
#endif //fopen_s
#endif //__unix
#include <opencv/cv.h>
#include <opencv/highgui.h>
#include "face_identification.h"
#include "recognizer.h"
#include "face_detection.h"
#include "face_alignment.h"
#include "math_functions.h"
#include <vector>
#include <string>
#include <iostream>
#include <algorithm>
using namespace seeta;
#define TEST(major, minor) major##_##minor##_Tester()
#define EXPECT_NE(a, b) if ((a) == (b)) std::cout << "ERROR: "
#define EXPECT_EQ(a, b) if ((a) != (b)) std::cout << "ERROR: "
#ifdef _WIN32
std::string DATA_DIR = "data/";
std::string MODEL_DIR = "model/";
#else
std::string DATA_DIR = "data/";
std::string MODEL_DIR = "model/";
#endif
// ----------------------------------------------------------------
// java与c字符转化
std::string jstring2str(JNIEnv* env, jstring jstr)
{
char* rtn = NULL;
jclass clsstring = env->FindClass("java/lang/String");
jstring strencode = env->NewStringUTF("GB2312");
jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
jbyteArray barr = (jbyteArray)env->CallObjectMethod(jstr, mid, strencode);
jsize alen = env->GetArrayLength(barr);
jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);
if (alen > 0)
{
rtn = (char*)malloc(alen + 1);
memcpy(rtn, ba, alen);
rtn[alen] = 0;
}
env->ReleaseByteArrayElements(barr, ba, 0);
std::string stemp(rtn);
free(rtn);
return stemp;
}
jstring str2jstring(JNIEnv* env, const char* pat)
{
//定义java String类 strClass
jclass strClass = (env)->FindClass("Ljava/lang/String;");
//获取String(byte[],String)的构造器,用于将本地byte[]数组转换为一个新String
jmethodID ctorID = (env)->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V");
//建立byte数组
jbyteArray bytes = (env)->NewByteArray(strlen(pat));
//将char* 转换为byte数组
(env)->SetByteArrayRegion(bytes, 0, strlen(pat), (jbyte*)pat);
// 设置String, 保存语言类型,用于byte数组转换至String时的参数
jstring encoding = (env)->NewStringUTF("GB2312");
//将byte数组转换为java String,并输出
return (jstring)(env)->NewObject(strClass, ctorID, bytes, encoding);
}
// ----------------------------------------------------------------
// ----------------------------------------------------------------
// base64解码
static std::string base64Decode(const char* Data, int DataByte)
{
//解码表
const char DecodeTable[] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
62, // '+'
0, 0, 0,
63, // '/'
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // '0'-'9'
0, 0, 0, 0, 0, 0, 0,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // 'A'-'Z'
0, 0, 0, 0, 0, 0,
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // 'a'-'z'
};
//返回值
std::string strDecode;
int nValue;
int i = 0;
while (i < DataByte)
{
if (*Data != '\r' && *Data != '\n')
{
nValue = DecodeTable[*Data++] << 18;
nValue += DecodeTable[*Data++] << 12;
strDecode += (nValue & 0x00FF0000) >> 16;
if (*Data != '=')
{
nValue += DecodeTable[*Data++] << 6;
strDecode += (nValue & 0x0000FF00) >> 8;
if (*Data != '=')
{
nValue += DecodeTable[*Data++];
strDecode += nValue & 0x000000FF;
}
}
i += 4;
}
else// 回车换行,跳过
{
Data++;
i++;
}
}
return strDecode;
}
static std::string base64Encode(const unsigned char* Data, int DataByte)
{
//编码表
const char EncodeTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
//返回值
std::string strEncode;
unsigned char Tmp[4] = { 0 };
int LineLength = 0;
for (int i = 0; i < (int)(DataByte / 3); i++)
{
Tmp[1] = *Data++;
Tmp[2] = *Data++;
Tmp[3] = *Data++;
strEncode += EncodeTable[Tmp[1] >> 2];
strEncode += EncodeTable[((Tmp[1] << 4) | (Tmp[2] >> 4)) & 0x3F];
strEncode += EncodeTable[((Tmp[2] << 2) | (Tmp[3] >> 6)) & 0x3F];
strEncode += EncodeTable[Tmp[3] & 0x3F];
if (LineLength += 4, LineLength == 76) { strEncode += "\r\n"; LineLength = 0; }
}
//对剩余数据进行编码
int Mod = DataByte % 3;
if (Mod == 1)
{
Tmp[1] = *Data++;
strEncode += EncodeTable[(Tmp[1] & 0xFC) >> 2];
strEncode += EncodeTable[((Tmp[1] & 0x03) << 4)];
strEncode += "==";
}
else if (Mod == 2)
{
Tmp[1] = *Data++;
Tmp[2] = *Data++;
strEncode += EncodeTable[(Tmp[1] & 0xFC) >> 2];
strEncode += EncodeTable[((Tmp[1] & 0x03) << 4) | ((Tmp[2] & 0xF0) >> 4)];
strEncode += EncodeTable[((Tmp[2] & 0x0F) << 2)];
strEncode += "=";
}
return strEncode;
}
static std::string Mat2Base64(const cv::Mat &img, std::string imgType)
{
//Mat转base64
std::string img_data;
std::vector<uchar> vecImg;
std::vector<int> vecCompression_params;
vecCompression_params.push_back(CV_IMWRITE_JPEG_QUALITY);
vecCompression_params.push_back(90);
imgType = "." + imgType;
cv::imencode(imgType, img, vecImg, vecCompression_params);
img_data = base64Encode(vecImg.data(), vecImg.size());
return img_data;
}
cv::Mat Base2Mat(std::string &base64_data)
{
cv::Mat img;
std::string s_mat;
s_mat = base64Decode(base64_data.data(), base64_data.size());
std::vector<char> base64_img(s_mat.begin(), s_mat.end());
img = cv::imdecode(base64_img, CV_LOAD_IMAGE_COLOR);
return img;
}
// ----------------------------------------------------------------
/*
* Class: com_vvyun_service_FaceRecognitionService
* Method: getfacepoint
* Signature: (Ljava/lang/String;)[I
*/
JNIEXPORT jintArray JNICALL Java_com_vvyun_service_FaceRecognitionService_getfacepoint
(JNIEnv *env, jclass, jstring imagb64) {
// Initialize face detection model
seeta::FaceDetection detector("model/seeta_fd_frontal_v1.0.bin");
detector.SetMinFaceSize(40);
detector.SetScoreThresh(2.f);
detector.SetImagePyramidScaleFactor(0.8f);
detector.SetWindowStep(4, 4);
std::string decode_str = jstring2str(env, imagb64);
cv::Mat gallery_img_color = Base2Mat(decode_str);
cv::Mat gallery_img_gray;
cv::cvtColor(gallery_img_color, gallery_img_gray, CV_BGR2GRAY);
ImageData gallery_img_data_color(gallery_img_color.cols, gallery_img_color.rows, gallery_img_color.channels());
gallery_img_data_color.data = gallery_img_color.data;
ImageData gallery_img_data_gray(gallery_img_gray.cols, gallery_img_gray.rows, gallery_img_gray.channels());
gallery_img_data_gray.data = gallery_img_gray.data;
// Detect faces
std::vector<seeta::FaceInfo> gallery_faces = detector.Detect(gallery_img_data_gray);
cv::Rect face_rect;
int32_t num_face = static_cast<int32_t>(gallery_faces.size());
std::string res = "";
for (int32_t i = 0; i < num_face; i++) {
face_rect.x = gallery_faces[i].bbox.x;
face_rect.y = gallery_faces[i].bbox.y;
face_rect.width = gallery_faces[i].bbox.width;
face_rect.height = gallery_faces[i].bbox.height;
// res = res + face_rect.x + (char)";" + (char)face_rect.y + (char)";";
//const char *cString = face_rect.x+"";
//jstring str = env->NewStringUTF(cString); //直
//构建jint型一维数组
jintArray intArray = env->NewIntArray(4);
jint temp[5]; //初始化一个容器,假设 dimion < 10 ;
temp[0] = face_rect.x; //赋值
temp[1] = face_rect.y; //赋值
temp[2] = face_rect.width; //赋值
temp[3] = face_rect.height; //赋值
//设置jit型一维数组的值
env->SetIntArrayRegion(intArray, 0, 4, temp);
return intArray;
}
}
/*
* Class: com_vvyun_service_FaceRecognitionService
* Method: getSimilaritybydata
* Signature: (Ljava/lang/String;Ljava/lang/String;)F
*/
JNIEXPORT jfloat JNICALL Java_com_vvyun_service_FaceRecognitionService_getSimilarity
(JNIEnv *env, jclass jc, jstring bef, jstring aft) {
// Initialize face detection model
seeta::FaceDetection detector("model/seeta_fd_frontal_v1.0.bin");
detector.SetMinFaceSize(40);
detector.SetScoreThresh(2.f);
detector.SetImagePyramidScaleFactor(0.8f);
detector.SetWindowStep(4, 4);
// Initialize face alignment model
seeta::FaceAlignment point_detector("model/seeta_fa_v1.1.bin");
// Initialize face Identification model
FaceIdentification face_recognizer((MODEL_DIR + "seeta_fr_v1.0.bin").c_str());
std::string test_dir = DATA_DIR + "test_face_recognizer/";
std::string decode_str = jstring2str(env, bef);
//std::cout << decode_str;
std::string decode_str1 = jstring2str(env, aft);
cv::Mat gallery_img_color = Base2Mat(decode_str);
cv::Mat gallery_img_gray;
cv::cvtColor(gallery_img_color, gallery_img_gray, CV_BGR2GRAY);
cv::Mat probe_img_color = Base2Mat(decode_str1);
cv::Mat probe_img_gray;
cv::cvtColor(probe_img_color, probe_img_gray, CV_BGR2GRAY);
ImageData gallery_img_data_color(gallery_img_color.cols, gallery_img_color.rows, gallery_img_color.channels());
gallery_img_data_color.data = gallery_img_color.data;
ImageData gallery_img_data_gray(gallery_img_gray.cols, gallery_img_gray.rows, gallery_img_gray.channels());
gallery_img_data_gray.data = gallery_img_gray.data;
ImageData probe_img_data_color(probe_img_color.cols, probe_img_color.rows, probe_img_color.channels());
probe_img_data_color.data = probe_img_color.data;
ImageData probe_img_data_gray(probe_img_gray.cols, probe_img_gray.rows, probe_img_gray.channels());
probe_img_data_gray.data = probe_img_gray.data;
// Detect faces
std::vector<seeta::FaceInfo> gallery_faces = detector.Detect(gallery_img_data_gray);
int32_t gallery_face_num = static_cast<int32_t>(gallery_faces.size());
std::vector<seeta::FaceInfo> probe_faces = detector.Detect(probe_img_data_gray);
int32_t probe_face_num = static_cast<int32_t>(probe_faces.size());
if (gallery_face_num == 0 || probe_face_num == 0)
{
//std::cout << "Faces are not detected.";
return 0;
}
// Detect 5 facial landmarks
seeta::FacialLandmark gallery_points[5];
point_detector.PointDetectLandmarks(gallery_img_data_gray, gallery_faces[0], gallery_points);
seeta::FacialLandmark probe_points[5];
point_detector.PointDetectLandmarks(probe_img_data_gray, probe_faces[0], probe_points);
for (int i = 0; i<5; i++)
{
cv::circle(gallery_img_color, cv::Point(gallery_points[i].x, gallery_points[i].y), 2,
CV_RGB(0, 255, 0));
cv::circle(probe_img_color, cv::Point(probe_points[i].x, probe_points[i].y), 2,
CV_RGB(0, 255, 0));
}
cv::imwrite("gallery_point_result.jpg", gallery_img_color);
cv::imwrite("probe_point_result.jpg", probe_img_color);
// Extract face identity feature
float gallery_fea[2048];
float probe_fea[2048];
face_recognizer.ExtractFeatureWithCrop(gallery_img_data_color, gallery_points, gallery_fea);
face_recognizer.ExtractFeatureWithCrop(probe_img_data_color, probe_points, probe_fea);
// Caculate similarity of two faces
float sim = face_recognizer.CalcSimilarity(gallery_fea, probe_fea);
return (jfloat)sim;
}