opencv 中SVM + PCA 人臉識別
阿新 • • 發佈:2018-12-31
這一週一直都在弄人臉識別的東西,這個也可以算是我個人第一個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也不是很久,對於裡面一些函式還不是很瞭解.
2 選擇最大的幾個特徵值所對應的特徵向量,(Opencv裡面自己排好序了,所以只要取前幾個),保留這一部分跟平均臉。
3 將原始的資料用Opencv裡面一個pcaProject對映的函式,對映到剛才的平均臉上,得到一組係數。
4 將這組係數送入SVM進行訓練。
5 識別的時候也是用新影象像PCA得到的平均臉,特徵向量進行對映,得到相應的係數,再送入SVM進行預測。
思路: 1 所有的人臉看成一組資料,送入opencv裡面的PCA做處理,得到平均臉,特徵向量,特徵值。// 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; }
2 選擇最大的幾個特徵值所對應的特徵向量,(Opencv裡面自己排好序了,所以只要取前幾個),保留這一部分跟平均臉。
3 將原始的資料用Opencv裡面一個pcaProject對映的函式,對映到剛才的平均臉上,得到一組係數。
4 將這組係數送入SVM進行訓練。
5 識別的時候也是用新影象像PCA得到的平均臉,特徵向量進行對映,得到相應的係數,再送入SVM進行預測。