人臉辨識演算法是指在偵測到人臉並定位臉部關鍵特徵點之後,主要的人臉區域就可以被裁切出來,經過預處理之後,饋入後端的辨識演算法。辨識演算法要完成人臉特徵的提取,並與庫存的已知人臉進行比對,完成最終的分類。人臉辨識演算法的原理:系統輸入一張或一系列含有未確定身分的人臉影像,以及人臉資料庫中的若干已知身分的人臉圖像或對應的編碼,而其輸出則是一系列相似度得分,顯示待辨識的人臉的身份。
本教學操作環境:windows7系統、Dell G3電腦。
人臉辨識(Facial Recognition),就是透過視訊擷取裝置取得使用者的臉部影像,再利用核心的演算法對其臉部的五官位置、臉型和角度進行計算分析,進而和自身資料庫裡已有的範本進行比對,後來判斷出使用者的真實身分。
人臉辨識演算法是指在偵測到人臉並定位臉部關鍵特徵點之後,主要的人臉區域就可以被裁切出來,經過預處理之後,饋入後端的辨識演算法。辨識演算法要完成人臉特徵的提取,並與庫存的已知人臉進行比對,完成最終的分類。
人臉辨識的演算法有 4 種:基於人臉特徵點的辨識演算法、基於整幅 人臉影像的辨識演算法、基於模板的辨識演算法、利用神經網路進行辨識的演算法。
人臉辨識演算法的原理:
系統輸入一般是一張或一系列含有未確定身分的人臉影像,以及人臉資料庫中的若干已知身份的人臉圖像或相應的編碼,而其輸出則是一系列相似度得分,表明待識別的人臉的身份。
人臉辨識的三個經典演算法
1、Eigenfaces(特徵臉)演算法
Eigenfaces是在人臉辨識的電腦視覺問題中使用的一組特徵向量的名稱,Eigenfaces是基於PCA(主成分分析)的,所以學習Eigenfaces需要我們了解PCA的原理。
基本概念
主成分分析(PCA)是一種矩陣的壓縮演算法,在減少矩陣維度的同時盡可能的保留原矩陣的訊息,簡單來說就是將n×m的矩陣轉換成n×k的矩陣,只保留矩陣中所存在的主要特性,因此可以大幅節省空間和資料量。 PCA的實現需要進行降維,也就是將矩陣變換,從更高的維度降到低的維度,然而PCA的降維離不開協方差矩陣。變異數是描述一維資料樣本本身相對於平均值的偏離程度,是一種用來度量兩個隨機變數關係的統計量,從角度來說,其夾角越小,值越大,方向越相近,也就是越正相關。協方差矩陣測量除了是兩個隨機變數的關係外,也是維度與維度之間的關係,而非樣本與樣本之間的關係。
學習一種新的東西,特別是知識,我們需要了解知識中的想法。我在了解和學習Eigenface演算法時它的思想是圖像識別首先要選擇一個合適的子空間,將所有的圖像集中到這個子空間中,然後在這個子空間中衡量相似性或者進行分類學習,再講子空間變換到另一個空間中,這樣的作用一是同一個類別的圖像離得更近,二是不同的類別的圖像會離得比較遠;這樣經過線性分類分開的圖像在新空間就能容易分開。同時特徵臉技術會尋找人臉影像分佈的基本元素,即人臉影像樣本集協方差矩陣的特徵向量,以此表徵人臉影像。人臉影像的基本元素有很多,例如眼、臉頰、唇等基本元素,這些特徵向量在特徵臉的圖像空間中對應生成的子空間被稱為子臉空間。
產生了子空間之後就要進行空間構造,那麼如何進行空間構造呢?首先要尋找人臉的共通性,其次是要尋找個體與共性的差異,還有就是要明白共性其實是空間,個體就是向量。利用協方差矩陣把目標集中所有人臉影像的特徵值分解,得到對應的特徵向量,這些特徵向量就是「特徵臉」。尋找特徵向量的特性,將其進行線性組合。在以每一個特徵子臉為基的空間,每個人臉就是一個點,這個點的座標就是每一個人臉在每個特徵基下的投影座標。
Eigenfaces演算法過程
# 獲得人臉圖像數據,將每個人臉圖像矩陣按行串成一維,每個人臉就是一個向量;
將M個人臉在對應維度上加起來,然後求平均得到「平均臉」;
將每個影像都減去平均臉向量;
計算協方差矩陣;
# 運用Eigenfaces記性人臉辨識;
#演算法實作過程;
訓練影像
求出平均臉
2、FisherFace演算法
FisherFace是Fisher線性判別在臉部辨識的應用。線性判別分析(LDA)演算法思想最早由英國統計與遺傳學家,現代統計科學的奠基者之一羅納德*費雪(Ronald)提出。 LDA演算法使用統計方法,嘗試找出物體間特徵的一個線性組合,在降維的同時考慮類別資訊。透過此演算法得到的線性組合可以用來作為一個線性分類器或實現降維。
基本想法
線性判別分析的基本想法是:將高維的模式樣本投影到低維最佳向量空間,以達到抽取重要分類資訊和壓縮特徵空間維度的效果,投影後保證模式樣本在新的子空間有最大的類間距離、最小的類內距離,即模式在該空間中有最佳的可分離性。理論和特徵臉裡用到的Eigenfaces有相似之處,都是對原有資料進行整體降維映射到低維度空間的方法,fisherfaces和Eigenfaces都是從資料整體入手而不同於LBP提取局部紋理特徵。
對降維後的樣本使用Fisher線性判別方法,確定一個最優的投影方向,建構一個一維的體徵空間,將多維的人臉影像投影到fisherfaces特徵空間,利用類內樣本資料形成一組特徵向量,這組特徵向量代表了人臉的特徵。 我們知道,演算法是在樣本資料對應到另一個特徵空間後,將類別內距離最小化,且類別間距最大化。 LDA演算法可以用作降維,演算法的原理和PCA演算法很相似,因此LDA演算法也同樣可以用在人臉辨識領域。透過使用PCA演算法來進行人臉辨識的演算法稱為特徵臉法,而使用LDA演算法進行人臉辨識的演算法稱為費雪臉法。LDA和PCA比較:
Fisherfaces演算法和Eigenfaces演算法比較:
Fisherface演算法流程
3、LBPH(Local Binary Patter Histogram)演算法
Local Binary Patterns Histograms即LBP特徵的統計直方圖,LBPH將LBP(局部二值編碼)特徵與影像的空間資訊結合在一起。如果直接使用LBP編碼影像用於人臉辨識。其實和不提取LBP特徵差別不大,因此在實際的LBP應用中,一般採用LBP編碼影像的統計直方圖作為特徵向量進行分類辨識。
原始的LBP算子定義為在33的視窗內,以視窗中心像素為閾值,將相鄰的8個像素的灰階值與其進行比較,若周圍像素值大於或等於中心像素值,則此像素點的位置被標記為1,否則為0。這樣,33鄰域內的8個點經比較可產生8位元二進位數(通常轉換為十進位數即LBP碼,共256種),即得到該視窗中心像素點的LBP值,並用這個值來反映該區域的紋理特徵。
LBPH的維度: 取樣點為8個,如果使用的是原始的LBP或Extended LBP特徵,其LBP特徵值的模式為256種,則一影像的LBP特徵向量維度為:64256 =16384維,而如果使用的UniformPatternLBP特徵,其LBP值的模式為59種,其特徵向量維度為:6459=3776維,可以看出,使用等價模式特徵,其特徵向量的維度大大減少,這意味著使用機器學習方法進行學習的時間將大大減少,而性能並沒有受到很大影響。
基本想法
建立在LBPH基礎上的人臉辨識法基本想法如下:首先以每個像素為中心,判斷與周圍像素灰階值大小關係,進行二進位編碼,從而獲得整個影像的LBP編碼影像;再將LBP影像分為個區域,取得每個區域的LBP編碼直方圖,進而得到整幅影像的LBP編碼直方圖,透過比較不同人臉影像LBP編碼直方圖達到人臉辨識的目的,其優點是不會受到光照、縮放、旋轉和平移的影響。
LBPH演算法「人」如其名,所採用的辨識方法是局部特徵提取的方法,這是與前兩種方法的最大差異。
LBPH 演算法流程
LBP特徵擷取:根據上述的均勻LBP算子處理原始影像;
LBP特徵匹配(計算直方圖):將影像分為若干個的子區域,並在子區域內根據LBP值統計其直方圖,以直方圖作為其判別特徵。
4、演算法的複現程式碼
#1)、EigenFaces演算法
#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')
2 )、FisherFaces演算法
#encoding=utf-8 import numpy as np import cv2 import os class FisherFace(object): def __init__(self,threshold,k,dsize): self.threshold = threshold # 阈值,暂未使用 self.k = k # 指定投影w的个数 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 = [] dataList = [] 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 index,filename in enumerate(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)) dataList.append(dataMat) label.append(subParent) return dataList,label def LDA(self,dataList,k): ''' 多分类问题的线性判别分析算法 :param dataList: 样本矩阵列表 :param k: 投影向量k的个数 :return: 变换后的矩阵列表和变换矩阵 ''' n = dataList[0].shape[0] W = np.zeros((n,self.k)) Sw = np.zeros((n,n)) Sb = np.zeros((n,n)) u = np.zeros((n,1)) N = 0 meanList = [] sampleNum = [] for dataMat in dataList: meanMat = np.mat(np.mean(dataMat,1)).T meanList.append(meanMat) sampleNum.append(dataMat.shape[1]) dataMat = dataMat-meanMat sw = dataMat*dataMat.T Sw += sw print 'Sw的维度',Sw.shape for index,meanMat in enumerate(meanList): m = sampleNum[index] u += m*meanMat N += m u = u/N print 'u的维度',u.shape for index,meanMat in enumerate(meanList): m = sampleNum[index] sb = m*(meanMat-u)*(meanMat-u).T Sb += sb print 'Sb的维度',Sb.shape eigVals, eigVects = np.linalg.eig(np.mat(np.linalg.inv(Sw)*Sb)) eigValInd = np.argsort(eigVals) eigValInd = eigValInd[::-1] eigValInd = eigValInd[:k] # 取出指定个数的前k大的特征值 print '选取的特征值',eigValInd.shape eigVects = eigVects/np.linalg.norm(eigVects,axis=0) #归一化特征向量 redEigVects = eigVects[:,eigValInd] print '变换矩阵维度',redEigVects.shape transMatList = [] for dataMat in dataList: transMatList.append(redEigVects.T*dataMat) return transMatList,redEigVects def compare(self,dataList,testImg,label): ''' 比较函数,这里只是用了最简单的欧氏距离比较,还可以使用KNN等方法,如需修改修改此处即可 :param dataList: 样本矩阵列表 :param testImg: 测试图像矩阵,最原始形式 :param label: 标签矩阵 :return: 与测试图片最相近的图像文件夹,也就是类别 ''' testImg = cv2.resize(testImg,self.dsize) testImg = cv2.cvtColor(testImg,cv2.COLOR_RGB2GRAY) testImg = np.reshape(testImg,(-1,1)) transMatList,redVects = fisherface.LDA(dataList,self.k) testImg = redVects.T*testImg print '检测样本变换后的维度',testImg.shape disList = [] testVec = np.reshape(testImg,(1,-1)) sumVec = np.mat(np.zeros((self.dsize[0]*self.dsize[1],1))) for transMat in transMatList: for sample in transMat.T: disList.append( np.linalg.norm(testVec-sample)) print disList sortIndex = np.argsort(disList) return label[sortIndex[0]/9] 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__": fisherface = FisherFace(10,20,(20,20)) ans = fisherface.predict('d:/face','d:/face_test/8.bmp') print ans
3)、LBPH演算法
#encoding=utf-8 import numpy as np import os import cv2 class LBP(object): def __init__(self,threshold,dsize,blockNum): self.dsize = dsize # 统一尺寸大小 self.blockNum = blockNum # 分割块数目 self.threshold = threshold # 阈值,暂未使用 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 loadImagesList(self,dirName): ''' 加载图像矩阵列表 :param dirName:文件夹路径 :return: 包含最原始的图像矩阵的列表和标签矩阵 ''' imgList = [] label = [] for parent,dirnames,filenames in os.walk(dirName): # print parent # print dirnames # print filenames for dirname in dirnames: for subParent,subDirName,subFilenames in os.walk(parent+'/'+dirname): for filename in subFilenames: img = self.loadImg(subParent+'/'+filename,self.dsize) imgList.append(img) # 原始图像矩阵不做任何处理,直接加入列表 label.append(subParent+'/'+filename) return imgList,label def getHopCounter(self,num): ''' 计算二进制序列是否只变化两次 :param num: 数字 :return: 01变化次数 ''' binNum = bin(num) binStr = str(binNum)[2:] n = len(binStr) if n < 8: binStr = "0"*(8-n)+binStr n = len(binStr) counter = 0 for i in range(n): if i != n-1: if binStr[i+1] != binStr[i]: counter += 1 else: if binStr[0] != binStr[i]: counter += 1 return counter def createTable(self): ''' 生成均匀对应字典 :return: 均匀LBP特征对应字典 ''' self.table = {} temp = 1 print type(temp) for i in range(256): if self.getHopCounter(i) <= 2: self.table[i] = temp temp += 1 else: self.table[i] = 0 return self.table def getLBPfeature(self,img): ''' 计算LBP特征 :param img:图像矩阵 :return: LBP特征图 ''' m = img.shape[0];n = img.shape[1] neighbor = [0]*8 featureMap = np.mat(np.zeros((m,n))) for y in xrange(1,m-1): for x in xrange(1,n-1): neighbor[0] = img[y-1,x-1] neighbor[1] = img[y-1,x] neighbor[2] = img[y-1,x+1] neighbor[3] = img[y,x+1] neighbor[4] = img[y+1,x+1] neighbor[5] = img[y+1,x] neighbor[6] = img[y+1,x-1] neighbor[7] = img[y,x-1] center = img[y,x] temp = 0 for k in range(8): temp += (neighbor[k] >= center)*(1<<k) featureMap[y,x] = self.table[temp] featureMap = featureMap.astype('uint8') # 数据类型转换为无符号8位型,如不转换则默认为float64位,影响最终效果 return featureMap def calcHist(self,roi): ''' 计算直方图 :param roi:图像区域 :return: 直方图矩阵 ''' hist = cv2.calcHist([roi],[0],None,[59],[0,256]) # 第四个参数是直方图的横坐标数目,经过均匀化降维后这里一共有59种像素 return hist def compare(self,sampleImg,testImg): ''' 比较函数,这里使用的是欧氏距离排序,也可以使用KNN,在此处更改 :param sampleImg: 样本图像矩阵 :param testImg: 测试图像矩阵 :return: k2值 ''' testImg = cv2.resize(testImg,self.dsize) testImg = cv2.cvtColor(testImg,cv2.COLOR_RGB2GRAY) testFeatureMap = self.getLBPfeature(testImg) sampleFeatureMap = self.getLBPfeature(sampleImg) # 计算步长,分割整个图像为小块 ystep = self.dsize[0]/self.blockNum xstep = self.dsize[1]/self.blockNum k2 = 0 for y in xrange(0,self.dsize[0],ystep): for x in xrange(0,self.dsize[1],xstep): testroi = testFeatureMap[y:y+ystep,x:x+xstep] sampleroi =sampleFeatureMap[y:y+ystep,x:x+xstep] testHist = self.calcHist(testroi) sampleHist = self.calcHist(sampleroi) k2 += np.sum((sampleHist-testHist)**2)/np.sum((sampleHist+testHist)) print 'k2的值为',k2 return k2 def predict(self,dirName,testImgName): ''' 预测函数 :param dirName:样本图像文件夹路径 :param testImgName: 测试图像文件名 :return: 最相近图像名称 ''' table = self.createTable() testImg = cv2.imread(testImgName) imgList,label = self.loadImagesList(dirName) k2List = [] for img in imgList: k2 = self.compare(img,testImg) k2List.append(k2) order = np.argsort(k2List) return label[order[0]] if __name__ == "__main__": lbp = LBP(20,(50,50),5) ans = lbp.predict('d:/face','d:/face_test/9.bmp') print ans
擴展知識:人臉辨識演算法研究的困難點
#人臉辨識演算法研究已久,在背景簡單的情形下,大部分演算法都能很好的處理。但是,人臉辨識的應用範圍頗廣,僅是簡單影像測試,是遠遠不能滿足現實需求的。所以人臉辨識演算法還是存在很多的困難。
光照
光照問題是機器視覺中的老問題,在人臉辨識中的表現特別明顯,演算法未能達到使用的程度。
姿態
與光照問題類似,姿態問題也是人臉辨識研究中需要解決的技術困難。針對姿態的研究相對比較少,多數的人臉辨識演算法主要是針對正面,或接近正面的人臉影像,當發生俯仰或左右側而比較厲害的情況下,人臉辨識演算法的辨識率也會急劇下降。
遮蔽
對於非配合情況下的人臉影像擷取,遮蔽問題是一個非常嚴重的問題,特別是在監控環境下,往往被監控對象都會帶著眼鏡﹑帽子等飾物,使得被採集出來的人臉影像有可能不完整,從而影響了後面的特徵提取與識別,甚至會導致人臉辨識演算法的失效。
年齡變化
隨著年齡的變化,臉部外觀也在變化,特別是對於青少年,這種變化更加的明顯。對於不同的年齡段,人臉辨識演算法的辨識率也不同。
影像品質
人臉影像的來源可能多種多樣,由於擷取裝置的不同,所得到的人臉影像品質也不同,特別是對於那些低解析度﹑雜訊大﹑品質差的人臉影像如何進行有效的人臉辨識是個需要關注的問題。同樣的,對於高分辨影像,對人臉辨識演算法的影響也需要進一步研究。
樣本缺乏
基於統計學習的人臉辨識演算法是人臉辨識領域中的主流演算法,但是統計學習方法需要大量的訓練。由於人臉影像在高維度空間中的分佈是一個不規則的流行分佈,能得到的樣本只是對人臉影像空間中的一個極小部分的取樣,如何解決小樣本下的統計學習問題有待進一步的研究。
大量資料
傳統人臉辨識演算法如PCA、LDA等在小規模資料中可以很容易進行訓練學習。但是對於大量數據,這些方法其訓練過程難以進行,甚至有可能崩潰。
大規模人臉辨識
隨著人臉資料庫規模的成長,人臉演算法的效能將會呈現下降。
更多相關知識,請造訪常見問題欄位!
以上是人臉辨識演算法是指什麼的詳細內容。更多資訊請關注PHP中文網其他相關文章!