【opencv】基于opencv2的人脸识别系统

    之前,曾写过一个较为完整的人脸识别小系统。开发环境为opencv2.4.9和VS2012,并加入了一个新模块cvui.h,用此模块为人脸识别系统写了一个简单界面。界面如下:

                    【opencv】基于opencv2的人脸识别系统_第1张图片

                    【opencv】基于opencv2的人脸识别系统_第2张图片

  此界面用到的元素比较简单,包含按钮、文本框、图片及文字。

本文章写作框架如下:

1. 人脸识别流程

                        【opencv】基于opencv2的人脸识别系统_第3张图片

2.各部分功能:详见下文“各模块讲解”。

3.各部分中遇到的细节问题

     例如:

       CascadeClassifier cascade;//建立级联分类器

cascade.load("haarcascade_frontalface_alt2.xml"); // 加载训练好的 人脸检测器(.xml)

        cascade.detectMultiScale(frameGray,faces,1.2, 2,0 |  CV_HAAR_FIND_BIGGEST_OBJECT );

        haarcascade_frontalface_alt2.xml到底是什么东西?

4.本系统的缺陷,是否可提升

  (1)  人脸采集问题:人脸旋转-》矫正

(2)训练模型问题:更换更好的模型来训练-》深度学习

(3)样本格式问题:直接利用彩色图像会不会更好

(4)训练样本问题:人脸数据库数量不足,以及ORL是西方面孔,与我们东方面孔的差异。-》采集更多东方人脸,进行训练

(5)人脸数组:现在是只识别一个人,能否识别多个,可以

(6)自己训练人脸检测器haar_cascade

5.所涉及的算法及原理,详见下文各模块讲解。

6.具体代码请戳:http://blog.csdn.net/u012679707/article/details/79520299 基于opencv2的人脸识别系统(二)具体代码


各模块讲解

第一部分:主函数

main.c

系统主函数,包含参数初始化、ui界面的设置以及整体流程控制。


第二部分:人脸采集

capture.cpp

人脸采集模块,功能是从摄像头画面中检测出人脸,并将人脸图像(矩形)截取下来,保存到训练文件中。其中人脸检测的详细过程是,

第一步,建立级联分类器

CascadeClassifier cascade;//建立级联分类器

第二步,加载Haar级联分类器模型.xml

cascade.load("haarcascade_frontalface_alt2.xml");	// 加载训练好的 人脸检测器(.xml)

第三步,用加载好的级联分类器进行人脸检测,返回检测到的人脸数组faces

cascade.detectMultiScale(frameGray,faces,1.2, 2,0 |  CV_HAAR_FIND_BIGGEST_OBJECT );

流程图如下:

                    【opencv】基于opencv2的人脸识别系统_第4张图片


这一模块中,有一个问题,haar是什么?haar.xml为什么可以做人脸检测模型?如何检测出人脸的?

首先,haar特征是一种特征提取的方法。其实,特征提取方法有很多种,比如说Haar特征,edgelet特征,shapelet特征,HOG特征,HOF特征,小波特征,边缘模板等等。

摘录自:http://blog.csdn.net/yang6464158/article/details/25103703(特征提取之——Haar特征

Haar分类器 = Haar-like特征 + 积分图方法 + AdaBoost +级联

Haar分类器算法的要点如下:

① 使用Haar-like特征做检测。

② 使用积分图(Integral Image)对Haar-like特征求值进行加速。

③ 使用AdaBoost算法训练区分人脸和非人脸的强分类器。

④ 使用筛选式级联把强分类器级联到一起,提高准确率。


大神贴在此,非常详细的算法过程讲解。

http://blog.csdn.net/beizhengren/article/details/77095724  (haar特征介绍与分析)

http://blog.csdn.net/beizhengren/article/details/77095759  (积分图,快速计算图像中任意位置的haar特征值)

http://blog.csdn.net/beizhengren/article/details/77095841  (强弱级联分类器与xml文件参数含义)

http://blog.csdn.net/beizhengren/article/details/77095883   (利用并查集合并检测窗口)

http://blog.csdn.net/beizhengren/article/details/77095969  (利用opencv_traincascade.exe训练自己的分类器)

http://blog.csdn.net/beizhengren/article/details/77095988     (具体训练过程分析)

opencv 用opencv_traincascade.exe训练haar分类器


第三部分:模型训练 train.cpp

流程图如下:

                                  

1.前期准备工作,将所有的人脸样本和类别标签生成一个.csv文件。

        生成csv文件方法:http://blog.csdn.net/u012679707/article/details/79519143    (gogo小Sa)

  2.训练时可直接读取csv文件,实现样本和类别标签的获取。

       读取csv文档方法: http://blog.csdn.net/u012679707/article/details/78711365 (gogo小Sa)

3.创建特征脸模型,选择20个主成分   (faceRecognizer 为cv2中的contrib模块)

Ptr model=createEigenFaceRecognizer(20); // 创建特征脸模型 20张主成分脸

4.通过样本和类别标签进行训练,最终得到训练好的主成分脸模型。

model->train(images,labels); //训练人脸模型,通过images和labels来训练人脸模型

5.将模型保存为.xml文件

model->save("MyFacePcaModel.xml"); //将训练模型保存到MyFacePcaModel.xml

注意:contrib模块中的人脸识别模型有三种,PCA、fisher、LBP。本系统选择的是主成分脸模型(PCA)


最终生成的MyFacePcaModel.xml文件内容如下图所示,其中

20   20是主特征脸的个数
1
10304       1*10304 这表示每个特征脸的大小,一行表示一张脸的数据,维度为10304(92*112)


                       图3.1 MyFacePcaModel.xml

其中,faceRecognizer源码解析如下:详细解析可参见大神贴                                                                                                                                  http://www.cnblogs.com/guoming0000/archive/2012/09/27/2706019.html

 class CV_EXPORTS_W FaceRecognizer : public Algorithm
    {
    public:
        //! virtual destructor
        virtual ~FaceRecognizer() {}

        // Trains a FaceRecognizer.
        CV_WRAP virtual void train(InputArrayOfArrays src, InputArray labels) = 0;

        // Updates a FaceRecognizer.
        CV_WRAP void update(InputArrayOfArrays src, InputArray labels);

        // Gets a prediction from a FaceRecognizer.
        virtual int predict(InputArray src) const = 0;

        // Predicts the label and confidence for a given sample.
        CV_WRAP virtual void predict(InputArray src, CV_OUT int &label, CV_OUT double &confidence) const = 0;

        // Serializes this object to a given filename.
        CV_WRAP virtual void save(const string& filename) const;

        // Deserializes this object from a given filename.
        CV_WRAP virtual void load(const string& filename);

        // Serializes this object to a given cv::FileStorage.
        virtual void save(FileStorage& fs) const = 0;

        // Deserializes this object from a given cv::FileStorage.
        virtual void load(const FileStorage& fs) = 0;

    };

    CV_EXPORTS_W Ptr createEigenFaceRecognizer(int num_components = 0, double threshold = DBL_MAX);
    CV_EXPORTS_W Ptr createFisherFaceRecognizer(int num_components = 0, double threshold = DBL_MAX);
    CV_EXPORTS_W Ptr createLBPHFaceRecognizer(int radius=1, int neighbors=8,
                                                            int grid_x=8, int grid_y=8, double threshold = DBL_MAX);


第四部分 :人脸识别(预测)  predict.cpp

1.创建人脸识别模型,为和训练相对应,仍选择特征脸模型。

Ptr modelPCA=createEigenFaceRecognizer();// 创建特征脸模型
2. 加载 特征脸模型器

   modelPCA->load("MyFacePcaModel.xml");

3.对测试图像进行分类
modelPCA->predict(face_test,predictPCA,confidence);  //confidence为置信度


----------------------------------------【全部实例代码】----------------------------------------------

第一部分:main.cpp

/*
Project Name:FaceRecognition
Author:Lisa
Data:2017_12
Version:V1

Abstract:
		the faceRecognition system includes 3 modules:
		1.capture.cpp ->capture face Image and  Detect face
		2.train.cpp ->train face module
		3.predict.cpp ->capture face image,face detection,face  recognition
 

Statement:
		You are free to use, change, or redistribute the code in any way you wish for
         non-commercial purposes, but please maintain the name of the original author.
		This code comes with no warranty(保证) of any kind.
*/ 
//#include"stdafx.h"
#include
#include
#include
#include
#include
#include //包含ifstream
#include"opencv2/cvui/cvui.h"

#include"capture.hpp"
#include"train.hpp"
#include"predict.hpp"
#include

using namespace cv;
using namespace std;
//using namespace cvui;

#define WINDOW_NAME "Face Recognition System ByLISA"
// 采集到的图片保存地址
const string savePath="F:\\opencv_project\\faceRecognition\\ORL\\prePhoto\\1.pgm";
/******************************* main()  ***************************************************************/
Mat Frame;
int main(int argc,char *argv[])
{
	system("color 5E");
	//Mat img=imread("lanyangyang.jpg");

	namedWindow(WINDOW_NAME);
	cvui::init(WINDOW_NAME);
	Frame=Mat(320,500,CV_8UC3);
	Frame = cv::Scalar(200,20,200);   //颜色填充49, 52, 49

	cvui::window(Frame,350,30,100,132,"predict photo");
	cvui::window(Frame,350,165,100,132,"predict result");
	
	while(1)
  {
	
    bool buttonCapture=cvui::button(Frame,50,100,"Capture"); 
    bool buttonTrain=cvui::button(Frame,50,130,"Train");
    bool buttonPredict=cvui::button(Frame,50,160,"Predict");
	
	
    if(buttonCapture) { 
		Mat capPhoto;
		if(photoCapture(capPhoto)) cvui::text(Frame, 150, 100, "capture is sucessful!"); 
		else  cvui::text(Frame, 150, 100, "capture failed!");

	}
	if(buttonTrain)  {
		if(train()) cvui::text(Frame, 150, 130, "train is sucessful!");
		else              cvui::text(Frame, 150, 100, "train failed!");
	}
	if(buttonPredict) { 
		  cvui::printf(Frame, 150, 160,0.4, 0x00ff00, "predict result is    ");

		Mat predictPhoto;//待识别照片
		int predictResult;//预测结果
		predict(predictPhoto,predictResult);
		imwrite(savePath,predictPhoto);
		cvui::printf(Frame, 150, 160,0.4, 0x00ff00, "predict result is %d",predictResult);
        	
		string fileName="F:\\opencv_project\\faceRecognition\\ORL\\s";
		fileName+=to_string(predictResult+1); //number To string
		fileName+="\\1.pgm";
		cout<

第二部分:capture.cpp

// capture.cpp

#include"capture.hpp"
#include



bool photoCapture(Mat &capPhoto )
{
		
/***************************************** 1.打开默认摄像头 ********************************************************/
	VideoCapture cap(0); //打开默认摄像头
	if(! cap.isOpened())
	{
		cout<<"camera open fail"< faces;

	/*********************************** 2.加载人脸检测器,加载人脸模型器******************************/
	CascadeClassifier cascade;//建立级联分类器
	cascade.load("haarcascade_frontalface_alt2.xml");	// 加载训练好的 人脸检测器(.xml)
	while(1)
	{
		cap>>frame;
		namedWindow("frame");
		imshow("frame",frame);  // 显示每一帧图像
		//cvui::update();
	    //imshow(WINDOW_NAME,Frame);
		string filename=format("F:\\opencv_project\\faceRecognition\\ORL\\s42\\%d.pgm",i);

		char key=waitKey(30);;
		switch(key) // 按下采集按钮
		{
	case 'p':
		
		cvtColor(frame,frameGray,CV_BGR2GRAY);
		//imshow("frameGray",frameGray);
		

		/*********************************** 3.人脸检测 ******************************/
	    cascade.detectMultiScale(frameGray,faces,1.2, 2,0 |  CV_HAAR_FIND_BIGGEST_OBJECT );
		if(faces.size()>0)
		{
		  for(size_t ii=0;ii Mat imgROI=img( Rect);
		  }	
	      resize(frameROI,face,Size(92,112));
		  imwrite(filename,face);
		  imshow("photo",face);
		  face.copyTo(capPhoto); // 将采集到的图片copy给capPhoto
		  i++;
		  waitKey(500);
		  destroyWindow("photo");
		}
		else 
		{
			//项目属性的常规项修改字符集,选择为多字符集 ,原为Unicode
			MessageBox(GetForegroundWindow(),"valid capture!please retry!","Warning",1); //MessageBox 
             //printf("%d\n",x); 
		}
		break;

	case 'P':
		cvtColor(frame,frameGray,CV_BGR2GRAY);
		/*********************************** 3.人脸检测 ******************************/
	    cascade.detectMultiScale(frameGray,faces,1.2, 2,0 |  CV_HAAR_FIND_BIGGEST_OBJECT );
		if(faces.size()>0)
		{
		for(size_t ii=0;ii Mat imgROI=img( Rect);
		}
		 resize(frameROI,face,Size(92,112));
		 imwrite(filename,face);
		 imshow("photo",face);
		 i++;
		 waitKey(500);
		 destroyWindow("photo");
		}
		break;
		
	default: break;
		}
		if(i>=(captureCount+1)) 
		{ 
			cout<<"capture is successful!"<=(captureCount+1)) return true;
	else                return false;
}


第三部分:train.cpp

// train.cpp

#include"train.hpp"
#include

bool train()
	{
	string csvFile="at.txt";
	vector images;
	vector labels;
	
	// [1]读取csv文件
	try
	{
	read_csv(csvFile,images,labels,CountMax,CountMin,';'); //读取csvFile中所有的img和label
	}
	catch(cv::Exception& e) // ???????????????????????????????????????????????????
	{                  // cerr:输出到标准错误的ostream对象,常用于程序错误信息
	cerr<<"Error opening file\" "<为模板类,定义model为指向FaceRecognizer类的指针。model为指针!
	*/
	Ptr model=createEigenFaceRecognizer(20); // 创建特征脸模型 20张主成分脸
	model->train(images,labels); //训练
	model->save("MyFacePcaModel.xml"); //将训练模型保存到MyFacePcaModel.xml

	cout<<"train2.创建脸模型ok"<images, vector 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& images, vector& labels,int max,int min,char separator=';')
{
	std::ifstream  file(filename.c_str(),ifstream::in); // 以in模式(读取文件模式)打开文件 ,实际是将filename文件关联给 流file  !!!!!!!!!!!!!!!!!! filename.c_str()

	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;

		}
	}
}

第四部分:predict.cpp

// predict.cpp

#include"predict.hpp"
#include

using namespace cv;
using namespace std;


// void predict();
void predict(Mat &predictPhoto,int& predictPCA)
{
	/*********************************** 1.打开默认摄像头******************************/
	VideoCapture cap(0); //
	if(! cap.isOpened())
	{
		cout<<"camera open fail"< faces(0); //矩形向量,存放检测出的人脸
	/*********************************** 2.加载人脸检测器,加载人脸模型器******************************/
	//建立级联分类器
	CascadeClassifier cascade;
	// 加载训练好的 人脸检测器(.xml)
	cascade.load("haarcascade_frontalface_alt2.xml");

	Ptr modelPCA=createEigenFaceRecognizer();// 创建特征脸模型
    // 加载 特征脸模型器
	modelPCA->load("MyFacePcaModel.xml");

	cout<<"predict 2.ok"<>frame; //将获取到的每一帧图像 写入 frame;
		namedWindow("frame");
		imshow("frame",frame);   // 显示摄像头
		key=waitKey(50);
		if(key=='p'||key=='P') 
		{
		capFace=frame.clone();
		// rgb To gray
		cvtColor(frame,gray,CV_BGR2GRAY);
		// 直方图均衡化,提高图像质量
		equalizeHist(gray,gray);
		/*********************************** 3.人脸检测 ******************************/
		cascade.detectMultiScale(gray,faces,1.2, 2,0 |  CV_HAAR_FIND_BIGGEST_OBJECT );
       
		cout<<"detect face number is :"<0)
		{
		/************************************* 4.人脸识别 ******************************/
		Mat face_temp,face_test;
		for(size_t i=0;i Mat imgROI=img( Rect);
		
		}	
		// 调整大小为112*92
		resize(face_temp,face_test,Size(92,112));
		namedWindow("capFace");
		imshow("capFace",face_test);
		face_test.copyTo(predictPhoto);
				
		// 测试图应该为灰度图
		double confidence;
		modelPCA->predict(face_test,predictPCA,confidence);
		cout<<"the predict result is "<< predictPCA<

                                       -----感谢欣赏,欢迎下边留言评论!-----

你可能感兴趣的:(opencv)