本文主要參考OpenCV人臉識別教程:http://docs.opencv.org/modules/contrib/doc/facerec/facerec_tutorial.html
1、OpenCV 從2.4開始支持3個新的人臉識別算法。
- Eigenfaces 極值特征臉 createEigenFaceRecognizer()
- Fisherfaces createFisherFaceRecognizer()
- Local Binary Patterns Histograms局部二值直方圖 createLBPHFaceRecognizer()
2、為了使用這三種算法,我們首先需要准備人臉訓練樣本,本文采用AT&T Facedatabase(點擊下載)提供的人臉訓練樣本,該樣本包括40個人,每人10張照片。照片在不同時間、不同光照、不同表情(睜眼閉眼、笑或者不笑)、不同人臉細節(戴眼鏡或者不戴眼鏡)下采集。所有的圖像都在一個黑暗均勻的背景下,正面豎直人臉(有些有輕微旋轉)。圖像格式為pgm,圖像大小為92*112,我們可以用gimp打開該格式的圖像。
解壓AT&T人臉數據庫后,我們把目錄att_faces拷貝到solution文件目錄。在att_faces目錄中,有s1,s2,...s40,共40個子目錄,每個子目錄中有1.pgm...10.pgm,10個文件,每個子目錄對應一個人,子目錄中的每副照片,對應一個人的各種人臉表情。比如s1中存放的10張人臉樣本如下所示:
下面我們我們創建一個txt文件facerec_at.txt,格式如下,每一行包括兩個字段,中間用“;”分開,第一個字段表示樣本圖片的路徑文件名,第二個參數是一個整數索引,表示第幾個人,例如第二個參數都為0,則表示第一個人,后面依次類推:
../att_faces/s13/2.pgm;12
../att_faces/s13/7.pgm;12
../att_faces/s13/6.pgm;12
../att_faces/s13/9.pgm;12
../att_faces/s13/5.pgm;12
../att_faces/s13/3.pgm;12
../att_faces/s13/4.pgm;12
../att_faces/s13/10.pgm;12
../att_faces/s13/8.pgm;12
../att_faces/s13/1.pgm;12
../att_faces/s17/2.pgm;16
../att_faces/s17/7.pgm;16
...
../att_faces/s38/10.pgm;37
../att_faces/s38/8.pgm;37
../att_faces/s38/1.pgm;37
3. Eigenfaces算法描述:
二維灰度圖像p*q大小,是一個m=pq維的向量空間,一個100*100像素大小的圖像就是10000維的圖像空間。我們可以通過主成分分析算法(PCA)來對m維的圖像向量進行降維操作。OpenCV中PCA算法細節,可以參考:http://www.cnblogs.com/mikewolf2002/p/3432243.html,通過PCA算法,我們可以得到k個特征臉,k就是我們選擇降到的維數。
算法描述Algorithmic Description
- 計算協方差矩陣 S
- 對特征值進行遞減排序,特征向量和它順序一致. k個主成分也就是k個最大的特征值對應的特征向量。
x的K個主成份:
PCA基的重構:
然后特征臉通過下面的方式進行人臉識別:
- 把所有的訓練數據投影到PCA子空間
- 把待識別圖像投影到PCA子空間
- 找到訓練數據投影后的向量和待識別圖像投影后的向量最近的那個。
4. 程序開始后,我們把樣本圖像和索引標簽讀到兩個vector變量中。
// 得到txt文件的名字
string fn_csv = string("facerec_at_t.txt");
// 定義一個Mat格式的vector用來保存圖像,int格式的vector表示圖像索引標簽
vector<Mat> images;
vector<int> labels;
//讀入圖像文件和索引標簽
try {
read_csv(fn_csv, images, labels);
} catch (cv::Exception& e)
{
cerr << "Error opening file \"" << fn_csv << "\". Reason: " << e.msg << endl;
exit(1);
}
我們選擇images中的最后一副圖片,作為檢測的圖像,並把它從images中移除。
Mat testSample = images[images.size() - 1];
int testLabel = labels[labels.size() - 1];
images.pop_back();
labels.pop_back();
通過下面的代碼,我們輸入待檢測的圖像,返回結果是對應人的索引標簽,我們輸入圖像是第37個人,從結果看是對的。
//創建特征臉算法模型,並通過樣本訓練數據
Ptr<FaceRecognizer> model = createEigenFaceRecognizer();
model->train(images, labels);
//通過predict輸入待檢測的圖像,返回結果是索引標簽
int predictedLabel = model->predict(testSample);
string result_message = format("Predicted class = %d / Actual class = %d.", predictedLabel, testLabel);
cout << result_message << endl;
5. 通過下面的代碼,我們可以求得特征值和特征向量值,並把特征向量顯示為特征臉。
// 特征值和特征向量
Mat eigenvalues = model->getMat("eigenvalues");
// And we can do the same to display the Eigenvectors (read Eigenfaces):
Mat W = model->getMat("eigenvectors");
//特征值列數是1,行數是特征值的數量399
//特征向量10304*399,每一列都是一個特征向量
//每一個特征值對應一個特征向量
printf("特征值數量 :%d\n", eigenvalues.rows);
printf("特征向量維數 :%d\n",W.rows);
//顯示10個特征向量
for (int i = 0; i < min(10, W.cols); i++)
{
string msg = format("Eigenvalue #%d = %.5f", i, eigenvalues.at<double>(i));
cout << msg << endl;
// 得到第i個特征向量
Mat ev = W.col(i).clone();
// 把特征向量歸一化到0-255,便於顯示
Mat grayscale = toGrayscale(ev.reshape(1, height));
// 用Jet colormap顯示灰度圖.
imshow(format("gray image%d", i), grayscale);
Mat cgrayscale;
applyColorMap(grayscale, cgrayscale, COLORMAP_JET);
imshow(format("%d", i), cgrayscale);
}
我們總共顯示了10個特征向量(特征臉),第一個特征臉的灰度圖和color map圖如下:
程序代碼:工程FirstOpenCV31















