关于使用OpenCV的LBPHFaceRecognizer实现人脸的采集、训练与更新、应用的C++实现DEMO

关于使用OpenCV的LBPHFaceRecognizer实现人脸的采集、训练与更新、应用的C++实现DEMO

作者:Simon Song

说明

在实际人脸识别中,有很多可用方法,如OpenCV自带的EigenFaceRecognizer(基于PCA降维),FisherFaceRecognizer(基于LDA降维),LBPHFaceRecognizer(基于LBPH特征),其中只有LBPHFaceRecognizer是支持直接更新的模型算法;再如faceNet深度网络模型(128个特征输出)加分类器(如SVM)方式。其他商业应用如旷视科技Megvii Face++,百度AI,等等很多。
我们需要应用的场景是结果好,对于硬件要求不是很高,方便更新模型,低成本的方式。因此我选择了OpenCV自带的LBPHFaceRecognizer算法,这种算法优点是不会受到光照、缩放、旋转和平移的影响,这钟算法最大的好处就是可以在性能不是很高的ARM板上也可执行,通用化非常好。

模块设计

模块分为三个部分:
1.采集图片部分:采集部分用于采集人脸图片,对应采集好的图片需要人工挑选和整理,并形成文件清单文件(如filelist.txt);对于更新部分,制作updatelist.txt文件;制作分类号与姓名的字典对应关系(dict.txt)。
此处的文件清单每行格式为:文件路径,分类号
此处的字典文件每行格式为:分类号:姓名
2.训练与更新部分:使用整理好的训练集合文件清单进行训练,生成xml模型文件;更新只是在原有模型基础上增加分类;对模型进行图片准确率测试。
3.应用部分:读入训练好的模型,对人脸进行识别。

程序建立环境

vs2015,OpenCV341,C++

一、采集部分代码

#include
#include
#include
#include//创建目录等
#include//文件是否存在

using namespace std;
using namespace cv;

/*
此文件用于采集人脸数据,人工点击采集的图片,再有人工检测可用性即可。
*/
string cascade_file = "G:/opencv_trainning/vs2015/myself_project/FaceRccognizer_LBPH/face_collection/haarcascade_frontalface_alt.xml";//定义级联文件路径
string save_path = "G:/opencv_trainning/vs2015/myself_project/FaceRccognizer_LBPH/sample/%d/";//保存图片路径

int main(int argc,char** argv) {
	//定义摄像头设备
	VideoCapture capture(2);
	//调整摄像头分辨率
	//capture.set(CV_CAP_PROP_FRAME_WIDTH,1024);//设置宽
	//capture.set(CV_CAP_PROP_FRAME_HEIGHT,768);//设置高
	if (!capture.isOpened()) {//未打开,报错
		cout << "capture open fault!" << endl;
		return -1;
	}
	//定义必要的变量
	CascadeClassifier cascade_face = CascadeClassifier(cascade_file);//定义级联变量并读取级联文件
	vector<Rect> faces;//定义人脸位置矩形向量
	Mat frame;//定义帧图,用于U读取
	int count = 0;//定义统计数量
	long current_tick_count = 0;//记录子目录名的数字
	bool save_state = false;//保存状态,用于用于控制
	//循环读取帧图
	while (capture.read(frame)){
		//printf("width=%d,height=%d\n",frame.cols,frame.rows);//打印提示
		//左右翻转,参数:(原图,目标图,反转代码)
		flip(frame,frame,1);
		//级联发现脸,参数:(图,矩形向量,尺度系数,高斯金字塔最小临近值,标记(0),最小尺寸,最大尺寸)
		cascade_face.detectMultiScale(frame,faces,1.09,1,0,Size(200,200),Size(400,400));
		//保存脸和绘制脸位置
		for (int i = 0; i < faces.size();i++) {//循环脸向量位置
			//获得人脸部分矩形
			Rect rect = faces[i];
			rect.x = rect.x + rect.width*0.16;//重定x
			rect.y = rect.y + rect.height*0.10;//重定y
			rect.width = rect.width*0.70;//重定宽
			rect.height = rect.height*0.78;//重定高
			//当为保存状态,且为10的倍数,且为小于401时,保存图片(保存40张图)
			if (save_state&&count%10==0&&count<401){
				Mat dst;//定义新图
				//调整脸图片大小,128*128,参数:(原图,目标图,尺寸,x比例,y比例,线性插值类型)
				resize(frame(rect),dst,Size(128,128),0,0,INTER_LINEAR);
				//保存到指定位置
				string path = format(save_path.c_str(), current_tick_count);//获得完整路径,以当前时间数的决定值为子目录名
				//判断路径是否存在
				/*
					R_OK 只判断是否有读权限  
					W_OK 只判断是否有写权限 
					X_OK 判断是否有执行权限 
					F_OK 只判断是否存在
					在宏定义里面分别对应:
					0x00 只存在
					0x02 写权限
					0x04 读权限
					0x06 读和写权限
				*/
				if (_access(path.c_str(), _A_NORMAL)==-1) {//当路径不存在(-1),创建路径
					_mkdir(path.c_str());//创建路径
				}
				path = path + string(format("%d.jpg",count/10));//添加路径文件名
				imwrite(path.c_str(),dst);//保存图片,参数:(路径名,矩阵图)
				//提示正在保存图片,头不要动,以眼睛作为基准线,变换表情。
				string content("Don't move your head. Use your eyes as a baseline to change your expression.");//指定文本
				//添加文本到图片,参数:(图,文本,原点,字体,缩放比,颜色,线宽,线型,左底起源状态(倒置效果))
				putText(frame,content, Point(20, 30), CV_FONT_NORMAL, 0.5, Scalar(0,255, 0), 1, 8, false);	
			}
			else if (save_state&& count<401) {//当保存状态为真,且统计数小于401时,说明在记录范围内,只提示
				//提示正在保存图片,头不要动,以眼睛作为基准线,变换表情。
				string content("Don't move your head. Use your eyes as a baseline to change your expression.");//指定文本
				//添加文本到图片,参数:(图,文本,原点,字体,缩放比,颜色,线宽,线型,左底起源状态(倒置效果))
				putText(frame, content, Point(20, 30), CV_FONT_NORMAL, 0.5, Scalar(0, 255, 0), 1, 8, false);
			}else {//否则提示等待状态
				//添加文本到图片,参数:(图,文本,原点,字体,缩放比,颜色,线宽,线型,左底起源状态(倒置效果))
				putText(frame,"Wait for the key to be pressed.",Point(20,30),CV_FONT_NORMAL,1.0,Scalar(255,0,0),1,8,false);
			}
			//绘制矩形,参数:(图,矩形变量,颜色,线宽,线型,偏移量)
			rectangle(frame, rect, Scalar(0, 255, 0), 1, 8, 0);
		}
		//显示图
		imshow("frame",frame);
		//按键判断
		char c = waitKey(33);//等待33毫秒并获得按键值,-1时表示没有输入
		if (c==27) {//当为ESC(27)时,退出循环
			break;
		}else if(c!=-1) {//当用户点击除ESC的任意键时,应拍照保存人脸部分或改变状态
			save_state = (save_state ? false : true);//反转保存状态,当保存状态为真时,给假,否则给真
			count = 0;//统计数量给0,从新计数
			current_tick_count = (save_state?abs(getTickCount()):0);//当保存状态为真是,获得当前时间统计值的绝对值,否则为0
		}
		count++;//统计变量加1
	}
	//释放摄像头资源
	capture.release();
	return 0;
}

二、训练与更新代码

#include
#include
#include
#include
#include//文件是否存在

using namespace std;
using namespace cv;
using namespace cv::face;//脸模块

/*
此文件用于训练LBPHFaceRecognizer模型。
*/
//定义路径
string train_image_path = "G:/opencv_trainning/vs2015/myself_project/FaceRccognizer_LBPH/sample/filelist.txt";//训练集清单文件
string update_image_path = "G:/opencv_trainning/vs2015/myself_project/FaceRccognizer_LBPH/sample/updatelist.txt";//更新集清单文件
string face_model = "G:/opencv_trainning/vs2015/myself_project/FaceRccognizer_LBPH/sample/face_model.xml";//face分类模型文件
string test_image_path = "G:/opencv_trainning/vs2015/myself_project/FaceRccognizer_LBPH/testimage/testlist.txt";//测试图路径
//声明函数
bool getMatAndLabels(string& filelist_txt,vector<Mat>& mats,vector<int>&labels);//获得图片矩阵和标签
bool train_lbphface(vector<Mat>& mats, vector<int>&labels);//模型训练
bool update_lbphface(vector<Mat>& mats, vector<int>&labels);//更新训练模型
vector<int> lbphface_predict(vector<Mat>& testImages);//lbphface预测函数

int main(int argc,char** argv) {
	//1.读取标签文件及对应的人脸图片
	vector<Mat> train_mats;//定义矩阵向量
	vector<int> train_labels;//定义标签向量
	int64 time_rec=getTickCount();//记录当前时间,用于统计训练时间
	bool result = false;//定义结果变量
	//2.判断操作
	//2.1当没有模型时,读取训练数据,否则不读取训练数据
	if(_access(face_model.c_str(),_A_NORMAL)==-1){//模型不存在的处理
		//2.1.1获取矩阵和标签(自定义),参数:(文件路径,训练矩阵向量,训练标签向量)
		result=getMatAndLabels(train_image_path,train_mats,train_labels);
		if (!result) {//当结果为假时
			cout << "getMatAndLabels is fault!" << endl;
			return -1;//直接返回
		}
		//2.1.2训练模型(自定义)
		result = train_lbphface(train_mats, train_labels);//参数:(矩阵向量,标签向量)
		if (!result) {//当结果为假时
			cout << "train_face is fault!" << endl;
			return -1;//直接返回
		}
	}
	//2.2检查是否有更新文件列表,有则读入,无则无操作
	if (_access(update_image_path.c_str(), _A_NORMAL) == 0) {//当有更新文件时,参数:(文件路径指针,检查标记)
		//2.2.1获取矩阵和标签(自定义),参数:(文件路径,训练矩阵向量,训练标签向量)
		result = getMatAndLabels(update_image_path, train_mats, train_labels);
		if (!result) {//当结果为假时
			cout << "getMatAndLabels is fault!" << endl;
			return -1;//直接返回
		}
		//2.2.2更新训练模型(自定义)
		result = update_lbphface(train_mats, train_labels);//参数:(矩阵向量,标签向量)
		if (!result) {//当结果为假时
			cout << "update_face is fault!" << endl;
			return -1;//直接返回
		}
	}
	cout << "total_train_time(s)="<<(getTickCount()-time_rec)/getTickFrequency()<<"s"<< endl;//计算训练时间并打印
	time_rec = getTickCount();//记录当前时间,用于统计总预测时间
	//3.给定图片测试结果
	//读取标签文件及对应人脸
	vector<Mat> test_mats;//测试矩阵
	vector<int> test_labels;//测试标签
	vector<int> pre_labels;//预测标签
	//获取矩阵和标签(自定义),参数:(文件路径,训练矩阵向量,训练标签向量)
	result=getMatAndLabels(test_image_path,test_mats,test_labels);
	if (!result) {//当结果为假时
		cout << "test:getMatAndLabels is fault!" << endl;
		return -1;//直接返回
	}
	//预测处理(自定义),参数:(测试图向量),返回预测结果向量
	pre_labels = lbphface_predict(test_mats);
	if (pre_labels.empty()) {//当返回向量为空
		cout << "pre_lables is empty!" << endl;
		return -1;//返回-1
	}
	//计算准确率
	Mat pre_mat = Mat::zeros(test_labels.size(),1,CV_8UC1);//定义一个预测矩阵,N行1列,1通道
	for (int i = 0; i < test_mats.size();i++) {//循环矩阵位置
		//比较是否相等
		if (test_labels[i]==pre_labels[i]) {//当相等时
			pre_mat.at<uchar>(i)=1;//预测矩阵位置给1
		}
		//printf("test_labels[%d]=%d,pre_labels[%d]=%g\n",i,test_labels[i],i,pre_labels[i]);//打印提示
	}
	//cout << "pre_mat=" << pre_mat <<","<
	Scalar tempVal=mean(pre_mat);//获得均值数据
	float matMean = tempVal.val[0];//获得数据中的第一个位置的值作为矩阵均值(单通道图),此均值为准确率
	cout << "total_predict_time(s)=" << (getTickCount() - time_rec) /getTickFrequency() <<"s"<< endl;//计算总预测时间并打印
	printf("Mat Mean(accuracy) is %.2f\n", matMean);//打印提示
	imshow("pre_mat", pre_mat);//为了时waitKey()有效等待,此处有个显示
	waitKey(0);//等待用户按键退出程序
	return 0;
}

bool getMatAndLabels(string& filelist_txt, vector<Mat>& mats, vector<int>&labels) {//获得图片矩阵和标签
	//定义必要的变量,参数:(文件名字符串,模式(in/out)),其他参数默认
	ifstream file(filelist_txt,ifstream::in);
	if (!file.is_open()) {//当不是被打开
		cout << "file is not open!" << endl;
		return false;//返回false
	}
	//读取文件每一行
	string lines;//定义行字符串变量
	while (!file.eof()) {//当不是EOF时,继续读取
		//读取一行,参数:(文件流,字符串变量)
		getline(file,lines);
		//定义字符串流
		stringstream line_stream(lines);
		//读取图片路径
		string path;//定义路径变量
		getline(line_stream,path,',');//读取路径,用逗号分隔符为结束
		//读取标签
		string label;//定义标签变量
		getline(line_stream,label);//读取其余部分,标签部分
		//读取路径指定的图片(彩色)
		Mat img = imread(path,IMREAD_GRAYSCALE);//读取灰度图
		//调整图片为128*128,参数:(原图矩阵,目标图矩阵,图尺寸,x比例,y比例,插值类型)
		resize(img,img,Size(128,128),0,0,INTER_LINEAR);
		//存入向量
		mats.push_back(img.clone());//存入矩阵向量
		labels.push_back(atoi(label.c_str()));//存入标签向量
	}
	//判读向量状态
	if (mats.empty()||labels.empty()||mats.size()!=labels.size()) {//当向量为空,或两向量个数不相等时,返回假
		return false;//返回false
	}
	return true;//正常返回真
}

bool train_lbphface(vector<Mat>& mats, vector<int>&labels) {//模型训练
	//判断参数
	if (mats.empty()||labels.empty()||mats.size()!=labels.size()) {//当向量为空,或尺寸不相等时,返回假
		return false;//返回false
	}
	//读取模型
	Ptr<LBPHFaceRecognizer> model = LBPHFaceRecognizer::create();
	//训练模型
	model->train(mats,labels);//参数:(矩阵向量,标签向量)
	//保存模型
	model->save(face_model);//参数:(文件名字符串)
	return true;//返回真
}

bool update_lbphface(vector<Mat>& mats, vector<int>&labels) {//更新训练模型
	//判断参数
	if (mats.empty() || labels.empty() || mats.size() != labels.size()) {//当向量为空,或尺寸不相等时,返回假
		return false;//返回false
	}
	//读取模型
	Ptr<LBPHFaceRecognizer> model = Algorithm::load<LBPHFaceRecognizer>(face_model);//参数:(模型全路径)
	//更新训练模型
	model->update(mats, labels);//参数:(矩阵向量,标签向量)
	//保存模型
	model->save(face_model);//参数:(文件名字符串)
	return true;//返回真
}

vector<int> lbphface_predict(vector<Mat>& testImages){//lbphface预测函数
	//定义结果向量
	vector<int> result;
	//判断向量合法性
	if (testImages.empty()) {//当向量为空时
		return result;//返回空的结果向量
	}
	//读取svm模型
	Ptr<LBPHFaceRecognizer> model = Algorithm::load<LBPHFaceRecognizer>(face_model);//参数:模型文件
	if(model->empty()){//当模型为空
		cout << "face model load fault!" << endl;
		return result;//返回空的结果向量
	}
	//定义必要的变量
	int label = -1;//标签变量
	double confidence_distance = 0;//置信值
	//循环遍历矩阵向量
	for (int i = 0; i < testImages.size();i++) {
		//预测分类结果,参数:(图矩阵,标签变量,置信概率变量)
		model->predict(testImages[i],label, confidence_distance);
		//cout << "confidence_value=" << confidence_distance << endl;//测试
		//cout << "label=" << label << endl;//测试
		if (confidence_distance>80) {//当置信距离大于80时,给标签-1表示未知
			label = -1;//标签给-1
		}
		//存入向量
		result.push_back(label);
	}
	return result;//返回向量
}

三、应用代码

#include
#include
#include
#include
#include//文件是否存在

using namespace std;
using namespace cv;
using namespace cv::face;//脸模块
/*
此文件用于人脸识别应用
*/
//定义路径
string face_model = "G:/opencv_trainning/vs2015/myself_project/FaceRccognizer_LBPH/sample/face_model.xml";//LBPH分类模型文件
string dict_path = "G:/opencv_trainning/vs2015/myself_project/FaceRccognizer_LBPH/sample/dict.txt";//字典路径
string cascade_file = "G:/opencv_trainning/vs2015/myself_project/FaceRccognizer_LBPH/sample/haarcascade_frontalface_alt.xml";//定义级联文件路径
//声明函数
vector<string> getDict(string& dict_path);//获得字典,返回字符串向量
vector<int> faces_predict(Ptr<LBPHFaceRecognizer>& face_model, vector<Mat>& testImages);//LBPHFaceRecognizer预测函数


int main(int argc,char** argv) {
	//判断级联文件是否存在
	if (_access(cascade_file.c_str(),_A_NORMAL)==-1) {//不存在(-1),直接退出
		cout << "cascade xml is not exist" << endl;
		return -1;
	}
	//判断face模型文件是否存在
	if (_access(face_model.c_str(),_A_NORMAL)==-1) {//不存在(-1),直接退出
		cout << "face_model xml is not exist" << endl;
		return -1;
	}
	//读入face模型
	Ptr<LBPHFaceRecognizer> model = Algorithm::load<LBPHFaceRecognizer>(face_model);//参数:(模型文件)
	if (model->empty()) {//当模型为空
		cout << "face model load fault!" << endl;
		return -1;//返回-1
	}
	//读取字典(自定义函数)
	vector<string> dict;//定义字典向量
	dict = getDict(dict_path);//参数:(字典路径)
	if (dict.empty()) {//当字典为空时
		cout << "dictionary file load fault!" << endl;
		return -1;
	}
	//定义摄像头设备
	VideoCapture capture(2);
	//摄像头像素
	capture.set(CAP_PROP_FRAME_WIDTH, 640);//宽1024
	capture.set(CAP_PROP_FRAME_HEIGHT, 480);//高768
	if (!capture.isOpened()) {//当设备为打开
		cout << "capture open fault!" << endl;
		return -1;//返回-1
	}
	//定义必要的变量
	Mat frame;//帧图
	CascadeClassifier cascade_face(cascade_file);//定义级联分类器
	vector<Rect> faces;//定义脸向量
	//制作一个掩码图,用于指定有效区域
	capture.read(frame);//读取一帧获取图片尺寸
	cout << "frame.size()=" << frame.size() << endl;
	Mat mask=Mat::zeros(frame.size(),CV_8UC3);//定义掩码图,与帧图保持一致
	//绘制一个矩形并填充,参数:(x,y,width,height)
	Rect mask_rect(frame.cols/6,frame.rows/6,frame.cols*0.67,frame.rows*0.67);//定义矩形信息
	rectangle(mask,mask_rect,Scalar(255,255,255),-1);//填充有效区域,参数:(图,矩形变量,颜色,线宽(-1填充),线型,偏移量),线型和偏移量默认
	rectangle(mask, mask_rect, Scalar(255, 0, 255), 20);//有效区域边缘绘色,参数:(图,矩形变量,颜色,线宽(-1填充),线型,偏移量),线型和偏移量默认
	//imshow("mask",mask);
	namedWindow("frame",CV_WINDOW_NORMAL);//定义显示窗口
	//setWindowProperty("frame",CV_WND_PROP_FULLSCREEN,CV_WINDOW_FULLSCREEN);//设置全屏显示
	//循环读取帧
	while (capture.read(frame)) {//可以读到时继续
		//左右翻转,参数:(图,目标图,翻转代码),翻转代码1表示左右,0表示上下,-1对角换
		flip(frame,frame,1);
		//合并图,参数:(图1,占比,图2,占比,目标图,标准差,数据类型(-1不变))
		addWeighted(frame,0.85,mask,0.15,0,frame,-1);
		//级联发现脸,参数:(图,矩形向量,尺度系数,高斯金字塔最小临近值,标记(0),最小尺寸,最大尺寸)
		cascade_face.detectMultiScale(frame(mask_rect), faces, 1.09, 1, 0, Size(200, 200), Size(400, 400));//此处在掩码区域识别脸
		if (faces.empty()) {//当向量为空时
			imshow("frame", frame);//显示
			char c = waitKey(33);//延时等待33ms并获取输入,无输入为-1
			if (c == 27) {//当输入为ESC(27)时,中断while
				break;//中断while
			}
			continue;//继续下一次
		}
		//保存脸和绘制脸位置
		vector<Mat> vec_faces;//定义脸向量,方便预测
		for (int i = 0; i < faces.size(); i++) {//循环脸向量位置
			//获得人脸部分矩形
			Rect rect = faces[i];
			rect.x = mask_rect.x+rect.x + rect.width*0.16;//重定x
			rect.y = mask_rect.y+rect.y + rect.height*0.10;//重定y
			rect.width = rect.width*0.70;//重定宽
			rect.height =rect.height*0.78;//重定高
			//发现脸并存入矩阵向量
			Mat dst;//定义新图
			//调整脸图片大小,128*128,参数:(原图,目标图,尺寸,x比例,y比例,线性插值类型)
			resize(frame(rect), dst, Size(128, 128), 0, 0, INTER_LINEAR);
			vec_faces.push_back(dst.clone());//添加到脸向量,需要clone()操作
			//绘制识别脸位置矩形,参数:(图,矩形变量,颜色,线宽,线型,偏移量)
			rectangle(frame,Rect(rect.x,rect.y,rect.width,rect.height),Scalar(255,0,0),1,8,0);
		}
		//预测结果,参数:(face模型,脸矩阵向量),返回int结果向量
		vector<int> pre_result = faces_predict(model,vec_faces);
		if (pre_result.empty()) {//当返回向量为空
			continue;//继续下一次操作
		}
		//绘制结果到图中
		for (int i = 0; i < faces.size(); i++) {//循环脸向量位置
			Rect rect = faces[i];//获得当前脸矩形
			//调整一下x和y
			rect.x += mask_rect.x;//加入大尺寸位置
			rect.y += mask_rect.y;//加入大尺寸位置
			//根据预测结果添加文字到图片,参数:(图,文本,原点,字体,缩放比,颜色,线宽,线型,左底起源状态(倒置效果))
			putText(frame,dict[static_cast<int>(pre_result[i])+1], Point(rect.x, rect.y), CV_FONT_NORMAL, 0.5, Scalar(0, 255, 0), 1, 8, false);
			//绘制矩形,参数:(图,矩形变量,颜色,线宽,线型,偏移量)
			rectangle(frame, rect, Scalar(255, 255, 0), 1, 8, 0);
		}
		//显示
		imshow("frame",frame);
		char c=waitKey(33);//延时等待33ms并获取输入,无输入为-1
		if (c==27) {//当输入为ESC(27)时,中断while
			break;//中断while
		}
		//后台或其他指令操作

	}
	return 0;
}

vector<string> getDict(string& dict_path) {//获得字典,返回字符串向量
	//定义必要的变量
	vector<string> result;//定义字符串结果向量
	ifstream file(dict_path, ifstream::in);//定义文件流,参数:(文件名字符串,模式(in/out)),其他参数默认
	if (!file.is_open()) {//当不是被打开
		cout << "file is not open!" << endl;
		return result;//返回false
	}
	//读取文件每一行
	string lines;//定义行字符串变量
	while (!file.eof()) {//当不是EOF时,继续读取
		//读取一行,参数:(文件流,字符串变量)
		getline(file, lines);
		//定义字符串流
		stringstream line_stream(lines);
		//读取图片路径
		string label;//定义标签变量
		getline(line_stream, label, ':');//读取标签,用分号分隔符为结束
		//读取标签
		string name;//定义名字变量
		getline(line_stream, name);//读取其余部分,名字部分
		//存入向量
		result.push_back(name);
	}
	return result;//返回结果向量
}

vector<int> faces_predict(Ptr<LBPHFaceRecognizer>& face_model, vector<Mat>& testImages){//LBPHFaceRecognizer预测函数
	//定义结果向量
	vector<int> result;
	//判断向量合法性
	if (testImages.empty()) {//当向量为空时
		return result;//返回空的结果向量
	}
	//判断模型可用性
	if (face_model->empty()) {//当模型为空
		cout << "face model is fault!" << endl;
		return result;//返回空的结果向量
	}
	//定义变量
	int label = -1;//分类值
	double confidence_distance = 0;//置信概率值
	//循环遍历矩阵向量
	for (int i = 0; i < testImages.size(); i++) {
		Mat img(testImages[i]);//定义新图
		//转化为灰度图
		Mat gray;
		cvtColor(img,gray,CV_BGR2GRAY);//参数:(原图,目标图,转换模式)
		//预测分类结果,参数:(图矩阵,标签变量,置信概率)
		face_model->predict(gray,label, confidence_distance);
		//printf("label=%d,confidence_distance=%g,", label, confidence_distance);//打印测试
		if (confidence_distance>80) {//当置信距离大于80时
			label = -1;//标签为-1,表示未知
		}
		//存入向量
		result.push_back(label);
	}
	//printf("\n");//打印测试
	return result;//返回向量
}

采集效果展示

关于使用OpenCV的LBPHFaceRecognizer实现人脸的采集、训练与更新、应用的C++实现DEMO_第1张图片

训练与更新展示

关于使用OpenCV的LBPHFaceRecognizer实现人脸的采集、训练与更新、应用的C++实现DEMO_第2张图片

应用展示

关于使用OpenCV的LBPHFaceRecognizer实现人脸的采集、训练与更新、应用的C++实现DEMO_第3张图片

采样整理展示图

关于使用OpenCV的LBPHFaceRecognizer实现人脸的采集、训练与更新、应用的C++实现DEMO_第4张图片

测试整理展示图

关于使用OpenCV的LBPHFaceRecognizer实现人脸的采集、训练与更新、应用的C++实现DEMO_第5张图片我是Simon,在这里期待与您的交流。

你可能感兴趣的:(关于使用OpenCV的LBPHFaceRecognizer实现人脸的采集、训练与更新、应用的C++实现DEMO)