原文地址:人臉識別算法-特征臉方法(Eigenface)及python實現
特征臉方法基本是將人臉識別推向真正可用的第一種方法,了解一下還是很有必要的。特征臉用到的理論基礎PCA我在這里就不說了,百度一大堆,主要講一下實現步驟和自己在用python實現是發現的問題。這里我所使用的訓練圖片是YALE的人臉數據庫點擊打開鏈接,這里面有15位志願者的165張圖片,包含光照,表情和姿態的變化。(我們做實驗的時候就會發現,特征臉算法對光照敏感。)在unpadded文件夾下。每張圖片的尺寸是98*116。
特征臉實現步驟(大家如果英語好可以看看這個點擊打開鏈接):
1.獲取包含M張人臉圖像的集合。我們這里使用15張xxx.normal.pgm來作為人臉訓練圖像,所以這里M=15.我們把導入的圖像拉平,
本來是98*116的矩陣,我們拉平也就是一個11368*1的矩陣,然后M張放在一個大矩陣下,該矩陣為11368*15。
2.我們計算平均圖像,並獲得偏差矩陣
。為11368*15.平均圖像也就把每一行的15個元素平均計算,
這樣最后的平均圖像就是一個我們所謂的大眾臉。然后每張人臉都減去這個平均圖像,最后得到。
3.求得的協方差矩陣。並計算的特征值和特征向量。這是標准的PCA算法流程。但是在這里一個很大的問題就是,協方差矩陣的維度會大到無法計算,例如我們這個11368*15的矩陣,它的協方差矩陣是11368*11368,這個計算量非常大,而且儲存也很困難,所以有大神推導出了下面的方法:
設 T 是預處理圖像的矩陣,每一列對應一個減去均值圖像之后的圖像。則,協方差矩陣為 S = TTT ,並且對 S 的特征值分解為
然而, TTT 是一個非常大的矩陣。因此,如果轉而使用如下的特征值分解
此時,我們發現如果在等式兩邊乘以T,可得到
這就意味着,如果 ui 是 TTT的一個特征向量,則 vi = Tui 是 S 的一個特征向量。
看懂上面這一些需要一些線性代數知識,這里的T 就是上面的偏差矩陣, 反正到最后我們得TTT的一個特征向量,再用T與之相乘就是協方差矩陣的特征向量。而此時我們求的特征向量是11368*15的矩陣,
每一行(11368*1)如果變成圖像大小矩陣(98*116)的話,都可以看做是一個新人臉,被稱為特征臉。這里展現我試驗中的其中一部分。
4.主成分分析。在求得的特征向量和特征值中,越大的特征值對於我們區分越重要,也就是我們說的主成分,我們只需要那些大的特征值對應的特征向量,
而那些十分小甚至為0的特征值對於我們來說,對應的特征向量幾乎沒有意義。在這里我們通過一個閾值selecthr來控制,當排序后的特征值的一部分相加
大於該閾值時,我們選擇這部分特征值對應的特征向量,此時我們剩下的矩陣是11368*M',M'根據情況在變化。 這樣我們不僅減少了計算量,而且保留
了主成分,減少了噪聲的干擾。
5.這一步就是開始進行人臉識別了。此時我們導入一個新的人臉,我們使用上面主成分分析后得到的特征向量,來求得一個每一個特征向量對於導入人臉的權重向量
。
這里的就是上面第2步求得的平均圖像。 特征向量其實就是訓練集合的圖像與均值圖像在該方向上的偏差,通過未知人臉在特征向量的投影,我們就可以知道未知人臉與平均圖像在不同方向上的差距。此時我們用上面第2步我們求得的偏差矩陣的每一行做這樣的處理,每一行都會得到一個權重向量。我們利用求得
與
的歐式距離來判斷未知人臉與第k張訓練人臉之間的差距。
在這里因為我假設我要識別的未知人肯定是訓練集合里有的,所以我通過比較,選擇最小的k就是這個人臉對應的訓練集合的臉。實際上,一般都是設定距離閾值,當距離小於閾值時說明要判別的臉和訓練集內的第k個臉是同一個人的。當遍歷所有訓練集都大於閾值時,根據距離值的大小又可分為是新的人臉或者不是人臉的兩種情況。根據訓練集的不同,閾值設定並不是固定的。
最后附上python代碼,這里的 ReconginitionVector函數就是求得特征向量的函數,就是按照上面說的順序寫下來的,judgeFace函數用來識別人臉。(大家可以借鑒一下,如果發現有什么不對的歡迎討論):
#coding:utf-8 from numpy import * from numpy import linalg as la import cv2 import os def loadImageSet(add): FaceMat = mat(zeros((15,98*116))) j =0 for i in os.listdir(add): if i.split('.')[1] == 'normal': try: img = cv2.imread(add+i,0) except: print 'load %s failed'%i FaceMat[j,:] = mat(img).flatten() j += 1 return FaceMat def ReconginitionVector(selecthr = 0.8): # step1: load the face image data ,get the matrix consists of all image FaceMat = loadImageSet('D:\python/face recongnition\YALE\YALE\unpadded/').T # step2: average the FaceMat avgImg = mean(FaceMat,1) # step3: calculate the difference of avgimg and all image data(FaceMat) diffTrain = FaceMat-avgImg #step4: calculate eigenvector of covariance matrix (because covariance matrix will cause memory error) eigvals,eigVects = linalg.eig(mat(diffTrain.T*diffTrain)) eigSortIndex = argsort(-eigvals) for i in xrange(shape(FaceMat)[1]): if (eigvals[eigSortIndex[:i]]/eigvals.sum()).sum() >= selecthr: eigSortIndex = eigSortIndex[:i] break covVects = diffTrain * eigVects[:,eigSortIndex] # covVects is the eigenvector of covariance matrix # avgImg 是均值圖像,covVects是協方差矩陣的特征向量,diffTrain是偏差矩陣 return avgImg,covVects,diffTrain def judgeFace(judgeImg,FaceVector,avgImg,diffTrain): diff = judgeImg.T - avgImg weiVec = FaceVector.T* diff res = 0 resVal = inf for i in range(15): TrainVec = FaceVector.T*diffTrain[:,i] if (array(weiVec-TrainVec)**2).sum() < resVal: res = i resVal = (array(weiVec-TrainVec)**2).sum() return res+1 if __name__ == '__main__': avgImg,FaceVector,diffTrain = ReconginitionVector(selecthr = 0.9) nameList = ['01','02','03','04','05','06','07','08','09','10','11','12','13','14','15'] characteristic = ['centerlight','glasses','happy','leftlight','noglasses','rightlight','sad','sleepy','surprised','wink'] for c in characteristic: count = 0 for i in range(len(nameList)): # 這里的loadname就是我們要識別的未知人臉圖,我們通過15張未知人臉找出的對應訓練人臉進行對比來求出正確率 loadname = 'D:\python/face recongnition\YALE\YALE\unpadded\subject'+nameList[i]+'.'+c+'.pgm' judgeImg = cv2.imread(loadname,0) if judgeFace(mat(judgeImg).flatten(),FaceVector,avgImg,diffTrain) == int(nameList[i]): count += 1 print 'accuracy of %s is %f'%(c, float(count)/len(nameList)) # 求出正確率