opencv 人臉識別 (一)訓練樣本的處理
阿新 • • 發佈:2018-12-27
本文實現基於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。