本文实现基于eigenface的人脸检测与识别。给定一个图像数据库,进行以下步骤:
环境: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.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; }
正确的输出就是一系列人脸检测时间,且原文件夹内的图片变成了检测出的人脸(如上面结果图所示)。
文章所用代码打包链接:http://download.csdn.net/detail/abcjennifer/7047853
关于Computer Vision更多的学习资料将继续更新,敬请关注本博客和新浪微博Rachel Zhang。