人臉識別系列二 | FisherFace,LBPH算法及Dlib人臉檢測
- 2019 年 12 月 9 日
- 筆記
前言
前面介紹了使用特徵臉法進行人臉識別,這裡介紹一下OpenCV人臉識別的另外兩種算法,一種是FisherFace算法,一種是LBPH算法。
FisherFace算法
FisherFace是基於線性判別分析
(LDA)實現的。LDA算法思想最早由英國統計與遺傳學家,現代統計科學的奠基人之一羅納德*費舍爾(Ronald)提出。LDA算法使用統計學方法,嘗試找到物體間特徵的一個線性組合,在降維的同時考慮類別信息。通過該算法得到的線性組合可以用來作為一個線性分類器或者實現降維。LDA的算法原理可以看我之前發的兩篇推文,和《周志華機器學習》的線性判別分析那一個節,我的推文地址如下:https://mp.weixin.qq.com/s/4gowEl_OA13y5u6VueOChg 和 https://mp.weixin.qq.com/s/K6ASbhVrV5-YXrp-rldP6A 。通過之前講過的LDA算法原理,我們知道,該算法是在樣本數據映射到另外一個特徵空間後,將類內距離最小化,類間距離最大化。LDA算法可以用作降維,該算法的原理和PCA算法很相似,因此LDA算法也同樣可以用在人臉識別領域。通過使用PCA算法來進行人臉識別的算法稱為特徵臉法,而使用LDA算法進行人臉識別的算法稱為費舍爾臉法。由於LDA算法與PCA算法很相似,我們簡單的對二者做一個比較。LDA和PCA算法的相似之處在於:
- 在降維的時候,兩者都使用了矩陣的特徵分解思想。
- 兩者都假設數據符合高斯分佈。LDA和PCA的不同之處在於:
- LDA是有監督的降維方法,而PCA是無監督的。
- 如果說數據是k維的,那麼LDA只能降到(k-1)維度,而PCA不受此限制。
- 從數學角度來看,LDA選擇分類性能最好的投影方向,而PCA選擇樣本投影點具有最大方差的方向。
通過LDA算法得到的這些特徵向量就是FisherFace,後續的人臉人臉識別過程和上一節的完全一致,只需要把特徵臉法模型改成FisherFace模型即可,要更改的代碼就一行,如下:

值得一提的是,FisherFace算法識別的錯誤率低於哈佛和耶魯人臉數據庫測試的特徵臉法識別結果。
LBPH
算法原理
OpenCV除了提供特徵臉法,FisherFace以外,還提供了另外一種經典的人臉識別算法即LBPH。KBPH是Local Binary Patterns Histograms的縮寫,翻譯過來就是局部二進制編碼直方圖。該算法基於提取圖像特徵的LBP算子。如果直接使用LBP編碼圖像用於人臉識別。其實和不提取LBP特徵區別不大,因此在實際的LBP應用中,一般採用LBP編碼圖像的統計直方圖作為特徵向量進行分類識別。該算法的大致思路是:
先使用LBP算子提取圖片特徵,這樣可以獲取整幅圖像的LBP編碼圖像。再將該LBP編碼圖像分為若干個區域,獲取每個區域的LBP編碼直方圖,從而得到整幅圖像的LBP編碼直方圖。該方法能夠在一定範圍內減少因為沒完全對準人臉區域而造成的誤差。另一個好處是我們可以根據不同的區域賦予不同的權重,例如人臉圖像往往在圖像的中心區域,因此中心區域的權重往往大於邊緣部分的權重。通過對圖片的上述處理,人臉圖像的特徵便提取完了。
當需要進行人臉識別時,只需要將待識別人臉數據與數據集中的人臉特徵進行對比即可,特徵距離最近的便是同一個人的人臉。在進行特徵距離度量的時候,通常使用基於直方圖的圖像相似度計算函數,該比較方法對應於OpenCV中的comparreHist()函數,該函數提供巴氏距離,相關性與基於卡方的相似度衡量方式。
關於LBPH的細節可以自己搜索一下。
代碼實現
這裡我還是用上次推文的代碼來測試一下LBPH人臉識別模型,仍然只需要改一行代碼,即是:

然後就可以和上次推文一樣獲得一個簡單的基於LBPH的人臉識別demo 了。
Dlib人臉檢測
原理
Dlib是一款優秀的跨平台開源的C++工具庫,該庫使用C++編寫,具有優異的性能。Dlib庫提供的功能十分豐富,包括線性代數,圖像處理,機器學習,網絡,最優化算法等眾多功能。同時該庫也提供了Python,這一節我們正是要用到這個Python接口。
Dlib的核心原理是使用了圖像Hog特徵來表示人臉,和其他特徵提取算子相比,它對圖像的幾何和光學的形變都能保持很好的不變形。該特徵與LBP特徵,Harr特徵共同作為三種經典的圖像特徵,該特徵提取算子通常和支持向量機(SVM)算法搭配使用,用在物體檢測場景。
Dlib 實現的人臉檢測方法便是基於圖像的Hog特徵,綜合支持向量機算法實現的人臉檢測功能,該算法的大致思路如下:
- 對正樣本(即包含人臉的圖像)數據集提取Hog特徵,得到Hog特徵描述子。
- 對負樣本(即不包含人臉的圖像)數據集提取Hog特徵,得到Hog描述子。其中負樣本數據集中的數據量要遠遠大於正樣本數據集中的樣本數,負樣本圖像可以使用不含人臉的圖片進行隨機裁剪獲取。
- 利用支持向量機算法訓練正負樣本,顯然這是一個二分類問題,可以得到訓練後的模型。
- 利用該模型進行負樣本難例檢測,也就是難分樣本挖掘(hard-negtive mining),以便提高最終模型的分類能力。具體思路為:對訓練集里的負樣本不斷進行縮放,直至與模板匹配位置,通過模板滑動串口搜索匹配(該過程即多尺度檢測過程),如果分類器誤檢出非人臉區域則截取該部分圖像加入到負樣本中。
- 集合難例樣本重新訓練模型,反覆如此得到最終分類模型。
應用最終訓練出的分類器檢測人臉圖片,對該圖片的不同尺寸進行滑動掃描,提取Hog特徵,並用分類器分類。如果檢測判定為人臉,則將其標定出來,經過一輪滑動掃描後必然會出現同一個人臉被多次標定的情況,這就用NMS完成收尾工作即可。
Dlib人臉檢測實戰
talk is cheep, show me the coder。這一節就用Python調用Dlib完成人臉檢測來看看效果。在調用之前首先要安裝Dlib人臉檢測庫,我使用的是Windows 10,Core i5的處理器。我的安裝方式為:到網站找到對應自己電腦型號的wheel文件,網站地址是:https://pypi.org/simple/dlib/ 然後pip install *** 這個庫才2M,下載起來十分輕鬆。
然後去這個網站下載訓練好的模型文件:http://dlib.net/files/ 。我這裡下載了這個網站的最下面一個,也就是:shape_predictor_68_face_landmarks.dat.bz2 ,解壓出來,我們就可以加載這個文件進行人臉68個關鍵點檢測了。
單張人臉檢測
這裡檢測一張胡歌的圖片。
代碼如下:
import cv2 import dlib import matplotlib.pyplot as plt import numpy as np # 讀取圖片 img_path = 'F:\face_recognize\huge.jpg' img = cv2.imread(img_path) origin_img = img.copy() # 定義人臉檢測器 detector = dlib.get_frontal_face_detector() # 定義人臉關鍵點檢測器 predictor = dlib.shape_predictor("F:\face_recognize\shape_predictor_68_face_landmarks.dat") # 檢測得到的人臉 faces = detector(img, 0) # 如果存在人臉 if len(faces): print("Found %d faces in this image." % len(faces)) for i in range(len(faces)): landmarks = np.matrix([[p.x, p.y] for p in predictor(img, faces[i]).parts()]) for point in landmarks: pos = (point[0, 0], point[0, 1]) cv2.circle(img, pos, 1, color=(0, 255, 255), thickness=3) else: print('Face not found!') cv2.namedWindow("Origin Face", cv2.WINDOW_FREERATIO) cv2.namedWindow("Detected Face", cv2.WINDOW_FREERATIO) cv2.imshow("Origin Face", origin_img) cv2.waitKey(0) cv2.imshow("Detected Face", img) cv2.waitKey(0)
原始圖和檢測結果如下:

實時檢測
不知道這個算法是否可以實時,我們寫一個代碼,打開一個攝像頭,並實時顯示一下幀率。
import cv2 import dlib import matplotlib.pyplot as plt import numpy as np import time def FaceRecognize(): # 定義人臉檢測器 detector = dlib.get_frontal_face_detector() # 定義人臉關鍵點檢測器 predictor = dlib.shape_predictor("F:\face_recognize\shape_predictor_68_face_landmarks.dat") #打開攝像頭 camera = cv2.VideoCapture(0) st = 0 en = 0 while(True): # 讀取一幀圖像 ret,img = camera.read() #判斷圖片讀取成功 start_time = time.time() if ret: # 檢測到的人臉 faces = detector(img, 0) # 如果存在人臉 if len(faces): print("Found %d faces in this image." % len(faces)) for i in range(len(faces)): landmarks = np.matrix([[p.x, p.y] for p in predictor(img, faces[i]).parts()]) for point in landmarks: pos = (point[0, 0], point[0, 1]) cv2.circle(img, pos, 1, color=(0, 255, 255), thickness=3) else: print('Face not found!') #rint("FPS: ", 1.0 / (time.time() - start_time)) cv2.putText(img, "FPS {0}" .format(str(1.0 / (time.time() - start_time))), (40, 40), 3, 1, (255, 0, 255), 2) cv2.imshow('Face',img) #如果按下q鍵則退出 if cv2.waitKey(100) & 0xff == ord('q') : break camera.release() cv2.destroyAllWindows() FaceRecognize()

(打了點馬賽克)
Dlib檢測的效果還不錯,而且速度在我的I5處理器1s都到80-90幀了,如果做一個簡單的人臉檢測任務可以考慮使用這個算法。
OK, 今天講到這裡了,有問題下方留言。