Author: kagula
Date: 2017-09-18
Environment:
[1]VS20217 Update3
[2]Seeta face engine
[3]opencv-2.4.13.3
[4]boost 1.64
introduction
a seeta face engine demo.
note face identification depends face alignment and face detection.
Content:
kagula.h
#ifndef _KAGULA_H_
#define _KAGULA_H_
#include <string>
namespace kagula
{
class seetaFace
{
public:
//初始化整个模块
void init();
//如果没有匹配返回.0f,否则返回匹配程度。
float bestMatch(const std::string srcFileName, const std::string dstFileName,std::string &outputFileName);
};
}
#endif
kagula.cpp
#include "kagula.h"
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
using namespace std;
#include <boost/shared_ptr.hpp>
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "face_detection.h"
#include "face_alignment.h"
#include "face_identification.h"
//seeta的三个class不能直接用boost智能指针,所以。。。 。。。
seeta::FaceDetection gDetector("seeta\\seeta_fd_frontal_v1.0.bin");//人脸检测
seeta::FaceAlignment gPointDetector("seeta\\seeta_fa_v1.1.bin");//landmark points(left eye, right eye, nose, left and right corner of mouth)
seeta::FaceIdentification gFaceRecognizer("seeta\\seeta_fr_v1.0.bin");
namespace kagula
{
struct FacialFeature
{
string fileName;//匹配的子图象,临时文件名。
float* pFeature;
cv::Mat image;//匹配的子图象
float similarity;
FacialFeature() :similarity(.0f)
{
pFeature = new float[gFaceRecognizer.feature_size()];
}
~FacialFeature()
{
if (pFeature != nullptr)
delete pFeature;
}
};//struct
}
//D:\SDK\swigwin-3.0.12\Lib\csharp
void initSeetaface();
void GetFacialFeatures(const string &imageFileName, const int unsigned diagonal,
const string &prefixFileName,
vector< boost::shared_ptr<kagula::FacialFeature>> &vecFF);
boost::shared_ptr<kagula::FacialFeature> GetBestMatched(vector<boost::shared_ptr<kagula::FacialFeature>> &vecFF,
boost::shared_ptr<kagula::FacialFeature> ffDest);
//
namespace kagula
{
//encapsulate for csharp.begin
void seetaFace::init()
{
initSeetaface();
}
float seetaFace::bestMatch(const std::string srcFileName, const std::string dstFileName,
std::string &outputFileName)
{
//含多个头像的图片
//string imageFileName("D:\\SDK\\FaceRec-2-vs2015 - 副本\\images\\IMG_20160527_074213.jpg");
int unsigned diagonal = 1000;
vector<boost::shared_ptr<kagula::FacialFeature>> vecFF;
string prefix = "debug";
GetFacialFeatures(srcFileName, diagonal, prefix, vecFF);
//含一个头像的图片
//string imageFileName2("D:\\workspace\\TestSeetaFace\\temp\\a.jpg");
int unsigned diagonal2 = 250;
vector< boost::shared_ptr<kagula::FacialFeature>> vecFF2;
string prefix2 = "debug2";
GetFacialFeatures(dstFileName, diagonal2, prefix2, vecFF2);
if (vecFF.size() > 0 && vecFF2.size() == 1)
{
//打印出最匹配的头像
boost::shared_ptr<kagula::FacialFeature> bestMatched = GetBestMatched(vecFF, vecFF2[0]);
outputFileName = bestMatched->fileName;
return bestMatched->similarity;
}
return .0f;
}
//encapsulate for csharp.end
}//namespace
void initSeetaface()
{
gDetector.SetMinFaceSize(40);//设置的越小检测时间越长,但是小脸也可以检测到
gDetector.SetScoreThresh(2.f);
gDetector.SetImagePyramidScaleFactor(0.8f);
gDetector.SetWindowStep(4, 4);
}
void GetFacialFeatures(const string &imageFileName, const int unsigned diagonal,
const string &prefixFileName,
vector< boost::shared_ptr<kagula::FacialFeature>> &vecFF)
{
//resize image
cv::Mat img0 = cv::imread(imageFileName.c_str());
if (img0.empty())
return;
float scale = 1.0f;
float newh = img0.rows;
float neww = img0.cols;
if (img0.rows*img0.cols > 200*200)
{
//如果图片太大,缩小之。
float scale = float(diagonal) / (img0.rows + img0.cols);
float newh = scale * img0.rows;
float neww = scale * img0.cols;
}
cv::Mat img1;
resize(img0, img1, cv::Size(neww, newh), 0, 0, CV_INTER_LINEAR);
//to gray image
cv::Mat img_gray;
cv::cvtColor(img1, img_gray, CV_BGR2GRAY);
//seeta gray image object//这是给identity用的。
seeta::ImageData seeta_img_color(img1.cols, img1.rows, img1.channels());
seeta_img_color.data = img1.data;
//seeta gray image object//这是给detect用的。
seeta::ImageData seeta_img_gray(img_gray.cols, img_gray.rows, img_gray.channels());
seeta_img_gray.data = img_gray.data;
// Detect faces
std::vector<seeta::FaceInfo> faces = gDetector.Detect(seeta_img_gray);
if ( faces.size() == 0 )
{
cout << "未检测到人脸" << endl;
}
else
{
cout << "检测到人脸[" << faces.size() << "]" << endl;
for (int32_t i = 0; i <faces.size(); i++) {
//把检测到的人脸profile用Rectangle标出来!
cv::Rect face_rect;
face_rect.x = faces[i].bbox.x;
face_rect.y = faces[i].bbox.y;
face_rect.width = faces[i].bbox.width;
face_rect.height = faces[i].bbox.height;
cv::rectangle(img1, face_rect, CV_RGB(0, 0, 255), 4, 8, 0);//blue
//把人脸的5个landmark points(left eye, right eye, nose, left and right corner of mouse)标出来
seeta::FacialLandmark points[5];
gPointDetector.PointDetectLandmarks(seeta_img_gray, faces[i], points);
/* 方便人眼识别
for (int j = 0; j<5; j++)
{
cv::circle(img1, cvPoint(points[j].x, points[j].y), 2, CV_RGB(0, 255, 0), CV_FILLED);//green
}
*/
//Extract face identity feature
boost::shared_ptr<kagula::FacialFeature> pFF(new kagula::FacialFeature());
face_rect.x = face_rect.x < 0 ? 0 : face_rect.x;
face_rect.y = face_rect.y < 0 ? 0 : face_rect.y;
face_rect.x = face_rect.x >= img1.size().width ? img1.size().width-1 : face_rect.x;
face_rect.y = face_rect.y >= img1.size().height ? img1.size().height - 1 : face_rect.y;
face_rect.width = face_rect.width + face_rect.x > img1.size().width ? img1.size().width - face_rect.x : face_rect.width;
face_rect.height = face_rect.height + face_rect.y > img1.size().height ? img1.size().height - face_rect.y: face_rect.height;
if (face_rect.width > 0 && face_rect.height > 0)
{
pFF->image = img1(face_rect);//crop region from img1 instance.
gFaceRecognizer.ExtractFeatureWithCrop(seeta_img_color, points, pFF->pFeature);
vecFF.push_back(pFF);
}
}//every facial part.
//for debug,output temporary images.
//D:\workspace\TestSeetaFace\TestSeetaFace
stringstream ss;
ss << prefixFileName << "_completeTemporaryPicture.jpg";
cv::imwrite(ss.str(), img1);
int k = 1;
for (vector< boost::shared_ptr<kagula::FacialFeature>>::iterator iter = vecFF.begin();
iter != vecFF.end(); k++, iter++)
{
stringstream ss;
ss << prefixFileName << "_subimage_" << k << ".jpg";
(*iter)->fileName = ss.str();
cv::imwrite(ss.str(), (*iter)->image);
}
}
}
bool SortCompare(boost::shared_ptr<kagula::FacialFeature> src, boost::shared_ptr<kagula::FacialFeature> dest)
{
return src->similarity > dest->similarity;
}
boost::shared_ptr<kagula::FacialFeature> GetBestMatched(vector<boost::shared_ptr<kagula::FacialFeature>> &vecFF,
boost::shared_ptr<kagula::FacialFeature> ffDest)
{
for (int i = 0; i < vecFF.size(); i++)
{
vecFF[i]->similarity = gFaceRecognizer.CalcSimilarity(vecFF[i]->pFeature, ffDest->pFeature);
#ifdef _DEBUG
cout << "similarity=" << vecFF[i]->similarity << ",src=" << vecFF[i]->pFeature << ",dest=" << ffDest->pFeature << endl;
#endif // DEBUG
}
//nth_element对给定范围进行"排序"
std::nth_element(vecFF.begin(), vecFF.begin() + 1, vecFF.end(), SortCompare);//对cosdis排序,求得最高的[需要对比阈值]返回SRClabel
return vecFF[0];
}