记得很久之前百度魔图有一个计算你和哪个明星最像的功能,还是挺有趣的,现在我们可以用SeetaFace来实现这个功能,其中对SeetaFace库的调用就使用教程(二)中封装的类。
在新建工程之前,我们首先需要一个明星面部的数据库。在网上找了一段时间,并没有发现现成的可以下载。后来看到了百度的这个页面:
感觉这些图片看起来合适,于是就写了个python脚本爬了一下,大概爬了10000+的图片。如果有人需要这个明星图片数据,可以在这里下载:链接: http://pan.baidu.com/s/1slVIBdr 密码: x6s5
我们新建一个项目,配好环境,在项目根目录新建一个images文件夹,然后把所有爬到的图片复制进去。
我们的程序要有两个部分,第一要把所有图片的特征提取出来,并保存成一个文件,第二就是根据一张新的图片去找最相近的Top K个明星。
为了达到这个目的,我们先把原始的float数组特征封装成结构体Feature,主要是添加了图片所在位置的信息。然后将多个Feature封装成FeatureGroup类,主要是为了实现可以保存到文件,再从文件恢复,以及找寻Top K三个功能。
Feature结构的声明,filename是这个特征对应的图片位置,data保存真正的特征,similarity_with_goal主要是为了后面找最相近的Top K使用:
struct Feature{
string filename;
float* data;
float similarity_with_goal;
friend bool operator< (Feature f1, Feature f2){
return f1.similarity_with_goal < f2.similarity_with_goal;
}
};
再看FeatureGroup类的声明:
class FeatureGroup{
public:
FeatureGroup(int feat_dims, SeetaFace* seeta_face);
FeatureGroup(string model_file, SeetaFace* seeta_face);
std::vector<Feature> feats;
bool AddFeature(float* feat, string filename);
bool SaveModel(string model_file);
int GetFeatureDims();
bool FindTopK(int k, float* feat, std::vector<Feature>& result);
~FeatureGroup();
private:
int feat_dims;
SeetaFace* seeta_face;
};
bool SaveModel(string model_file);是把所有特征保存起来。而构造函数FeatureGroup(string model_file, SeetaFace* seeta_face);可以从文件中恢复。
std::vector<Feature> feats;存放所有的特征。使用AddFeature添加特征,使用FindTopK找寻和目标图片特征最像的K张图片。
具体实现在文末贴出。
程序编译运行后,输入1会检测项目目录images下所有图片,并生成所有图片中的人脸特征。
注意:如果使用Debug模式生成,速度会非常慢。建议从头开始用release模式编译所有库,并在release模式下生成,速度会大大提高。
程序运行结束后,会自动生成test.index文件保存所有特征。
现在就可以找一些图片来测试了,这里为了方便起见,还是直接用明星自己的图片来测试。
随便找了一张梁朝伟的图片:
程序运行后选择2,加载数据库完成后输入图片路径测试,检测成功了:
我们再找一张梁朝伟的图片来检测,这次的图片略微夸张:
也检测出来了,说明还是比较靠谱的!
下面粘贴出所有代码供参考:
SeetaFace.h文件:
#include <string>
#include<iostream>
#include<vector>
using std::string;
#include <opencv/cv.h>
#include <opencv/highgui.h>
#include "face_detection.h"
#include "face_alignment.h"
#include "face_identification.h"
#include <fstream>
#include <io.h>
#include<queue>
class Detector : public seeta::FaceDetection{
public:
Detector(const char * model_name);
};
class SeetaFace{
public:
SeetaFace();
Detector* detector;
seeta::FaceAlignment* point_detector;
seeta::FaceIdentification* face_recognizer;
bool GetFeature(string filename, float* feat);
float* NewFeatureBuffer();
float FeatureCompare(float* feat1, float* feat2);
int GetFeatureDims();
};
struct Feature{
string filename;
float* data;
float similarity_with_goal;
friend bool operator< (Feature f1, Feature f2){
return f1.similarity_with_goal < f2.similarity_with_goal;
}
};
class FeatureGroup{
public:
FeatureGroup(int feat_dims, SeetaFace* seeta_face);
FeatureGroup(string model_file, SeetaFace* seeta_face);
std::vector<Feature> feats;
bool AddFeature(float* feat, string filename);
bool SaveModel(string model_file);
int GetFeatureDims();
bool FindTopK(int k, float* feat, std::vector<Feature>& result);
~FeatureGroup();
private:
int feat_dims;
SeetaFace* seeta_face;
};
SeetaFace.cpp文件:
#include "SeetaFace.h"
Detector::Detector(const char* model_name): seeta::FaceDetection(model_name){
this->SetMinFaceSize(40);
this->SetScoreThresh(2.f);
this->SetImagePyramidScaleFactor(0.8f);
this->SetWindowStep(4, 4);
}
SeetaFace::SeetaFace(){
this->detector = new Detector("model/seeta_fd_frontal_v1.0.bin");
this->point_detector = new seeta::FaceAlignment("model/seeta_fa_v1.1.bin");
this->face_recognizer = new seeta::FaceIdentification("model/seeta_fr_v1.0.bin");
}
float* SeetaFace::NewFeatureBuffer(){
return new float[this->face_recognizer->feature_size()];
}
bool SeetaFace::GetFeature(string filename, float* feat){
//如果有多张脸,输出第一张脸,返回true
//如果没有脸,输出false
//read pic greyscale
cv::Mat src_img = cv::imread(filename, 0);
seeta::ImageData src_img_data(src_img.cols, src_img.rows, src_img.channels());
src_img_data.data = src_img.data;
//read pic color
cv::Mat src_img_color = cv::imread(filename, 1);
seeta::ImageData src_img_data_color(src_img_color.cols, src_img_color.rows, src_img_color.channels());
src_img_data_color.data = src_img_color.data;
std::vector<seeta::FaceInfo> faces = this->detector->Detect(src_img_data);
int32_t face_num = static_cast<int32_t>(faces.size());
if (face_num == 0)
{
return false;
}
seeta::FacialLandmark points[5];
this->point_detector->PointDetectLandmarks(src_img_data, faces[0], points);
this->face_recognizer->ExtractFeatureWithCrop(src_img_data_color, points, feat);
return true;
};
int SeetaFace::GetFeatureDims(){
return this->face_recognizer->feature_size();
}
float SeetaFace::FeatureCompare(float* feat1, float* feat2){
return this->face_recognizer->CalcSimilarity(feat1, feat2);
}
FeatureGroup::FeatureGroup(int feat_dims, SeetaFace* seeta_face){
this->feat_dims = feat_dims;
this->seeta_face = seeta_face;
}
int FeatureGroup::GetFeatureDims(){
return this->feat_dims;
}
bool FeatureGroup::AddFeature(float* feat, string filename){
Feature temp;
float* new_feat = new float[this->feat_dims];
memcpy(new_feat, feat, sizeof(new_feat)*this->feat_dims);
temp.data=new_feat;
temp.filename=filename;
this->feats.push_back(temp);
return true;
}
bool FeatureGroup::SaveModel(string model_file){
std::ofstream file;
file.open(model_file);
file<<int(this->feats.size())<<std::endl;
file<<this->feat_dims<<std::endl;
for (int i=0; i<int(this->feats.size()); i++){
file<<this->feats[i].filename<<std::endl;
for(int j=0;j<this->feat_dims;j++)
file<<this->feats[i].data[j]<<" ";
file<<std::endl;
}
file.close();
return true;
}
FeatureGroup::FeatureGroup(string model_file, SeetaFace* seeta_face){
std::ifstream file;
file.open(model_file);
int size;
float* new_feat;
char* buffer = new char[1000];
file>>size;
file>>this->feat_dims;
for (int i=0; i<size; i++){
Feature temp;
file.getline(buffer, 1000);
while (buffer[0]=='\\0' || buffer[0]==' '){
file.getline(buffer, 1000);
}
temp.filename=buffer;
new_feat = new float[this->feat_dims];
for(int j=0;j<this->feat_dims;j++)
file>>new_feat[j];
temp.data=new_feat;
this->feats.push_back(temp);
}
file.close();
this->seeta_face = seeta_face;
}
FeatureGroup::~FeatureGroup(){
for (int i=0; i<int(this->feats.size()); i++)
delete [](this->feats[i].data);
}
bool FeatureGroup::FindTopK(int k, float* feat, std::vector<Feature>& result){
std::cout<<"Calculating Similarities..."<<std::endl;
for (int i=0; i<int(this->feats.size()); i++){
this->feats[i].similarity_with_goal = this->seeta_face->FeatureCompare(this->feats[i].data, feat);
}
std::cout<<"Finding TopK..."<<std::endl;
std::priority_queue<Feature> q;
for (int i=0; i<int(this->feats.size()); i++)
q.push(this->feats[i]);
for (int i=0;i<k;i++){
if(q.empty()) return true;
result.push_back(q.top());
q.pop();
}
return 0;
}
bool GetFilenameUnderPath(string file_path, std::vector<string>& files){
long hFile = 0;
//文件信息
struct _finddata_t fileinfo;
string p;
if((hFile = _findfirst(p.assign(file_path).append("\\\\*").c_str(),&fileinfo)) != -1)
{
do
{
//如果是目录,迭代之
//如果不是,加入列表
if((fileinfo.attrib & _A_SUBDIR))
{
if(strcmp(fileinfo.name,".") != 0 && strcmp(fileinfo.name,"..") != 0)
GetFilenameUnderPath( p.assign(file_path).append("\\\\").append(fileinfo.name), files );
}
else
{
char *ext=strrchr(fileinfo.name,'.');
if (ext){
ext++;
if(_stricmp(ext,"jpg")==0 || _stricmp(ext, "png")==0)
files.push_back(p.assign(file_path).append("\\\\").append(fileinfo.name) );
}
}
}while(_findnext(hFile, &fileinfo) == 0);
_findclose(hFile);
}
return true;
}
int main(int argc, char* argv[]){
int choice;
std::cout<<"Input 1 to Build Database Index, 2 to Test Image:"<<std::endl;
std::cin>>choice;
if(choice==1){
std::vector<string> filenames;
GetFilenameUnderPath(".\\\\images", filenames);
std::cout<<"Detected "<<filenames.size()<<" images...."<<std::endl;
SeetaFace sf;
FeatureGroup feature_group(sf.GetFeatureDims(), &sf);
float* feat=sf.NewFeatureBuffer();
for (int i=0; i<int(filenames.size());i++){
if (sf.GetFeature(filenames[i], feat))
feature_group.AddFeature(feat, filenames[i]);
if ((i+1) % 1 == 0)
std::cout<<(i+1)<<" / "<<int(filenames.size())<<std::endl;
}
feature_group.SaveModel("test.index");
std::cout<<"Finished."<<std::endl;
}else{
SeetaFace sf;
string pic_file;
std::cout<<"Loading Database..."<<std::endl;
FeatureGroup feature_group("test.index", &sf);
float* feat=sf.NewFeatureBuffer();
while(true){
std::vector<Feature> result;
std::cout<<"Please Input Your Filename: ";
std::cin>>pic_file;
if(sf.GetFeature(pic_file, feat)==false){
std::cout<<"Wrong Filename or Can't Detect Face.."<<std::endl;
continue;
}
feature_group.FindTopK(10, feat, result);
for (int i=0;i<int(result.size());i++)
std::cout<<"Top "<<(i+1)<<" : "<<result[i].filename<<" Similarity: "<<result[i].similarity_with_goal<<std::endl;
}
std::cout<<std::endl;
}
return 0;
}
公司名称: 8A-8A娱乐-注册登录商务站
手 机: 13800000000
电 话: 400-123-4567
邮 箱: admin@youweb.com
地 址: 广东省广州市天河区88号