OpenCV人臉識別的原理 .


OpenCV人臉識別的原理 .

 

 

在之前講到的人臉測試后,提取出人臉來,並且保存下來,以供訓練或識別是用,提取人臉的代碼如下:

 

[html]  view plain  copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. void GetImageRect(IplImage* orgImage, CvRect rectInImage, IplImage* imgRect,double scale)  
  2. {  
  3.     //從圖像orgImage中提取一塊(rectInImage)子圖像imgRect  
  4.     IplImage *result=imgRect;  
  5.     CvRect size;  
  6.     size.x=rectInImage.x*scale;  
  7.     size.y=rectInImage.y*scale;  
  8.     size.width=rectInImage.width*scale;  
  9.     size.height=rectInImage.height*scale;  
  10.       
  11.     //result=cvCreateImage( size, orgImage->depth, orgImage->nChannels );  
  12.     //從圖像中提取子圖像  
  13.     cvSetImageROI(orgImage,size);  
  14.     cvCopy(orgImage,result);  
  15.     cvResetImageROI(orgImage);  
  16. }  

 


人臉預處理

 

 

 

現在你已經得到一張人臉,你可以使用那張人臉圖片進行人臉識別。然而,假如你嘗試這樣簡單地從一張普通圖片直接進行人臉識別的話,你將會至少損失10%的准確率!

 

在一個人臉識別系統中,應用多種預處理技術對將要識別的圖片進行標准化處理是極其重要的。多數人臉識別算法對光照條件十分敏感,所以假如在暗室訓練,在明亮的房間就可能不會被識別出來等等。這個問題可歸於“lumination dependent”,並且還有其它很多例子,比如臉部也應當在圖片的一個十分固定的位置(比如眼睛位置為相同的像素坐標),固定的大小,旋轉角度,頭發和裝飾,表情(笑,怒等),光照方向(向左或向上等),這就是在進行人臉識別前,使用好的圖片預處理過濾器十分重要的原因。你還應該做一些其它事情,比如去除臉部周圍的多余像素(如用橢圓遮罩,只顯示其內部的人臉區域而不是頭發或圖片背景,因為他們的變化多於臉部區域)。

 

為簡單起見,我展示給你的人臉識別系統是使用灰度圖像的特征臉方法。所以我將向你說明怎樣簡單地把彩色圖像轉化為灰度圖像,並且之后簡單地使用直方圖均衡化(Histogram Equalization)作為一種自動的標准化臉部圖像亮度和對比度的方法。為了得到更好的結果,你可以使用彩色人臉識別(color face recognition,ideally with color histogram fitting in HSV or another color space instead of RGB),或者使用更多的預處理,比如邊緣增強(edge enhancement),輪廓檢測(contour detection),手勢檢測(motion detection),等等。
你可以看到一個預處理階段的例子:

 

 

 

這是把一幅RGB格式的圖像或灰度圖像轉變為灰度圖像的基本代碼。它還把圖像調整成了固定的維度,然后應用直方圖均衡化來實現固定的亮度和對比度。

 

PCA原理

 

現在你已經有了一張經過預處理后的臉部圖片,你可以使用特征臉(PCA)進行人臉識別。OpenCV自帶了執行PCA操作的”cvEigenDecomposite()”函數,然而你需要一個圖片數據庫(訓練集)告訴機器怎樣識別當中的人。

 

所以你應該收集每個人的一組預處理后的臉部圖片用於識別。比如,假如你想要從10人的班級當中識別某個人,你可以為每個人存儲20張圖片,總共就有200張大小相同(如100×100像素)的經預處理的臉部圖片。

 

特征臉的理論在Servo Magazine的兩篇文章(Face Recognition with Eigenface)中解釋了,但我仍會在這里嘗試着向你解釋。

 

我們使用“主元分析”把你的200張訓練圖片轉換成一個代表這些訓練圖片主要區別的“特征臉”集。首先它將會通過獲取每個像素的平均值,生成這些圖片的“平均人臉圖片”。然后特征臉將會與“平均人臉”比較。第一個特征臉是最主要的臉部區別,第二個特征臉是第二重要的臉部區別,等……直到你有了大約50張代表大多數訓練集圖片的區別的特征臉。

 

  
   
在上面這些示例圖片中你可以看到平均人臉和第一個以及最后一個特征臉。注意到,平均人臉顯示的是一個普通人的平滑臉部結構,排在最前的一些特征臉顯示了一些主要的臉部特征,而最后的特征臉(比如Eigenface 119)主要是圖像噪聲。你可以在下面看到前32張特征臉。

 


 
簡單地說,特征臉方法(Principal Component Analysis)計算出了訓練集中圖片的主要區別,並且用這些“區別”的組合來代表每幅訓練圖片。
比如,一張訓練圖片可能是如下的組成:

 

(averageFace) + (13.5% of eigenface0) – (34.3% of eigenface1) + (4.7% of eigenface2) + … + (0.0% of eigenface199).
一旦計算出來,就可以認為這張訓練圖片是這200個比率(ratio):

 

{13.5, -34.3, 4.7, …, 0.0}.

 

用特征臉圖片分別乘以這些比率,並加上平均人臉圖片 (average face),從這200個比率還原這張訓練圖片是完全可以做到的。但是既然很多排在后面的特征臉是圖像噪聲或者不會對圖片有太大作用,這個比率表可以被降低到只剩下最主要的,比如前30個,不會對圖像質量有很大影響。所以現在可以用30個特征臉,平均人臉圖片,和一個含有30個比率的表,來代表全部的200張訓練圖片。

 

在另一幅圖片中識別一個人,可以應用相同的PCA計算,使用相同的200個特征臉來尋找200個代表輸入圖片的比率。並且仍然可以只保留前30個比率而忽略其余的比率,因為它們是次要的。然后通過搜索這些比率的表,尋找在數據庫中已知的20個人,來看誰的前30個比率與輸入圖片的前30個比率最接近。這就是尋找與輸入圖片最相似的訓練圖片的基本方法,總共提供了200張訓練圖片。

 

訓練圖片

 

創建一個人臉識別數據庫,就是訓練一個列出圖片文件和每個文件代表的人的文本文件,形成一個facedata.xml“文件。
比如,你可以把這些輸入一個名為”trainingphoto.txt”的文本文件:
joke1.jpg
joke2.jpg
joke3.jpg
joke4.jpg
lily1.jpg
lily2.jpg
lily3.jpg
lily4.jpg
它告訴這個程序,第一個人的名字叫“joke,而joke有四張預處理后的臉部圖像,第二個人的名字叫”lily”,有她的四張圖片。這個程序可以使用”loadFaceImgArray()”函數把這些圖片加載到一個圖片數組中。

 

為了從這些加載好的圖片中創建一個數據庫,你可以使用OpenCV的”cvCalcEigenObjects()”和”cvEigenDecomposite()”函數。

 

獲得特征空間的函數:

 

[html]  view plain  copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. void cvCalcEigenObjects( int nObjects, void* input, void* output, int ioFlags, int ioBufSize, void* userData,CvTermCriteria* calcLimit, IplImage* avg, float* eigVals )  

 

nObjects:目標的數目,即輸入訓練圖片的數目。
input:輸入訓練的圖片。
output:輸出特征臉,總共有nEigens
ioFlags、ioBufSize:默認為0
userData:指向回調函數(callback function)必須數據結構體的指針。
calcLimit:終止迭代計算目標特征的條件。根據calcLimit的參數,計算會在前nEigens主要特征目標被提取后結束(這句話有點繞,應該就是提取了前nEigens個特征值,),另一種結束的情況是:目前特征值同最s大特征值的比值降至calcLimit的epsilon值之下。
賦值如下calcLimit = cvTermCriteria( CV_TERMCRIT_ITER, nEigens, 1);
它的類型定義如下:
typedef struct CvTermCriteria
{
  int type;  int max_iter;    //最大迭代次數
  double epsilon;    //結果精確性
}
avg:訓練樣本的平均圖像
eigVals:以降序排列的特征值的行向量指針。可以為0。

 

 

 

最后將所得數據形成一個facedata.xml“文件保存下來,它可以隨時被重新載入來識別經訓練過的人。

 

圖像在特征空間的投影:

 

[html]  view plain  copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. void cvEigenDecomposite( IplImage* obj, int nEigObjs, void* eigInput,int ioFlags, void* userData, IplImage* avg, float* coeffs );  

 


 

 

obj:輸入圖像,訓練或識別圖像
nEigObjs:特征空間的eigen數量
eigInput:特征空間中的特征臉
ioFlags、userData:默認為0
avg:特征空間中的平均圖像
coeffs:這是唯一一個輸出,即人臉在子空間的投影,特征值

識別的過程

1. 讀取用於測試的圖片。

 

2. 平均人臉,特征臉和特征值(比率)使用函數“loadTrainingData()” 從人臉識別數據庫文件(the face recognition database fil)“facedata.xml”載入。

 

3. 使用OpenCV的函數“cvEigenDecomposite()”,每張輸入的圖片都被投影到PCA子空間,來觀察哪些特征臉的比率最適合於代表這張圖片。

 

4. 現在有了特征值(特征臉圖片的比率)代表這張輸入圖片,程序需要查找原始的訓練圖片,找出擁有最相似比率的圖片。這些用數學的方法在“findNearestNeighbor()”函數中執行,采用的是“歐幾里得距離(Euclidean Distance)”,但是它只是基本地檢查輸入圖片與每張訓練圖片的相似性,找到最相似的一張:一張在歐幾里得空間上與輸入圖片距離最近的圖片。就像在 Servo Magazine的文章上提到的那樣,如果使用馬氏距離( the Mahalanobis space,需要在代碼里定義 USE_MAHALANOBIS_DISTANCE),你可以得到更准確的結果。

 

5. 在輸入圖片與最相似圖片之間的距離用於確定可信度(confidence),作為是否識別出某人的指導。1.0的可信度意味着完全相同,0.0或者負的可信度意味着非常不相似。但是需要注意,我在代碼中用到的可信度公式只是一個非常基本的可信度測量,不是很可靠,但是我覺得多數人會想要看到一個粗略的可信度值。你可能發現它對你的圖片給出錯誤的值,所以你可以禁用它(比如:把可信度設為恆定的1.0)。

 

一旦指導哪張訓練圖片和輸入圖片最相似,並假定可信度值不是太低(應該至少是0.6或更高),那么它就指出了那個人是誰,換句話說,它識別出了那個人!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM