C++ Opencv人脸检测+分类器训练+识别 程序制作

时间:2022-11-19 04:13:05

学习总要从兴趣开始,

自己制作一个利用opencv的分类器来检测人脸,再将检测出的人脸用程序扣图出来,创建一个分类器,

实现不同人的人脸识别,标记出他的名字;

程序可以在这里下载:http://download.csdn.net/download/qq_36576377/10213638

程序效果图:

C++ Opencv人脸检测+分类器训练+识别 程序制作


// RlsbDlg.cpp : 实现文件
//

#include "stdafx.h"
#include "Rlsb.h"
#include "RlsbDlg.h"
#include "afxdialogex.h"
#include <thread>
#include "Psapi.h"
#include <io.h>
#include <iostream>
#include <sstream>
#include <fstream>
#include <string> 
#include <opencv2\opencv.hpp>
#include <opencv\cv.h>
#include <opencv\cv.hpp>
#include <map>
//#pragma comment(lib,"E:\\opencv\\build\\x86\\vc11\\lib\\opencv_core249.lib")

using namespace std;
using namespace cv;
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
BOOL modelsave=FALSE;
BOOL loadover=FALSE;
map<int,string> maps;
BOOL Close=TRUE;
char chPath[261]={0};//程序自身路径;
VideoCapture cap;
Mat img,sbimg;
CascadeClassifier face_cascade;
string face_cascade_name;
int ctimes=0;
VideoWriter writecap;
Ptr<FaceRecognizer> model = createEigenFaceRecognizer();
void thread01();
void detectface(Mat mat,CString path,int count,BOOL over);
void Createtxt(CString path,CString csInt,int inNub);
void findfile(string path,std::vector<std::string>  &files,vector<int> &xuhao);
static void read_csv(const string& filename, vector<Mat>& images, vector<int>& labels, char separator = ';') {    
std::ifstream file(filename.c_str(), ifstream::in);    
if (!file) {        
string error_message = "No valid input file was given, please check the given filename.";        CV_Error(CV_StsBadArg, error_message);   
 }    
string line, path, classlabel;   
Mat tempmat;
while (getline(file, line)) {        
stringstream liness(line);        
getline(liness, path, separator);        
getline(liness, classlabel);        
if (!path.empty() && !classlabel.empty()) {   
tempmat=imread(path,0);
equalizeHist(tempmat,tempmat);
normalize(tempmat,tempmat,0,tempmat.rows,NORM_MINMAX,-1,Mat());
images.push_back(tempmat);            
labels.push_back(atoi(classlabel.c_str()));       
 }  
  }
 }




//---------得到程序自身路径;
		DWORD idProcess;
		char path[261]={0};
		GetWindowThreadProcessId(AfxGetMainWnd()->m_hWnd, &idProcess);
	HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, idProcess);
	if(hProcess)
	{
		 HMODULE hMod;
            DWORD cbNeeded;
            if (EnumProcessModules(hProcess, &hMod, sizeof(hMod), &cbNeeded))
            {
                GetModuleFileNameEx(hProcess, hMod, path, MAX_PATH);			
				string stringpath;
				stringpath=path;
				int pos=0;			
				pos=stringpath.rfind("\\",strlen(path));
				if(pos!=stringpath.npos)
				{
				 CString cspos;
				 cspos.Format("%d",pos);
				 strncpy_s(chPath,path,pos);
				}		    
            }
		
     }
	::MoveWindow(GetDlgItem(IDC_pic)->m_hWnd,0,0,320,240,1);//
//加载opencv自带的分类器;
	face_cascade_name.assign(chPath).append("\\haarcascade_frontalface_alt.xml");
	if(!face_cascade.load(face_cascade_name))
	{
		MessageBox("无法加载分类器");
	exit(0);
	}
	HWND hWnd;
	HWND hParent;
    namedWindow("img", WINDOW_AUTOSIZE);
    hWnd = (HWND)cvGetWindowHandle("img");
    hParent = ::GetParent(hWnd);
    ::SetParent(hWnd, GetDlgItem(IDC_pic)->m_hWnd);
    ::ShowWindow(hParent, SW_HIDE);


	// TODO: 在此添加额外的初始化代码

	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}


//-----采集图片并保存到当前程序目录
void CRlsbDlg::OnBnClickedButton1()
{

	char myname[10]={0};
	::GetDlgItemText(AfxGetMainWnd()->m_hWnd,IDC_name,myname,20);
	if(strlen(myname)==0)
{
	MessageBox("请输入要模板人的名字");
	return;
}    
	GetDlgItem(IDC_BUTTON1)->EnableWindow(0);
	GetDlgItem(IDC_train)->EnableWindow(0);
	ctimes=0;
	CString stPath,csInt;
	stPath.Format("%s",chPath);
	if(_access(stPath+"\\s01",0)==-1)
	::CreateDirectory(stPath+"\\s1", 0);
	//stPath+="\\";
	int i=0;
	do
	{
	++i;
	csInt.Format("s%d",i);
	}
	while(_access(stPath+"\\"+csInt,0)!=-1);

	::CreateDirectory(stPath+"\\"+csInt, 0);
	CString saveimgPath=stPath+"\\"+csInt;
	if(cap.isOpened())
	cap.release();
	cap.open(0);
	if(!cap.isOpened())
	return;
	CRect rect;
	Mat outimg;
	::GetClientRect((HWND)GetDlgItem(IDC_pic)->m_hWnd,rect);
	BOOL overCap=TRUE;
	while(overCap)
	{
	 cap>>img;
	 if(!img.data)
	  return;
	
	outimg=img.clone();
	resize(outimg,outimg,Size(rect.Width(),rect.Height()));
	imshow("img",outimg);
	detectface(img,saveimgPath,10,TRUE);
	if(ctimes>15)
	{
	overCap=FALSE;
	cap.release();
	}
	waitKey(30);
	
	}
	
	Createtxt(stPath,csInt,i);
		GetDlgItem(IDC_BUTTON1)->EnableWindow(0);
	GetDlgItem(IDC_train)->EnableWindow(1);
		GetDlgItem(IDC_reco)->EnableWindow(0);
::SetDlgItemText(AfxGetMainWnd()->m_hWnd,IDC_stat,"信息录入完毕");

	// TODO: 在此添加控件通知处理程序代码
}
//创建txt文件
void Createtxt(CString path,CString csInt,int inNub)
{
ofstream out;
std::vector<std::string> files;//通过容器存储 
vector<int> xh;
int i=0; 

findfile(path.GetBuffer(0), files,xh); 
CString csATpath=path+"\\at.txt";	
out.open(csATpath);
cout.rdbuf(out.rdbuf());
for (int i = 0;i<files.size();i++) 
cout<<files[i]<<";"<<xh[i]<<endl;
out.close();
ofstream out1;
CString listpath=path+"\\list.txt";
out1.open(listpath,ios::app);
cout.rdbuf(out1.rdbuf());
	char myname[10]={0};
	::GetDlgItemText(AfxGetMainWnd()->m_hWnd,IDC_name,myname,20);
cout<<inNub<<";"<<myname<<endl;
out1.close();

}
//找图形文件
void findfile(string path,std::vector<std::string> &files,vector<int> &xuhao)
{
	string sss;
	string csInt="s1";
	int i=1;
	struct _finddata_t filefind;
	intptr_t hfile =0;
	std::string s;
	CString paths;
	sss.assign(path);
	
	do
	{
	

		if ((hfile = _findfirst(s.assign(sss).append("/*.jpg").c_str(), &filefind)) != -1) 
		{  
				do  {
						if (filefind.attrib == _A_SUBDIR) 
						{
						if(strcmp(filefind.name,".")&&strcmp(filefind.name,".."))
						findfile(s.assign(sss).append("/").append(filefind.name), files,xuhao);
						
						}      
						else 
						{    
						files.push_back(s.assign(sss).append("/").append(filefind.name));   
						xuhao.push_back(i);
						}  
				}while(_findnext(hfile, &filefind) == 0); 
		}
	
	++i;
stringstream stream;
stream.clear();
stream<<"s";
stream << i;    
stream >> csInt;
	}while(_access(sss.assign(path).append("\\").append(csInt).c_str(),0)!=-1);
	
_findclose(hfile);
 
}
void detectface(Mat mat,CString path,int count,BOOL over)
{
    std::vector<Rect> faces;
    Mat frame_gray;
	Mat writeimg;
	
	cvtColor( mat, frame_gray, CV_BGR2GRAY );
    equalizeHist( frame_gray, frame_gray );
    //-- 多尺寸检测人脸
    face_cascade.detectMultiScale( frame_gray, faces, 1.1, 2, 0|CV_HAAR_SCALE_IMAGE, Size(30, 30) );

    for( int i = 0; i < faces.size(); i++ )
    {	
        Point center( faces[i].x + faces[i].width*0.5, faces[i].y + faces[i].height*0.5 );
        ellipse( mat, center, Size( faces[i].width*0.5, faces[i].height*0.5), 0, 0, 360, Scalar( 255, 0, 255 ), 4, 8, 0 );
		cv::Rect rect(center.x-faces[i].width*0.5,center.y-faces[i].height*0.5,faces[i].width,faces[i].height);
		//rectangle(img,rect,RGB(0,0,255),5,8,0);
	
		Mat faceROI = img(faces[i]);               
		 if (faceROI.cols > 30)                
		{             
		resize(faceROI, writeimg, Size(92, 112)); 
		string  str = format("%s\\%d.jpg",path.GetBuffer(0),ctimes); 
		if(over==TRUE && ctimes>5)
		imwrite(str, writeimg);
		else if(over==FALSE)
		sbimg=writeimg;
		ctimes++;
		 } 	
	}
}
void CRlsbDlg::OnBnClickedtrain()
{
	GetDlgItem(IDC_BUTTON1)->EnableWindow(0);
	GetDlgItem(IDC_train)->EnableWindow(0);
	GetDlgItem(IDC_reco)->EnableWindow(0);
		string fn_csv=chPath;
	fn_csv.append("\\at.txt");
//	AfxMessageBox(fn_csv.c_str());
// 2个容器来存放图像数据和对应的标签    
vector<Mat> images;    
vector<int> labels;       
 try   
 {        
read_csv(fn_csv, images, labels);    
}   
 catch (cv::Exception& e)    
{        
cerr << "Error opening file \"" << fn_csv << "\". Reason: " << e.msg << endl;             
exit(1);    
}      
 if (images.size() <= 1) {        
string error_message = "This demo needs at least 2 images to work. Please add more images to your data set!";        
CV_Error(CV_StsError, error_message);    
}    
// 下面的几行代码仅仅是从你的数据集中移除最后一张图片    
//[gm:自然这里需要根据自己的需要修改,他这里简化了很多问题]    
 
//Mat testSample = images[images.size() - 1];    
//int testLabel = labels[labels.size()-1 ];    
images.pop_back();    
labels.pop_back();    
// 下面几行创建了一个特征脸模型用于人脸识别,    
// 通过CSV文件读取的图像和标签训练它。   
 // T这里是一个完整的PCA变换    
//如果你只想保留10个主成分,使用如下代码   
 //      cv::createEigenFaceRecognizer(10);    
//    
// 如果你还希望使用置信度阈值来初始化,使用以下语句:   
 //      cv::createEigenFaceRecognizer(10, 123.0);    
//    
// 如果你使用所有特征并且使用一个阈值,使用以下语句:   
 //      cv::createEigenFaceRecognizer(0, 123.0);    
 
model->train(images, labels);  
string xmlpath=chPath;
xmlpath.append("\\PCAModel.xml");
if(_access(xmlpath.c_str(),0)!=-1)
{
	DeleteFile(xmlpath.c_str());
	::SetDlgItemText(AfxGetMainWnd()->m_hWnd,IDC_stat,"训练完毕;");
}
	model->save(xmlpath);     
	GetDlgItem(IDC_BUTTON1)->EnableWindow(1);
	GetDlgItem(IDC_train)->EnableWindow(0);
	GetDlgItem(IDC_reco)->EnableWindow(1);
	modelsave=TRUE;
/* 
Ptr<FaceRecognizer> model1 = createFisherFaceRecognizer();    
model1->train(images, labels);    
model1->save("MyFaceFisherModel.xml");   
 Ptr<FaceRecognizer> model2 = createLBPHFaceRecognizer();    
model2->train(images, labels);    
model2->save("MyFaceLBPHModel.xml");   */
}
void CRlsbDlg::OnBnClickedreco()
{
::SetDlgItemText(AfxGetMainWnd()->m_hWnd,IDC_stat,"开始识别模式");

thread task(thread01);
task.detach();

	GetDlgItem(IDC_BUTTON1)->EnableWindow(0);
	GetDlgItem(IDC_train)->EnableWindow(0);
	GetDlgItem(IDC_reco)->EnableWindow(0);
	GetDlgItem(IDC_strop)->EnableWindow(1);
	maps.clear();
	std::ifstream getmap;
	string listpath=chPath;
	string line;
	string lefstring,rightstring;
	listpath.append("\\list.txt");
	getmap.open(listpath);
	while(getline(getmap,line))
	{
	stringstream liness(line);//这里采用stringstream主要作用是做字符串的分割
	getline(liness, lefstring, ';');//读入图片文件路径以分好作为限定符
	getline(liness, rightstring);//读入图片标签,默认限定符
	maps.insert(map<int, string> :: value_type(atoi(lefstring.c_str()),rightstring));
	}
	map<int,string>::iterator it;
::SetDlgItemText(AfxGetMainWnd()->m_hWnd,IDC_stat,"Map装载完毕");
//::SendMessage((HWND)(::GetDlgItem(AfxGetMainWnd()->m_hWnd,IDC_stat)),WM_SETTEXT,0,(LPARAM)"111");


if(cap.isOpened())
	cap.release();
	cap.open(0);
	if(!cap.isOpened())
	return;
	CRect rect;
	Mat outimg;
	::GetClientRect((HWND)GetDlgItem(IDC_pic)->m_hWnd,rect);
	Close=TRUE;
		 int oldpredict=0,b=0;
	while(Close)
	{
	 cap>>img;
	 if(!img.data)
	  return;

	outimg=img.clone();
	resize(outimg,outimg,Size(rect.Width(),rect.Height()));
//	normalize(img , img , 1.0,cv::NORM_L2);
	/*Mat img2=img.clone();
	Mat img3;
	cvtColor(img2,img2,CV_RGB2GRAY);	
	imshow("gray",img2);
	img3=img2.clone();
	equalizeHist(img2,img2);
	imshow("直方图增强",img2);
	normalize(img2,img2,0,img2.rows,NORM_MINMAX,-1,Mat());
	imshow("直方图增强-->归一",img3);
	equalizeHist(img3,img3);
	imshow("直方图--归一--直方图",img3);*/
	imshow("img",outimg);
	if(sbimg.data)
	resize(sbimg,sbimg,Size(1,1));
	detectface(img,"111",10,FALSE);
	if(loadover)
    if(sbimg.cols>5)
	{
		cvtColor(sbimg,sbimg,CV_RGB2GRAY);
		equalizeHist(sbimg,sbimg);
		normalize(sbimg,sbimg,0,sbimg.rows,NORM_MINMAX,-1,Mat());
		int predictedLabel = model->predict(sbimg); 
		if(oldpredict!=predictedLabel)
			{
			b=0;
			oldpredict=predictedLabel;
			}
		else
			b++;
		if(b>11)
		for(it=maps.begin();it!=maps.end();++it)
		{
			if(predictedLabel==it->first)
			{
			::SetDlgItemText(AfxGetMainWnd()->m_hWnd,IDC_result,it->second.c_str());
			b=0;
			}			
		}
		::SetDlgItemText(AfxGetMainWnd()->m_hWnd,IDC_stat,"识别中");
	}
	waitKey(30);

	}
}
void CRlsbDlg::OnBnClickedButton2()
{
	if(cap.isOpened())
	cap.release();
	GetDlgItem(IDC_BUTTON1)->EnableWindow(1);
	GetDlgItem(IDC_train)->EnableWindow(0);
	GetDlgItem(IDC_reco)->EnableWindow(1);
	// TODO: 在此添加控件通知处理程序代码
}


void CRlsbDlg::OnBnClickedstrop()
{
	Close=FALSE;
		if(cap.isOpened())
	cap.release();
		GetDlgItem(IDC_BUTTON1)->EnableWindow(1);
	GetDlgItem(IDC_train)->EnableWindow(0);
	GetDlgItem(IDC_reco)->EnableWindow(1);
	::SetDlgItemText(AfxGetMainWnd()->m_hWnd,IDC_stat,"已停止视频");

}


BOOL CRlsbDlg::DestroyWindow()
{
	// TODO: 在此添加专用代码和/或调用基类
	if(cap.isOpened())
	cap.release();
	return CDialogEx::DestroyWindow();
}


void CRlsbDlg::OnCancel()
{
	// TODO: 在此添加专用代码和/或调用基类
			if(cap.isOpened())
	cap.release();
	CDialogEx::OnCancel();
}
void thread01()
{
string sbpath;
sbpath=chPath;
sbpath.append("\\PCAModel.xml");
char *fs=NULL;
if(modelsave==FALSE)
model->load(sbpath);
loadover=TRUE;
}

void CRlsbDlg::OnBnClickedload()
{
	Close=FALSE;
	if(cap.isOpened())
	cap.release();
	GetDlgItem(IDC_BUTTON1)->EnableWindow(1);
	GetDlgItem(IDC_train)->EnableWindow(0);
	GetDlgItem(IDC_reco)->EnableWindow(1);
	::SetDlgItemText(AfxGetMainWnd()->m_hWnd,IDC_stat,"加载视频");
	CString strFile = _T("");
    CFileDialog    dlgFile(TRUE, NULL, NULL, OFN_HIDEREADONLY, _T("影像文件 (*.avi)|*.avi|Mp4 Files (*.mp4)|*.mp4||"), NULL);
    if (dlgFile.DoModal())
    strFile = dlgFile.GetPathName();
	cap.open(strFile.GetBuffer(0));
	if(!cap.isOpened())
		{MessageBox("没有文件");return;}
	Close=TRUE;
	while(Close)
	{
		
		if(cap.read(img))
		resize(img,img,Size(320,240));
		else 
		{
			img.create(Size(320,240),CV_8UC1);
		putText(img,"OVER",Point(img.cols/2,img.rows/2),FONT_HERSHEY_SIMPLEX,1,RGB(255,255,0),3,8);
		imshow("img",img);
		break;}
	 imshow("img",img);
	 waitKey(33);
	}
	if(cap.isOpened())
	cap.release();

	
}


void CRlsbDlg::OnBnClickedsave()
{
	CTime t=CTime::GetCurrentTime();
	CString savepath;
	savepath.Format("%s\\%d-%d-%d.avi",chPath,t.GetHour(),t.GetMinute(),t.GetSecond());

	if(!cap.isOpened())
	cap.open(0);
	if(!cap.isOpened())
{
	MessageBox("摄像头打开失败");
	return;
}
	Close=TRUE;

		int ww = static_cast<int>(cap.get(CV_CAP_PROP_FRAME_WIDTH));
		int hh = static_cast<int>(cap.get(CV_CAP_PROP_FRAME_HEIGHT));
		double r = cap.get(CV_CAP_PROP_FPS);
		writecap.open(savepath.GetBuffer(0), CV_FOURCC('M', 'J', 'P', 'G'), 25.0, Size(640, 480));
		while(Close)
		{
			if(cap.read(img))
			{
				writecap<<img;
				resize(img,img,Size(320,240));
				imshow("img",img);
				//writecap.write(img);
				
			}
			else
				break;

		waitKey(33);

		}

}


void CRlsbDlg::OnBnClickedstoprecord()
{
	Close=FALSE;
			cap.release();
		writecap.release();
}