參考連接:《Face Recognition: From Traditional to Deep Learning Methods》,《人臉識別合集 | 人臉識別概述》html
人臉識別(Face Recognition)是指 可以識別或驗證圖像或視頻中的主體的身份 的技術。自上個世紀七十年代首我的臉識別算法被提出以來,人臉識別已經成爲了計算機視覺與生物識別領域被研究最多的主題之一。究其火爆的緣由,一方面是它的挑戰性——在無約束條件的環境中的人臉信息,也就是所謂天然人臉(Faces in-the-wild),具備高度的可變性,以下圖所示;另外一方面是因爲相比於指紋或虹膜識別等傳統上被認爲更加穩健的生物識別方法,人臉識別本質上是非侵入性的,這意味着它是最天然、最符合人類直覺的一種生物識別方法。python
現代人臉識別技術的研究熱潮,已經從使用人工設計的特徵(如邊和紋理描述量等)與機器學習技術(如主成分分析、線性判別分析和支持向量機等)組合的傳統方法的研究,逐漸轉移到使用龐大人臉數據集搭建與在其基礎上訓練深度神經網絡的研究。可是,不管是基於傳統方法仍是深度神經網絡,人臉識別的流程都是類似的,大概由如下四個模塊組成:git
準確地說,是 基於主成分分析的Eigenfaces特徵臉方法 與 基於線性判別分析的Fisherfaces特徵臉方法,這是根據總體特徵進行人臉辨別的兩種方法。github
使用 PCA 或 LDA 進行人臉識別的算法流程十分類似,具體步驟以下。算法
參考連接:《PCA的數學原理》 (強烈推薦,講得很是透徹!)數據庫
PCA(Principal Component Analysis,主成分分析)是一種經常使用的數據分析方法。PCA經過線性變換將原始數據變換爲一組各維度線性無關的表示,可用於提取數據的主要特徵份量,經常使用於高維數據的降維。數組
數據分析時,原始數據的維度與算法的複雜度有着密切的關係,在保留原始數據特徵的狀況下,對數據進行降維能夠有效地提升時間效率,減小算力損失。網絡
降維意味着信息的丟失,可是因爲實際數據內部每每具備相關性,因此咱們能夠利用這種相關性,經過某些方法使得在數據維度減小的同時保留儘量多的原始特徵,這就是PCA算法的初衷。架構
那麼這一想法如何轉化爲算法呢?咱們知道,在N維空間對應了由N個線性無關的基向量構成的一組基,空間中的任一貫量均可用這組基來表示。咱們要將一組N維向量降爲K維(K大於0,小於N),其實只須要經過矩陣乘法將原N維空間中的向量轉化爲由K個線性無關的基向量構成的一組基下的表示。app
可是,這一組K維的基並非隨便指定的。爲了儘量保留原始特徵,咱們但願將原始數據向量投影到低維空間時,投影后各字段(行向量)不重合(顯然重合會覆蓋特徵),也就是使變換後數據點儘量分散,這就天然地聯繫到了線性代數中的方差與協方差。
因此,綜合來看,咱們降維的目標爲 選擇K個基向量(通常轉化爲單位長度的正交基),使得原始數據變換到這組基上後,各字段兩兩間協方差爲0,而字段的方差則儘量大(在正交的約束下,取最大的K個方差)。這也就是PCA的原理——PCA本質上是將方差最大的方向做爲主要特徵,而且在各個正交方向上將數據「離相關」,也就是讓它們在不一樣正交方向上沒有相關性。
在上一個問題中,咱們其實已經介紹了PCA的算法流程。轉化成具體的數學方法,主要有如下幾步(設有 \(m\) 條 \(n\) 維數據,將其降爲 \(k\) 維):
PCA是一種無參數技術,沒法進行個性化的優化;PCA能夠解除線性相關,但沒法處理高階的相關性;PCA假設數據各主特徵分佈在正交方向,沒法較好應對主特徵在非正交方向的狀況。
用 Python 語言實現上述算法,代碼以下,僅展現代碼核心部分,詳細代碼請見附件。
參考連接:《人臉識別經典算法實現(一)——特徵臉法》,《opencv學習之路(40)、人臉識別算法——EigenFace、FisherFace、LBPH》,《經典人臉識別算法小結——EigenFace, FisherFace & LBPH(下)》
def algorithm_pca(data_mat): """ PCA函數,用於數據降維 :param data_mat: 樣本矩陣 :return: 降維後的樣本矩陣和變換矩陣 """ mean_mat = np.mat(np.mean(data_mat, 1)).T cv2.imwrite('./data/face_test/mean_face.jpg', np.reshape(mean_mat, IMG_SIZE)) diff_mat = data_mat - mean_mat # print('差值矩陣', diff_mat.shape, diff_mat) cov_mat = (diff_mat.T * diff_mat) / float(diff_mat.shape[1]) # print('協方差矩陣', cov_mat.shape, cov_mat) eig_vals, eig_vecs = np.linalg.eig(np.mat(cov_mat)) # print('特徵值(全部):', eig_vals, '特徵向量(全部):', eig_vecs.shape, eig_vecs) eig_vecs = diff_mat * eig_vecs eig_vecs = eig_vecs / np.linalg.norm(eig_vecs, axis=0) eig_val = (np.argsort(eig_vals)[::-1])[:DIM] eig_vec = eig_vecs[:, eig_val] # print('特徵值(選取):', eig_val, '特徵向量(選取):', eig_vec.shape, eig_vec) low_mat = eig_vec.T * diff_mat # print('低維矩陣:', low_mat) return low_mat, eig_vec
參考連接:《Linear Discriminant Analysis》,《人臉識別經典算法三:Fisherface(LDA)》,《人臉識別系列二 | FisherFace,LBPH算法及Dlib人臉檢測》
LDA(Linear Discriminant Analysis,線性判別分析)算法的思路與PCA相似,都是對圖像的總體分析。不一樣之處在於,PCA是經過肯定一組正交基對數據進行降維,而LDA是經過肯定一組投影向量使得數據集不一樣類的數據投影的差異較大、同一類的數據通過投影更加聚合。在形式上,PCA與LDA的最大區別在於,PCA中最終求得的特徵向量是正交的,而LDA中的特徵向量不必定正交。
在上面咱們已經介紹了LDA的目標:不一樣的分類獲得的投影點要儘可能分開;同一個分類投影后獲得的點要儘可能聚合。
爲了定量分析這兩點,以計算合適的投影矩陣,咱們定義了類內散列度矩陣 \(S_{w}\)和 類間散列度矩陣 $S_B $ 。 其中 \(S_w=\sum ^c_{i=1}{\sum _{x\in ω_i}(x-μ_i)(x-μ_i)^T}\), \(c\) 爲類別總數,\(μ_i\)表明類別 \(i\) 的均值矩陣;\(S_B=\sum ^c_{i=1}N_i(μ_i-μ)(μ_i-μ)^T\),\(N_i\) 爲類別\(i\) 的數據點數。定義 \(J(w)=\frac{|W^TS_BW|}{|W^TS_wW|}\) 爲目標函數,其中矩陣 \(W\) 是投影矩陣,那麼咱們就是要求出使 \(J(w)\) 取最大值的投影矩陣 \(W\) 。該最大值可由拉格朗日乘數法求得,再也不贅述。最終問題可化簡爲,\(S_w^{-1}S_Bw_i=λw_i\) ,即求得矩陣的特徵向量,而後取按特徵值從大到小排列的前 \(k\) 個特徵向量即爲所需的投影矩陣。
LDA與PCA算法的不一樣之處:
LDA與PCA算法的相同之處:
用python語言實現上述算法,代碼以下,僅展現代碼核心部分,詳細代碼請見附件。
參考連接:《人臉識別經典算法實現(二)——Fisher線性判別分析》,《opencv學習之路(40)、人臉識別算法——EigenFace、FisherFace、LBPH》,《經典人臉識別算法小結——EigenFace, FisherFace & LBPH(下)》
def algorithm_lda(data_list): """ 多分類問題的線性判別分析算法 :param data_list: 樣本矩陣列表 :return: 變換後的矩陣列表和變換矩陣 """ n = data_list[0].shape[0] Sw = np.zeros((n, n)) u = np.zeros((n, 1)) Sb = np.zeros((n, n)) N = 0 mean_list = [] sample_num = [] for data_mat in data_list: mean_mat = np.mat(np.mean(data_mat, 1)).T mean_list.append(mean_mat) sample_num.append(data_mat.shape[1]) data_mat = data_mat - mean_mat Sw += data_mat * data_mat.T for index, mean_mat in enumerate(mean_list): m = sample_num[index] u += m * mean_mat N += m u = u / N for index, mean_mat in enumerate(mean_list): m = sample_num[index] sb = m * (mean_mat - u) * (mean_mat - u).T Sb += sb eig_vals, eig_vecs = np.linalg.eig(np.mat(np.linalg.inv(Sw) * Sb)) eig_vecs = eig_vecs / np.linalg.norm(eig_vecs, axis=0) eig_val = (np.argsort(eig_vals)[::-1])[:DIM] eig_vec = eig_vecs[:, eig_val] trans_mat_list = [] for data_mat in data_list: trans_mat_list.append(eig_vec.T * data_mat) return trans_mat_list, eig_vec
LBPH算法「人」如其名,採用的識別方法是局部特徵提取的方法,這是與前兩種方法的最大區別。相似的局部特徵提取算法還有離散傅里葉變換(DCT)與蓋伯小波(Gabor Waelets)等。
LBPH(Local Binary Pattern Histograms,局部二進制模式直方圖)人臉識別方法的核心是 LBP算子。LBP是一種用來描述圖像局部紋理特徵的算子,它反映內容是每一個像素與周圍像素的關係。
參考連接:《LBP簡介》,《人臉識別經典算法二:LBP方法》
原始 LBP
最初的LBP是定義在像素3x3鄰域內的,以鄰域中心像素爲閾值,將相鄰的8個像素的灰度值與其進行比較,若周圍像素值大於中心像素值,則該像素點的位置被標記爲1,不然爲0。這樣,3x3鄰域內的8個點經比較可產生8位二進制數(一般轉換爲十進制數即LBP碼,共256種),即獲得該鄰域中心像素點的LBP值,並用這個值來反映該區域的紋理信息。以下圖所示:
均勻 LBP(本次做業中代碼採用的LBP算子)
研究者發現根據原始LBP計算出來的90%以上的值具備某種特性,即屬於 均勻模式(Uniform Pattern)——二進制序列(這個二進制序列首尾相連)中數字從0到1或是從1到0的變化不超過2次。好比,01011111
的變化次數爲3次,那麼該序列不屬於均勻模式。根據這個算法,全部的8位二進制數中共有58(變化次數爲0的有2種,變化次數爲1的有0種,變化次數爲2的有56種)個均勻模式。
因此,咱們能夠根據這一數據分佈特色將原始的LBP值分爲59類,58個均勻模式爲一類,其他爲第59類。這樣就將直方圖從原來的256維變成了59維,起到了降維的效果。
LBPH的算法其實很是簡單,只有兩步:
LBP的優勢是對光照不敏感。根據算法,每一個像素都會根據鄰域信息獲得一個LBP值,若是以圖像的形式顯示出來能夠獲得下圖。相比於PCA或者LDA直接使用灰度值去參與運算,LBP算子是一種相對性質的數量關係,這是LBP應對不一樣光照條件下人臉識別場景的優點所在。可是對於不一樣角度、遮擋等場景,LBP也無能爲力。
用python語言實現上述算法,代碼以下,僅展現代碼核心部分,詳細代碼請見附件。
參考連接:《人臉識別經典算法實現(三)——LBP算法》,《opencv學習之路(40)、人臉識別算法——EigenFace、FisherFace、LBPH》,《經典人臉識別算法小結——EigenFace, FisherFace & LBPH(下)》
class AlgorithmLbp(object): def load_img_list(self, dir_name): """ 加載圖像矩陣列表 :param dir_name:文件夾路徑 :return: 包含最原始的圖像矩陣的列表和標籤矩陣 """ # 請見附件 def get_hop_counter(self, num): """ 計算二進制序列是否只變化兩次 :param num: 數字 :return: 01變化次數 """ bin_num = bin(num) bin_str = str(bin_num)[2:] n = len(bin_str) if n < 8: bin_str = "0" * (8 - n) + bin_str n = len(bin_str) counter = 0 for i in range(n): if i != n - 1: if bin_str[i + 1] != bin_str[i]: counter += 1 else: if bin_str[0] != bin_str[i]: counter += 1 return counter def get_table(self): """ 生成均勻對應字典 :return: 均勻LBP特徵對應字典 """ counter = 1 for i in range(256): if self.get_hop_counter(i) <= 2: self.table[i] = counter counter += 1 else: self.table[i] = 0 return self.table def get_lbp_feature(self, img_mat): """ 計算LBP特徵 :param img_mat:圖像矩陣 :return: LBP特徵圖 """ m = img_mat.shape[0] n = img_mat.shape[1] neighbor = [0] * 8 feature_map = np.mat(np.zeros((m, n))) for y in range(1, m - 1): for x in range(1, n - 1): neighbor[0] = img_mat[y - 1, x - 1] neighbor[1] = img_mat[y - 1, x] neighbor[2] = img_mat[y - 1, x + 1] neighbor[3] = img_mat[y, x + 1] neighbor[4] = img_mat[y + 1, x + 1] neighbor[5] = img_mat[y + 1, x] neighbor[6] = img_mat[y + 1, x - 1] neighbor[7] = img_mat[y, x - 1] center = img_mat[y, x] temp = 0 for k in range(8): temp += (neighbor[k] >= center) * (1 << k) feature_map[y, x] = self.table[temp] feature_map = feature_map.astype('uint8') return feature_map def get_hist(self, roi): """ 計算直方圖 :param roi:圖像區域 :return: 直方圖矩陣 """ hist = cv2.calcHist([roi], [0], None, [59], [0, 256]) return hist
CNN(Convolutional Neural Networks,卷積神經網絡)是人臉識別方面最經常使用的一類深度學習方法。深度學習方法的主要優點是可用大量數據來訓練,從而學到對訓練數據中出現的變化狀況穩健的人臉表徵。這種方法不須要設計對不一樣類型的類內差別(好比光照、姿式、面部表情、年齡等)穩健的特定特徵,而是能夠從訓練數據中學到它們。
深度學習方法的主要短板是它們須要使用很是大的數據集來訓練,並且這些數據集中須要包含足夠的變化,從而能夠泛化到不曾見過的樣本上。幸運的是,一些包含天然人臉圖像的大規模人臉數據集已被公開;不幸的是,本人設備算力實在有限,沒法支持在大數據集上運行深度學習代碼。
參考連接:《人臉識別合集 | 緒論與目錄》,《Face Recognition: From Traditional to Deep Learning Methods》
DeepFace,2014年
DeepFace主要先訓練Softmax多分類器人臉識別框架;而後抽取特徵層,用特徵再訓練另外一個神經網絡、孿生網絡或組合貝葉斯等人臉驗證框架。想同時擁有人臉驗證和人臉識別系統,須要分開訓練兩個神經網絡。但線性變換矩陣W的大小隨着身份數量n的增長而線性增大。
DeepFace的主要貢獻是,(1)一個基於明確的 3D 人臉建模的高效的人臉對齊系統;(2)一個包含局部鏈接的層的 CNN 架構 ,這些層不一樣於常規的卷積層,能夠從圖像中的每一個區域學到不一樣的特徵。
DeepID系列,2014年
DeepID框架與DeepFace相似,採用的是 CNN+Softmax;而DeepID二、DeepID2+、DeepID3都採用 CNN+Softmax+Contrastive Loss,使得同類特徵的L2距離儘量小,不一樣類特徵的L2距離大於某個間隔。
FaceNet,2015年
2015年FaceNet提出了一個絕大部分人臉問題的統一解決框架,直接學習嵌入特徵,而後人臉識別、人臉驗證和人臉聚類等都基於這個特徵來作。FaceNet在 DeepID2 的基礎上,拋棄了分類層,再將 Contrastive Loss 改進爲 Triplet Loss,得到類內緊湊和類間差別。但人臉三元組的數量出現爆炸式增加,特別是對於大型數據集,致使迭代次數顯著增長;樣本挖掘策略形成很難有效的進行模型的訓練。
參考連接:《人臉識別合集 | 人臉識別概述》,《Face Recognition: From Traditional to Deep Learning Methods》
用於人臉識別的 CNN 模型主要有如下兩種設計思路:
卷積神經網絡部分的代碼非本人編寫,但願老師與助教知悉。【引用地址】
與電腦環境鬥爭一天的我,面對無數的DDL,遂選擇擱置,往後我會將從新編寫的代碼放在個人博客中。但爲了使報告更加完整,兼顧傳統方法與深度學習方法,綜合對比不一樣方法在人臉識別中的表現,我使用了 17373489 張佳一 同窗的 CNN 代碼與數據(見結果分析部分)。
#實現CNN卷積神經網絡,並測試最終訓練樣本實現的檢測機率 #tf.layer方法能夠直接實現一個卷積神經網絡的搭建 #經過卷積方法實現 layer1 = tf.layers.conv2d(inputs=data_input, filters = 32,kernel_size=2, strides=1,padding='SAME',activation=tf.nn.relu) #實現池化層,減小數據量,pool_size=2表示數據量減小一半 layer1_pool = tf.layers.max_pooling2d(layer1,pool_size=2,strides=2) #第二層設置輸出,完成維度的轉換,以第一次輸出做爲輸入,創建n行的32*32*32輸出 layer2 = tf.reshape(layer1_pool,[-1,32*32*32]) #設置輸出激勵函數 layer2_relu = tf.layers.dense(layer2, 1024, tf.nn.relu) #完成輸出,設置輸入數據和輸出維度 output = tf.layers.dense(layer2_relu, num_people) #創建損失函數 loss = tf.losses.softmax_cross_entropy(onehot_labels=label_input,logits=output) #使用梯度降低法進行訓練 train = tf.train.GradientDescentOptimizer(0.01).minimize(loss) #定義檢測機率 accuracy = tf.metrics.accuracy( labels=tf.arg_max(label_input, 1), predictions=tf.arg_max(output, 1)) [1]
Faces94(153人) | Faces95(72人) | Faces96(152人) | Grimace(18人) |
---|---|---|---|
在代碼實現部分,我將 均值矩陣 與 特徵矩陣 從新映射到 \([0,255]\) 的灰度值區間,獲得了所謂 「平均臉」 與 「特徵臉」。(從上到下,依次爲在 Faces9四、Faces9五、Faces9六、Grimace的結果)
平均臉 | 特徵臉 1 | 特徵臉 2 | 特朗普 3 😜 | 特徵臉 4 |
平均臉 | 特徵臉 1 | 特徵臉 2 | 特徵臉 3 | 特徵臉 4 |
平均臉 | 特徵臉 1 | 特徵臉 2 | 特徵臉 3 | 特徵臉 4 |
平均臉 | 特徵臉 1 | 特徵臉 2 | 特徵臉 3 | 特徵臉 4 |
這裏展現的是該算法在不一樣數據集上識別的準確率,以及在本機上運行的時間(單位:ms)。
Faces94 | Faces95 |
---|---|
Faces96 | Grimace |
---|---|
準確率方面:咱們發現,PCA在Faces94與Grimace上的識別準確率居然達到了100%,Faces96上次之,而在Faces95上的識別率最低。這與咱們的預期截然不同。結合「平均臉」與數據集特徵,——Faces95數據集中人臉的角度變化較大,而Faces96中背景的干擾較大——我認爲應該是由於在代碼實現時,數據預處理中沒有進行良好的人臉檢測與對齊,這也反映了,PCA在適應背景干擾與人臉角度變化方面,表現不佳;
識別速度方面:隨數據集規模擴大,識別耗時基本呈指數增加的趨勢(橙色虛線爲指數擬合曲線)。
代碼運行在Faces94與Faces95子集上會出現數組溢出的BUG,這與數據集中非法數據(類別中不足20張,包含.gif圖片等)有關,須要對數據集進行過濾,現僅在其他兩個數據集上測試。
Faces96 | Grimace |
---|---|
Faces96 | Grimace |
---|---|
準確率方面:實驗結果顯示,LDA在Faces96上表現不佳,而在Grimace上則達到了100%識別。分析其緣由,LDA;
識別速度方面:與PCA技術相比,通常狀況下,在相同的數據集上,LDA算法耗時要比PCA長,且當數據集增大時,LDA算法耗時與PCA的差值逐漸增大;可是,當數據集較小時,LDA的速度反而比PCA略快。
注意,爲了提升識別效率,在圖片讀取時,我已經將原圖尺寸統一調整爲 50 × 50 。
原圖 | 原始LBP | 均衡模式 |
---|---|---|
LBPH人臉比對時間過長,在測試時,我將其讀取到的圖片調整爲20 × 20,以提升識別速度。
Faces94 | Faces95 |
---|---|
Faces96 | Grimace |
---|---|
不一樣算法下,在不一樣特色的數據集中,人臉識別準確率的對好比下。
運行算法進行人臉表徵提取所需時間(單位:ms)。
不一樣算法進行人臉識別所需時間(單位:ms)。
用到的數據集:PIE dataset ,包含了68我的在五種不一樣姿態下的共11554張面部圖像數據,並以MAT格式的文件保存。
Pose07 | Pose09 | Pose27 | Pose29 |
---|---|---|---|
def load_img(file_name): """ 載入圖像,統一尺寸,灰度化處理,直方圖均衡化 :param file_name: 圖像文件名 :return: 圖像矩陣 """ t_img_mat = cv2.imread(file_name) # 載入圖像 t_img_mat = cv2.resize(t_img_mat, IMG_SIZE) # 統一尺寸 t_img_mat = cv2.cvtColor(t_img_mat, cv2.COLOR_RGB2GRAY) # 轉化爲灰度圖 img_mat = cv2.equalizeHist(t_img_mat) # 直方圖均衡 return img_mat def create_img_mat(dir_name, algorithm=0): """ 生成圖像樣本矩陣,組織形式爲行爲屬性,列爲樣本 :param dir_name: 包含訓練數據集的圖像文件夾路徑 :param algorithm: 識別算法,0-EigenFace,1-Fisher,2-LBP :return: 樣本矩陣,標籤矩陣 """ data_mat = np.zeros((IMG_SIZE[0] * IMG_SIZE[1], 1)) label = [] data_list = [] for parent, dir_names, file_names in os.walk(dir_name): for t_dir_name in dir_names: for sub_parent, sub_dir_name, sub_file_names in os.walk(parent + '/' + t_dir_name): for t_index, t_file_name in enumerate(sub_file_names): if not t_file_name.endswith('.jpg'): continue if t_file_name.endswith('.10.jpg'): continue t_img_mat = load_img(sub_parent + '/' + t_file_name) img_mat = np.reshape(t_img_mat, (-1, 1)) if algorithm == 0: data_mat = np.column_stack((data_mat, img_mat)) else: data_mat = img_mat if t_index == 0 else np.column_stack((data_mat, img_mat)) label.append(sub_parent + '/' + t_file_name) data_list.append(data_mat[:, 1:] if algorithm == 0 else data_mat) return data_mat[:, 1:], label, data_list
class AlgorithmLbp(object): def __init__(self): self.table = {} self.ImgSize = IMG_SIZE self.BlockNum = DIM self.count = 0 def load_img_list(self, dir_name): """ 加載圖像矩陣列表 :param dir_name:文件夾路徑 :return: 包含最原始的圖像矩陣的列表和標籤矩陣 """ img_list = [] label = [] for parent, dir_names, file_names in os.walk(dir_name): for t_dir_name in dir_names: for sub_parent, sub_dir_name, sub_filenames in os.walk(parent + '/' + t_dir_name): for file_name in sub_filenames: img_list.append(load_img(sub_parent + '/' + file_name)) label.append(sub_parent + '/' + file_name) return img_list, label
class AlgorithmLbp(object): def get_hop_counter(self, num): """ 計算二進制序列是否只變化兩次 :param num: 數字 :return: 01變化次數 """ bin_num = bin(num) bin_str = str(bin_num)[2:] n = len(bin_str) if n < 8: bin_str = "0" * (8 - n) + bin_str n = len(bin_str) counter = 0 for i in range(n): if i != n - 1: if bin_str[i + 1] != bin_str[i]: counter += 1 else: if bin_str[0] != bin_str[i]: counter += 1 return counter def get_table(self): """ 生成均勻對應字典 :return: 均勻LBP特徵對應字典 """ counter = 1 for i in range(256): if self.get_hop_counter(i) <= 2: self.table[i] = counter counter += 1 else: self.table[i] = 0 return self.table def get_lbp_feature(self, img_mat): """ 計算LBP特徵 :param img_mat:圖像矩陣 :return: LBP特徵圖 """ m = img_mat.shape[0] n = img_mat.shape[1] neighbor = [0] * 8 feature_map = np.mat(np.zeros((m, n))) t_map = np.mat(np.zeros((m, n))) for y in range(1, m - 1): for x in range(1, n - 1): neighbor[0] = img_mat[y - 1, x - 1] neighbor[1] = img_mat[y - 1, x] neighbor[2] = img_mat[y - 1, x + 1] neighbor[3] = img_mat[y, x + 1] neighbor[4] = img_mat[y + 1, x + 1] neighbor[5] = img_mat[y + 1, x] neighbor[6] = img_mat[y + 1, x - 1] neighbor[7] = img_mat[y, x - 1] center = img_mat[y, x] temp = 0 for k in range(8): temp += (neighbor[k] >= center) * (1 << k) feature_map[y, x] = self.table[temp] t_map[y, x] = temp feature_map = feature_map.astype('uint8') t_map = t_map.astype('uint8') self.count += 1 return feature_map def get_hist(self, roi): """ 計算直方圖 :param roi:圖像區域 :return: 直方圖矩陣 """ hist = cv2.calcHist([roi], [0], None, [59], [0, 256]) return hist
def method_compare(mat_list, test_img_vec, algorithm=0): """ 比較函數,這裏只是用了最簡單的歐氏距離比較,還可使用KNN等方法,如需修改修改此處便可 :param mat_list: 樣本向量集 :param test_img_vec: 測試圖像向量 :param algorithm: 識別算法,0-EigenFace,1-Fisher,2-LBP :return: 與測試圖片最相近的圖像文件名的index """ dis_list = [] if algorithm == 0: for sample_vec in mat_list.T: dis_list.append(np.linalg.norm(test_img_vec - sample_vec)) elif algorithm == 1: # print('mat_list.len=', len(mat_list)) for trans_mat in mat_list: for sample_vec in trans_mat.T: dis_list.append(np.linalg.norm(test_img_vec - sample_vec)) index = np.argsort(dis_list)[0] return index
class AlgorithmLbp(object): def compare(self, sampleImg, test_img): """ 比較函數,這裏使用的是歐氏距離排序,也可使用KNN,在此處更改 :param sampleImg: 樣本圖像矩陣 :param test_img: 測試圖像矩陣 :return: k2值 """ testFeatureMap = self.get_lbp_feature(test_img) sampleFeatureMap = self.get_lbp_feature(sampleImg) # 計算步長,分割整個圖像爲小塊 ystep = int(self.ImgSize[0] / self.BlockNum) xstep = int(self.ImgSize[1] / self.BlockNum) k2 = 0 for y in range(0, self.ImgSize[0], ystep): for x in range(0, self.ImgSize[1], xstep): testroi = testFeatureMap[y:y + ystep, x:x + xstep] sampleroi = sampleFeatureMap[y:y + ystep, x:x + xstep] testHist = self.get_hist(testroi) sampleHist = self.get_hist(sampleroi) k2 += np.sum((sampleHist - testHist) ** 2) / np.sum((sampleHist + testHist)) return k2