本节讲如何训练人脸模型,所用平台为opencv3.4.0+VS2017+win10。因为人脸模型训练模块在opencv_contrib模块中,而在opencv2.x版本中是有这个模块的,但考虑到此模块的不稳定性,opencv3.x版本将此模块去掉了,所以若需要应用opencv_contrib模块,需自行下载opencv_contrib_3.4.0模块,然后用cmake软件将opencv3.4和opencv_contrib_3.4.0一起进行重新编译。
此处需注意,opencv_contrib的版本要和opencv的版本相一致,不然程序无法运行。
一.说明
首先说明:opencv3.x较opencv2.x在FaceRecogna的使用区别:
- 首先需要配置contrib模块,这在很多博客中有介绍
- OpenCV3.4.0的人脸识别API相较于之前opencv2.x的版本有些变动
- 原3.x以前的是createEngenFaceRecognize()
- 现更改为EigenFaceRecognize::create()
- 其他几个创建函数也做了如上改变。
- 包含头文件#include< face/face.hpp >
- 命名空间using namespace cv::face;
opencv中有三种方法可以使用:
EigenFaceRecognizer::create()、FisherFaceRecognizer::create()、LBPHFaceRecognizer::create()。
我选择的是EigenFaceRecognizer::create() ,其他的两种也一样,只需要改一下定义即可。
Ptr<face::FaceRecognizer> model = EigenFaceRecognizer::create(20);// 创建特征脸模型 20张主成分脸
要进行人脸识别,需要一个人脸识别的模型,本实验采用PCA模型,取前20个主成分脸。
训练人脸模型的流程图如下:
人脸模型训练的步骤如下:
1.前期准备工作,将所有的人脸样本和类别标签生成一个.csv文件。
生成csv文件方法:http://blog.csdn.net/u012679707/article/details/79519143
本实验所采用的实验数据来自ORL人脸库,人脸图为灰度图,大小为92*112。
2.训练时可直接读取csv文件,实现样本和类别标签的获取。
读取csv文档方法: http://blog.csdn.net/u012679707/article/details/78711365
3.创建特征脸模型,选择20个主成分 (faceRecognizer 为cv2中的contrib模块)
Ptr<FaceRecognizer> model=createEigenFaceRecognizer(20); // 创建特征脸模型 20张主成分脸
4.通过样本和类别标签进行训练,最终得到训练好的主成分脸模型。
model->train(images,labels); //训练人脸模型,通过images和labels来训练人脸模型
5.将模型保存为.xml文件
model->save("MyFacePcaModel.xml"); //将训练模型保存到MyFacePcaModel.xml
注意:contrib模块中的人脸识别模型有三种,PCA、fisher、LBP。本系统选择的是主成分脸模型(PCA)
//face_recog_from_video.cpp 定义控制台应用程序的入口点。 // train_PCA_model.cpp //用于训练人脸识别所需的pCA模型 #include "stdafx.h" #include"opencv2/opencv.hpp" #include"opencv2/core.hpp" #include"opencv2/imgproc.hpp" #include"opencv2/highgui.hpp" #include"opencv2/face.hpp" // 包含FaceRecognizer #include<iostream> using namespace std; using namespace cv; using namespace cv::face; static void read_csv(const string& filename, vector<Mat>& images, vector<int>& labels, int max, int min, char separator); int CountMax = 9, CountMin = 0; int main(int argc, char *argv[]) { string csvFile = ".\\data\\at.txt"; vector<Mat> images; vector<int> labels; // 读取csv文件 try { read_csv(csvFile, images, labels, CountMax, CountMin, ';'); //读取csvFile中所有的img和label } catch (cv::Exception& e) // 异常检查 { // cerr:输出到标准错误的ostream对象,常用于程序错误信息 cerr << "Error opening file\" " << csvFile << "\".reason: " << e.msg << endl; //异常 发生的原因 exit(-1); } // 若未读取到足够图片,也退出 if (images.size() <= 1) { string errMsg = "THis demo needs at least 2 images to work.please add images!"; CV_Error(CV_StsError, errMsg); } cout << "train1.读取ok" << endl; // 训练数据,并将训练好的人脸模型保存到.xml中 // Ptr<>为模板类,定义model为指向FaceRecognizer类的指针。model为指针! // Ptr<EigenFaceRecognizer> model= EigenFaceRecognizer::create();// createEigenFaceRecognizer(20); // 创建特征脸模型 20张主成分脸 EigenFaceRecognizer::create();// Ptr<EigenFaceRecognizer> model = EigenFaceRecognizer::create(); model->train(images, labels); //训练 model->save("MyFacePcaModel.xml"); //将训练模型保存到MyFacePcaModel.xml cout << "train2.创建脸模型ok" << endl; return 0; } /* 函数:static void read_csv(const string& filename,vector<Mat>images, vector<int> labels,int CountMax,int CountMin, char separator=';') 功能:读取csv文件的图像路径和标签。主要使用stringstream和getline() 参数说明:filename--要读取的csv文件 images----读取的图片(向量) labels----读取的图片对应标签 (向量) CountMax,int CountMin--读取的每一类别的图片下标的最大值和最小值(默认每个类别共10张照片) separator-分隔符,起控制读取的作用。可自定义为逗号空格等,(此程序中)默认为分号 返回值:空 */ /* 备注:(函数内部涉及到的部分类和方法说明) 1. stringstream:字符串流。 功能:将内存中的对象与流绑定。 2. getline(): 函数原型:istream &getline( ifstream &input,string &out,char dielm) 参数说明:Input--输入文件 out----输出字符串 dielm--读取到该字符停止(起到控制作用),默认是换行符‘\n’ 功能: 读取文件Input中的字符串到out中。 返回值:返回Input,若是文件末尾会返回文件尾部标识eof 3. ifstream: 从硬盘打开文件(读取),从磁盘输入文件,读到内存中 ofstream: 从内存打开文件(读取),从内存输入文件,读到磁盘中) */ static void read_csv(const string& filename, vector<Mat>& images, vector<int>& labels, int max, int min, char separator) { std::ifstream file(filename.c_str(), ifstream::in); // 以in模式(读取文件模式)打开文件 ,实际是将filename文件关联给 流file if (!file) { string error_message = "No valid input file was given,please check the given filename"; CV_Error(CV_StsBadArg, error_message); } int ii = 0; /**********************读取文件.txt内容****************************/ string line, path, label; // [1]读取file文件中的一行字符串给 line while (getline(file, line, '\n')) // 控制:直到读到file文件末尾(eof标识),才跳出while { // [2]将line整行字符串读取到lines(流)中 stringstream lines(line); //区别->lines是流,读取字符时,指针会随流而动;而line是string,固定的,下文中的读取每次都是从line头开始 // [3]读取文件中的路径和标签 getline(lines, path, separator); //此时光标已走到path之后的位置(即;分号处) getline(lines, label); // [4]将图片和标签加入imgs 和 labels if ((path.empty() == 0) && (label.empty() == 0)) { if (ii % 10 <= max && ii % 10 >= min) //默认每个类别共10张照片 { Mat img = imread(path, 0); //第二个参数为0 !!! //Mat img = imread(ImageFileAddress, CV_LOAD_IMAGE_GRAYSCALE),CV_LOAD_IMAGE_GRAYSCALE值为 0,指灰图(原本为“CV_LOAD_IMAGE_UNCHANGED”) if (img.data != 0) { images.push_back(img); // 将图片 添加到images中 labels.push_back(atoi(label.c_str())); } } if (ii<9) ii++; else ii = 0; } } }
注意:程序中数据读取,主要是先生成csv文件,再读取csv文件,具体可见:
运行结果:最终生成的MyFacePcaModel.xml文件内容如下图所示,其中
<num_components>20</num_components> 20是主特征脸的个数
<rows>1</rows>
<cols>10304</cols> 1*10304 这表示每个特征脸的大小,一行表示一张脸的数据,维度为10304(92*112)
MyFacePcaModel.xml如下:
这样,我们就得到了人脸模型文件“MyFacePcaModel.xml”,可用于后边的预测和识别。
------------------------------------------- END -------------------------------------