使用Python進行人臉聚類的詳細教程
編譯:chux
出品:ATYUN訂閱號
這篇文章的靈感來自讀者Leonard Bogdonoff的一個問題:
你好Adrian,能介紹下身份聚類嗎?我有一個照片數據集,但我無法確定如何處理它們來識別特定的人。
類似這種「人臉聚類」或者說「身份聚類」的應用可用於輔助執法。
思考下面這個場景:兩名劫匪在搶劫波士頓或紐約等繁華城市的銀行。銀行的安全攝像頭工作正常,捕捉到了搶劫行為,但劫匪戴著頭套,沒辦法看到他們的臉。
劫匪將現金藏在衣服下面逃離銀行,摘掉面具,並將它們扔在附近的垃圾桶中,以免在公眾場合顯得可疑。
那麼,他們會逃脫追責嗎?也許會。
但安裝在附近的加油站,餐館和紅燈/主要交叉路口的安全攝像頭捕獲了附近的所有行人活動。
在警察到達之後,他們可以利用人臉聚類來查找該區域內所有視頻信息的所有獨特的面孔 – 得到獨特的面孔,可以:(1)手動調查它們並將它們與銀行出納員描述進行比較,(2)運行自動搜索將面孔與已知的罪犯資料庫進行比較,或者(3)找好的刑警尋找可疑人員。
這當然是一個虛構的例子,但我希望你看到人臉聚類在現實世界中使用的價值。
使用Python進行人臉聚類
人臉識別和人臉聚類並不相同,但概念高度相關。當進行面部識別時,我們使用監督學習,其中我們同時具有(1)我們想要識別的面部的示例圖像,以及(2)與每個面部相對應的名字(即,「類標籤」)。
但對人臉聚類,我們需要執行無監督學習,我們只有沒有名字或者說標籤的人臉本身。從這裡,我們需要識別和計算數據集中某些獨特的人。
在本文的第一部分中,我們將討論我們的人臉聚類數據集以及我們將用於構建項目的項目結構。
在這裡,我將幫助你編寫兩個Python腳本:
一個用於提取和量化數據集中的人臉
另一個是對面部進行聚類,其中每個結果聚類(理想情況下)代表一個獨特的個體
然後,我們將在樣本數據集上運行我們的人臉聚類管道並檢查結果。
配置開發環境
參考:https://www.pyimagesearch.com/2018/06/18/face-recognition-with-opencv-python-and-deep-learning/
以下是你在Python環境中需要的所有內容:
OpenCV
dlib
face_recognition
imutils
scikit-learn
如果有GPU,則需要安裝帶有CUDA的dlib。
我們的人臉聚類數據集
由於2018年世界盃半決賽,我認為將人臉聚類應用於著名足球運動員的面孔會很有趣。
從上面的圖1中可以看出,我已經整理了五個足球運動員的數據集,包括:
穆罕默德·薩拉赫
內馬爾
C羅
里奧·梅西
路易斯·蘇亞雷斯
數據集中有129個圖像。
我們的目標是提取量化圖像中每個面部的特徵,並將得到的「面部特徵向量」聚類。理想情況下,每個足球運動員都擁有自己的簇,僅包含他們自己的臉。
面對集群項目結構
我們的項目結構如下:
我們的項目有一個目錄和三個文件:
dataset / :包含我們五個足球運動員的129張照片。請注意,在上面的輸出中,文件名或其他文件中沒有用於標識每個圖像中的人員標識信息!根據文件名單獨知道哪個足球運動員在哪個圖像中是不可能的。我們將設計一個人臉聚類演算法來識別數據集中相似且唯一的臉。
encode_faces .py :第一個腳本,它為數據集中的所有的人臉計算面部嵌入並輸出一個序列化的編碼文件。
encodings.pickle :我們的面部嵌入序列化的pickle文件。
cluster_faces .py :在這個腳本中我們將聚類相似的人臉並找到異常值。
通過深度學習編碼面孔
為了用數字表示人臉,我們用神經網路生成的128維特徵向量對數據集中的所有人臉進行量化。
在我們對一組人臉進行聚類之前,我們首先需要對它們進行量化。這個量化人臉的過程將使用深度神經網路完成,該網路負責:
接受輸入圖像
並輸出128維特徵向量,量化人臉
我將討論這個深度神經網路如何工作以及如何進行訓練。我們的encode_faces .py 腳本包含為每張臉提取128維特徵向量表示的所有代碼。
要查看此進程的執行方式,請創建名為encode_faces .py的文件 ,並插入以下代碼:
我們所需的包在2-7行導入。注意:
paths,來自我的imutils包
face_recognition,(https://github.com/ageitgey/face_recognition)
然後,我們解析命令行參數上(10-17):
–dataset:人臉和圖像輸入目錄的路徑。
–encodings:包含面部編碼的輸出序列化pickle文件的路徑。
–detection-method:你可以使用卷積神經網路(CNN)或方向梯度直方圖(HOG)方法在量化面部之前檢測輸入圖像中的人臉。CNN方法更準確(但更慢),而HOG方法更快(但不太準確)。
我還要提到的是,如果你認為這個腳本運行緩慢,或者你希望在沒有GPU的情況下實時運行人臉聚類,可以將–detection-method設置為hog ,替代cnn。雖然CNN臉檢測更準確,但在沒有GPU運行實時檢測速度太慢。
讓我們抓取輸入數據集中所有圖像的路徑:
在第4行,我們使用命令行參數中提供的數據集路徑創建數據集中所有的imagePath列表 。
然後,初始化我們的data列表,我們稍後會填充圖像路徑,邊界框和面部編碼。
讓我們開始遍歷所有的imagePaths:
在 第2行,我們開始遍歷imagePaths並繼續載入(第8行)。然後我們在圖像中交換顏色通道, 因為dlib默認rgb排序而不是OpenCV默認的bgr(第9行)。
現在已經處理了圖像,讓我們檢測所有的人臉並抓取它們的邊界框坐標:
我們必須先檢測圖像中人臉的實際位置,然後再對其進行量化(3-4)。你會注意到 face_recognition API非常易於使用。
注意: 我們使用CNN面部檢測器以獲得更高的精度,但如果使用的是CPU而不是GPU,則運行時間會長得多。如果希望編碼腳本運行得更快或系統運行更快,並且你的系統沒有足夠的內存或CPU支持CNN面部檢測器,請改用HOG + Linear SVM方法。
讓我們來看看這個腳本主要部分。在下一個部分中,我們將計算面部編碼:
在這裡,我們計算rgb圖像中每個檢測到的人臉的128維面部編碼(第2行)。
對於每個檢測到的面部+編碼,我們構建一個字典(第6和7行),其中包括:
輸入圖像的路徑
圖像中人臉的位置(即邊界框)
128維編碼本身
然後我們將字典添加到我們的data列表中(第8行)。稍後當我們想要查看哪些人臉屬於哪個簇時,我們會用到此信息。
要結束此腳本,我們只需將數據列表寫入序列化的pickle文件:
使用我們的命令行參數 args [ 「encodings」 ] 作為路徑+文件名,我們將數據列表作為序列化的pickle文件寫入磁碟(第3-5行)。
運行面部編碼腳本
請訪問文末鏈接下載代碼和圖像數據集,數據集可以換成自己的。
然後,打開一個終端並激活你的Python虛擬環境(如果你用了虛擬環境的話)。
然後,使用兩個命令行參數,執行腳本編碼球員的臉,如下:
此過程可能需要一段時間,你可以用終端輸出跟蹤進度。
如果你使用GPU,大約1-2分鐘。只要確保你安裝DLIB與CUDA,把你的GPU的優勢發揮出來。
但是,如果只使用CPU在筆記本電腦上執行腳本,則腳本可能需要運行20-30分鐘。
聚類面孔
現在我們已經將數據集中的所有的人臉都量化並編碼為128維向量,下一步就是將它們聚類成組。
我們希望每個人都有自己獨立的簇。問題是,許多聚類演算法,如k-means和Hierarchical Agglomerative Clustering,要求我們提前指定簇的數量。
在這個例子中,我們知道只有五個足球運動員,但在實際應用中,你可能並不知道數據集中有多少個人。
因此,我們需要使用基於密度或基於圖的聚類演算法,這樣的演算法不僅可以聚類數據點,還可以根據數據密度確定聚類數量。
對於人臉聚類,我推薦使用兩種演算法:
Density-based spatial clustering of applications with noise (DBSCAN)
Chinese whispers聚類
我們將在本教程中使用DBSCAN,因為我們的數據集相對較小。對於真正龐大的數據集,應該考慮使用Chinese whispers 演算法,因為它是time linear的(詳見wiki:Chinese Whispers)。
DBSCAN演算法通過將在n維空間中緊密排列的點分組 。靠在一起的點被分到同一個簇中。
DBSCAN也可以輕易的處理異常值,如果它們落在他們的「最近鄰」很遠的低密度區域,則標記它們。
讓我們繼續使用DBSCAN實現人臉聚類。
打開一個新文件,將其命名為cluster_faces .py ,然後插入以下代碼:
DBSCAN內置在scikit-learn中。我們在第2行導入DBSCAN實現 。
我們還從imutils導入build_montages從模塊(3行)。我們將使用此函數為每個簇構建「蒙太奇的臉」。
我們的其他導入在第4-7行 。
我們解析兩個命令行參數:
–encodings:我們在之前的腳本中生成的編碼pickle文件的路徑。
–jobs:DBSCAN是多線程的,可以將參數傳遞給包含要運行的並行作業數的構造函數。值 – 1 的意思為使用所有可用的CPU(也是該命令行參數的默認值)。
讓我們載入面部嵌入數據:
在這個塊中我們有:
從磁碟載入面部編碼的data(第5行)。
將data處理為NumPy數組(第6行)。
從data中提取128維編碼 ,將它們放在一個列表中(第7行)。
現在我們可以 在下一個代碼塊中對編碼進行聚類 :
為了對編碼進行聚類,我們只需創建一個DBSCAN 對象,然後 將模型fit(擬合)到encodings本身(第3和4行)。
現在讓我們確定數據集中的獨特人類!
第7行, clt 。labels_ 包含數據集中所有人臉的標籤ID(即每個人臉所屬的簇)。要查找獨特面孔或標籤的ID,我們只需使用NumPy的unique功能。結果是唯一的labelIDs列表 。
在 第8行, 我們計算numUniqueFaces 。有可能是值 – 1 ,在labelIDs中這個值對應於「異常值」類,即128維嵌入遠離添加好的其他簇很多的點。這些點被稱為「異常值」(或者說,離群值),根據人臉聚類的應用它可能值得研究或簡單地丟棄。
在我們的例子中,我們設計 了計數中的負的labelID,因為我們知道我們的數據集只包含5個人的圖像。是否這樣做在很大程度上取決於你的項目。
我們接下來的三個代碼塊的目標是在我們的數據集中生成獨特的球員的面部蒙太奇。
循環遍歷所有獨特的labelID :
在第7-9行, 我們找到當前labelID的所有索引 ,然後抓取最多25個圖像的隨機樣本嵌入蒙太奇中。
face列表白喊面部圖像本身(10行)。我們需要另一個循環來填充此列表:
我們開始在隨機樣本中循環遍歷所有的idx(第2行)。
在循環的第一部分內,我們:
從磁碟載入image並使用在我們的面部嵌入步驟中找到的邊界框坐標提取face ROI(第4-6行)。
調整人臉到固定尺寸96×96(行10),所以我們可以把它添加到臉部蒙太奇(11行)用於可視化每個簇。
要完成我們的最外層的循環,讓我們構建蒙太奇並將其顯示在屏幕上:
我們採用 build_montages 的功能imutils以生成單個圖像 蒙太奇 含有5×5的網格 面 (2線)。
從那裡,我們 標題 窗口(第5和6行),然後 在我們的屏幕上顯示窗口中的蒙太奇。
只要OpenCV打開的窗口打開,你可以按一個鍵顯示下一個人臉蒙太奇。
面對聚類結果
此腳本只需要一個命令行參數 – 編碼文件的路徑。要為執行人臉聚類,只需在終端中輸入以下命令:
識別出五個人臉簇的類。face ID為-1包含找到的所有異常值。你將在屏幕上看到群集蒙太奇。按鍵生成下一個面部簇的蒙太奇(窗口處於焦點位置,以便OpenCV的highgui模塊可以捕獲你的按鍵)。
以下是我們的128維面部嵌入和DBSCAN聚類演算法在我們的數據集上生成的人臉聚類:
最後,陌生的人類被挑了出來(實際上它是先顯示的):
這張梅西的照片並沒有被聚類成功,而是識別為一張「未知的面孔」。我們的Python人臉聚類演算法很好地完成了對圖像的聚類,只是對這個人臉圖像進行了錯誤的聚類。
在我們數據集中的5個人的129張圖像中,只有一張臉沒有被分組到現有的簇中。
我們的無監督學習DBSCAN方法生成了五個簇。不幸的是,梅西有一個圖片並沒有與他的其他圖片放在一起,但整體來說這種方法效果不錯。
TAG:Python |