SSD+LBP实现人脸识别

Posted 思禾

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SSD+LBP实现人脸识别相关的知识,希望对你有一定的参考价值。

版本:opencv3.4.1
平台:win10
#<io.h>是windows专有库,在linux中不能使用

本文参考github训练的ssd模型(在本文后悔给出链接),然后结合LBP实现人脸检测,之所以选择ssd+lbp其原因在于快,相比于其他浮点计算人脸检测方法,如EigenFaceRecognizer,FisherFaceRecognizer,在速度上,因为lbp采用整数进行计算,因此,在速度上有很大的提示,这对于在一些羸弱的计算机如树莓派也能良好的工作。

下面是演示代码:
使用方法:
在第一次使用的时候,将Flag 设为1,他会采集人脸,并进行训练
在后面的执行中,就可以将flag设为0,他会自动去加载上一下训练的模型
该代码使用了两个自实现函数:getfiles和SplitString,主要用来获取目录下的所有文件和用来进行字符串分割,在使用时,可不比纠结于这两个算法的实现,知道其使用方法即可:

void getFiles(string path, vector<string>& files)
第一个参数为文件路径,第二个是一个vector向量,用于存放该文件夹下所有文件的路径
void SplitString(const string& s, vector<string>& v, const string& c)
第一个参数为要进行分割的字符串,第二个是一个vector向量,用于存放分割的字符串,第三个参数为要使用什么字符串进行风格,如“,”、"_"等。

#include<opencv2/opencv.hpp>
#include <iostream>
#include<opencv2/face.hpp>
#include<opencv2/dnn.hpp>
#include <string>
#include<io.h>
#include <fstream>

using namespace std;
using namespace cv;
using namespace cv::face;
using namespace cv::dnn;

int Flag = 1; //1表示需需要进行添加新人脸,进行训练,2表示加载模型直接运行

String image_base = "F:\\\\VS\\\\opencv\\\\data\\\\orl_faces";
String modelFile = "E:\\\\opencv_install\\\\model\\\\caffe_ssd_face_detactor\\\\res10_300x300_ssd_iter_140000.caffemodel"; //caffe模型
String model_text_file = "E:\\\\opencv_install\\\\model\\\\caffe_ssd_face_detactor\\\\deploy.prototxt";

String train_data_dir = "F:\\\\VS\\\\opencv\\\\边角检测\\\\边角检测\\\\data";
void getFiles(string path, vector<string>& files);
void SplitString(const string& s, vector<string>& v, const string& c);
map<int, string> ID_NAME;
int main(int argc, char** argv)
{
	//namedWindow("image", WINDOW_AUTOSIZE);
	// import Caffe SSD model
	Net net = readNetFromCaffe(model_text_file, modelFile);

	if (net.empty()) {
		printf("read caffe model data failure...\\n");
		return -1;
	}
	VideoCapture capture(0);
	if (!capture.isOpened()) {
		printf("could not open camera...\\n");
		return -1;
	}
	//采集人脸数据进行训练
	if (Flag == 1)  //添加人脸,进行训练
	{
		/*getFiles(train_data_dir, face_image_list);*/
		//使用摄像头进行采集10张需要添加的人脸
		int cnt = 10;
		int ID = 1;
		cout << "请输入姓名" << endl;
		string name = "";
		cin >> name;
		//循环遍历文件夹,为了避免ID重复,需要先获取当前已有ID;
		vector<string> face_image_list;
		getFiles(train_data_dir, face_image_list);
		if (face_image_list.size() == 0)
			ID = 1;
		else
		{
			//循环遍历,获取最大ID值
			for (int i = 0; i < face_image_list.size(); i++)
			{
				//存储格式为"F:\\\\VS\\\\opencv\\\\边角检测\\\\边角检测\\\\data\\\\name_ID_1.jpg"
				//获取ID方式,使用_进行分割
				vector<string> ID_list;
				SplitString(face_image_list[i], ID_list, "_");
				if (atoi(ID_list[1].c_str()) > ID)
					ID = atoi(ID_list[1].c_str());
			}
		}
		//新用户ID+1
		ID++;
		namedWindow("image", WINDOW_AUTOSIZE);
		while (true)
		{
			Mat tem_frame;
			capture.read(tem_frame);

			Mat inputBlob = blobFromImage(tem_frame, 1.0, Size(300, 300), Scalar(104, 117, 123), false, false);
			//crop一定要设置成false,让他进行缩放,而不能从中心进行裁剪
			net.setInput(inputBlob, "data");
			Mat prob = net.forward("detection_out");
			Mat detectionMat(prob.size[2], prob.size[3], CV_32F, prob.ptr<float>());//size[2]存放宽度,3存放高度
			//设置置信区间

			float confidence_threshold = 0.8;
			float confidence = detectionMat.at<float>(0, 2); //第二列存放的是置信度
			float tl_x = -1;
			float tl_y = -1;
			float br_x = -1;
			float br_y = -1;
			if (confidence > confidence_threshold)
			{
				size_t objIndex = (size_t)(detectionMat.at<float>(0, 1)); //第1列存放的是标签索引
				tl_x = detectionMat.at<float>(0, 3) * tem_frame.cols; //后面依次为x,y的坐标
				tl_y = detectionMat.at<float>(0, 4) * tem_frame.rows;
				br_x = detectionMat.at<float>(0, 5) * tem_frame.cols;
				br_y = detectionMat.at<float>(0, 6) * tem_frame.rows;
				Rect object_box((int)tl_x, (int)tl_y, (int)(br_x - tl_x), (int)(br_y - tl_y)); //绘制矩形,这里坐标需要转换为int
				rectangle(tem_frame, object_box, Scalar(0, 0, 255), 2, 8, 0); //绘制
			}
			imshow("image", tem_frame);
			char c = cv::waitKey(10);
			if (c == 115) //调整好姿势,按s进行采集数据
			{
				if (tl_x != -1)
				{
					Rect object_box((int)tl_x, (int)tl_y, (int)(br_x - tl_x), (int)(br_y - tl_y)); //绘制矩形,这里坐标需要转换为int
					Mat test_roi = tem_frame(object_box);
					imwrite(format((train_data_dir + "\\\\" + name + "_ % d_%d.jpg").c_str(), ID, cnt--), test_roi);
					cout << "已采集" << 10 - cnt << "张" << endl;
					cv::waitKey(500);//放在误触,设定1s采集一张
				}
				else
				{
					cout << "调整角度进行人脸采集" << endl;
				}
			}
			if (cnt == 0)
				break;
		}
		destroyWindow("image");

		//获取训练集,
		vector<string> train_image_list;
		getFiles(train_data_dir, train_image_list);
		vector<int> train_label;

		vector<Mat> tran_data;
		for (int j = 0; j < train_image_list.size(); j++)
		{
			//存储格式为"F:\\\\VS\\\\opencv\\\\边角检测\\\\边角检测\\\\data\\\\name_ID_1.jpg"
			tran_data.push_back(imread(train_image_list[j], 0));//以灰度模式进行读取
			vector<string> label_tem;
			SplitString(train_image_list[j], label_tem, "_");
			train_label.push_back(atoi(label_tem[1].c_str()));
			vector<string> name_tem;
			SplitString(label_tem[0], name_tem, "\\\\");
			ID_NAME[atoi(label_tem[1].c_str())] = name_tem[name_tem.size() - 1];
		}
		//train it
		int height = 300;
		int width = 300;
		//确保每张图片宽高一样
		for (int i = 0; i < tran_data.size(); i++)
		{
			resize(tran_data[i], tran_data[i], Size(width, height));
		}
		
		Ptr<LBPHFaceRecognizer> model = LBPHFaceRecognizer::create();
		cout << "train it....." << endl;
		model->train(tran_data, train_label);
		//模型保存
		model->save("F:\\\\VS\\\\opencv\\\\边角检测\\\\边角检测\\\\model\\\\face.xml");
		cout << "save it....." << endl;
	}
	// 创建并加载模型
	Ptr<LBPHFaceRecognizer> model = LBPHFaceRecognizer::create();
	cout << "load it....." << endl;
	model->read("F:\\\\VS\\\\opencv\\\\边角检测\\\\边角检测\\\\model\\\\face.xml");

	//获取ID_NMAE
	if (ID_NAME.size() == 0)
	{

		vector<string> train_image_list;
		getFiles(train_data_dir, train_image_list);


		for (int j = 0; j < train_image_list.size(); j++)
		{
			//存储格式为"F:\\\\VS\\\\opencv\\\\边角检测\\\\边角检测\\\\data\\\\name_ID_1.jpg"

			vector<string> label_tem;
			SplitString(train_image_list[j], label_tem, "_");

			vector<string> name_tem;
			SplitString(label_tem[0], name_tem, "\\\\");
			ID_NAME[atoi(label_tem[1].c_str())] = name_tem[name_tem.size() - 1];
		}

	}
	Mat frame;
	while (true)
	{

		capture.read(frame);
		if (frame.empty())
		{
			continue;
		}
		flip(frame, frame, 1);
		Mat inputBlob = blobFromImage(frame, 1.0, Size(300, 300), Scalar(104, 117, 123), false, false);
		//crop一定要设置成false,让他进行缩放,而不能从中心进行裁剪
		net.setInput(inputBlob, "data");
		Mat prob = net.forward("detection_out");
		Mat detectionMat(prob.size[2], prob.size[3], CV_32F, prob.ptr<float>());//size[2]存放宽度,3存放高度

		//设置置信区间

		float confidence_threshold = 0.8;
		for (int i = 0; i < detectionMat.rows; i++)
		{
			float confidence = detectionMat.at<float>(i, 2); //第二列存放的是置信度
			if (confidence > confidence_threshold)
			{
				size_t objIndex = (size_t)(detectionMat.at<float>(i, 1)); //第1列存放的是标签索引
				float tl_x = detectionMat.at<float>(i, 3) * frame.cols; //后面依次为x,y的坐标
				float tl_y = detectionMat.at<float>(i, 4) * frame.rows;
				float br_x = detectionMat.at<float>(i, 5) * frame.cols;
				float br_y = detectionMat.at<float>(i, 6) * frame.rows;
				Rect object_box((int)tl_x, (int)tl_y, (int)(br_x - tl_x), (int)(br_y - tl_y)); //绘制矩形,这里坐标需要转换为int
				rectangle(frame, object_box, Scalar(0, 0, 255), 2, 8, 0); //绘制
				//预测是谁
				Mat testSample_roi;
				try {
					testSample_roi = frame(object_box);
				}
				catch (Exception e)
				{
					testSample_roi = frame;
				}
				Mat gray;
				cvtColor(testSample_roi, gray, CV_BGR2GRAY);//因为训练的时候采用的是灰度
				resize(gray, gray, Size(300, 300));
				//int predictedLabel = model->predict(gray);
				int predictedLabel;
				double conf;
				model->predict(gray, predictedLabel,conf);

				cout << predictedLabel <<"      " << conf << endl;
				if(conf<40)
					putText(frame, ID_NAME[predictedLabel], Point(tl_x, br_y), FONT_HERSHEY_SIMPLEX, 1.0, Scalar(255, 0, 0), 2);
				else
					putText(frame, "Unknow", Point(tl_x, br_y), FONT_HERSHEY_SIMPLEX, 1.0, Scalar(0, 255, 0), 2);
			}
		}
		imshow("image", frame);
		cv::waitKey(1);

	}

	return 0;
}

void SplitString(const string& s, vector<string>& v, const string& c)
{
	string::size_type pos1, pos2;
	pos2 = s.find(c);
	pos1 = 0;
	while (string::npos != pos2)
	{
		v.push_back(s.substr(pos1, pos2 - pos1));

		pos1 = pos2 + c.size();
		pos2 = s.find(c, pos1);
	}
	if (pos1 != s.length())
		v.push_back(s.substr(pos1));
}

void getFiles(string path, vector<string>& files)
{
	//文件句柄
	long long   hFile = 0;
	//文件信息,声明一个存储文件信息的结构体
	struct _finddata_t fileinfo;
	string p;//字符串,存放路径
	if ((hFile = _findfirst(p.assign(path).append("\\\\*").c_str(), &fileinfo)) != -1)//若查找成功,则进入
	{
		do
		{
			//如果是目录,迭代之(即文件夹内还有文件夹)
			if ((fileinfo.attrib & _A_SUBDIR))
			{
				//文件名不等于"."&&文件名不等于".."
				//.表示当前目录
				//..表示当前目录的父目录
				//判断时,两者都要忽略,不然就无限递归跳不出去了!
				if (

以上是关于SSD+LBP实现人脸识别的主要内容,如果未能解决你的问题,请参考以下文章

使用 LBP、深度学习和 OpenCV 进行实时人脸识别

人脸识别基于matlab GUI LBP人脸识别含Matlab源码 1282期

opencv人脸识别用哪种方法比较好?Eigenfaces?Fisherfaces?LBP?

lbp和pca用啥软件

图像识别基于LBP+LPQ算法融合人脸表情识别matlab源码

人脸表情识别基于matlab GUI LBP+SVM脸部动态特征人脸表情识别含Matlab源码 1369期