計算機視覺使很多任務成為現實,其中兩項任務就是人臉檢測
(在圖像中定位人臉)和人臉識別(將人臉識別為特定的人)。
OpenCV實現了一些人臉檢測和識別的算法。從安全到娛樂,這些技術
在現實環境中都有應用。
本章介紹OpenCV的一些人臉檢測和識別功能,並定義特定類型的
可跟蹤物體的數據文件。具體來說,將研究Haar級聯分類器,通過分
析相鄰圖像區域之間的對比度,確定給定圖像或子圖像是否與已知類
型匹配。我們來考慮如何在層次結構中組合多個Haar級聯分類器,以
便用一個分類器識別父區域(就我們的目標而言,是一張人臉),用
其他分類器識別子區域(比如眼睛)。
我們還介紹了“矩形”這個不起眼但卻很重要的主體。通過繪
制、復制及調整矩形圖像區域的大小,我們可以對正在跟蹤的圖像區
域執行簡單的操作。
本章將介紹以下主題:
·理解Haar級聯。
·找到OpenCV自帶的預訓練Haar級聯,包括了一些人臉檢測器。
·利用Haar級聯檢測靜態圖像和視頻中的人臉。
·采集圖像訓練和測試人臉檢測器。
·使用多種不同的人臉識別算法:Eigenface、Fisherface以及局部
二值模式直方圖(Local Binary Pattern Histogram,LBPH)。
·將矩形區域從一幅圖像復制到另一幅圖像,即可包括也可不包
括掩模。
·使用深度攝像頭基於深度區分人臉和背景。
·在交互式應用程序中交換兩個人的臉。
本章結束時,我們將人臉跟蹤和矩形操作集成到我們在前幾章中
開發的交互式應用程序Cameo中。最後,我們將會有一些人臉到人臉的
交互!
5.1 技術需求
本章使用了Python、OpenCV以及NumPy。作為OpenCV的一部分,使
用了可選的opencv_contrib模塊,其中包括人臉識別功能。本章的某
些部分使用了OpenCV對OpenNI 2的可選支持來從深度攝像頭捕捉圖
像。安裝說明請參閱第1章。
本章的完整代碼可以在本書的GitHub庫
(https://github.com/PacktPublishing/Learning-OpenCV-4-
Computer-Vision-with-Python-Third-Edition)的chapter05文件夾
中找到。示例圖像在本書GitHub庫的images文件夾中。
5.2 Haar級聯的概念化
在談到分類物體並跟蹤其位置時,我們到底想要探究什麼呢?構
成物體的可識別部分的是什麼?
即使來自網絡攝像頭的攝影圖像,也可能包含很多賞心悅目的細
節。但是,因為光線、視角、視覺距離、攝像頭抖動和數字噪聲的變
化,圖像細節往往不穩定。此外,即使物理細節上的真實差異也不可
能會引起我們對分類的興趣。約瑟夫·豪斯(本書作者之一)在學校
學過,在顯微鏡下,沒有兩片雪花看起來是一樣的。幸運的是,作為
一個加拿大的孩子,他已經學會了不用顯微鏡就能識別雪花,因為雪
花在整體上的相似之處更明顯。
因此,抽象圖像細節的一些方法有助於產生穩定的分類和跟蹤結
果。這些抽象稱為特征,據說是從圖像數據中抽象的。盡管任何像素
都可能影響多個特征,但是特征應該比像素少。把一組特征表示為一
個向量,可以根據圖像的對應特征向量之間的距離來度量兩幅圖像之
間的相似程度。
類Haar特征是應用於實時人臉檢測的常用特征之一。在論文
“Robust Real-Time Face Detection”(International Journal of
Computer Vision 57(2),137–154,Kluwer Academic
Publishers,2001)中,作者Paul Viola和Michael Jones首次將類
Haar特征用於人臉檢測。可以在
http://www.vision.caltech.edu/html-files/EE148-2005-
Spring/pprs/viola04ijcv.pdf處找到這篇論文的電子版。每個類Haar
特征描述了相鄰圖像區域之間的對比度模式。例如,邊、頂點和細線
都生成了一種特征。有些特征是獨特的,因為這些特征通常出現在某
一類對象(如人臉)上,而不會出現在其他對象上。可以把這些特征
組織成一個層次結構,稱為級聯,其中最高層包含最顯著的特征,使
分類器能夠快速拒絕缺乏這些特征的主體。
對於任意給定的主體,特征可能會根據圖像大小和正在評估對比
度的鄰域大小而有所不同。正在評估對比度的鄰域大小稱為窗口大
小。為使Haar級聯分類器尺度不變,或者對尺度變化具有魯棒性,窗
口大小應保持不變,但是將圖像重新縮放多次,因此在某種程度上縮
放時對象(如人臉)大小可能匹配窗口的大小。原始圖像和縮放圖像
一起稱為圖像金字塔,圖像金字塔中的每個連續的層都是一幅更小的
縮小圖像。OpenCV提供了一個尺度不變的分類器,可以以一種特定的
格式從XML文件加載級聯分類器。這個分類器在內部將任意給定的圖像
轉換為圖像金字塔。
用OpenCV實現的Haar級聯分類器對旋轉角度或者透視圖的變化並
不魯棒。例如,認為倒立的人臉和正立的人臉不一樣,認為側面看的
臉和正面看的臉不一樣。通過考慮圖像的多種變換以及多個窗口大
小,更復雜、資源更密集的實現可以提升Haar級聯對旋轉角度的魯棒
性。但是,我們將只介紹OpenCV的實現。
5.3 獲取Haar級聯數據
OpenCV 4源代碼或者安裝的OpenCV 4預包構建,應該包含名為
data/haarcascades的子文件夾。如果無法找到這個文件夾,請回到第
1章獲取OpenCV 4的源代碼說明。
data/haarcascades文件夾包含可以由名為
cv2.CascadeClassifier的OpenCV類加載的XML文件。該類的實例把給
定的XML文件解釋為Haar級聯,為某種類型的物體(如人臉)提供一個
檢測模型。cv2.CascadeClassifier可以檢測任意圖像中的這種類型的
物體。通常,我們可以從文件中獲取靜態圖像,或者從視頻文件或視
頻攝像頭獲取一系列幀。
找到data/haarcascades後,在其他地方為項目創建一個目錄。在
這個文件夾中創建名為cascades的子文件夾,把下面的文件從
data/haarcascades復制到cascades:
顧名思義,這些級聯就是用來跟蹤臉和眼睛的。它們需要是觀察
對象正面、直立的視圖。稍後,在建立人臉檢測器時將會用到這些級
聯。
如果你想知道如何生成這些級聯文件,請參閱約瑟夫·豪斯
的OpenCV 4 for Secret Agents [1] (原書於2019年由Packt出版社出版)中
第3章。只要有足夠的耐心和一台強大的計算機,你就可以自己創建級
聯,並針對各種類型的對象對創建的級聯進行訓練。
[1] 本書第2版的中文版《OpenCV項目開發實戰(原書第2版)(ISBN
978-7-111-65234-2)已於2020年由機械工業出版社出版。——編輯注
5.4 使用OpenCV進行人臉檢測
不論是在靜態圖像還是在視頻回傳信號上進行人臉檢測,
cv2.CascadeClassifier幾乎沒有任何區別。視頻只是連續的靜態圖
像:視頻中的人臉檢測只是將人臉檢測應用於每一幀。自然,有了更
先進的技術,就可以在多幀中連續跟蹤檢測到的人臉,並確定每一幀
中的人臉是否相同。但是,最好知道基本的順序方法也是有效的。
我們來檢測一些人臉。
5.4.1 在靜態圖像上進行人臉檢測
進行人臉檢測的第一個也是最基本的方法是加載一幅圖像並檢測
其中的人臉。為了使結果在視覺上有意義,我們將在原始圖像中的人
臉周圍繪制矩形。請記住,人臉檢測器是針對直立的正面人臉設計
的,我們將使用有多人(伐木工)站成一排的圖像,他們肩並肩站
立、面對鏡頭。
將Haar級聯XML文件復制到級聯文件夾後,我們創建下列基本腳本
來執行人臉檢測:
我們逐步浏覽一下前面的代碼。首先,使用必要的cv2導入,本書
的每個腳本都有這個導入。然後,聲明一個face_cascade變量,這是
一個CascadeClassifier對象,用於加載人臉檢測級聯:
然後,用cv2.imread加載圖像文件,將其轉換成灰度圖像,因為
Cascade Classifier需要灰度圖像。下一步,用
face_cascade.detectMultiScale進行實際的人臉檢測:
detectMultiScale的參數包括scaleFactor和minNeighbors。
scaleFactor參數應該大於1.0,確定人臉檢測過程中每次迭代時圖像
的降尺度比率。正如5.2節介紹的,這種降尺度的目的是通過把不同的
人臉與窗口大小匹配實現尺度不變性。minNeighbors參數是為了保留
檢測結果所需要的最小重疊檢測次數。通常,我們期望可以在多個重
疊窗口中檢測到某人臉,更多的重疊檢測使我們更加確信檢測到的人
臉是一個真正的人臉。
從檢測操作返回的值是一個表示人臉矩形的元組列表。OpenCV的
cv2.rectangle函數允許我們在指定的坐標處繪制矩形。x和y分別表示
左邊坐標和頂部坐標,w和h分別表示人臉矩形的寬度和高度。通過循
環遍歷faces變量,在所有人臉周圍繪制藍色矩形,請確保使用的是原
始圖像,而不是灰度圖像:
最後,調用cv2.imshow顯示處理後的圖像。通常,為了防止圖像
窗口自動關閉,插入一個對waitKey的調用,當用戶按下任意鍵時返
回:
好了,我們在圖像中檢測到一群伐木工,如圖5-1所示。
這個例子中的照片是謝爾蓋·普羅庫金–戈爾斯基(Sergey
Prokudin-Gorsky)(1863—1944)的作品,普羅庫金–戈爾斯基是彩色
攝影的先驅。沙皇尼古拉二世讓普羅庫金–戈爾斯基拍攝俄羅斯帝國
的人物和地點,將其作為一個龐大的紀錄片項目。1909年,普羅庫金–
戈爾斯基在俄羅斯西北部的絲薇爾河附近拍攝了這些伐木工人。
5.4.2 在視頻上進行人臉檢測
現在,我們了解了如何在靜態圖像上進行人臉檢測。如前所述,
我們可以在視頻(攝像頭回傳信號或者預先錄制的視頻文件)的每一
幀上重復人臉檢測的過程。
下一個腳本將打開一個攝像頭回傳信號,讀取一幀,檢查該幀中
的人臉,並在檢測到的人臉內掃描眼睛。最後,在人臉周圍繪制藍色
的矩形,在眼睛周圍繪制綠色的矩形。以下是完整的腳本:
我們將上面的例子分解成更小、更容易理解的部分:
(1)像往常一樣,導入cv2模塊。之後,初始化兩個
CascadeClassifier對象,一個用於人臉,另一個用於眼睛:
(2)就像大多數交互式腳本一樣,打開一個攝像頭回傳信號,開
始迭代幀。繼續,直到用戶按下某個鍵。當成功捕捉到一幀時,將其
轉換為灰度作為處理的第一步:
(3)利用人臉檢測器的detectMultiScale方法對人臉進行檢測。
正如之前所述,我們使用了scaleFactor和minNeighbors參數。我們還
使用minSize參數指定了人臉的最小尺寸,具體為120×120,因此不會
嘗試去檢測比這個尺寸小的臉。(假設用戶坐在攝像頭附近,可以有
把握地說,圖像中用戶的臉將大於120×120像素。)以下是對
detectMultiScale的調用:
(4)迭代檢測到的人臉。在原始彩色圖像的每個矩形周圍繪制一
個藍色邊界。然後,在灰度圖像的同一個矩形區域內進行眼睛檢測:
眼睛檢測器的准確率比人臉檢測器要低一些。你可能會看到
陰影、部分鏡框或其他被誤認為是眼睛的人臉區域。為了改善結果,
可以嘗試將roi_gray定義為人臉的一個較小區域,因為我們很容易猜測
到眼睛在直立人臉中的位置。還可以試著使用maxSize參數來避免那些
太大不可能是眼睛的誤報。此外,可以調整minSize和maxSize,使尺寸
與w和h(即檢測到的人臉大小)成比例。作為一個練習,你可以隨意
更改這些參數和其他參數。
(5)循環遍歷生成的眼睛矩形,並在其周圍繪制綠色輪廓:
(6)最後,在窗口中顯示生成的幀:
運行腳本。如果檢測器產生了准確的結果,而且如果有任何人臉
在攝像頭的視野內,應該會看到人臉周圍有一個藍色的矩形,眼睛周
圍有一個綠色的矩形,如圖5-2所示。
圖5-2 腳本運行結果
用此腳本進行實驗,研究人臉和眼睛檢測器在不同條件下的表
現。試著在更亮或更暗的房間進行。如果戴著眼鏡,試著摘掉眼鏡再
進行一次。嘗試在不同人臉和不同的表情下進行。調整腳本中的檢測
參數,看看這些參數對結果的影響。當你感到滿意時,我們再來考慮
在OpenCV中還可以做些什麼。
5.4.3 進行人臉識別
人臉檢測是OpenCV的一個非常棒的特性,也是構成更高級的操作
——人臉識別——的基礎。什麼是人臉識別?人臉識別是程序在給出
包含人臉的圖像或視頻時識別此人的能力。實現這一目標的方法之一
(也是OpenCV所采用的方法)是通過為程序提供一組分類圖片(人臉
數據庫)來訓練程序,然後根據這些圖片的特征進行識別。
OpenCV人臉識別模塊的另一個重要特征是,每次識別都有一個置
信度,這允許我們在實際應用程序中設置阈值以限制錯誤識別的發生
率。
讓我們從頭開始。為了進行人臉識別,我們需要待識別的人臉。
人臉可以通過兩種方式來獲取:自己提供圖像或者獲取免費的人臉數
據庫。在http://www.face-rec.org/databases/上有一個大的人臉數
據庫在線可用。以下是其中幾個著名的例子:
·耶魯大學人臉數據庫(Yalefaces),網址為
http://vision.ucsd.edu/content/yale-face-database。
·擴展的耶魯大學人臉數據庫B,網址為
http://vision.ucsd.edu/content/extended-yale-face-database-b-b。
·人臉數據庫(來自劍橋AT&T實驗室),網址為
http://www.cl.cam.ac.uk/research/dtg/attarchive/facedatabase.html。
為了對這些樣本進行人臉識別,我們必須對包含被采樣人的人臉
圖像進行人臉識別。這個過程可能是有教學意義的,但是可能不如提
供我們自己的圖像那樣令人滿意。許多計算機視覺學習者有同樣的想
法:是否可以編寫一個程序來識別自己的臉並且有一定的置信度。
1.生成人臉識別數據
我們來編寫生成這些圖像的腳本。包含不同表情的圖像正是我們
所需要的,但是訓練圖像最好是正方形的,並且大小相同。我們的示
例腳本要求圖像的大小為200×200,但是大多數免費可用的數據集的
圖像都比這個尺寸小。
下面是腳本:
在這裡,我們利用新掌握的如何在視頻中檢測人臉的知識來生成
樣本圖像。我們檢測人臉,裁剪灰度轉換幀的人臉區域,將其大小調
整為200×200像素,然後保存為PGM文件,並在特定文件夾中指定名稱
(在本例中為jm,其中一個作者姓名的首字母,你可以使用自己姓名
的首字母)。與許多窗口應用程序一樣,程序會一直運行,直到用戶
按下某個鍵。
之所以出現count變量是因為我們需要圖像的累加名稱。運行腳本
幾秒鐘,更改幾次臉部表情,並檢查腳本中指定的目標文件夾。運行
腳本幾秒鐘,改變面部表情幾次,檢查你在腳本中指定的目標文件
夾。你會發現很多你的臉部圖像,它們變為了灰度版本,調整了大
小,並用<count>.pgm格式命名。
修改output_folder變量,使其與你的名字匹配。例如,你可以選
擇'../data/at/my_name'。運行腳本,等待它在許多幀(比如20幀或
更多)中檢測到你的臉,然後按任意鍵退出。現在,再次修改
output_folder變量,使其與你想要識別的一個朋友的名字匹配。例
如,你可以選擇'../data/at/name_of_my_friend'。不改變文件夾的
基本部分(本例中為'../data/at'),因為在後面“加載人臉識別的
訓練數據”部分,我們將編寫代碼從這個基本文件夾的所有子文件夾
中加載訓練圖像。讓你的朋友坐在攝像頭前,再次運行腳本,讓它在
許多幀中檢測你朋友的臉,然後退出。對你想識別的其他任何人重復
這個過程。
現在,我們繼續嘗試在視頻回傳信號中識別用戶的臉。這會很好
玩!
2.識別人臉
OpenCV 4實現了3種不同的人臉識別算法:特征臉
(Eigenface)、Fisherface和局部二值模式直方圖(Local Binary
Pattern Histogram,LBPH)。特征臉和Fisherface來源於一個名為主
成分分析(Principal Component Analysis,PCA)的通用算法。有關
算法的詳細描述,請參見以下鏈接:
·PCA:Jonathon Shlens在http://arxiv.org/pdf/1404.1100v1.pdf上提
供了一個直觀的介紹。該算法是由卡爾·皮爾森(Karl Pearson)在
1901年發明的,並且原創論文“On Lines and Planes of Closest Fit to
Systems of Points in Space”網址為http://pca.narod.ru/pearson1901.pdf。
·Eigenface:見論文“Eigenfaces for Recognition”(1991)(作者
是Matthew Turk和Alex Pentland),網址為
http://www.cs.ucsb.edu/~mturk/Papers/jcn.pdf。
·Fisherface:開創性論文“The Use of Multiple Measurements in
Taxonomic Problems”(1936)(作者是R.A.Fisher),網址為
http://onlinelibrary.wiley.com/doi/10.1111/j.1469-
1809.1936.tb02137.x/pdf。
·局部二值模式:介紹該算法的第一篇論文是“Performance
evaluation of texture measures with classification based on Kullback
discrimination of distributions”(1994)(作者是T.Ojala、M.Pietikainen和
D.Harwood),網址為https://ieeexplore.ieee.org/document/576366。
就本書而言,我們對這些算法做一個概述。首先,這些算法都遵
循一個相似的過程:進行一組分類觀察(我們的人臉數據庫,每個人
都包含大量樣本),在此基礎上訓練一個模型,對臉部圖像(這可能
是我們在圖像或視頻中檢測到的臉部區域)進行分析並確定兩件事
——主體的身份以及對這個身份是正確的信心的度量。後者通常被稱
為置信度。
特征臉算法執行主成分分析(PCA),識別某一組觀察數據(同樣
是人臉數據庫)的主成分,計算當前觀測值(圖像或幀中檢測到的人
臉)相對於數據集的散度,並產生一個值。該值越小,人臉數據庫與
檢測到的人臉之間的差異就越小,因此值為0表示精確匹配。
Fisherface也是從主成分分析(PCA)衍生的,並應用了更復雜的
邏輯。雖然計算更密集,但是產生的結果往往比特征臉算法更准確。
相反,LBPH將檢測到的人臉分成小單元格,並為每個單元格建立
一個直方圖,描述在比較給定方向的鄰域像素時圖像的亮度是否在增
加。這個單元格的直方圖可以與模型中相應的單元格進行比較,從而
產生相似性度量。在OpenCV的人臉識別器中,LBPH的實現是唯一允許
模型樣本人臉和檢測到的人臉具有不同形狀、不同大小的人臉識別
器。因此,它很方便,本書的作者發現該算法的准確度優於其他兩個
算法。
3.加載人臉識別的訓練數據
無論選擇什麼樣的人臉識別算法,我們都可以用同樣的方式加載
訓練圖像。在前面的“生成人臉識別數據”部分中,我們生成了訓練
圖像,並將它們保存在根據人名或人名首字母進行組織的文件夾中。
例如,下面的文件夾結構可以包含本書作者Joseph Howse(J.H.)和
Joe Minichino(J.M.)的臉部圖像樣本:
我們編寫一個腳本加載這些圖像,並以一種OpenCV的人臉識別器
能夠理解的方式對它們進行標簽。要處理文件系統和數據,我們將使
用Python標准庫的os模塊以及cv2和numpy模塊。我們創建以下面
import語句開頭的腳本:
我們添加以下read_images函數,該函數可以遍歷目錄的子目錄,
加載圖像,將這些圖像調整為指定的大小,並將調整後的圖像放入一
個列表中。同時,該函數還構建了另外兩個列表:第一個是人名或人
名首字母列表(基於子文件夾名稱),第二個是與加載的圖像相關聯
的標簽列表或數字ID列表。例如,jh是一個名稱,0可以是從jh子文件
夾加載的所有圖像的標簽。最後,該函數將圖像和標簽列表轉換為
NumPy數組,並返回3個變量:名稱列表、圖像的NumPy數組和標簽的
NumPy數組。以下是該函數的實現:
通過添加如下代碼調用read_images函數:
編輯之前代碼塊中的path_to_training_images變量,以確保該變
量與之前“生成人臉識別數據”部分的代碼中定義的output_folder變量
的基本文件夾相匹配。
到目前為止,我們已經有了有用格式的訓練數據,但是還沒有創
建人臉識別器,也沒有進行任何訓練。我們將在接下來的內容中完成
這些任務,同時將繼續實現同一腳本。
4.基於特征臉進行人臉識別
既然有了訓練圖像數組及其標簽數組,只用兩行代碼就可以創建
和訓練一個人臉識別器:
我們在這裡做了什麼?我們使用OpenCV的
cv2.EigenFaceRecognizer_create函數創建特征臉人臉識別器,通過
傳遞圖像數組和標簽(數字ID)數組訓練識別器。我們還可以選擇將
兩個參數傳遞給cv2.EigenFaceRecognizer_create:
·num_components:這是PCA需要保留的主成分量數。
·threshold:這是一個浮點值,指定置信度阈值。丟棄置信度低於
阈值的人臉。默認情況下,該阈值是最大浮點值,因此不會丟棄任何
人臉。
為了測試識別器,我們使用一個人臉檢測器和一個攝像頭的回傳
視頻信號。正如在前面的腳本中所做的那樣,我們可以使用以下代碼
來初始化人臉檢測器:
下面的代碼初始化攝像頭回傳信號,遍歷幀(直到用戶按下任意
鍵)並對每一幀進行人臉檢測和識別:
我們來看看前面代碼塊中最重要的功能。對於每個檢測到的人
臉,我們對它進行轉換並調整它的大小,以便獲得匹配預期大小的灰
度版本(在本例中,預期大小是“加載人臉識別的訓練數據”小節中
training_image_size變量定義的200×200像素)。然後,將調整後的
灰度人臉傳遞給人臉識別器的predict函數。該函數將返回一個標簽和
置信度。我們查找對應於這張臉的數字標簽的人名。(請記住,我們
在“加載人臉識別的訓練數據”中創建了names數組。)我們在識別的
人臉上方用藍色文本給出姓名和置信度。在遍歷所有檢測到的人臉之
後,顯示帶注釋的圖像。
我們采用了一種簡單的方法進行人臉檢測和識別,其目的是
讓你能夠運行一個基本應用程序,並理解OpenCV 4中的人臉識別過
程。要想改進方法並使其更加魯棒,可以采取進一步的步驟,例如,
正確地對准並旋轉檢測到的人臉從而最大限度地提高識別准確度。
運行腳本,你應該會看到類似於圖5-3的效果。
接下來,我們將考慮如何調整這些腳本,用其他人臉識別算法來
替換特征臉。
5.基於Fisherface進行人臉識別
如何基於Fisherface進行人臉識別?這個過程與上述過程並無多
大變化,只需要實例化一個不同的算法。使用默認參數,model變量的
聲明如下:
cv2.face.FisherFaceRecognizer_create接受與
cv2.createEigenFace-Recognizer_create相同的兩個可選參數:要保
留的主成分數量和置信度阈值。
6.基於LBPH進行人臉識別
最後,我們快速了解一下LBPH算法。同樣,該過程也類似上述過
程。可是,算法工廠接受以下可選參數(按順序):
·radius:用於計算單元格直方圖的鄰域之間的像素距離(默認為
1)。
·neighbors:用於計算單元格直方圖的鄰域數(默認為8)。
·grid_x:水平分割人臉的單元格數量(默認為8)。
·grid_y:垂直分割人臉的單元格數量(默認為8)。
·confidence:置信度阈值(默認情況下,為可能的最高浮點值,
這樣就不會丟棄任何結果)。
使用默認參數,model聲明如下所示:
請注意,使用LBPH,我們不需要調整圖像的大小,因為劃分
為網格允許對每個單元格中識別的模式進行比較。
7.基於置信度丟棄結果
predict方法返回一個元組,其中第一個元素是識別到的個體的標
簽,第二個元素是置信度。所有的算法都帶有設置置信度阈值的選
項,該阈值測量識別到的人臉與原始模型的匹配程度,因此,分值為0
表示完全匹配。
在某些情況下,你可能寧願保留所有的識別結果,再應用進一步
的處理,這樣你就可以提出自己的算法來估計識別結果的置信度。例
如,如果你嘗試著識別視頻中的人,你可能希望分析隨後幀中的置信
度,以確定是否識別成功。在這種情況下,你可以查看算法獲得的置
信度,並得出自己的結論。
置信度典型的取值范圍取決於算法。特征臉和Fishface產生的
值的范圍(大致)在0~20 000,低於4000的所有分值都表示是一個相
當有信心的識別結果。對於LBPH,好的識別結果的參考值低於50,所
有超過80的值都被認為是糟糕的置信度。
常規的自定義方法是,直到有足夠多的具有令人滿意的任意置信
度的幀數時,才在識別到的人臉周圍繪制矩形,但是你完全可以使用
OpenCV的人臉識別模塊來根據需要定制應用程序。
5.5 在紅外線下換臉
人臉檢測和識別並不局限於可見光譜。在近紅外(Near-
Infrared,NIR)攝像頭和近紅外光源下,即使在人眼看來是全黑的場
景中,人臉檢測和識別也是可能的。這個功能在安全和監視應用程序
中非常有用。
在第4章中,我們研究了華碩Xtion PRO等近紅外深度攝像頭的基
本用法。我們擴展了交互式應用程序Cameo的面向對象代碼。我們用深
度攝像頭拍攝了一些畫面。根據深度,我們將每一幀分割成一個主層
(如用戶的臉)和其他層。把其他圖層塗成黑色,這樣就達到了隱藏
背景的效果,使得交互視頻信號中只有主層(用戶的臉)出現在屏幕
上。
現在,我們修改Cameo,練習之前的深度分割技能以及人臉檢測的
新技能。我們來檢測人臉,當同一幀中檢測到至少兩張人臉時,交換
這兩張臉,使一個人的頭出現在另一個人的身體上。我們不會復制檢
測到的人臉矩形中的所有像素,而是只復制該矩形主深度層的一部分
像素。這應該實現了換臉的效果,而不交換臉周圍的背景像素。
一旦完成修改,Cameo將能夠產生類似圖5-4的輸出。
我們看到約瑟夫·豪斯的臉和他母親珍妮特·豪斯(Janet
Howse)的臉互換了。盡管Cameo是從矩形區域復制像素(在前景中,
交換區域的底部清晰可見),一些背景像素沒有交換,所以我們不會
看到所有地方都有矩形邊緣。
你可以在https://github.com/PacktPublishing/Learning-
OpenCV-4-Computer-Vision-with-Python-Third-Edition處找到本書
庫中對Cameo源代碼的所有相關更改,特別是在chapter05/cameo文件
夾中的修改。為簡單起見,我們不會在本書中討論所有的修改,但是
將在接下來的兩個小節中討論一些重點內容。
5.5.1 修改應用程序的循環
為了支持換臉功能,Cameo項目有兩個新模塊,名為rects和
trackers。rects模塊包含用於復制和交換矩形的函數,帶有一個可選
的掩模,可將復制或交換操作限制在特定像素。trackers模塊包含一
個名為FaceTracker的類,它使OpenCV的人臉檢測功能適應面向對象的
編程風格。
因為我們已經在本章前面介紹了OpenCV的人臉檢測功能,並且已
經在前面的章節中演示了面向對象的編程風格,所以這裡不再討論
FaceTracker的實現。你可以在本書的庫中查看FaceTracker的實現。
打開cameo.py,這樣就可以浏覽對應用程序的全部更改:
(1)在文件的頂部,需要導入新的模塊,如下列代碼塊中的粗體
所示:
(2)現在,我們把注意力轉向對CameoDepth類的__init__方法的
修改。更新後的應用程序使用了一個FaceTracker實例。作為其功能的
一部分,FaceTracker可以在檢測到的人臉周圍繪制矩形。我們給
Cameo的用戶一個選項來啟用或禁用臉部矩形的繪制。我們將通過一個
布爾變量跟蹤當前選擇的選項。下面的代碼塊顯示了初始化
FaceTracker對象和布爾變量所需的更改(以粗體顯示):
我們在CameoDepth的run方法中使用了FaceTracker對象,該方法
包含捕獲和處理幀的應用程序主循環。每成功捕獲一幀,就調用
FaceTracker的方法來更新人臉檢測結果,並獲取最新檢測到的人臉。
然後,對於每張臉,根據深度攝像頭視差圖創建掩模。(在第4章中,
我們針對整個圖像創建了一個這樣的掩模,而不是針對每個人臉矩形
創建掩模。)然後,調用函數rects.swapRects來執行人臉矩形的掩模
交換。(稍後,我們將在5.5.2節中討論swapRects的實現。)
(3)根據當前選擇的選項,我們可能會讓FaceTracker在人臉周
圍繪制矩形。所有相關的更改在下面的代碼塊中以粗體顯示:
(4)最後,修改onKeypress方法以便用戶可以按X鍵開始或停止
顯示檢測到的人臉周圍的矩形。同樣,相關的更改在下面的代碼塊中
以粗體顯示:
接下來,我們來看在本節前面導入的rects模塊的實現。
5.5.2 掩模復制操作
rects模塊是在rects.py中實現的。在5.5.1節中,我們已經看到
對rects.swapRects函數的一個調用。可是,在考慮swapRects的實現
之前,我們首先需要一個更基本的copyRect函數。
回到第2章,我們學習了如何使用NumPy的切片語法將數據從一個
感興趣的矩形區域復制到另一個感興趣的矩形區域。在感興趣的矩形
區域之外,源和目標圖像不受影響。現在,我們想進一步限制這個復
制操作。我們想要使用一個與源矩形相同大小的給定掩模。
我們將只復制源矩形中掩模值不為零的那些像素。其他像素應保
留目標圖像的原始值。這個邏輯包含一個條件數組和兩個可能的輸出
值數組,可以用numpy.where函數簡潔地表達。
牢記這種方法,我們來考慮copyRect函數。它接受一幅源圖像和
目標圖像、一個源矩形和目標矩形,以及一個掩模作為參數。後者可
能是None,在這種情況下,只需調整源矩形的內容大小以匹配目標矩
形,然後將調整後的內容分配給目標矩形即可。否則,接下來就要確
保掩模和圖像有相同的通道數。假設掩模有一個通道,但是圖像可能
有三個通道(BGR)。我們可以使用numpy.array的repeat和reshape方
法將重復通道添加到掩模中。最後,使用numpy.where執行復制操作。
完整的實現如下: