人臉識別經典演算法實現(一)——特徵臉法
阿新 • • 發佈:2019-01-09
近來想要做一做人臉識別相關的內容,主要是想整合一個系統,看到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的工作過程和除錯。
程式碼如下:
- #encoding=utf-8
- import numpy as np
- import cv2
- import os
- class EigenFace(object):
- def __init__(self,threshold,dimNum,dsize):
- self.threshold = threshold # 閾值暫未使用
- self.dimNum = dimNum
- self.dsize = dsize
- def loadImg(self,fileName,dsize):
-
''
- 載入影象,灰度化處理,統一尺寸,直方圖均衡化
- :param fileName: 影象檔名
- :param dsize: 統一尺寸大小。元組形式
- :return: 影象矩陣
- '''
- img = cv2.imread(fileName)
- retImg = cv2.resize(img,dsize)
- retImg = cv2.cvtColor(retImg,cv2.COLOR_RGB2GRAY)
- retImg = cv2.equalizeHist(retImg)
- # cv2.imshow('img',retImg)
- # cv2.waitKey()
- return retImg
- def createImgMat(self,dirName):
- '''''
- 生成影象樣本矩陣,組織形式為行為屬性,列為樣本
- :param dirName: 包含訓練資料集的影象資料夾路徑
- :return: 樣本矩陣,標籤矩陣
- '''
- dataMat = np.zeros((10,1))
- label = []
- for parent,dirnames,filenames in os.walk(dirName):
- # print parent
- # print dirnames
- # print filenames
- index = 0
- for dirname in dirnames:
- for subParent,subDirName,subFilenames in os.walk(parent+'/'+dirname):
- for filename in subFilenames:
- img = self.loadImg(subParent+'/'+filename,self.dsize)
- tempImg = np.reshape(img,(-1,1))
- if index == 0 :
- dataMat = tempImg
- else:
- dataMat = np.column_stack((dataMat,tempImg))
- label.append(subParent+'/'+filename)
- index += 1
- return dataMat,label
- def PCA(self,dataMat,dimNum):
- '''''
- PCA函式,用於資料降維
- :param dataMat: 樣本矩陣
- :param dimNum: 降維後的目標維度
- :return: 降維後的樣本矩陣和變換矩陣
- '''
- # 均值化矩陣
- meanMat = np.mat(np.mean(dataMat,1)).T
- print'平均值矩陣維度',meanMat.shape
- diffMat = dataMat-meanMat
- # 求協方差矩陣,由於樣本維度遠遠大於樣本數目,所以不直接求協方差矩陣,採用下面的方法
- covMat = (diffMat.T*diffMat)/float(diffMat.shape[1]) # 歸一化
- #covMat2 = np.cov(dataMat,bias=True)
- #print '基本方法計算協方差矩陣為',covMat2
- print'協方差矩陣維度',covMat.shape
- eigVals, eigVects = np.linalg.eig(np.mat(covMat))
- print'特徵向量維度',eigVects.shape
- print'特徵值',eigVals
- eigVects = diffMat*eigVects
- eigValInd = np.argsort(eigVals)
- eigValInd = eigValInd[::-1]
- eigValInd = eigValInd[:dimNum] # 取出指定個數的前n大的特徵值
- print'選取的特徵值',eigValInd
- eigVects = eigVects/np.linalg.norm(eigVects,axis=0) #歸一化特徵向量
- redEigVects = eigVects[:,eigValInd]
- print'選取的特徵向量',redEigVects.shape
- print'均值矩陣維度',diffMat.shape
- lowMat = redEigVects.T*diffMat
- print'低維矩陣維度',lowMat.shape
- return lowMat,redEigVects
- def compare(self,dataMat,testImg,label):
- '''''
- 比較函式,這裡只是用了最簡單的歐氏距離比較,還可以使用KNN等方法,如需修改修改此處即可
- :param dataMat: 樣本矩陣
- :param testImg: 測試影象矩陣,最原始形式
- :param label: 標籤矩陣
- :return: 與測試圖片最相近的影象檔名
- '''
- testImg = cv2.resize(testImg,self.dsize)
- testImg = cv2.cvtColor(testImg,cv2.COLOR_RGB2GRAY)
- testImg = np.reshape(testImg,(-1,1))
- lowMat,redVects = self.PCA(dataMat,self.dimNum)
- testImg = redVects.T*testImg
- print'檢測樣本變換後的維度',testImg.shape
- disList = []
- testVec = np.reshape(testImg,(1,-1))
- for sample in lowMat.T:
- disList.append(np.linalg.norm(testVec-sample))
- print disList
- sortIndex = np.argsort(disList)
- return label[sortIndex[0]]
- def predict(self,dirName,testFileName):
- '''''
- 預測函式
- :param dirName: 包含訓練資料集的資料夾路徑
- :param testFileName: 測試影象檔名
- :return: 預測結果
- '''
- testImg = cv2.imread(testFileName)
- dataMat,label = self.createImgMat(dirName)
- print'載入圖片標籤',label
- ans = self.compare(dataMat,testImg,label)
- return ans
- if __name__ == '__main__':
- eigenface = EigenFace(20,50,(50,50))
- print eigenface.predict('d:/face','D:/face_test/1.bmp')