不斷維護的地址:http://plzcoding.com/face-recognition-with-opencv/
怎樣使用OpenCV進行人臉識別
本文大部分來自OpenCV官網上的Face Reconition with OpenCV這節內容(http://docs.opencv.org/modules/contrib/doc/facerec/facerec_tutorial.html),小弟我嘗試翻譯一些重要內容。這部分內容是Philipp Wagner寫的,他的github:https://github.com/bytefish,他的網站http://www.bytefish.de/,應該是個德國人。下面應該是他的照片。
友情提示,要看懂代碼前,你得先知道OpenCV的安裝和配置,會用C++,用過一些OpenCV函數。基本的圖像處理和矩陣知識也是需要的。[gm:我是簫鳴的注釋]由於我僅僅是翻譯,對於六級才過的我,肯定有一些翻譯錯的或者不當的地方,所以請大家糾錯。
1.1.介紹Introduction
從OpenCV2.4開始,加入了新的類FaceRecognizer,我們可以使用它便捷地進行人臉識別實驗。本文既介紹代碼使用,又介紹算法原理。(他寫的源代碼,我們可以在OpenCV的opencv\modules\contrib\doc\facerec\src下找到,當然也可以在他的github中找到,如果你想研究源碼,自然可以去看看,不復雜)
目前支持的算法有
Eigenfaces特征臉createEigenFaceRecognizer()
Fisherfaces createFisherFaceRecognizer()
LocalBinary Patterns Histograms局部二值直方圖 createLBPHFaceRecognizer()
下面所有的例子中的代碼在OpenCV安裝目錄下的samples/cpp下面都能找到,所有的代碼商用或者學習都是免費的。
1.2.人臉識別Face Recognition
對人類來說,人臉識別很容易。文獻[Tu06]告訴我們,僅僅是才三天的嬰兒已經可以區分周圍熟悉的人臉了。那么對於計算機來說,到底有多難?其實,迄今為止,我們對於人類自己為何可以區分不同的人所知甚少。是人臉內部特征(眼睛、鼻子、嘴巴)還是外部特征(頭型、發際線)對於人類識別更有效?我們怎么分析一張圖像,大腦是如何對它編碼的?David Hubel和TorstenWiesel向我們展示,我們的大腦針對不同的場景,如線、邊、角或者運動這些局部特征有專門的神經細胞作出反應。顯然我們沒有把世界看成零散的塊塊,我們的視覺皮層必須以某種方式把不同的信息來源轉化成有用的模式。自動人臉識別就是如何從一幅圖像中提取有意義的特征,把它們放入一種有用的表示方式,然后對他們進行一些分類。基於幾何特征的人臉的人臉識別可能是最直觀的方法來識別人臉。第一個自動人臉識別系統在[Kanade73]中又描述:標記點(眼睛、耳朵、鼻子等的位置)用來構造一個特征向量(點與點之間的距離、角度等)。通過計算測試和訓練圖像的特征向量的歐氏距離來進行識別。這樣的方法對於光照變化很穩健,但也有巨大的缺點:標記點的確定是很復雜的,即使是使用最先進的算法。一些幾何特征人臉識別近期工作在文獻[Bru92]中有描述。一個22維的特征向量被用在一個大數據庫上,單靠幾何特征不能提供足夠的信息用於人臉識別。
特征臉方法在文獻[TP91]中有描述,他描述了一個全面的方法來識別人臉:面部圖像是一個點,這個點是從高維圖像空間找到它在低維空間的表示,這樣分類變得很簡單。低維子空間低維是使用主元分析(Principal Component Analysis,PCA)找到的,它可以找擁有最大方差的那個軸。雖然這樣的轉換是從最佳重建角度考慮的,但是他沒有把標簽問題考慮進去。[gm:讀懂這段需要一些機器學習知識]。想象一個情況,如果變化是基於外部來源,比如光照。軸的最大方差不一定包含任何有鑒別性的信息,因此此時的分類是不可能的。因此,一個使用線性鑒別(Linear Discriminant Analysis,LDA)的特定類投影方法被提出來解決人臉識別問題[BHK97]。其中一個基本的想法就是,使類內方差最小的同時,使類外方差最大。
近年來,各種局部特征提取方法出現。為了避免輸入的圖像的高維數據,僅僅使用的局部特征描述圖像的方法被提出,提取的特征(很有希望的)對於局部遮擋、光照變化、小樣本等情況更強健。有關局部特征提取的方法有蓋伯小波(Gabor Waelets)([Wiskott97]),離散傅立葉變換(DiscreteCosinus Transform,DCT)([Messer06]),局部二值模式(LocalBinary Patterns,LBP)([AHP04])。使用什么方法來提取時域空間的局部特征依舊是一個開放性的研究問題,因為空間信息是潛在有用的信息。
1.3.人臉庫Face Database
我們先獲取一些數據來進行實驗吧。我不想在這里做一個幼稚的例子。我們在研究人臉識別,所以我們需要一個真的人臉圖像!你可以自己創建自己的數據集,也可以從這里(http://face-rec.org/databases/)下載一個。
AT&TFacedatabase又稱ORL人臉數據庫,40個人,每人10張照片。照片在不同時間、不同光照、不同表情(睜眼閉眼、笑或者不笑)、不同人臉細節(戴眼鏡或者不戴眼鏡)下采集。所有的圖像都在一個黑暗均勻的背景下采集的,正面豎直人臉(有些有有輕微旋轉)。
YaleFacedatabase A ORL數據庫對於初始化測試比較適合,但它是一個簡單的數據庫,特征臉已經可以達到97%的識別率,所以你使用其他方法很難得到更好的提升。Yale人臉數據庫是一個對於初始實驗更好的數據庫,因為識別問題更復雜。這個數據庫包括15個人(14個男人,1個女人),每一個都有11個灰度圖像,大小是320*243像素。數據庫中有光照變化(中心光照、左側光照、右側光照)、表情變化(開心、正常、悲傷、瞌睡、驚訝、眨眼)、眼鏡(戴眼鏡或者沒戴)。
壞消息是它不可以公開下載,可能因為原來的服務器壞了。但我們可以找到一些鏡像(比如 theMIT)但我不能保證它的完整性。如果你需要自己剪裁和校准圖像,可以閱讀我的筆記(bytefish.de/blog/fisherfaces)。
ExtendedYale Facedatabase B 此數據庫包含38個人的2414張圖片,並且是剪裁好的。這個數據庫重點是測試特征提取是否對光照變化強健,因為圖像的表情、遮擋等都沒變化。我認為這個數據庫太大,不適合這篇文章的實驗,我建議使用ORL數據庫。
1.3.1. 准備數據
我們從網上下了數據,下了我們需要在程序中讀取它,我決定使用CSV文件讀取它。一個CSV文件包含文件名,緊跟一個標簽。
/path/to/image.ext;0
假設/path/to/image.ext是圖像,就像你在windows下的c:/faces/person0/image0.jpg。最后我們給它一個標簽0。這個標簽類似代表這個人的名字,所以同一個人的照片的標簽都一樣。我們對下載的ORL數據庫進行標識,可以獲取到如下結果:
./at/s1/1.pgm;0
./at/s1/2.pgm;0
...
./at/s2/1.pgm;1
./at/s2/2.pgm;1
...
./at/s40/1.pgm;39
./at/s40/2.pgm;39
想象我已經把圖像解壓縮在D:/data/at下面,而CSV文件在D:/data/at.txt。下面你根據自己的情況修改替換即可。一旦你成功建立CSV文件,就可以像這樣運行示例程序:
facerec_demo.exe D:/data/at.txt
1.3.2 Creating the CSV File
你不需要手工來創建一個CSV文件,我已經寫了一個Python程序來做這事。
[gm:說一個我實現的方法
如果你會cmd命令,或者稱DOS命令,那么你打開命令控制台。假設我們的圖片放在J:下的Faces文件夾下,可以輸入如下語句:
J:\Faces\ORL>dir /b/s *.bmp > at.txt
然后你打開at.txt文件可能看到如下內容(后面的0,1..標簽是自己加的):
。。。。
J:\Faces\ORL\s1\1.bmp;0
J:\Faces\ORL\s1\10.bmp;0
J:\Faces\ORL\s1\2.bmp;0
J:\Faces\ORL\s1\3.bmp;0
J:\Faces\ORL\s1\4.bmp;0
J:\Faces\ORL\s1\5.bmp;0
J:\Faces\ORL\s1\6.bmp;0
J:\Faces\ORL\s1\7.bmp;0
J:\Faces\ORL\s1\8.bmp;0
J:\Faces\ORL\s1\9.bmp;0
J:\Faces\ORL\s10\1.bmp;1
J:\Faces\ORL\s10\10.bmp;1
J:\Faces\ORL\s10\2.bmp;1
J:\Faces\ORL\s10\3.bmp;1
J:\Faces\ORL\s10\4.bmp;1
J:\Faces\ORL\s10\5.bmp;1
J:\Faces\ORL\s10\6.bmp;1
。。。。
自然還有c++編程等方法可以做得更好,看這篇文章反響,如果很多人需要,我就把這部分的代碼寫出來。(遍歷多個文件夾,標上標簽)
]
特征臉Eigenfaces
我們講過,圖像表示的問題是他的高維問題。二維灰度圖像p*q大小,是一個m=qp維的向量空間,所以一個100*100像素大小的圖像就是10,000維的圖像空間。問題是,是不是所有的維數空間對我們來說都有用?我們可以做一個決定,如果數據有任何差異,我們可以通過尋找主元來知道主要信息。主成分分析(Principal Component Analysis,PCA)是KarlPearson (1901)獨立發表的,而 Harold Hotelling (1933)把一些可能相關的變量轉換成一個更小的不相關的子集。想法是,一個高維數據集經常被相關變量表示,因此只有一些的維上數據才是有意義的,包含最多的信息。PCA方法尋找數據中擁有最大方差的方向,被稱為主成分。
令
表示一個隨機特征,其中
.
- 計算均值向量



- 計算協方差矩陣 S


- 計算 的特征值
和對應的特征向量
- 對特征值進行遞減排序,特征向量和它順序一致. K個主成分也就是k個最大的特征值對應的特征向量。
x的K個主成份:
其中 .
PCA基的重構:
其中 .
然后特征臉通過下面的方式進行人臉識別:
A. 把所有的訓練數據投影到PCA子空間
B. 把待識別圖像投影到PCA子空間
C. 找到訓練數據投影后的向量和待識別圖像投影后的向量最近的那個。
還有一個問題有待解決。比如我們有400張圖片,每張100*100像素大小,那么PCA需要解決協方差矩陣 的求解,而X的大小是10000*400,那么我們會得到10000*10000大小的矩陣,這需要大概0.8GB的內存。解決這個問題不容易,所以我們需要另一個計策。就是轉置一下再求,特征向量不變化。文獻 [Duda01]中有描述。
[gm:這個PCA還是自己搜着看吧,這里的講的不清楚,不適合初學者看]
OpenCV中使用特征臉Eigenfaces in OpenCV
給出示例程序源代碼
#include "opencv2/core/core.hpp"
#include "opencv2/contrib/contrib.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
#include <fstream>
#include <sstream>
usingnamespace cv;
usingnamespace std;
static Mat norm_0_255(InputArray _src) {
Mat src = _src.getMat();
// 創建和返回一個歸一化后的圖像矩陣:
Mat dst;
switch(src.channels()) {
case1:
cv::normalize(_src, dst, 0,255, NORM_MINMAX, CV_8UC1);
break;
case3:
cv::normalize(_src, dst, 0,255, NORM_MINMAX, CV_8UC3);
break;
default:
src.copyTo(dst);
break;
}
return dst;
}
//使用CSV文件去讀圖像和標簽,主要使用stringstream和getline方法
staticvoid read_csv(const string& filename, vector<Mat>& images, vector<int>& labels, char separator =';') {
std::ifstream file(filename.c_str(), ifstream::in);
if (!file) {
string error_message ="No valid input file was given, please check the given filename.";
CV_Error(CV_StsBadArg, error_message);
}
string line, path, classlabel;
while (getline(file, line)) {
stringstream liness(line);
getline(liness, path, separator);
getline(liness, classlabel);
if(!path.empty()&&!classlabel.empty()) {
images.push_back(imread(path, 0));
labels.push_back(atoi(classlabel.c_str()));
}
}
}
int main(int argc, constchar*argv[]) {
// 檢測合法的命令,顯示用法
// 如果沒有參數輸入則退出!.
if (argc <2) {
cout <<"usage: "<< argv[0]<<" <csv.ext> <output_folder> "<< endl;
exit(1);
}
string output_folder;
if (argc ==3) {
output_folder = string(argv[2]);
}
//讀取你的CSV文件路徑.
string fn_csv = string(argv[1]);
// 2個容器來存放圖像數據和對應的標簽
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);
}
// 如果沒有讀取到足夠圖片,我們也得退出.
if(images.size()<=1) {
string error_message ="This demo needs at least 2 images to work. Please add more images to your data set!";
CV_Error(CV_StsError, error_message);
}
// 得到第一張照片的高度. 在下面對圖像
// 變形到他們原始大小時需要
int height = images[0].rows;
// 下面的幾行代碼僅僅是從你的數據集中移除最后一張圖片
//[gm:自然這里需要根據自己的需要修改,他這里簡化了很多問題]
Mat testSample = images[images.size() -1];
int testLabel = labels[labels.size() -1];
images.pop_back();
labels.pop_back();
// 下面幾行創建了一個特征臉模型用於人臉識別,
// 通過CSV文件讀取的圖像和標簽訓練它。
// T這里是一個完整的PCA變換
//如果你只想保留10個主成分,使用如下代碼
// cv::createEigenFaceRecognizer(10);
//
// 如果你還希望使用置信度閾值來初始化,使用以下語句:
// cv::createEigenFaceRecognizer(10, 123.0);
//
// 如果你使用所有特征並且使用一個閾值,使用以下語句:
// cv::createEigenFaceRecognizer(0, 123.0);
//
Ptr<FaceRecognizer> model = createEigenFaceRecognizer();
model->train(images, labels);
// 下面對測試圖像進行預測,predictedLabel是預測標簽結果
int predictedLabel = model->predict(testSample);
//
// 還有一種調用方式,可以獲取結果同時得到閾值:
// int predictedLabel = -1;
// double confidence = 0.0;
// model->predict(testSample, predictedLabel, confidence);
//
string result_message = format("Predicted class = %d / Actual class = %d.", predictedLabel, testLabel);
cout << result_message << endl;
// 這里是如何獲取特征臉模型的特征值的例子,使用了getMat方法:
Mat eigenvalues = model->getMat("eigenvalues");
// 同樣可以獲取特征向量:
Mat W = model->getMat("eigenvectors");
// 得到訓練圖像的均值向量
Mat mean = model->getMat("mean");
// 現實還是保存:
if(argc==2) {
imshow("mean", norm_0_255(mean.reshape(1, images[0].rows)));
} else {
imwrite(format("%s/mean.png", output_folder.c_str()), norm_0_255(mean.reshape(1, images[0].rows)));
}
// 現實還是保存特征臉:
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 = norm_0_255(ev.reshape(1, height));
// 使用偽彩色來顯示結果,為了更好的感受.
Mat cgrayscale;
applyColorMap(grayscale, cgrayscale, COLORMAP_JET);
// 顯示或者保存:
if(argc==2) {
imshow(format("eigenface_%d", i), cgrayscale);
} else {
imwrite(format("%s/eigenface_%d.png", output_folder.c_str(), i), norm_0_255(cgrayscale));
}
}
// 在一些預測過程中,顯示還是保存重建后的圖像:
for(int num_components =10; num_components <300; num_components+=15) {
// 從模型中的特征向量截取一部分
Mat evs = Mat(W, Range::all(), Range(0, num_components));
Mat projection = subspaceProject(evs, mean, images[0].reshape(1,1));
Mat reconstruction = subspaceReconstruct(evs, mean, projection);
// 歸一化結果,為了顯示:
reconstruction = norm_0_255(reconstruction.reshape(1, images[0].rows));
// 顯示或者保存:
if(argc==2) {
imshow(format("eigenface_reconstruction_%d", num_components), reconstruction);
} else {
imwrite(format("%s/eigenface_reconstruction_%d.png", output_folder.c_str(), num_components), reconstruction);
}
}
// 如果我們不是存放到文件中,就顯示他,這里使用了暫定等待鍵盤輸入:
if(argc==2) {
waitKey(0);
}
return0;
}
我使用了偽彩色圖像,所以你可以看到在特征臉中灰度值是如何分布的。你可以看到特征臉不但對人臉特征進行編碼,還對這些圖像中的光照進行編碼。(看第四張圖像是左側的光照,而第五張是右側的光照)[gm:PCA對光照變化圖像識別效果很差,自然有一些改進方法,有后再談]
我們已經看到了,我們可以利用低維近似來重構人臉,我們看看對於一個好的重構,需要多少特征臉。我將依次畫出10,30,。。310張特征臉時的效果。
for(int num_components =10; num_components <300; num_components+=15) {
Mat evs = Mat(W, Range::all(), Range(0, num_components));
Mat projection = subspaceProject(evs, mean, images[0].reshape(1,1));
Mat reconstruction = subspaceReconstruct(evs, mean, projection);
reconstruction = norm_0_255(reconstruction.reshape(1, images[0].rows));
if(argc==2) {
imshow(format("eigenface_reconstruction_%d", num_components), reconstruction);
} else {
imwrite(format("%s/eigenface_reconstruction_%d.png", output_folder.c_str(), num_components), reconstruction);
}
}
顯然10個特征向量[gm:1個特征向量可以變形成一個特征臉,這里特征向量和特征臉概念有些近似]是不夠的,50個特征向量可以有效的編碼出重要的人臉特征。在ORL數據庫中,當使用300個特征向量時,你將獲取一個比較好的和重構結果。有定理指出重構需要選擇多少特征臉才合適,但它嚴重依賴於人臉數據庫。[gm:也就是沒啥討論意義,針對現實情況做出考慮吧]。文獻[Zhao03]是一個好的開始研究起點。
主成分分析是一種基於特征臉的方法,找到使數據中最大方差的特征線性組合。這是一個表現數據的強大方法,但它沒有考慮類別信息,並且在扔掉主元時,同時許多有鑒別的信息都被扔掉。假設你數據庫中的變化主要是光照變化,那么PCA此時幾乎失效了。[gm:把光照情況類似的照片認為一樣,而不管人臉其他細節]可以看去http://www.bytefish.de/wiki/pca_lda_with_gnu_octave 看下例子。
線性鑒別分析在降維的同時考慮類別信息,由統計學家 Sir R. A. Fisher發明。在他1936年的文獻中,他成功對花進行了分類:The useof multiple measurements in taxonomic problems [Fisher36]。為了找到一種特征組合方式,達到最大的類間離散度和最小的類內離散度。這個想法很簡單:在低維表示下,相同的類應該緊緊的聚在一起,而不同的類別盡量距離越遠。 這也被Belhumeur, Hespanha 和 Kriegman所認同,所以他們把鑒別分析引入到人臉識別問題中[BHK97]。
令x是一個來自c個類中的隨機向量,
散度矩陣 和S_{W}如下計算:
, 其中
是全部數據的均值
:
而
是某個類的均值
:
Fisher的分類算法可以看出一個投影矩陣 , 使得類的可分性最大:
接下來 [BHK97], 一個解決這個普通特征值優化問題的方法被提出:
還有一個問題未解決, Sw的排列最多只有 (N-c), N 個樣本和c個類別。在模式識別中,樣本數據個數N的大小一般小於輸入數據的維數。 [gm:比如說之前的圖片,N=400,而10000就是數據維數]那么,散度矩陣Sw就是奇異的(可以看文獻[RJ91])。在文獻[BHK97]中,使用PCA把數據投影到(N-c)維的子空間,然后再使用線性鑒別分析,因為Sw不是奇異矩陣了(可逆矩陣)。
然后優化問題可以寫成:
投影矩陣W,可以把樣本投影到(c-1)維的空間上,可以表示為
#include "opencv2/core/core.hpp"
#include "opencv2/contrib/contrib.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
#include <fstream>
#include <sstream>
usingnamespace cv;
usingnamespace std;
static Mat norm_0_255(InputArray _src) {
Mat src = _src.getMat();
// 創建和返回歸一化的圖像:
Mat dst;
switch(src.channels()) {
case1:
cv::normalize(_src, dst, 0,255, NORM_MINMAX, CV_8UC1);
break;
case3:
cv::normalize(_src, dst, 0,255, NORM_MINMAX, CV_8UC3);
break;
default:
src.copyTo(dst);
break;
}
return dst;
}
staticvoid read_csv(const string& filename, vector<Mat>& images, vector<int>& labels, char separator =';') {
std::ifstream file(filename.c_str(), ifstream::in);
if (!file) {
string error_message ="No valid input file was given, please check the given filename.";
CV_Error(CV_StsBadArg, error_message);
}
string line, path, classlabel;
while (getline(file, line)) {
stringstream liness(line);
getline(liness, path, separator);
getline(liness, classlabel);
if(!path.empty()&&!classlabel.empty()) {
images.push_back(imread(path, 0));
labels.push_back(atoi(classlabel.c_str()));
}
}
}
int main(int argc, constchar*argv[]) {
// 判斷輸入命令是否有效,輸出用法
// 如果沒有輸入參數.
if (argc <2) {
cout <<"usage: "<< argv[0]<<" <csv.ext> <output_folder> "<< endl;
exit(1);
}
string output_folder;
if (argc ==3) {
output_folder = string(argv[2]);
}
// 獲取CSV文件的路徑.
string fn_csv = string(argv[1]);
// 這些容器存放圖片和標簽.
vector<Mat> images;
vector<int> labels;
// 載入數據.如果不合理,會出錯
// 輸入文件名fn_csv已經有了.
try {
read_csv(fn_csv, images, labels);
} catch (cv::Exception& e) {
cerr <<"Error opening file \""<< fn_csv <<"\". Reason: "<< e.msg << endl;
// 什么也不能做了
exit(1);
}
// 如果沒有足夠圖像就退出掉.
if(images.size()<=1) {
string error_message ="This demo needs at least 2 images to work. Please add more images to your data set!";
CV_Error(CV_StsError, error_message);
}
int height = images[0].rows;
Mat testSample = images[images.size() -1];
int testLabel = labels[labels.size() -1];
images.pop_back();
labels.pop_back();
// 如果想保存10個fisherfaces
// cv::createFisherFaceRecognizer(10);
//
// 如果要以123.0作為置信閾值
// cv::createFisherFaceRecognizer(0, 123.0);
//
Ptr<FaceRecognizer> model = createFisherFaceRecognizer();
model->train(images, labels);
int predictedLabel = model->predict(testSample);
//
// model->predict(testSample, predictedLabel, confidence);
//
string result_message = format("Predicted class = %d / Actual class = %d.", predictedLabel, testLabel);
cout << result_message << endl;
Mat eigenvalues = model->getMat("eigenvalues");
Mat W = model->getMat("eigenvectors");
Mat mean = model->getMat("mean");
if(argc==2) {
imshow("mean", norm_0_255(mean.reshape(1, images[0].rows)));
} else {
imwrite(format("%s/mean.png", output_folder.c_str()), norm_0_255(mean.reshape(1, images[0].rows)));
}
//顯示還是保存, 最多16 Fisherfaces:
for (int i =0; i < min(16, W.cols); i++) {
string msg = format("Eigenvalue #%d = %.5f", i, eigenvalues.at<double>(i));
cout << msg << endl;
Mat ev = W.col(i).clone();
Mat grayscale = norm_0_255(ev.reshape(1, height));
// 使用Bone偽彩色圖像來顯示.
Mat cgrayscale;
applyColorMap(grayscale, cgrayscale, COLORMAP_BONE);
if(argc==2) {
imshow(format("fisherface_%d", i), cgrayscale);
} else {
imwrite(format("%s/fisherface_%d.png", output_folder.c_str(), i), norm_0_255(cgrayscale));
}
}
for(int num_component =0; num_component < min(16, W.cols); num_component++) {
Mat ev = W.col(num_component);
Mat projection = subspaceProject(ev, mean, images[0].reshape(1,1));
Mat reconstruction = subspaceReconstruct(ev, mean, projection);
reconstruction = norm_0_255(reconstruction.reshape(1, images[0].rows));
if(argc==2) {
imshow(format("fisherface_reconstruction_%d", num_component), reconstruction);
} else {
imwrite(format("%s/fisherface_reconstruction_%d.png", output_folder.c_str(), num_component), reconstruction);
}
}
if(argc==2) {
waitKey(0);
}
return0;
}
在這個例子中,我使用YaleA人臉數據庫,僅僅因為顯示更好些。每一個Fisherface都和原始圖像有同樣長度,因此它可以被顯示成圖像。下面顯示了16張Fisherfaces圖像。
Fisherfaces方法學習一個正對標簽的轉換矩陣,所依它不會如特征臉那樣那么注重光照。鑒別分析是尋找可以區分人的面部特征。需要說明的是,Fisherfaces的性能也很依賴於輸入數據。實際上,如果你對光照好的圖片上學習Fisherfaces,而想對不好的光照圖片進行識別,那么他可能會找到錯誤的主元,因為在不好光照圖片上,這些特征不優越。這似乎是符合邏輯的,因為這個方法沒有機會去學習光照。[gm:那么采集圖像時就要考慮光照變化,訓練時考慮所有光照情況,數據庫multi-pie就考慮很多種光照]
Fisherfaces允許對投影圖像進行重建,就行特征臉一樣。但是由於我們僅僅使用這些特征來區分不同的類別,因此你無法期待對原圖像有一個好的重建效果。[gm:也就是特征臉把每個圖片看成一個個體,重建時效果也有保證,而Fisherfaces把一個人的照片看成一個整體,那么重建時重建的效果則不是很好]。對於Fisherfaces方法我們將把樣本圖像逐個投影到Fisherfaces上。因此你可以獲得一個好的可視效果,每個Fisherfaces特征可以被描述為
for(int num_component =0; num_component < min(16, W.cols); num_component++) {
Mat ev = W.col(num_component);
Mat projection = subspaceProject(ev, mean, images[0].reshape(1,1));
Mat reconstruction = subspaceReconstruct(ev, mean, projection);
reconstruction = norm_0_255(reconstruction.reshape(1, images[0].rows));
if(argc==2) {
imshow(format("fisherface_reconstruction_%d", num_component), reconstruction);
} else {
imwrite(format("%s/fisherface_reconstruction_%d.png", output_folder.c_str(), num_component), reconstruction);
}
}
對於人類眼睛來說,差異比較微妙,但你還是可以看到一些差異的。
局部二值模式直方圖Local Binary Patterns Histograms
Eigenfaces和Fisherfaces使用整體方法來進行人臉識別[gm:直接使用所有的像素]。你把你的數據當作圖像空間的高維向量。我們都知道高維數據是糟糕的,所以一個低維子空間被確定,對於信息保存可能很好。Eigenfaces是最大化總的散度,這樣可能導致,當方差由外部條件產生時,最大方差的主成分不適合用來分類。所以為使用一些鑒別分析,我們使用了LDA方法來優化。Fisherfaces方法可以很好的運作,至少在我們假設的模型的有限情況下。
現實生活是不完美的。你無法保證在你的圖像中光照條件是完美的,或者說1個人的10張照片。所以,如果每人僅僅只有一張照片呢?我們的子空間的協方差估計方法可能完全錯誤,所以識別也可能錯誤。是否記得Eigenfaces在AT&T數據庫上達到了96%的識別率?對於這樣有效的估計,我們需要多少張訓練圖像呢?下圖是Eigenfaces和Fisherfaces方法在AT&T數據庫上的首選識別率,這是一個簡單的數據庫:
因此,若你想得到好的識別率,你大約需要每個人有8(7~9)張圖像,而Fisherfaces在這里並沒有好的幫助。以上的實驗是10個圖像的交叉驗證結果,使用了facerec框架: https://github.com/bytefish/facerec。這不是一個刊物,所以我不會用高深的數學分析來證明這個圖像。 當遇到小的訓練數據集時,可以看一下文獻[KM01],了解二種方法的細節分析。
一些研究專注於圖像局部特征的提取。主意是我們不把整個圖像看成一個高維向量,僅僅用局部特征來描述一個物體。通過這種方式提取特征,你將獲得一個低維隱式。一個好主意!但是你很快發現這種圖像表示方法不僅僅遭受光照變化。你想想圖像中的尺度變化、形變、旋轉—我們的局部表示方式起碼對這些情況比較穩健。正如SIFT,LBP方法在2D紋理分析中舉足輕重。LBP的基本思想是對圖像的像素和它局部周圍像素進行對比后的結果進行求和。把這個像素作為中心,對相鄰像素進行閾值比較。如果中心像素的亮度大於等於他的相鄰像素,把他標記為1,否則標記為0。你會用二進制數字來表示每個像素,比如11001111。因此,由於周圍相鄰8個像素,你最終可能獲取2^8個可能組合,被稱為局部二值模式,有時被稱為LBP碼。第一個在文獻中描述的LBP算子實際使用的是3*3的鄰域。
一個更加正式的LBP操作可以被定義為
其中 是中心像素,亮度是
;而
則是相鄰像素的亮度。s是一個符號函數:
這種描述方法使得你可以很好的捕捉到圖像中的細節。實際上,研究者們可以用它在紋理分類上得到最先進的水平。正如剛才描述的方法被提出后,固定的近鄰區域對於尺度變化的編碼失效。所以,使用一個變量的擴展方法,在文獻[AHP04]中有描述。主意是使用可變半徑的圓對近鄰像素進行編碼,這樣可以捕捉到如下的近鄰:
對一個給定的點 ,他的近鄰點
可以由如下計算:
其中,R是圓的半徑,而P是樣本點的個數。
這個操作是對原始LBP算子的擴展,所以有時被稱為擴展LBP(又稱為圓形LBP)。如果一個在圓上的點不在圖像坐標上,我們使用他的內插點。計算機科學有一堆聰明的插值方法,而OpenCV使用雙線性插值。
LBP算子,對於灰度的單調變化很穩健。我們可以看到手工改變后的圖像的LBP圖像(你可以看到LBP圖像是什么樣子的!)
那么剩下來的就是如何合並空間信息用於人臉識別模型。Ahonenet. Al在文獻 [AHP04]中提出表示方法,對LBP圖像成m個塊,每個塊提取直方圖。通過連接局部特直方圖(而不是合並)然后就能得到空間增強的特征向量。這些直方圖被稱為局部二值模式直方圖。
OpenCV中的局部二值模式直方圖Local Binary Patterns Histograms inOpenCV
#include "opencv2/core/core.hpp"
#include "opencv2/contrib/contrib.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
#include <fstream>
#include <sstream>
usingnamespace cv;
usingnamespace std;
staticvoid read_csv(const string& filename, vector<Mat>& images, vector<int>& labels, char separator =';') {
std::ifstream file(filename.c_str(), ifstream::in);
if (!file) {
string error_message ="No valid input file was given, please check the given filename.";
CV_Error(CV_StsBadArg, error_message);
}
string line, path, classlabel;
while (getline(file, line)) {
stringstream liness(line);
getline(liness, path, separator);
getline(liness, classlabel);
if(!path.empty()&&!classlabel.empty()) {
images.push_back(imread(path, 0));
labels.push_back(atoi(classlabel.c_str()));
}
}
}
int main(int argc, constchar*argv[]) {
if (argc !=2) {
cout <<"usage: "<< argv[0]<<" <csv.ext>"<< endl;
exit(1);
}
string fn_csv = string(argv[1]);
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;
// nothing more we can do
exit(1);
}
if(images.size()<=1) {
string error_message ="This demo needs at least 2 images to work. Please add more images to your data set!";
CV_Error(CV_StsError, error_message);
}
int height = images[0].rows;
Mat testSample = images[images.size() -1];
int testLabel = labels[labels.size() -1];
images.pop_back();
labels.pop_back();
// TLBPHFaceRecognizer 使用了擴展的LBP
// 在其他的算子中他可能很容易被擴展
// 下面是默認參數
// radius = 1
// neighbors = 8
// grid_x = 8
// grid_y = 8
//
// 如果你要創建 LBPH FaceRecognizer 半徑是2,16個鄰域
// cv::createLBPHFaceRecognizer(2, 16);
//
// 如果你需要一個閾值,並且使用默認參數:
// cv::createLBPHFaceRecognizer(1,8,8,8,123.0)
//
Ptr<FaceRecognizer> model = createLBPHFaceRecognizer();
model->train(images, labels);
int predictedLabel = model->predict(testSample);
// int predictedLabel = -1;
// double confidence = 0.0;
// model->predict(testSample, predictedLabel, confidence);
//
string result_message = format("Predicted class = %d / Actual class = %d.", predictedLabel, testLabel);
cout << result_message << endl;
// 有時你需要設置或者獲取內部數據模型,
// 他不能被暴露在 cv::FaceRecognizer類中.
//
// 首先我們對FaceRecognizer的閾值設置到0.0,而不是重寫訓練模型
// 當你重新估計模型時很重要
//
model->set("threshold",0.0);
predictedLabel = model->predict(testSample);
cout <<"Predicted class = "<< predictedLabel << endl;
// 由於確保高效率,LBP圖沒有被存儲在模型里面。D
cout <<"Model Information:"<< endl;
string model_info = format("\tLBPH(radius=%i, neighbors=%i, grid_x=%i, grid_y=%i, threshold=%.2f)",
model->getInt("radius"),
model->getInt("neighbors"),
model->getInt("grid_x"),
model->getInt("grid_y"),
model->getDouble("threshold"));
cout << model_info << endl;
// 我們可以獲取樣本的直方圖:
vector<Mat> histograms = model->getMatVector("histograms");
// 我需要現實它嗎? 或許它的長度才是我們感興趣的:
cout <<"Size of the histograms: "<< histograms[0].total()<< endl;
return0;
}
你已經學會如何在真實應用下,使用新的FaceRecognizer類。讀完算法,可能到你進行實驗的時候了,使用它們,改進它們,讓OpenCV社區參與其中!
[AHP04] |
(1, 2, 3) Ahonen, T., Hadid, A., and Pietikainen, M. Face Recognition with Local Binary Patterns. Computer Vision - ECCV 2004 (2004), 469–481. |
[BHK97] |
(1, 2, 3, 4, 5) Belhumeur, P. N., Hespanha, J., and Kriegman, D. Eigenfaces vs. Fisherfaces: Recognition Using Class Specific Linear Projection. IEEE Transactions on Pattern Analysis and Machine Intelligence 19, 7 (1997), 711–720. |
Brunelli, R., Poggio, T. Face Recognition through Geometrical Features. European Conference on Computer Vision (ECCV) 1992, S. 792–800. |
Duda, Richard O. and Hart, Peter E. and Stork, David G., Pattern Classification (2nd Edition) 2001. |
Fisher, R. A. The use of multiple measurements in taxonomic problems. Annals Eugen. 7 (1936), 179–188. |
[GBK01] |
Georghiades, A.S. and Belhumeur, P.N. and Kriegman, D.J., From Few to Many: Illumination Cone Models for Face Recognition under Variable Lighting and Pose IEEE Transactions on Pattern Analysis and Machine Intelligence 23, 6 (2001), 643-660. |
Kanade, T. Picture processing system by computer complex and recognition of human faces. PhD thesis, Kyoto University, November 1973 |
Martinez, A and Kak, A. PCA versus LDA IEEE Transactions on Pattern Analysis and Machine Intelligence, Vol. 23, No.2, pp. 228-233, 2001. |
Lee, K., Ho, J., Kriegman, D. Acquiring Linear Subspaces for Face Recognition under Variable Lighting. In: IEEE Transactions on Pattern Analysis and Machine Intelligence (PAMI) 27 (2005), Nr. 5 |
Messer, K. et al. Performance Characterisation of Face Recognition Algorithms and Their Sensitivity to Severe Illumination Changes. In: In: ICB, 2006, S. 1–11. |
|
[Tan10] |
Tan, X., and Triggs, B. Enhanced local texture feature sets for face recognition under difficult lighting conditions. IEEE Transactions on Image Processing 19 (2010), 1635–650. |
Turk, M., and Pentland, A. Eigenfaces for recognition. Journal of Cognitive Neuroscience 3 (1991), 71–86. |
Chiara Turati, Viola Macchi Cassia, F. S., and Leo, I. Newborns face recognition: Role of inner and outer facial features. Child Development 77, 2 (2006), 297–311. |
Wiskott, L., Fellous, J., Krüger, N., Malsburg, C. Face Recognition By Elastic Bunch Graph Matching.IEEE Transactions on Pattern Analysis and Machine Intelligence 19 (1997), S. 775–779 |
Zhao, W., Chellappa, R., Phillips, P., and Rosenfeld, A. Face recognition: A literature survey. ACM Computing Surveys (CSUR) 35, 4 (2003), 399–458. |
對於圖像數據的對其很重要,特別遇到情感檢測這類任務,你需要越多的細節越好。相信我,你不會要自己動手做吧。我給你提供了一個Python代碼。
# CropFace(image, eye_left, eye_right, offset_pct, dest_sz)
# eye_left is the position of the left eye
# eye_right is the position of the right eye
# offset_pct is the percent of the image you want to keep next to the eyes (horizontal, vertical direction)
# dest_sz is the size of the output image
#
importsys,math,Image
defDistance(p1,p2):
dx = p2[0]- p1[0]
dy = p2[1]- p1[1]
return math.sqrt(dx*dx+dy*dy)
defScaleRotateTranslate(image, angle, center =None, new_center =None, scale =None, resample=Image.BICUBIC):
if (scale isNone)and (center isNone):
return image.rotate(angle=angle, resample=resample)
nx,ny = x,y = center
sx=sy=1.0
if new_center:
(nx,ny) = new_center
if scale:
(sx,sy) = (scale, scale)
cosine = math.cos(angle)
sine = math.sin(angle)
a = cosine/sx
b = sine/sx
c = x-nx*a-ny*b
d =-sine/sy
e = cosine/sy
f = y-nx*d-ny*e
return image.transform(image.size, Image.AFFINE, (a,b,c,d,e,f), resample=resample)
defCropFace(image, eye_left=(0,0), eye_right=(0,0), offset_pct=(0.2,0.2), dest_sz = (70,70)):
# calculate offsets in original image
offset_h = math.floor(float(offset_pct[0])*dest_sz[0])
offset_v = math.floor(float(offset_pct[1])*dest_sz[1])
# get the direction
eye_direction = (eye_right[0]- eye_left[0], eye_right[1]- eye_left[1])
# calc rotation angle in radians
rotation =-math.atan2(float(eye_direction[1]),float(eye_direction[0]))
# distance between them
dist = Distance(eye_left, eye_right)
# calculate the reference eye-width
reference = dest_sz[0]-2.0*offset_h
# scale factor
scale =float(dist)/float(reference)
# rotate original around the left eye
image = ScaleRotateTranslate(image, center=eye_left, angle=rotation)
# crop the rotated image
crop_xy = (eye_left[0]- scale*offset_h, eye_left[1]- scale*offset_v)
crop_size = (dest_sz[0]*scale, dest_sz[1]*scale)
image = image.crop((int(crop_xy[0]),int(crop_xy[1]),int(crop_xy[0]+crop_size[0]),int(crop_xy[1]+crop_size[1])))
# resize it
image = image.resize(dest_sz, Image.ANTIALIAS)
return image
if __name__ =="__main__":
image = Image.open("arnie.jpg")
CropFace(image, eye_left=(252,364), eye_right=(420,366), offset_pct=(0.1,0.1), dest_sz=(200,200)).save("arnie_10_10_200_200.jpg")
CropFace(image, eye_left=(252,364), eye_right=(420,366), offset_pct=(0.2,0.2), dest_sz=(200,200)).save("arnie_20_20_200_200.jpg")
CropFace(image, eye_left=(252,364), eye_right=(420,366), offset_pct=(0.3,0.3), dest_sz=(200,200)).save("arnie_30_30_200_200.jpg")
CropFace(image, eye_left=(252,364), eye_right=(420,366), offset_pct=(0.2,0.2)).save("arnie_20_20_70_70.jpg")
設想我們有一張施瓦辛格的照片 thisphoto of Arnold Schwarzenegger,人眼坐標是(252,364)和(420,366)。參數是水平偏移、垂直偏移和你縮放后的圖像大小。[gm:我建議使用最小的那張圖片]
Configuration |
Cropped, Scaled, Rotated Face |
0.1 (10%), 0.1 (10%), (200,200) |
|
0.2 (20%), 0.2 (20%), (200,200) |
|
0.3 (30%), 0.3 (30%), (200,200) |
|
0.2 (20%), 0.2 (20%), (70,70) |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 |
/home/philipp/facerec/data/at/s13/2.pgm;12 /home/philipp/facerec/data/at/s13/7.pgm;12 /home/philipp/facerec/data/at/s13/6.pgm;12 /home/philipp/facerec/data/at/s13/9.pgm;12 /home/philipp/facerec/data/at/s13/5.pgm;12 /home/philipp/facerec/data/at/s13/3.pgm;12 /home/philipp/facerec/data/at/s13/4.pgm;12 /home/philipp/facerec/data/at/s13/10.pgm;12 /home/philipp/facerec/data/at/s13/8.pgm;12 /home/philipp/facerec/data/at/s13/1.pgm;12 /home/philipp/facerec/data/at/s17/2.pgm;16 /home/philipp/facerec/data/at/s17/7.pgm;16 /home/philipp/facerec/data/at/s17/6.pgm;16 /home/philipp/facerec/data/at/s17/9.pgm;16 /home/philipp/facerec/data/at/s17/5.pgm;16 /home/philipp/facerec/data/at/s17/3.pgm;16 /home/philipp/facerec/data/at/s17/4.pgm;16 /home/philipp/facerec/data/at/s17/10.pgm;16 /home/philipp/facerec/data/at/s17/8.pgm;16 /home/philipp/facerec/data/at/s17/1.pgm;16 /home/philipp/facerec/data/at/s32/2.pgm;31 /home/philipp/facerec/data/at/s32/7.pgm;31 /home/philipp/facerec/data/at/s32/6.pgm;31 /home/philipp/facerec/data/at/s32/9.pgm;31 /home/philipp/facerec/data/at/s32/5.pgm;31 /home/philipp/facerec/data/at/s32/3.pgm;31 /home/philipp/facerec/data/at/s32/4.pgm;31 /home/philipp/facerec/data/at/s32/10.pgm;31 /home/philipp/facerec/data/at/s32/8.pgm;31 /home/philipp/facerec/data/at/s32/1.pgm;31 /home/philipp/facerec/data/at/s10/2.pgm;9 /home/philipp/facerec/data/at/s10/7.pgm;9 /home/philipp/facerec/data/at/s10/6.pgm;9 /home/philipp/facerec/data/at/s10/9.pgm;9 /home/philipp/facerec/data/at/s10/5.pgm;9 /home/philipp/facerec/data/at/s10/3.pgm;9 /home/philipp/facerec/data/at/s10/4.pgm;9 /home/philipp/facerec/data/at/s10/10.pgm;9 /home/philipp/facerec/data/at/s10/8.pgm;9 /home/philipp/facerec/data/at/s10/1.pgm;9 /home/philipp/facerec/data/at/s27/2.pgm;26 /home/philipp/facerec/data/at/s27/7.pgm;26 /home/philipp/facerec/data/at/s27/6.pgm;26 /home/philipp/facerec/data/at/s27/9.pgm;26 /home/philipp/facerec/data/at/s27/5.pgm;26 /home/philipp/facerec/data/at/s27/3.pgm;26 /home/philipp/facerec/data/at/s27/4.pgm;26 /home/philipp/facerec/data/at/s27/10.pgm;26 /home/philipp/facerec/data/at/s27/8.pgm;26 /home/philipp/facerec/data/at/s27/1.pgm;26 /home/philipp/facerec/data/at/s5/2.pgm;4 /home/philipp/facerec/data/at/s5/7.pgm;4 /home/philipp/facerec/data/at/s5/6.pgm;4 /home/philipp/facerec/data/at/s5/9.pgm;4 /home/philipp/facerec/data/at/s5/5.pgm;4 /home/philipp/facerec/data/at/s5/3.pgm;4 /home/philipp/facerec/data/at/s5/4.pgm;4 /home/philipp/facerec/data/at/s5/10.pgm;4 /home/philipp/facerec/data/at/s5/8.pgm;4 /home/philipp/facerec/data/at/s5/1.pgm;4 /home/philipp/facerec/data/at/s20/2.pgm;19 /home/philipp/facerec/data/at/s20/7.pgm;19 /home/philipp/facerec/data/at/s20/6.pgm;19 /home/philipp/facerec/data/at/s20/9.pgm;19 /home/philipp/facerec/data/at/s20/5.pgm;19 /home/philipp/facerec/data/at/s20/3.pgm;19 /home/philipp/facerec/data/at/s20/4.pgm;19 /home/philipp/facerec/data/at/s20/10.pgm;19 /home/philipp/facerec/data/at/s20/8.pgm;19 /home/philipp/facerec/data/at/s20/1.pgm;19 /home/philipp/facerec/data/at/s30/2.pgm;29 /home/philipp/facerec/data/at/s30/7.pgm;29 /home/philipp/facerec/data/at/s30/6.pgm;29 /home/philipp/facerec/data/at/s30/9.pgm;29 /home/philipp/facerec/data/at/s30/5.pgm;29 /home/philipp/facerec/data/at/s30/3.pgm;29 /home/philipp/facerec/data/at/s30/4.pgm;29 /home/philipp/facerec/data/at/s30/10.pgm;29 /home/philipp/facerec/data/at/s30/8.pgm;29 /home/philipp/facerec/data/at/s30/1.pgm;29 /home/philipp/facerec/data/at/s39/2.pgm;38 /home/philipp/facerec/data/at/s39/7.pgm;38 /home/philipp/facerec/data/at/s39/6.pgm;38 /home/philipp/facerec/data/at/s39/9.pgm;38 /home/philipp/facerec/data/at/s39/5.pgm;38 /home/philipp/facerec/data/at/s39/3.pgm;38 /home/philipp/facerec/data/at/s39/4.pgm;38 /home/philipp/facerec/data/at/s39/10.pgm;38 /home/philipp/facerec/data/at/s39/8.pgm;38 /home/philipp/facerec/data/at/s39/1.pgm;38 /home/philipp/facerec/data/at/s35/2.pgm;34 /home/philipp/facerec/data/at/s35/7.pgm;34 /home/philipp/facerec/data/at/s35/6.pgm;34 /home/philipp/facerec/data/at/s35/9.pgm;34 /home/philipp/facerec/data/at/s35/5.pgm;34 /home/philipp/facerec/data/at/s35/3.pgm;34 /home/philipp/facerec/data/at/s35/4.pgm;34 /home/philipp/facerec/data/at/s35/10.pgm;34 /home/philipp/facerec/data/at/s35/8.pgm;34 /home/philipp/facerec/data/at/s35/1.pgm;34 /home/philipp/facerec/data/at/s23/2.pgm;22 /home/philipp/facerec/data/at/s23/7.pgm;22 /home/philipp/facerec/data/at/s23/6.pgm;22 /home/philipp/facerec/data/at/s23/9.pgm;22 /home/philipp/facerec/data/at/s23/5.pgm;22 /home/philipp/facerec/data/at/s23/3.pgm;22 /home/philipp/facerec/data/at/s23/4.pgm;22 /home/philipp/facerec/data/at/s23/10.pgm;22 /home/philipp/facerec/data/at/s23/8.pgm;22 /home/philipp/facerec/data/at/s23/1.pgm;22 /home/philipp/facerec/data/at/s4/2.pgm;3 /home/philipp/facerec/data/at/s4/7.pgm;3 /home/philipp/facerec/data/at/s4/6.pgm;3 /home/philipp/facerec/data/at/s4/9.pgm;3 /home/philipp/facerec/data/at/s4/5.pgm;3 /home/philipp/facerec/data/at/s4/3.pgm;3 /home/philipp/facerec/data/at/s4/4.pgm;3 /home/philipp/facerec/data/at/s4/10.pgm;3 /home/philipp/facerec/data/at/s4/8.pgm;3 /home/philipp/facerec/data/at/s4/1.pgm;3 /home/philipp/facerec/data/at/s9/2.pgm;8 /home/philipp/facerec/data/at/s9/7.pgm;8 /home/philipp/facerec/data/at/s9/6.pgm;8 /home/philipp/facerec/data/at/s9/9.pgm;8 /home/philipp/facerec/data/at/s9/5.pgm;8 /home/philipp/facerec/data/at/s9/3.pgm;8 /home/philipp/facerec/data/at/s9/4.pgm;8 /home/philipp/facerec/data/at/s9/10.pgm;8 /home/philipp/facerec/data/at/s9/8.pgm;8 /home/philipp/facerec/data/at/s9/1.pgm;8 /home/philipp/facerec/data/at/s37/2.pgm;36 /home/philipp/facerec/data/at/s37/7.pgm;36 /home/philipp/facerec/data/at/s37/6.pgm;36 /home/philipp/facerec/data/at/s37/9.pgm;36 /home/philipp/facerec/data/at/s37/5.pgm;36 /home/philipp/facerec/data/at/s37/3.pgm;36 /home/philipp/facerec/data/at/s37/4.pgm;36 /home/philipp/facerec/data/at/s37/10.pgm;36 /home/philipp/facerec/data/at/s37/8.pgm;36 /home/philipp/facerec/data/at/s37/1.pgm;36 /home/philipp/facerec/data/at/s24/2.pgm;23 /home/philipp/facerec/data/at/s24/7.pgm;23 /home/philipp/facerec/data/at/s24/6.pgm;23 /home/philipp/facerec/data/at/s24/9.pgm;23 /home/philipp/facerec/data/at/s24/5.pgm;23 /home/philipp/facerec/data/at/s24/3.pgm;23 /home/philipp/facerec/data/at/s24/4.pgm;23 /home/philipp/facerec/data/at/s24/10.pgm;23 /home/philipp/facerec/data/at/s24/8.pgm;23 /home/philipp/facerec/data/at/s24/1.pgm;23 /home/philipp/facerec/data/at/s19/2.pgm;18 /home/philipp/facerec/data/at/s19/7.pgm;18 /home/philipp/facerec/data/at/s19/6.pgm;18 /home/philipp/facerec/data/at/s19/9.pgm;18 /home/philipp/facerec/data/at/s19/5.pgm;18 /home/philipp/facerec/data/at/s19/3.pgm;18 /home/philipp/facerec/data/at/s19/4.pgm;18 /home/philipp/facerec/data/at/s19/10.pgm;18 /home/philipp/facerec/data/at/s19/8.pgm;18 /home/philipp/facerec/data/at/s19/1.pgm;18 /home/philipp/facerec/data/at/s8/2.pgm;7 /home/philipp/facerec/data/at/s8/7.pgm;7 /home/philipp/facerec/data/at/s8/6.pgm;7 /home/philipp/facerec/data/at/s8/9.pgm;7 /home/philipp/facerec/data/at/s8/5.pgm;7 /home/philipp/facerec/data/at/s8/3.pgm;7 /home/philipp/facerec/data/at/s8/4.pgm;7 /home/philipp/facerec/data/at/s8/10.pgm;7 /home/philipp/facerec/data/at/s8/8.pgm;7 /home/philipp/facerec/data/at/s8/1.pgm;7 /home/philipp/facerec/data/at/s21/2.pgm;20 /home/philipp/facerec/data/at/s21/7.pgm;20 /home/philipp/facerec/data/at/s21/6.pgm;20 /home/philipp/facerec/data/at/s21/9.pgm;20 /home/philipp/facerec/data/at/s21/5.pgm;20 /home/philipp/facerec/data/at/s21/3.pgm;20 /home/philipp/facerec/data/at/s21/4.pgm;20 /home/philipp/facerec/data/at/s21/10.pgm;20 /home/philipp/facerec/data/at/s21/8.pgm;20 /home/philipp/facerec/data/at/s21/1.pgm;20 /home/philipp/facerec/data/at/s1/2.pgm;0 /home/philipp/facerec/data/at/s1/7.pgm;0 /home/philipp/facerec/data/at/s1/6.pgm;0 /home/philipp/facerec/data/at/s1/9.pgm;0 /home/philipp/facerec/data/at/s1/5.pgm;0 /home/philipp/facerec/data/at/s1/3.pgm;0 /home/philipp/facerec/data/at/s1/4.pgm;0 /home/philipp/facerec/data/at/s1/10.pgm;0 /home/philipp/facerec/data/at/s1/8.pgm;0 /home/philipp/facerec/data/at/s1/1.pgm;0 /home/philipp/facerec/data/at/s7/2.pgm;6 /home/philipp/facerec/data/at/s7/7.pgm;6 /home/philipp/facerec/data/at/s7/6.pgm;6 /home/philipp/facerec/data/at/s7/9.pgm;6 /home/philipp/facerec/data/at/s7/5.pgm;6 /home/philipp/facerec/data/at/s7/3.pgm;6 /home/philipp/facerec/data/at/s7/4.pgm;6 /home/philipp/facerec/data/at/s7/10.pgm;6 /home/philipp/facerec/data/at/s7/8.pgm;6 /home/philipp/facerec/data/at/s7/1.pgm;6 /home/philipp/facerec/data/at/s16/2.pgm;15 /home/philipp/facerec/data/at/s16/7.pgm;15 /home/philipp/facerec/data/at/s16/6.pgm;15 /home/philipp/facerec/data/at/s16/9.pgm;15 /home/philipp/facerec/data/at/s16/5.pgm;15 /home/philipp/facerec/data/at/s16/3.pgm;15 /home/philipp/facerec/data/at/s16/4.pgm;15 /home/philipp/facerec/data/at/s16/10.pgm;15 /home/philipp/facerec/data/at/s16/8.pgm;15 /home/philipp/facerec/data/at/s16/1.pgm;15 /home/philipp/facerec/data/at/s36/2.pgm;35 /home/philipp/facerec/data/at/s36/7.pgm;35 /home/philipp/facerec/data/at/s36/6.pgm;35 /home/philipp/facerec/data/at/s36/9.pgm;35 /home/philipp/facerec/data/at/s36/5.pgm;35 /home/philipp/facerec/data/at/s36/3.pgm;35 /home/philipp/facerec/data/at/s36/4.pgm;35 /home/philipp/facerec/data/at/s36/10.pgm;35 /home/philipp/facerec/data/at/s36/8.pgm;35 /home/philipp/facerec/data/at/s36/1.pgm;35 /home/philipp/facerec/data/at/s25/2.pgm;24 /home/philipp/facerec/data/at/s25/7.pgm;24 /home/philipp/facerec/data/at/s25/6.pgm;24 /home/philipp/facerec/data/at/s25/9.pgm;24 /home/philipp/facerec/data/at/s25/5.pgm;24 /home/philipp/facerec/data/at/s25/3.pgm;24 /home/philipp/facerec/data/at/s25/4.pgm;24 /home/philipp/facerec/data/at/s25/10.pgm;24 /home/philipp/facerec/data/at/s25/8.pgm;24 /home/philipp/facerec/data/at/s25/1.pgm;24 /home/philipp/facerec/data/at/s14/2.pgm;13 /home/philipp/facerec/data/at/s14/7.pgm;13 /home/philipp/facerec/data/at/s14/6.pgm;13 /home/philipp/facerec/data/at/s14/9.pgm;13 /home/philipp/facerec/data/at/s14/5.pgm;13 /home/philipp/facerec/data/at/s14/3.pgm;13 /home/philipp/facerec/data/at/s14/4.pgm;13 /home/philipp/facerec/data/at/s14/10.pgm;13 /home/philipp/facerec/data/at/s14/8.pgm;13 /home/philipp/facerec/data/at/s14/1.pgm;13 /home/philipp/facerec/data/at/s34/2.pgm;33 /home/philipp/facerec/data/at/s34/7.pgm;33 /home/philipp/facerec/data/at/s34/6.pgm;33 /home/philipp/facerec/data/at/s34/9.pgm;33 /home/philipp/facerec/data/at/s34/5.pgm;33 /home/philipp/facerec/data/at/s34/3.pgm;33 /home/philipp/facerec/data/at/s34/4.pgm;33 /home/philipp/facerec/data/at/s34/10.pgm;33 /home/philipp/facerec/data/at/s34/8.pgm;33 /home/philipp/facerec/data/at/s34/1.pgm;33 /home/philipp/facerec/data/at/s11/2.pgm;10 /home/philipp/facerec/data/at/s11/7.pgm;10 /home/philipp/facerec/data/at/s11/6.pgm;10 /home/philipp/facerec/data/at/s11/9.pgm;10 /home/philipp/facerec/data/at/s11/5.pgm;10 /home/philipp/facerec/data/at/s11/3.pgm;10 /home/philipp/facerec/data/at/s11/4.pgm;10 /home/philipp/facerec/data/at/s11/10.pgm;10 /home/philipp/facerec/data/at/s11/8.pgm;10 /home/philipp/facerec/data/at/s11/1.pgm;10 /home/philipp/facerec/data/at/s26/2.pgm;25 /home/philipp/facerec/data/at/s26/7.pgm;25 /home/philipp/facerec/data/at/s26/6.pgm;25 /home/philipp/facerec/data/at/s26/9.pgm;25 /home/philipp/facerec/data/at/s26/5.pgm;25 /home/philipp/facerec/data/at/s26/3.pgm;25 /home/philipp/facerec/data/at/s26/4.pgm;25 /home/philipp/facerec/data/at/s26/10.pgm;25 /home/philipp/facerec/data/at/s26/8.pgm;25 /home/philipp/facerec/data/at/s26/1.pgm;25 /home/philipp/facerec/data/at/s18/2.pgm;17 /home/philipp/facerec/data/at/s18/7.pgm;17 /home/philipp/facerec/data/at/s18/6.pgm;17 /home/philipp/facerec/data/at/s18/9.pgm;17 /home/philipp/facerec/data/at/s18/5.pgm;17 /home/philipp/facerec/data/at/s18/3.pgm;17 /home/philipp/facerec/data/at/s18/4.pgm;17 /home/philipp/facerec/data/at/s18/10.pgm;17 /home/philipp/facerec/data/at/s18/8.pgm;17 /home/philipp/facerec/data/at/s18/1.pgm;17 /home/philipp/facerec/data/at/s29/2.pgm;28 /home/philipp/facerec/data/at/s29/7.pgm;28 /home/philipp/facerec/data/at/s29/6.pgm;28 /home/philipp/facerec/data/at/s29/9.pgm;28 /home/philipp/facerec/data/at/s29/5.pgm;28 /home/philipp/facerec/data/at/s29/3.pgm;28 /home/philipp/facerec/data/at/s29/4.pgm;28 /home/philipp/facerec/data/at/s29/10.pgm;28 /home/philipp/facerec/data/at/s29/8.pgm;28 /home/philipp/facerec/data/at/s29/1.pgm;28 /home/philipp/facerec/data/at/s33/2.pgm;32 /home/philipp/facerec/data/at/s33/7.pgm;32 /home/philipp/facerec/data/at/s33/6.pgm;32 /home/philipp/facerec/data/at/s33/9.pgm;32 /home/philipp/facerec/data/at/s33/5.pgm;32 /home/philipp/facerec/data/at/s33/3.pgm;32 /home/philipp/facerec/data/at/s33/4.pgm;32 /home/philipp/facerec/data/at/s33/10.pgm;32 /home/philipp/facerec/data/at/s33/8.pgm;32 /home/philipp/facerec/data/at/s33/1.pgm;32 /home/philipp/facerec/data/at/s12/2.pgm;11 /home/philipp/facerec/data/at/s12/7.pgm;11 /home/philipp/facerec/data/at/s12/6.pgm;11 /home/philipp/facerec/data/at/s12/9.pgm;11 /home/philipp/facerec/data/at/s12/5.pgm;11 /home/philipp/facerec/data/at/s12/3.pgm;11 /home/philipp/facerec/data/at/s12/4.pgm;11 /home/philipp/facerec/data/at/s12/10.pgm;11 /home/philipp/facerec/data/at/s12/8.pgm;11 /home/philipp/facerec/data/at/s12/1.pgm;11 /home/philipp/facerec/data/at/s6/2.pgm;5 /home/philipp/facerec/data/at/s6/7.pgm;5 /home/philipp/facerec/data/at/s6/6.pgm;5 /home/philipp/facerec/data/at/s6/9.pgm;5 /home/philipp/facerec/data/at/s6/5.pgm;5 /home/philipp/facerec/data/at/s6/3.pgm;5 /home/philipp/facerec/data/at/s6/4.pgm;5 /home/philipp/facerec/data/at/s6/10.pgm;5 /home/philipp/facerec/data/at/s6/8.pgm;5 /home/philipp/facerec/data/at/s6/1.pgm;5 /home/philipp/facerec/data/at/s22/2.pgm;21 /home/philipp/facerec/data/at/s22/7.pgm;21 /home/philipp/facerec/data/at/s22/6.pgm;21 /home/philipp/facerec/data/at/s22/9.pgm;21 /home/philipp/facerec/data/at/s22/5.pgm;21 /home/philipp/facerec/data/at/s22/3.pgm;21 /home/philipp/facerec/data/at/s22/4.pgm;21 /home/philipp/facerec/data/at/s22/10.pgm;21 /home/philipp/facerec/data/at/s22/8.pgm;21 /home/philipp/facerec/data/at/s22/1.pgm;21 /home/philipp/facerec/data/at/s15/2.pgm;14 /home/philipp/facerec/data/at/s15/7.pgm;14 /home/philipp/facerec/data/at/s15/6.pgm;14 /home/philipp/facerec/data/at/s15/9.pgm;14 /home/philipp/facerec/data/at/s15/5.pgm;14 /home/philipp/facerec/data/at/s15/3.pgm;14 /home/philipp/facerec/data/at/s15/4.pgm;14 /home/philipp/facerec/data/at/s15/10.pgm;14 /home/philipp/facerec/data/at/s15/8.pgm;14 /home/philipp/facerec/data/at/s15/1.pgm;14 /home/philipp/facerec/data/at/s2/2.pgm;1 /home/philipp/facerec/data/at/s2/7.pgm;1 /home/philipp/facerec/data/at/s2/6.pgm;1 /home/philipp/facerec/data/at/s2/9.pgm;1 /home/philipp/facerec/data/at/s2/5.pgm;1 /home/philipp/facerec/data/at/s2/3.pgm;1 /home/philipp/facerec/data/at/s2/4.pgm;1 /home/philipp/facerec/data/at/s2/10.pgm;1 /home/philipp/facerec/data/at/s2/8.pgm;1 /home/philipp/facerec/data/at/s2/1.pgm;1 /home/philipp/facerec/data/at/s31/2.pgm;30 /home/philipp/facerec/data/at/s31/7.pgm;30 /home/philipp/facerec/data/at/s31/6.pgm;30 /home/philipp/facerec/data/at/s31/9.pgm;30 /home/philipp/facerec/data/at/s31/5.pgm;30 /home/philipp/facerec/data/at/s31/3.pgm;30 /home/philipp/facerec/data/at/s31/4.pgm;30 /home/philipp/facerec/data/at/s31/10.pgm;30 /home/philipp/facerec/data/at/s31/8.pgm;30 /home/philipp/facerec/data/at/s31/1.pgm;30 /home/philipp/facerec/data/at/s28/2.pgm;27 /home/philipp/facerec/data/at/s28/7.pgm;27 /home/philipp/facerec/data/at/s28/6.pgm;27 /home/philipp/facerec/data/at/s28/9.pgm;27 /home/philipp/facerec/data/at/s28/5.pgm;27 /home/philipp/facerec/data/at/s28/3.pgm;27 /home/philipp/facerec/data/at/s28/4.pgm;27 /home/philipp/facerec/data/at/s28/10.pgm;27 /home/philipp/facerec/data/at/s28/8.pgm;27 /home/philipp/facerec/data/at/s28/1.pgm;27 /home/philipp/facerec/data/at/s40/2.pgm;39 /home/philipp/facerec/data/at/s40/7.pgm;39 /home/philipp/facerec/data/at/s40/6.pgm;39 /home/philipp/facerec/data/at/s40/9.pgm;39 /home/philipp/facerec/data/at/s40/5.pgm;39 /home/philipp/facerec/data/at/s40/3.pgm;39 /home/philipp/facerec/data/at/s40/4.pgm;39 /home/philipp/facerec/data/at/s40/10.pgm;39 /home/philipp/facerec/data/at/s40/8.pgm;39 /home/philipp/facerec/data/at/s40/1.pgm;39 /home/philipp/facerec/data/at/s3/2.pgm;2 /home/philipp/facerec/data/at/s3/7.pgm;2 /home/philipp/facerec/data/at/s3/6.pgm;2 /home/philipp/facerec/data/at/s3/9.pgm;2 /home/philipp/facerec/data/at/s3/5.pgm;2 /home/philipp/facerec/data/at/s3/3.pgm;2 /home/philipp/facerec/data/at/s3/4.pgm;2 /home/philipp/facerec/data/at/s3/10.pgm;2 /home/philipp/facerec/data/at/s3/8.pgm;2 /home/philipp/facerec/data/at/s3/1.pgm;2 /home/philipp/facerec/data/at/s38/2.pgm;37 /home/philipp/facerec/data/at/s38/7.pgm;37 /home/philipp/facerec/data/at/s38/6.pgm;37 /home/philipp/facerec/data/at/s38/9.pgm;37 /home/philipp/facerec/data/at/s38/5.pgm;37 /home/philipp/facerec/data/at/s38/3.pgm;37 /home/philipp/facerec/data/at/s38/4.pgm;37 /home/philipp/facerec/data/at/s38/10.pgm;37 /home/philipp/facerec/data/at/s38/8.pgm;37 /home/philipp/facerec/data/at/s38/1.pgm;37 |
C#版的人臉識別(只實現了特征臉)EMGU Multiple Face Recognition using PCA and Parallel Optimisatioin: