1. 程式人生 > >opencv 中SVM + PCA 人臉識別

opencv 中SVM + PCA 人臉識別

這一週一直都在弄人臉識別的東西,這個也可以算是我個人第一個DIY專案,雖然沒有在MFC框架下來實現,但我覺得 SVM + PCA 人臉識別這個東西 最主要的還是演算法 和效果沒有必要一定要在MFC框架下去實現。 從不懂到慢慢明白一些道理,寫這樣一個技術文件無非就是怕以後忘記這樣一個過程。 這個專案得到的經驗: 1:我真正領悟到了“二八法則”的真諦。準備加程式設計階段看了不少論文,試了不少方法,但 最後真正用到的無非就只有20%不到,但並非是白用功,沒有多餘的付出,也就不會有多餘的回報; 2:體會到了理論與應用之間有著一道很深的鴻溝,理論再紮實,到頭來程式設計還是個新手。當然了,我並不是說我理論有多麼深厚,本人也是個菜鳥而已; 3:在除錯的時候切記耐心,細心;很多錯誤都是基於一時意識的模糊造成,並非水平問題, 細心可以解決一切問題; 好了,我再說說在程式設計中遇到的問題及解決方案吧!希望對來看的人有所幫助! 1:opencv 有PCA函式,用起來還是比較好用的,但是PCA各個函式的引數設定還是需要經過仔細推敲的;比如: CvMat data; cvInitMatHeader( &data, (訓練樣本數), (單一圖片維數), CV_32FC1, (資料儲存矩陣(行:訓練樣本數;列:單一圖片維數)); CvMat *pMeanVector = cvCreateMat( 1, (單一圖片維數), CV_32FC1);  CvMat *pEigenValue = cvCreateMat( (使用者取出的特徵向量個數), 1, CV_32FC1);  CvMat *pEigenVector = cvCreateMat( (使用者取出的特徵向量個數), (單一圖片維數), CV_32FC1);  這些事PCA中很重要的幾個引數;當然了圖片是需要做標準處理的;其餘的原理什麼的還希望大家都去找找論文;多看論文有很大的幫助; 2
:SVM訓練問題,這個也是整了我好久的一個問題,下面我說的只是我個人的經驗,並不能代表絕對正確,只是在我的程式中這樣做成功了而已: 其實SVM訓練問題一定要看那個日本人寫的例子,寫的非常的好,但是他的例子有3個,人臉檢測部分效果一般,我覺得這個跟我在做識別的時候效果不好可能有相同的不足之處。後來在經過別人指導,在做SVM訓練之前一定要將用PCA提取出來能夠代表人臉的權值矩陣歸一化,這用到了cvNormalize函式,這個函式還是自己找找怎麼用的吧! 不光在訓練之前要做歸一化,也要在識別的時候做歸一化的處理。我未做歸一化處理之前識別率為0;做了歸一化後識別率為75%, 雖然識別率不是很高,這個可能與我選取的特徵向量個數,還有就是人臉前期未進行預處理造成的。切記一定要歸一化。 這樣
一個小專案,用了VS2008 + opencv2.0 這樣的環境,其實接觸opencv也不是很久,對於裡面一些函式還不是很瞭解.
// pca_svm.cpp : 定義控制檯應用程式的入口點。
//

#include "stdafx.h"
#include "cv.h"
#include "highgui.h"
#include "cvaux.h"
#include "ml.h"
using namespace cv;
using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{   
 vector<Mat> images; //This variable will be loaded with a set of images to perform PCA on.
 char filename[100];
 int imageFaceNum = 100;
 int imageNonFaceNum = 25;
 int num = 0;
 Mat src; 
 Mat values(imageFaceNum+imageNonFaceNum, 1, CV_32SC1); //Values are the corresponding values to each of my images.
 for(int i=1; i<=imageFaceNum; i++) {
  sprintf(filename,"E:/圖片資料庫/MIT人臉庫/train_face/%d.bmp",i);
  values.at<int>(i-1,0) = 1;
  src=cvLoadImage(filename,0);
  images.push_back(src);
  num++;
 } 
 for(int i=1; i<=imageNonFaceNum; i++) {
  sprintf(filename,"E:/圖片資料庫/MIT人臉庫/train_nonface/%d.bmp",i);
  values.at<int>(num-1,0) = 0;
  src=cvLoadImage(filename,0);
  images.push_back(src);
  num++;
 }
 int nEigens = images.size() - 1; //Number of Eigen Vectors. 
 //Load the images into a Matrix 
 Mat desc_mat(images.size(), images[0].rows * images[0].cols, CV_8UC1); 
 for (int i=0; i<images.size(); i++) { 
   desc_mat.row(i) = images[i].reshape(1, 1) + 0; 
 } 
 Mat average; 
 PCA pca(desc_mat, average, CV_PCA_DATA_AS_ROW, nEigens); 
 Mat data(desc_mat.rows, nEigens, CV_32FC1); //This Mat will contain all the Eigenfaces that will be used later with SVM for detection
 //Project the images onto the PCA subspace 
 for(int i=0; i<images.size(); i++) { 
   Mat projectedMat(1, nEigens, CV_32FC1); 
   pca.project(desc_mat.row(i), projectedMat); 
   data.row(i) = projectedMat.row(0) + 0; 
 } 
  
 CvMat d1 = (CvMat)data; 
 CvMat d2 = (CvMat)values; 
 CvSVM svm = CvSVM();    
 CvSVMParams param;    
 CvTermCriteria criteria;    
 criteria = cvTermCriteria( CV_TERMCRIT_EPS, 1000, FLT_EPSILON );    
 param = CvSVMParams( CvSVM::C_SVC, CvSVM::RBF, 10.0, 0.09, 1.0, 10.0, 0.5, 1.0, NULL, criteria );         
 //☆☆☆☆☆☆☆☆☆(5)SVM學習☆☆☆☆☆☆☆☆☆☆☆☆          
 svm.train( &d1, &d2, NULL, NULL, param );    
 svm.save("svmdata.xml"); 

 
////////////////////////////////////////////////////////////////////////

PCA pca();
 Mat cs;
 Mat cs1(1,400,CV_32FC1);
 for(int i=1; i<=100; i++) {
  sprintf(filename,"E:/圖片資料庫/MIT人臉庫/train_face/%d.bmp",i);
  src=cvLoadImage(filename,0);
  cs = src.reshape(1, 1) + 0;  
  Mat projectedMat(1, nEigens, CV_32FC1); 
     pca.project(cs, projectedMat); 
  CvMat d3 = (CvMat)projectedMat; 
  int ret = svm.predict(&d3);
  cout<<ret<<endl;

 }


 return 0;
}
思路: 1 所有的人臉看成一組資料,送入opencv裡面的PCA做處理,得到平均臉,特徵向量,特徵值。
2 選擇最大的幾個特徵值所對應的特徵向量,(Opencv裡面自己排好序了,所以只要取前幾個),保留這一部分跟平均臉。
3 將原始的資料用Opencv裡面一個pcaProject對映的函式,對映到剛才的平均臉上,得到一組係數。
4 將這組係數送入SVM進行訓練。
5 識別的時候也是用新影象像PCA得到的平均臉,特徵向量進行對映,得到相應的係數,再送入SVM進行預測。