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实现人脸识别的主要内容,如果未能解决你的问题,请参考以下文章
人脸识别基于matlab GUI LBP人脸识别含Matlab源码 1282期
opencv人脸识别用哪种方法比较好?Eigenfaces?Fisherfaces?LBP?