天天看点

SeetaFace人脸识别系统

简介

SeetaFace人脸识别解决方案已开源许久,一直拖到最近才看。它提供了一套完整的人脸检测,人脸对齐和人脸验证方案,比较适合做学习。由于开源的代码是DLL项,现编译完成之后,对其进了一定封装,,实现了人脸特征提取,入库保存,以及人脸识别等。 对该系统的介绍可以参见: SeetaFace开源人脸识别引擎介绍 本文主要参考: SeetaFace教程: 在 VS 中的编译安装和环境配置  并做了部分整合与修改。

准备

在这之前,请自行下载 SeetaFace源码,并编译出三个lib文件和三个dll文件。我编译出的是 FaceIdentification.lib  FaceIdentification.dll

FaceAlignment.lib   FaceAlignment.dll

FaceDetection.lib    FaceDetection.dll

编译时直接打开源码里面的example解决方案即可,编译dll文件时无需任何额外的依赖项,只有在测试时才需要opencv。如果不太明白,也可参考上面简介中给出的SeetaFace教程链接,不过我觉得他那有点多余。

项目

新建win32控制台项目,此时需要opencv,我在测试时使用的是opencv2.4.10,其他的应该也行。 ① 将项目属性改为x64 ② 在配置属性-VC++目录中,将opencv的包含目录和库目录修改好,记得使用x64的lib库 ③ 链接器-输入,加入FaceIdentification.lib  FaceAlignment.lib  FaceDetection.lib  opencv_core2410.lib  opencv_highgui2410.lib  opencv_imgproc2410.lib ④ 与这6个lib文件相对于的dll文件放入可执行文件目录下即可。 ⑤ 将seetaface源码中的三个model,放在一起,存于项目文件夹下即可。images文件夹中为一些测试图片 项目整体结构

SeetaFace人脸识别系统

源码

FeatureGroup.h

#ifndef FEATUREGROUP_H
#define FEATUREGROUP_H

#include <fstream>
#include <io.h>
#include <queue>
#include <string>
#include "FaceRecognition.h"
struct Feature
{
	std::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, FaceRecognition* fr);
	FeatureGroup(string model_file, FaceRecognition* fr);
	bool AddFeature(float* feat, string filename);
	bool SaveModel(string model_file);
	int GetFeatureDims();
	bool FindTopK(int k, float* feat, std::vector<Feature>& result);
	~FeatureGroup();

public:
	std::vector<Feature> features;

private:
	int feat_dims;  
	FaceRecognition* fr;
};



#endif //FEATUREGROUP_H
           

FeatureGroup.cpp

#include "FeatureGroup.h"

FeatureGroup::FeatureGroup(int feat_dims, FaceRecognition* fr)
{
	this->feat_dims = feat_dims;
	this->fr = fr;
}

FeatureGroup::FeatureGroup(string model_file, FaceRecognition* fr)
{
	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 tmp;
		file.getline(buffer, 1000);
		while (buffer[0] == '\0' || buffer[0] == ' ')
		{
			file.getline(buffer, 1000);
		}
		tmp.filename = buffer;
		new_feat = new float[this->feat_dims];
		for (int j = 0; j < this->feat_dims; j++)
		{
			file >> new_feat[j];
		}
		tmp.data = new_feat;
		this->features.push_back(tmp);
	}
	file.close();
	this->fr = fr;
}

int FeatureGroup::GetFeatureDims()
{
	return this->feat_dims;
}

bool FeatureGroup::AddFeature(float* feat, string filename)
{
	Feature tmp;
	float* new_feat = new float[this->feat_dims];
	//memcpy(new_feat, feat, sizeof(new_feat) * this->feat_dims);// 这句话执行有问题,改成下面的循环
	for (int i = 0; i < this->feat_dims; i++)
	{
		new_feat[i] = feat[i];
	}
	tmp.data = new_feat;
	tmp.filename = filename;
	this->features.push_back(tmp);
	return true;
}

bool FeatureGroup::SaveModel(string model_file)
{
	std::ofstream file;
	file.open(model_file);
	file << int(this->features.size()) << std::endl; //先输出特征的个数
	file << this->feat_dims << std::endl; //再输出特征的维数
	//依次写入数据
	for (int i = 0; i < int(this->features.size()); i++)
	{
		file << this->features[i].filename << std::endl;
		for (int j = 0; j < this->feat_dims; j++)
		{
			file << this->features[i].data[j] << " ";
		}
		file << std::endl;
	}
	file.close();
	return true;
}

bool FeatureGroup::FindTopK(int k, float* feat, std::vector<Feature>& result)
{
	std::cout << "Calculating Similarities..." << std::endl;
	for (int i = 0; i < int(this->features.size()); i++)
	{
		this->features[i].similarity_with_goal = this->fr->FeatureCompare(this->features[i].data, feat);
	}
	std::cout << "Finding Topk..." << std::endl;
	std::priority_queue<Feature> q;
	for (int i = 0; i < int(this->features.size()); i++)
	{
		q.push(this->features[i]);
	}
	for (int i = 0; i < k; i++)
	{
		if (q.empty())
			return true;
		result.push_back(q.top());
		q.pop();
	}

	return true;

}

FeatureGroup::~FeatureGroup()  //如果出错,再回来修改
{
	for (int i = 0; i < int(this->features.size()); i++)
	{
		delete [](this->features[i].data);
	}
		
}
           

FaceRecognition.h

#ifndef FACERECOGNITION_H
#define FACERECOGNITION_H

#include <string>
#include<iostream>
using std::string;
#include <opencv/cv.h>
#include <opencv/highgui.h>
#include "face_detection.h"
#include "face_alignment.h"
#include "face_identification.h"

class Detector : public seeta::FaceDetection{
public:
	Detector(const char * model_name);
};

class FaceRecognition{
public:
	FaceRecognition();
	float* NewFeatureBuffer();
	float FeatureCompare(float* feat1, float* feat2);
	int GetFeatureDims();
	bool GetFeature(string filename, float* feat);
	~FaceRecognition();
public:
	Detector* detector;
	seeta::FaceAlignment* point_detector;
	seeta::FaceIdentification* face_recognizer;
	
};

#endif // !FACERECOGNITION_H
           

FaceRecognition.cpp

#include "FaceRecognition.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);
}

FaceRecognition::FaceRecognition()
{
	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* FaceRecognition::NewFeatureBuffer()
{
	return new float[this->face_recognizer->feature_size()];
}

int FaceRecognition::GetFeatureDims()
{
	return this->face_recognizer->feature_size();
}

float FaceRecognition::FeatureCompare(float* feat1, float* feat2)
{
	return this->face_recognizer->CalcSimilarity(feat1, feat2);
}


bool FaceRecognition::GetFeature(string filename, float* feat)
{
	//load image and convert to gray
	cv::Mat src_img_color = cv::imread(filename, 1);
	cv::Mat src_img_gray;
	cv::cvtColor(src_img_color, src_img_gray, CV_BGR2GRAY);

	//convert to ImageData type
	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;

	seeta::ImageData src_img_data_gray(src_img_gray.cols, src_img_gray.rows, src_img_gray.channels());
	src_img_data_gray.data = src_img_gray.data;

	//Detect faces
	std::vector<seeta::FaceInfo> faces = this->detector->Detect(src_img_data_gray);
	int32_t face_num = static_cast<int32_t>(faces.size());
	if (face_num == 0)
	{
		std::cout << "Faces are not detected." << std::endl;
		return false;
	}

	//Detect 5 facial landmarks
	seeta::FacialLandmark points[5];
	this->point_detector->PointDetectLandmarks(src_img_data_gray, faces[0], points);

	//Extract face identity feature
	this->face_recognizer->ExtractFeatureWithCrop(src_img_data_color, points, feat);

	return true;

}

FaceRecognition::~FaceRecognition()
{
	if (detector)
		delete detector;
	if (point_detector)
		delete point_detector;
	if (face_recognizer)
		delete face_recognizer;
}
           

mian.cpp

#include "FaceRecognition.h"
#include "FeatureGroup.h"

/*
* given two images and calculate the similarity
*/
int faceverification()
{
	FaceRecognition fr;
	float* feat1 = fr.NewFeatureBuffer();
	float* feat2 = fr.NewFeatureBuffer();

	std::vector<std::pair<string, string>> pic_pair;
	pic_pair.push_back(std::make_pair("0_1.jpg", "0_2.jpg"));
	pic_pair.push_back(std::make_pair("1_1.jpg", "1_2.jpg"));
	pic_pair.push_back(std::make_pair("2_1.jpg", "2_2.jpg"));

	double averageDetectTime = 0.0;
	for (size_t i = 0; i < pic_pair.size(); i++)
	{
		long t0 = cv::getTickCount();
		fr.GetFeature(pic_pair[i].first, feat1);
		fr.GetFeature(pic_pair[i].second, feat2);
		float sim = fr.FeatureCompare(feat1, feat2);
		std::cout << "similarity:" << sim << std::endl;

		long t1 = cv::getTickCount();
		double secs = (t1 - t0) / cv::getTickFrequency();
		averageDetectTime += secs;
		std::cout << "Image pair " << i << " takes " << secs << " seconds to detect,alignment and verification !" << std::endl;;

	}
	std::cout << std::endl << "All verification takes " << averageDetectTime << " secs!" << std::endl;
	std::cout << "The average verification times for one image takes " << double(averageDetectTime / pic_pair.size() / 2) << " secs !" << std::endl;

	delete feat1;
	delete feat2;
	return 0;
}


bool GetAllFileNames(string file_path, std::vector<string>& files){
	intptr_t 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)
					GetAllFileNames(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::cin >> choice;
	if (1 == choice)
	{
		std::vector<string> filenames;
		GetAllFileNames("./images", filenames);
		std::cout << "Detected " << filenames.size() << " images..." << std::endl;
		FaceRecognition fr;
		FeatureGroup fg(fr.GetFeatureDims(), &fr);
		float * feat = fr.NewFeatureBuffer();

		double averageExtractFeatureTime = 0.0;
		for (int i = 0; i < filenames.size(); i++)
		{
			long t0 = cv::getTickCount();
			if (fr.GetFeature(filenames[i], feat))
			{
				fg.AddFeature(feat, filenames[i]);
			}
			long t1 = cv::getTickCount();
			double secs = (t1 - t0) / cv::getTickFrequency();
			averageExtractFeatureTime += secs;
			if ((i + 1) % 5 == 0)
			{
				std::cout << i + 1 << " / " << int(filenames.size()) << "  has been extracted!"<< std::endl;
			}
		}
		std::cout << std::endl << "All verification takes " << averageExtractFeatureTime << " secs!" << std::endl;
		std::cout << "The average extract feature times for one image takes " << double(averageExtractFeatureTime / filenames.size() / 2) << " secs !" << std::endl;

		fg.SaveModel("feature.index");
		std::cout << "Feature Extraction has been finished!" << std::endl;
	}
	else if (2 == choice)
	{
		FaceRecognition fr;
		string pic_file_path;
		std::cout << "Loading DataBase..." << std::endl;
		FeatureGroup fg("feature.index", &fr);
		std::cout << "Database has been loaded!" << std::endl;
		float * feat = fr.NewFeatureBuffer();
		while (true)
		{
			std::vector<Feature> result;
			std::cout << "please input your filename:";
			std::cin >> pic_file_path;
			if (!fr.GetFeature(pic_file_path, feat))
			{
				std::cout << "Wrong Filename or Can't detect face..." << std::endl;
				continue;
			}
			fg.FindTopK(10, feat, result);
			for (int i = 0; i < 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;
}
           

最后,将完整的项目代码献上:链接: http://pan.baidu.com/s/1nvyNbRF 密码: 3g4b

继续阅读