1. 程式人生 > >opencv 人臉識別 (一)訓練樣本的處理

opencv 人臉識別 (一)訓練樣本的處理

本文實現基於eigenface的人臉檢測與識別。給定一個影象資料庫,進行以下步驟:

  • 進行人臉檢測,將檢測出的人臉存入資料庫2
  • 對資料庫2進行人臉建模
  • 在測試集上進行recognition
本篇實現第一步:
  • 進行人臉檢測,將檢測出的人臉存入資料庫2

環境:vs2010+opencv 2.4.6.0

特徵:eigenface

Input:一個人臉資料庫,15個人,每人20個樣本(左右)。

Output:人臉檢測,並識別出每張檢測到的人臉。

===============================

本文完成第一步,資料預處理:自動檢測所有資料夾中每個sample中的人臉,作為訓練資料。

Input:一個color資料夾,每個資料夾中有1~N這N個子資料夾,每個子資料夾內有n張包括第n類人的照片,如圖。


最終結果:


核心:face detection(detectAndDraw)

輔助:截圖並儲存部分圖片(CutImg),資料夾內圖片遍歷(read_img),圖片轉換成相同大小(normalizeone)

括號內分別是函式名,下面分別給出程式碼及說明。

1. 遍歷資料夾:CBrowseDir類和CStatDir類(具體見這篇),三個檔案如下:

1.1 BrowseDir.h

#pragma once
#include "direct.h"
#include "string.h"
#include "io.h"
#include "stdio.h" 
#include <vector>
#include <iostream>
using namespace std;
class CBrowseDir
{
protected:
	char m_szInitDir[_MAX_PATH];

public:
	CBrowseDir();
	bool SetInitDir(const char *dir);
	bool BeginBrowse(const char *filespec);
	vector<char*> BeginBrowseFilenames(const char *filespec);

protected:
	bool BrowseDir(const char *dir,const char *filespec);
	vector<char*> GetDirFilenames(const char *dir,const char *filespec);
	virtual bool ProcessFile(const char *filename);
	virtual void ProcessDir(const char *currentdir,const char *parentdir);
};


1.2 BrowseDir.cpp

#include "BrowseDir.h"
#include "direct.h"
#include "string.h"
#include "io.h"
#include "stdio.h" 
#include <vector>
#include <iostream>
using namespace std;

CBrowseDir::CBrowseDir()
{
	getcwd(m_szInitDir,_MAX_PATH);
	int len=strlen(m_szInitDir);
	if (m_szInitDir[len-1] != '\\')
		strcat(m_szInitDir,"\\");
}

bool CBrowseDir::SetInitDir(const char *dir)
{
	if (_fullpath(m_szInitDir,dir,_MAX_PATH) == NULL)
		return false;

	if (_chdir(m_szInitDir) != 0)
		return false;
	int len=strlen(m_szInitDir);
	if (m_szInitDir[len-1] != '\\')
		strcat(m_szInitDir,"\\");

	return true;
}

vector<char*>CBrowseDir:: BeginBrowseFilenames(const char *filespec)
{
	ProcessDir(m_szInitDir,NULL);
	return GetDirFilenames(m_szInitDir,filespec);
}

bool CBrowseDir::BeginBrowse(const char *filespec)
{
	ProcessDir(m_szInitDir,NULL);
	return BrowseDir(m_szInitDir,filespec);
}

bool CBrowseDir::BrowseDir(const char *dir,const char *filespec)
{
	_chdir(dir);
	long hFile;
	_finddata_t fileinfo;
	if ((hFile=_findfirst(filespec,&fileinfo)) != -1)
	{
		do
		{
			if (!(fileinfo.attrib & _A_SUBDIR))
			{
				char filename[_MAX_PATH];
				strcpy(filename,dir);
				strcat(filename,fileinfo.name);
				cout << filename << endl;
				if (!ProcessFile(filename))
					return false;
			}
		} while (_findnext(hFile,&fileinfo) == 0);
		_findclose(hFile);
	}
	_chdir(dir);
	if ((hFile=_findfirst("*.*",&fileinfo)) != -1)
	{
		do
		{
			if ((fileinfo.attrib & _A_SUBDIR))
			{
				if (strcmp(fileinfo.name,".") != 0 && strcmp
					(fileinfo.name,"..") != 0)
				{
					char subdir[_MAX_PATH];
					strcpy(subdir,dir);
					strcat(subdir,fileinfo.name);
					strcat(subdir,"\\");
					ProcessDir(subdir,dir);
					if (!BrowseDir(subdir,filespec))
						return false;
				}
			}
		} while (_findnext(hFile,&fileinfo) == 0);
		_findclose(hFile);
	}
	return true;
}

vector<char*> CBrowseDir::GetDirFilenames(const char *dir,const char *filespec)
{
	_chdir(dir);
	vector<char*>filename_vec;
	filename_vec.clear();

	long hFile;
	_finddata_t fileinfo;
	if ((hFile=_findfirst(filespec,&fileinfo)) != -1)
	{
		do
		{
			if (!(fileinfo.attrib & _A_SUBDIR))
			{
				char *filename = new char[_MAX_PATH];
				strcpy(filename,dir);
				//int st = 0;	while (dir[st++]!='\0');
				strcat(filename,fileinfo.name); //filename[st]='\0';
				filename_vec.push_back(filename);
			}
		} while (_findnext(hFile,&fileinfo) == 0);
		_findclose(hFile);
	}
	_chdir(dir);
	if ((hFile=_findfirst("*.*",&fileinfo)) != -1)
	{
		do
		{
			if ((fileinfo.attrib & _A_SUBDIR))
			{
				if (strcmp(fileinfo.name,".") != 0 && strcmp
					(fileinfo.name,"..") != 0)
				{
					char subdir[_MAX_PATH];
					strcpy(subdir,dir);
					strcat(subdir,fileinfo.name);
					strcat(subdir,"\\");
					ProcessDir(subdir,dir);
					return GetDirFilenames(subdir,filespec);
				}
			}
		} while (_findnext(hFile,&fileinfo) == 0);
		_findclose(hFile);
	}
	return filename_vec;
}

bool CBrowseDir::ProcessFile(const char *filename)
{
	return true;
}

void CBrowseDir::ProcessDir(const char 
	*currentdir,const char *parentdir)
{
}


1.3 StatDir.h

#pragma once
#include "browsedir.h"
class CStatDir:public CBrowseDir
{
protected:
	int m_nFileCount;   //儲存檔案個數
	int m_nSubdirCount; //儲存子目錄個數

public:
	CStatDir()
	{
		m_nFileCount=m_nSubdirCount=0;
	}

	int GetFileCount()
	{
		return m_nFileCount;
	}

	int GetSubdirCount()
	{
		return m_nSubdirCount-1;
	}

protected:
	virtual bool ProcessFile(const char *filename)
	{
		m_nFileCount++;
		return CBrowseDir::ProcessFile(filename);
	}

	virtual void ProcessDir
		(const char *currentdir,const char *parentdir)
	{
		m_nSubdirCount++;
		CBrowseDir::ProcessDir(currentdir,parentdir);
	}
};

2. 輔助函式Prehelper.h, Prehelper.cpp:負責返回資料夾內所有圖片(read_img),檢測人臉(detectAndDraw並可以在原圖中畫出),截圖(CutImg),提取(DetectandExtract)

2.1 Prehelper.h

//preprocessing helper
//@ Author : Rachel-Zhang

#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/contrib/contrib.hpp"
#include <cv.h>
#include <vector>
#include <utility>
using namespace cv;
using namespace std;

void normalizeone(const char* dir,IplImage* standard);

void CutImg(IplImage* src, CvRect rect,IplImage* res);

vector<Rect> detectAndDraw( Mat& img, CascadeClassifier& cascade,
	CascadeClassifier& nestedCascade,
	double scale, bool tryflip,bool draw );

IplImage* DetectandExtract(Mat& img, CascadeClassifier& cascade,
	CascadeClassifier& nestedCascade,
	double scale, bool tryflip);

int read_img(const string& dir, vector<Mat> &images);

vector<pair<char*,Mat>>  read_img(const string& dir);


2.2 Prehelper.cpp

#include "Prehelper.h"
#include "BrowseDir.h"
#include "StatDir.h"

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <cv.h>
using namespace cv;

void normalizeone(const char* dir,IplImage* standard)
{
	CStatDir statdir;
	if (!statdir.SetInitDir(dir))
	{
		puts("Dir not exist");
		return;
	}
	vector<char*>file_vec = statdir.BeginBrowseFilenames("*.*");
	int i;
	for (i=0;i<file_vec.size();i++)
	{
		IplImage* cur_img = cvLoadImage(file_vec[i],CV_LOAD_IMAGE_GRAYSCALE);
		//IplImage*cur_gray = cvCreateImage(cvGetSize(cur_img),cur_img->depth,1);
		cvResize(cur_img,standard,CV_INTER_AREA);
		//cvCvtColor(standard,cur_gray,CV_RGB2GRAY);
		// 		cvNamedWindow("cur_img",CV_WINDOW_AUTOSIZE);
		// 		cvNamedWindow("standard",CV_WINDOW_AUTOSIZE);
		// 		cvShowImage("cur_img",cur_img);
		// 		cvShowImage("standard",standard);
		// 		cvWaitKey();
		cvSaveImage(file_vec[i],cur_img);
	}
}

void CutImg(IplImage* src, CvRect rect,IplImage* res)
{
	CvSize imgsize;
	imgsize.height = rect.height;
	imgsize.width = rect.width;
	cvSetImageROI(src,rect);
	cvCopy(src,res);
	cvResetImageROI(res);
}

int read_img(const string& dir, vector<Mat> &images)
{
	CStatDir statdir;
	if (!statdir.SetInitDir(dir.c_str()))
	{
		cout<<"Direct "<<dir<<"  not exist!"<<endl;
		return 0;
	}
	int cls_id = dir[dir.length()-1]-'0';
	vector<char*>file_vec = statdir.BeginBrowseFilenames("*.*");
	int i,s = file_vec.size();
	for (i=0;i<s;i++)
	{
		Mat graymat = imread(file_vec[i],0);
		//graymat.reshape(1,1);//flatten to one row
		images.push_back(graymat);
	}
	return s;
}

vector<pair<char*,Mat>>  read_img(const string& dir)
{
	CStatDir statdir;
	pair<char*,Mat> pfi;
	vector<pair<char*,Mat>> Vp;
	if (!statdir.SetInitDir(dir.c_str()))
	{
		cout<<"Direct "<<dir<<"  not exist!"<<endl;
		return Vp;
	}
	int cls_id = dir[dir.length()-1]-'0';
	vector<char*>file_vec = statdir.BeginBrowseFilenames("*.*");
	int i,s = file_vec.size();
	for (i=0;i<s;i++)
	{
		pfi.first = file_vec[i];
		pfi.second = imread(file_vec[i]);
		Vp.push_back(pfi);
	}
	return Vp;
}

vector<Rect> detectAndDraw( Mat& img, CascadeClassifier& cascade,
	CascadeClassifier& nestedCascade,
	double scale, bool tryflip, bool draw )
{
	int i = 0;
	double t = 0;
	vector<Rect> faces, faces2;
	const static Scalar colors[] =  { CV_RGB(0,0,255),
		CV_RGB(0,128,255),
		CV_RGB(0,255,255),
		CV_RGB(0,255,0),
		CV_RGB(255,128,0),
		CV_RGB(255,255,0),
		CV_RGB(255,0,0),
		CV_RGB(255,0,255)} ;
	Mat gray, smallImg( cvRound (img.rows/scale), cvRound(img.cols/scale), CV_8UC1 );

	cvtColor( img, gray, CV_BGR2GRAY );
	resize( gray, smallImg, smallImg.size(), 0, 0, INTER_LINEAR );
	equalizeHist( smallImg, smallImg );

	t = (double)cvGetTickCount();
	cascade.detectMultiScale( smallImg, faces,
		1.1, 2, 0
		|CV_HAAR_FIND_BIGGEST_OBJECT
		//|CV_HAAR_DO_ROUGH_SEARCH
		//|CV_HAAR_SCALE_IMAGE
		,
		Size(30, 30) );
	if( tryflip )
	{
		flip(smallImg, smallImg, 1);
		cascade.detectMultiScale( smallImg, faces2,
			1.1, 2, 0
			|CV_HAAR_FIND_BIGGEST_OBJECT
			//|CV_HAAR_DO_ROUGH_SEARCH
			//|CV_HAAR_SCALE_IMAGE
			,
			Size(30, 30) );
		for( vector<Rect>::const_iterator r = faces2.begin(); r != faces2.end(); r++ )
		{
			faces.push_back(Rect(smallImg.cols - r->x - r->width, r->y, r->width, r->height));
		}
	}
	t = (double)cvGetTickCount() - t;
	printf( "detection time = %g ms\n", t/((double)cvGetTickFrequency()*1000.) );
	if(draw)
	{
		for( vector<Rect>::const_iterator r = faces.begin(); r != faces.end(); r++, i++ )
		{
			Mat smallImgROI;
			vector<Rect> nestedObjects;
			Point center;
			Scalar color = colors[i%8];
			int radius;

			double aspect_ratio = (double)r->width/r->height;
			rectangle( img, cvPoint(cvRound(r->x*scale), cvRound(r->y*scale)),
				cvPoint(cvRound((r->x + r->width-1)*scale), cvRound((r->y + r->height-1)*scale)),
				color, 3, 8, 0);
			if( nestedCascade.empty() )
				continue;
			smallImgROI = smallImg(*r);
			nestedCascade.detectMultiScale( smallImgROI, nestedObjects,
				1.1, 2, 0
				|CV_HAAR_FIND_BIGGEST_OBJECT
				//|CV_HAAR_DO_ROUGH_SEARCH
				//|CV_HAAR_DO_CANNY_PRUNING
				//|CV_HAAR_SCALE_IMAGE
				,
				Size(30, 30) );
			//draw eyes
			//         for( vector<Rect>::const_iterator nr = nestedObjects.begin(); nr != nestedObjects.end(); nr++ )
			//         {
			//             center.x = cvRound((r->x + nr->x + nr->width*0.5)*scale);
			//             center.y = cvRound((r->y + nr->y + nr->height*0.5)*scale);
			//             radius = cvRound((nr->width + nr->height)*0.25*scale);
			//             circle( img, center, radius, color, 3, 8, 0 );
			//         }
		}
		cv::imshow( "result", img );
	}
	return faces;
}

IplImage* DetectandExtract(Mat& img, CascadeClassifier& cascade,
	CascadeClassifier& nestedCascade,
	double scale, bool tryflip)
{
	vector<Rect> Rvec = detectAndDraw(img,cascade,nestedCascade,scale,tryflip,0);
	int i,maxxsize=0,id=-1,area;
	for (i=0;i<Rvec.size();i++)
	{
		area = Rvec[i].width*Rvec[i].height;
		if(maxxsize<area)
		{
			maxxsize = area;
			id = i;
		}
	}
	IplImage* transimg = cvCloneImage(&(IplImage)img);
	if(id!=-1)
	{
		CvSize imgsize;
		imgsize.height = Rvec[id].height;
		imgsize.width = Rvec[id].width;
		IplImage* res = cvCreateImage(imgsize,transimg->depth,transimg->nChannels);
		CutImg(transimg,Rvec[id],res);

		return res;
	}
	return NULL;
}


3. 主函式

//Detect.cpp
//Preprocessing - Detect, Cut and Save
//@Author : Rachel-Zhang

#include "opencv2/objdetect/objdetect.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

#include <cctype>
#include <iostream>
#include <iterator>
#include <stdio.h>
#include "BrowseDir.h"
#include "StatDir.h"
#include "Prehelper.h"

using namespace std;
using namespace cv;
#define CAM 2
#define PHO 1
#define K 5

string cascadeName = "E:/software/opencv2.4.6.0/data/haarcascades/haarcascade_frontalface_alt.xml";
string nestedCascadeName = "E:/software/opencv2.4.6.0/data/haarcascades/haarcascade_eye_tree_eyeglasses.xml";

int main( )
{
	CvCapture* capture = 0;
	Mat frame, frameCopy, image;
	string inputName;
	bool tryflip = false;
	int mode;
	CascadeClassifier cascade, nestedCascade; 
	double scale = 1.0;
	if( !cascade.load( cascadeName ) ||!nestedCascade.load( nestedCascadeName))
	{
		cerr << "ERROR: Could not load classifier cascade or nestedCascade" << endl;//若出現該問題請去檢查cascadeName,可能是opencv版本路徑問題
		return -1;
	}

// 	printf("select the mode of detection: \n1: from picture\t 2: from camera\n");
// 	scanf("%d",&mode);
	char** pics = (char**) malloc(sizeof*pics);

	/************************************************************************/
	/*                                  detect face and save                                    */
	/************************************************************************/
	int i,j;
	cout<<"detect and save..."<<endl;
	const char dir[256] = "D:\\Face_recognition\\pic\\"; 
		string cur_dir;
		char id[5];
	for(i=1; i<=K; i++)
	{
		cur_dir = dir;
		_itoa(i,id,10);
		cur_dir.append("color\\");
		cur_dir.append(id);
		vector<pair<char*,Mat>> imgs=read_img(cur_dir);
		for(j=0;j<imgs.size();j++)
		{
			IplImage* res = DetectandExtract(imgs[j].second,cascade,nestedCascade,scale,tryflip);
			if(res)
				cvSaveImage(imgs[j].first,res);
		}
	}
	return 0;
}


正確的輸出就是一系列人臉檢測時間,且原資料夾內的圖片變成了檢測出的人臉(如上面結果圖所示)。


關於Computer Vision更多的學習資料將繼續更新,敬請關注本部落格和新浪微博Rachel Zhang