1. 程式人生 > >人臉識別經典演算法實現(一)——特徵臉法

人臉識別經典演算法實現(一)——特徵臉法

近來想要做一做人臉識別相關的內容,主要是想整合一個系統,看到opencv已經集成了三種性能較好的演算法,但是還是想自己動手試一下,畢竟演算法都比較初級。

操作環境:python2.7

第三方庫:opencv for python、numpy

第一種比較經典的演算法就是特徵臉法,本質上其實就是PCA降維,這種演算法的基本思路是,把二維的影象先灰度化,轉化為一通道的影象,之後再把它首尾相接轉化為一個列向量,假設影象大小是20*20的,那麼這個向量就是400維,理論上講組織成一個向量,就可以應用任何機器學習演算法了,但是維度太高演算法複雜度也會隨之升高,所以需要使用PCA演算法降維,然後使用簡單排序或者KNN都可以。

只當搬運工,送上鍊接。

PCA,,這篇部落格講得非常好了,從原理到實現基本看這個就能搞出來了:http://blog.codinglabs.org/articles/pca-tutorial.html

特徵臉法:PCA應用在人臉識別當中:http://blog.csdn.net/smartempire/article/details/21406005,這裡與PCA有不同的操作就是特徵值分解的時候,由於影象組成的列向量維度太高,直接按照PCA演算法求解會很慢,所以這裡有一種特殊的處理方法。

資料組織形式為若干樣本圖片分類放入對應資料夾中,然後在統一存放入face資料夾下,測試影象單獨一張影象即可。

另外,由於PCA中維度是一個很麻煩的事情,所以在程式中,我列印了很多維度資訊,有助於我們理解PCA的工作過程和除錯。

程式碼如下:

  1. #encoding=utf-8
  2. import numpy as np  
  3. import cv2  
  4. import os  
  5. class EigenFace(object):  
  6.     def __init__(self,threshold,dimNum,dsize):  
  7.         self.threshold = threshold # 閾值暫未使用
  8.         self.dimNum = dimNum  
  9.         self.dsize = dsize  
  10.     def loadImg(self,fileName,dsize):  
  11.         ''
    ''' 
  12.         載入影象,灰度化處理,統一尺寸,直方圖均衡化 
  13.         :param fileName: 影象檔名 
  14.         :param dsize: 統一尺寸大小。元組形式 
  15.         :return: 影象矩陣 
  16.         '''
  17.         img = cv2.imread(fileName)  
  18.         retImg = cv2.resize(img,dsize)  
  19.         retImg = cv2.cvtColor(retImg,cv2.COLOR_RGB2GRAY)  
  20.         retImg = cv2.equalizeHist(retImg)  
  21.         # cv2.imshow('img',retImg)
  22.         # cv2.waitKey()
  23.         return retImg  
  24.     def createImgMat(self,dirName):  
  25.         ''''' 
  26.         生成影象樣本矩陣,組織形式為行為屬性,列為樣本 
  27.         :param dirName: 包含訓練資料集的影象資料夾路徑 
  28.         :return: 樣本矩陣,標籤矩陣 
  29.         '''
  30.         dataMat = np.zeros((10,1))  
  31.         label = []  
  32.         for parent,dirnames,filenames in os.walk(dirName):  
  33.             # print parent
  34.             # print dirnames
  35.             # print filenames
  36.             index = 0
  37.             for dirname in dirnames:  
  38.                 for subParent,subDirName,subFilenames in os.walk(parent+'/'+dirname):  
  39.                     for filename in subFilenames:  
  40.                         img = self.loadImg(subParent+'/'+filename,self.dsize)  
  41.                         tempImg = np.reshape(img,(-1,1))  
  42.                         if index == 0 :  
  43.                             dataMat = tempImg  
  44.                         else:  
  45.                             dataMat = np.column_stack((dataMat,tempImg))  
  46.                         label.append(subParent+'/'+filename)  
  47.                         index += 1
  48.         return dataMat,label  
  49.     def PCA(self,dataMat,dimNum):  
  50.         ''''' 
  51.         PCA函式,用於資料降維 
  52.         :param dataMat: 樣本矩陣 
  53.         :param dimNum: 降維後的目標維度 
  54.         :return: 降維後的樣本矩陣和變換矩陣 
  55.         '''
  56.         # 均值化矩陣
  57.         meanMat = np.mat(np.mean(dataMat,1)).T  
  58.         print'平均值矩陣維度',meanMat.shape  
  59.         diffMat = dataMat-meanMat  
  60.         # 求協方差矩陣,由於樣本維度遠遠大於樣本數目,所以不直接求協方差矩陣,採用下面的方法
  61.         covMat = (diffMat.T*diffMat)/float(diffMat.shape[1]) # 歸一化
  62.         #covMat2 = np.cov(dataMat,bias=True)
  63.         #print '基本方法計算協方差矩陣為',covMat2
  64.         print'協方差矩陣維度',covMat.shape  
  65.         eigVals, eigVects = np.linalg.eig(np.mat(covMat))  
  66.         print'特徵向量維度',eigVects.shape  
  67.         print'特徵值',eigVals  
  68.         eigVects = diffMat*eigVects  
  69.         eigValInd = np.argsort(eigVals)  
  70.         eigValInd = eigValInd[::-1]  
  71.         eigValInd = eigValInd[:dimNum] # 取出指定個數的前n大的特徵值
  72.         print'選取的特徵值',eigValInd  
  73.         eigVects = eigVects/np.linalg.norm(eigVects,axis=0#歸一化特徵向量
  74.         redEigVects = eigVects[:,eigValInd]  
  75.         print'選取的特徵向量',redEigVects.shape  
  76.         print'均值矩陣維度',diffMat.shape  
  77.         lowMat = redEigVects.T*diffMat  
  78.         print'低維矩陣維度',lowMat.shape  
  79.         return lowMat,redEigVects  
  80.     def compare(self,dataMat,testImg,label):  
  81.         ''''' 
  82.         比較函式,這裡只是用了最簡單的歐氏距離比較,還可以使用KNN等方法,如需修改修改此處即可 
  83.         :param dataMat: 樣本矩陣 
  84.         :param testImg: 測試影象矩陣,最原始形式 
  85.         :param label: 標籤矩陣 
  86.         :return: 與測試圖片最相近的影象檔名 
  87.         '''
  88.         testImg = cv2.resize(testImg,self.dsize)  
  89.         testImg = cv2.cvtColor(testImg,cv2.COLOR_RGB2GRAY)  
  90.         testImg = np.reshape(testImg,(-1,1))  
  91.         lowMat,redVects = self.PCA(dataMat,self.dimNum)  
  92.         testImg = redVects.T*testImg  
  93.         print'檢測樣本變換後的維度',testImg.shape  
  94.         disList = []  
  95.         testVec = np.reshape(testImg,(1,-1))  
  96.         for sample in lowMat.T:  
  97.             disList.append(np.linalg.norm(testVec-sample))  
  98.         print disList  
  99.         sortIndex = np.argsort(disList)  
  100.         return label[sortIndex[0]]  
  101.     def predict(self,dirName,testFileName):  
  102.         ''''' 
  103.         預測函式 
  104.         :param dirName: 包含訓練資料集的資料夾路徑 
  105.         :param testFileName: 測試影象檔名 
  106.         :return: 預測結果 
  107.         '''
  108.         testImg = cv2.imread(testFileName)  
  109.         dataMat,label = self.createImgMat(dirName)  
  110.         print'載入圖片標籤',label  
  111.         ans = self.compare(dataMat,testImg,label)  
  112.         return ans  
  113. if __name__ == '__main__':  
  114.     eigenface = EigenFace(20,50,(50,50))  
  115.     print eigenface.predict('d:/face','D:/face_test/1.bmp')