選自pyimagesearch

  者: Adrian Rosebrock

  機器之心編譯

  參與:李詩萌、張倩

  照片、視頻中的人臉有時也能騙過一些不成熟的人臉識別系統,讓人們對人臉解鎖的安全性產生很大懷疑。在這篇 4 千多字的教程中,作者介紹瞭如何用 OpenCV 進行活體檢測(liveness detection)。跟隨作者給出的代碼和講解,你可以在人臉識別系統中創建一個活體檢測器,用於檢測僞造人臉並執行反人臉欺騙。

  我在過去的一年裏寫了不少人臉識別的教程,包括:

  penCV 人臉識別

  用 dlib、Python 和深度學習進行人臉識別

  用樹莓派實現人臉識別

  https://www.pyimagesearch.com/2018/09/24/opencv-face-recognition/

  https://www.pyimagesearch.com/2018/06/18/face-recognition-with-opencv-python-and-deep-learning/

  https://www.pyimagesearch.com/2018/06/25/raspberry-pi-face-recognition/

  但在我的郵件和人臉識別相關帖子下面的評論中經常會出現以下問題:

  我該如何識別真假人臉呢?

  想想如果有壞人試圖攻破你的人臉識別系統會發生什麼?

  這樣的用戶可能會拿到另一個人的照片。甚至可能他們的手機上就有其他人的照片或視頻,他們可以用這樣的照片或視頻來欺騙識別人臉的相機(就像本文開頭的圖片那樣)。

  在這種情況下,照相機完全有可能將其識別爲正確的人臉,從而讓未經授權的用戶騙過人臉識別系統!

  如何識別這些真假人臉呢?如何在人臉識別應用中使用反人臉欺騙算法?

  答案是用

  OpenCV

  實現活體檢測——這也是我今天要介紹的內容。

  要了解如何用 OpenCV 將活體檢測結合到你自己的人臉識別系統中,請繼續往下讀。

  你可以在文末的下載部分下載源代碼:

  https://www.pyimagesearch.com/2019/03/11/liveness-detection-with-opencv/#

  用 OpenCV 實現活體檢測

  本教程第一部分將討論什麼是活體檢測以及爲什麼要藉助活體檢測提升我們的人臉識別系統。

  從這裏開始要先研究一下用於活體檢測的數據集,包括:

  如何構建活體檢測的數據集?

  真假面部圖像的樣例。

  我們還將回顧用於活體檢測器項目的項目結構。

  爲了創建活體檢測器,我們要訓練一個能分辨真假人臉的深度神經網絡。

  因此,我們還需要:

  構建圖像數據集;

  實現可以執行活體檢測的 CNN(我們將這個網絡稱爲「LivenessNet」);

  訓練活體檢測器網絡;

  創建一個 Python+OpenCV 的腳本,可以通過該腳本使用我們訓練好的活體檢測器模型,並將其應用於實時視頻。

  那我們就開始吧!

  什麼是活體檢測?我們爲什麼需要活體檢測?

  圖 1:用 OpenCV 進行活體檢測。左圖是我的實時(真實)視頻,而右圖中我拿着自己的 iPhone(欺騙)。

  人臉識別系統與以往任何時候相比都更加普遍。從 iPhone(智能手機)中的人臉識別,到中國大規模監控中的人臉識別,人臉識別系統的應用無處不在。

  但人臉識別系統也很容易被「僞造」和「不真實」的面部所欺騙。

  在面部識別相機前拿着一個人的照片(無論是印出來的還是手機上的)可以輕而易舉地騙過人臉識別系統。

  爲了讓人臉識別系統更加安全,我們需要檢測出這樣僞造的面部——活體檢測(術語)指的就是這樣的算法。

  活體檢測的方法有很多,包括:

  紋理分析(Texture analysis),該方法計算了面部區域的局部二值模式(Local Binary Patterns,LBP),用 SVM 將面部分爲真實面部和僞造面部;

  頻率分析(Frequency analysis),比如檢查面部的傅立葉域;

  可變聚焦分析(Variable focusing analysis),例如檢查連續兩幀間像素值的變化;

  啓發式算法(Heuristic-Based algorithms),包括眼球運動、嘴脣運動和眨眼檢測。這些算法試圖追蹤眼球運動和眨眼行爲,來確保用戶不是拿着誰的照片(因爲照片不會眨眼也不會動嘴脣);

  光流算法(Optical Flow algorithm),即檢測 3D 對象和 2D 平面產生的光流的屬性和差異;

  3D 面部形狀(3D face shape),類似於 iPhone 上的面部識別系統,這種算法可以讓面部識別系統區分真實面部和其他人的照片或打印出來的圖像;

  結合以上算法,這種方法可以讓面部識別系統工程師挑選適用於自己應用的活體檢測模型。

  Chakraborty 和 Das 2014 年的論文(《An Overview of Face liveness Detection》)對活體檢測算法做了全面的綜述。

  我們在本教程中將活體檢測視爲一個二分類問題。

  給定輸入圖像,我們要訓練一個能區分真實面部和僞造面部的卷積神經網絡(Convolutional Neural Network)。

  但在訓練活體檢測模型之前,我們要先檢查一下數據集。

  我們的活體檢測視頻

  圖 2:真實面部和僞造面部的樣例。左邊的視頻是我的面部的真實視頻,右邊是在播放同樣的視頻時筆記本錄製的視頻。

  爲了讓例子更直觀,本文建立的活體檢測器側重於區分真實面部和屏幕上的僞造面部。

  這一算法可以輕易擴展到其他類型的僞造面部上,比如打印輸出的僞造面部和高分辨率輸出的僞造面部等。

  爲了建立活體檢測數據集,我做了下列工作:

  拿着我的 iPhone,將它設置爲人像或自拍模式;

  錄製約 25 秒我在辦公室裏來回走的視頻;

  重播這段 25 秒的視頻,這次用我的 iPhone 對着錄製了重播視頻的電腦;

  這樣就產生了兩段樣例視頻,一段用於「真實」面部,一段用於「僞造」面部;

  最後,我在這兩段視頻上都用了人臉檢測,爲這兩類提取出單獨的面部 ROI(Reign of Interest)。

  我在本文的「下載」部分提供了真實面部和僞造面部的視頻文件。

  你可以將這些視頻作爲數據集的起點,但我建議你多收集一些數據,這可以讓你的活體檢測器更魯棒也更安全。

  通過測試,我確定模型有些偏向我的臉,這是意料之中的結果,因爲所有的模型都是基於我的面部訓練出來的。此外,由於我是白人(高加索人),所以如果數據集中有其他膚色或其他人種的面部時,這個模型效果會沒有那麼好。

  在理想情況下,你應該用不同膚色和不同人種的面部來訓練模型。請參考本文的「限制和後續工作」部分,來了解其他改善活體檢測模型的建議。

  你將在本教程剩下的部分學習如何獲取我錄製的數據集以及如何將它實際應用於通過 OpenCV 和深度學習建立的活體檢測器。

  項目結構

  你可以通過本教程的「Downloads」部分下載代碼、數據和活體模型,然後解壓縮存檔。

  進入項目目錄後,你能看到這樣的結構:

  目錄中有四個主目錄:

  dataset/:我們的數據集目錄中包含兩類圖像:

  1. 在播放我的面部視頻時通過錄制屏幕得到的僞造圖像;

  2. 手機直接拍攝我的面部視頻得到的真實圖像。

  face_detector/:由預訓練的 Caffe 面部檢測器組成,用來定位面部 ROI;

  Pyimagesearch/:該模塊包含了 LivenessNet 類;

  videos/:這裏提供了兩段用於訓練 LivenessNet 分類器的輸入視頻。

  今天我們會詳細地學習三個 Python 腳本。在文章結束後,你可以在自己的數據和輸入視頻上運行這三個腳本。按在教程中出現的順序,這三個腳本分別是:

  1. gather_examples.py:這個腳本從輸入的視頻文件中提取了面部 ROI,幫助我們創建了深度學習面部活體數據集;

  2. train_liveness.py:如文件名所示,這個腳本用來訓練 LivenessNet 分類器。我們將用 Keras 和 TensorFlow 訓練模型。在訓練過程中會產生一些文件:

  le.pickle:分類標籤編碼器。

  liveness.model: 可以檢測面部活性的序列化 Keras 模型。

  plot.png:訓練歷史圖呈現了準確率和損失曲線,我們可以根據它來評估模型(是否過擬合或欠擬合。)

  3. liveness_demo.py:演示腳本,它會啓動你的網絡攝像頭抓取幀,可以進行實時的面部活體檢測。

  從訓練(視頻)數據集中檢測並提取面部 ROI

  圖 3:爲了構建活體檢測數據集,在視頻中檢測面部 ROI。

  現在有機會看到初始數據集和項目結構了,讓我們看看該如何從輸入視頻中提取出真實面部圖像和僞造面部圖像吧。

  最終目標是用這個腳本填充兩個目錄:

  dataset/fake/:fake.mp4 中的面部 ROI;

  dataset/real/:real.mov 中的面部 ROI。

  根據這些幀,我們後續將在這些圖像上訓練基於深度學習的活體檢測器。

  打開 gataer_examples.py,插入下面的代碼:

  2~5 行導入了我們需要的包。除了內置的 Python 模塊外,該腳本只需要 OpenCV 和 NumPy。

  8~19 行解析了命令行參數:

  --input:輸入視頻文件的路徑

  --output:輸出目錄的路徑,截取的每一張面部圖像都存儲在這個目錄中。

  --detector:面部檢測器的路徑。我們將使用 OpenCV 的深度學習面部檢測器。方便起見,本文的「Downloads」部分也有這個 Caffe 模型。

  --confidence:過濾弱面部檢測的最小概率,默認值爲 50%。

  --skip:我們不需要檢測和存儲每一張圖像,因爲相鄰的幀是相似的。因此我們在檢測時會跳過 N 個幀。你可以使用這個參數並更改默認值(16)。

  繼續加載面部檢測器並初始化視頻流:

  23~26 行加載了 OpenCV 的深度學習面部檢測器:

  https://www.pyimagesearch.com/2018/02/26/face-detection-with-opencv-and-deep-learning/

  從 30 行開始打開視頻流。

  我們還初始化了兩個參數——讀取的幀的數量和執行循環時保存的幀的數量(31 和 32 行)。

  接着要創建處理幀的循環:

  while 循環是從 35 行開始的。

  從這裏開始我們抓取一幀並進行驗證(37~42 行)。

  此時,因爲已經讀取了一個幀,我們將增加讀取計數器(48 行)。如果我們跳過特定的幀,也會跳過後面的處理,再繼續下一個循環(48 和 49 行)。

  接着檢測面部:

  爲了進行面部檢測,我們要在 53 和 54 行根據圖像創建一個 blob。爲了適應 Caffe 面部識別器,這個 blob 是 300*300 的。之後還要縮放邊界框,因此 52 行抓取了幀的維度。

  58 和 59 行通過深度學習面部識別器執行了 blob 的前向傳輸。

  我們的腳本假設視頻的每一幀中只有一張面部(62~65 行)。這有助於減少假陽性。如果你要處理的視頻中不止有一張面部,我建議你根據需要調整邏輯。

  因此,第 65 行抓取了概率最高的面部檢測索引。66 行用這個索引計算了檢測的置信度。

  接下來要過濾弱檢測並將面部 ROI 寫進磁盤:

  71 行確保我們的面部檢測 ROI 滿足最小閾值,從而減少假陽性。

  在 74~76 行提取了面部 ROI 和相應的邊界框。

  在 79~81 行爲面部 ROI 生成了路徑和文件名,並將它寫在磁盤上。此時,我們就可以增加保存的面部圖像數量了。

  處理完成後,我們將在 86 和 87 行執行清理工作。

  建立活體檢測圖像數據集

  圖 4:OpenCV 面部活體檢測數據集。我們要用 Keras 和 OpenCV 來訓練並演示活體模型。

  現在我們已經實現了 gather_example.py 腳本,接下來要讓它開始工作。

  確保你已經用這篇教程的「Downloads」部分獲取了源代碼和輸入視頻樣例。

  打開終端並執行下面的命令來提取「僞造」類的面部:

  也可以對「真實」類別的面部執行同樣的操作:

  因爲「真」視頻比「假」視頻長,因此我們得把跳過幀的值設置得更長,來平衡每一類輸出的面部 ROI 數量。

  在執行這個腳本之後,你的圖像數量應該如下:

  僞造面部:150 張圖片;

  真實面部:161 張圖片;

  總數:311 張圖片。

  實現「LivenessNet」——我們的深度學習活體檢測器

  圖 5:LivenessNet(一個用來檢測圖片和視頻中面部活性的 CNN)的深度學習架構。

  下一步就要實現基於深度學習的活體檢測器「LivenessNet」了。

  從核心上講,LivenessNet 實際上就是一個簡單的卷積神經網絡。

  我們要讓這個網絡儘可能淺,並用儘可能少的參數,原因如下:

  避免因數據集小而導致的過擬合;

  確保活性檢測器足夠快,能夠實時運行(即便是在像樹莓派這樣資源受限的設備上)。

  現在來實現 LivenessNet 吧。打開 livenessnet.py 並插入下面的代碼:

  所有導入(import)的包都來自 Keras(2~10 行)。要深入瞭解這些層和函數,請參考《Deep Learning for Computer Vision with Python》。

  第 12 行定義了 LivenessNet 的類。這裏用了一種靜態方法——build(14 行)。build 方法接受 4 個參數:

  width:圖片/體積的寬度;

  height:圖片的高度;

  depth:圖像的通道數量(本例中是 3,因爲我們處理的是 RGB 圖像);

  classes:類的數量。我們總共有兩類:「真」和「假」。

  在 17 行初始化模型。

  在 18 行定義了 inputShape,在 23~25 行確定了通道順序。

  接着給 CNN 添加層:

  我們的 CNN 展現了 VGGNet 特有的品質——只學習了少量的過濾器。在理想情況下,我們不需要能區分真實面部和僞造面部的深度網絡。

  28~36 行是第一個層的集合——CONV => RELU => CONV => RELU => POOL,這裏還添加了批歸一化和 dropout。

  39~46 行添加了另一個層集合——CONV => RELU => CONV => RELU => POOL。

  最後,我們還要添加 FC => RELU 層:

  49~57 行添加了全連接層和帶有 softmax 分類器 head 的 ReLU 激活層。

  模型在 60 行返回到訓練腳本。

  創建訓練活體檢測器的腳本

  圖 6:LivenessNet 的訓練過程。同時用「真實」圖像和「僞造」圖像作爲數據集,可以用 OpenCV、Keras 和深度學習訓練活體檢測模型。

  鑑於我們已經有了真實/僞造圖像,且已經實現了 LivenessNet,我們現在準備訓練網絡了。

  打開 train_liveness.py 文件,插入下列代碼:

  訓練面部活體檢測器的腳本要導入大量的包(2~19 行)。大概瞭解一下:

  matplotlib:用於生成訓練圖。在第 3 行將後端參數設爲「Agg」,這樣就可以將生成的圖保存在磁盤上了。

  LivenessNet:我們之前定義好的用於活體檢測的 CNN;

  train_test_split:scikit-learn 中的函數,用於將數據分割成訓練數據和測試數據;

  classification_report:scikit-learn 中的函數,這個工具可以根據模型性能生成簡要的統計報告;

  ImageDataGenerator:用於數據增強,它生成了一批隨機變換後的圖像;

  Adam:適用於該模型的優化器(也可以用 SGD、RMSprop 等替換);

  paths:來自 imutils 包,這個模塊可以幫助我們收集磁盤上所有圖像文件的路徑;

  pyplot:用來生成漂亮的訓練圖;

  numpy:Python 的數字處理庫。對 OpenCV 來說這個庫也是必需的;

  argparse:用來處理命令行參數;

  pickle:將標籤編碼器序列化到磁盤;

  cv2:綁定了 OpenCV;

  os:這個模塊可以做很多事,但我們只用它來作操作系統路徑分隔符。

  現在你知道導入的都是些什麼了,可以更直接地查看腳本剩餘的部分。

  這個腳本接受四個命令行參數:

  --dataset:輸入數據集的路徑。我們在本教程前面的部分用 gather_examples.py 腳本創建了數據集。

  --model:我們的腳本會生成一個輸出模型文件,在這個參數中提供路徑。

  --le:這裏要提供輸出序列化標籤編碼器文件的路徑。

  --plot:訓練腳本會生成一張圖。如果要覆蓋默認值「plog.png」,那你就要在命令行中指定值。

  下面的代碼塊要進行大量的初始化工作,還要構建數據:

  在 35~37 行要設置訓練參數,包含初始化學習率、批大小和 epoch 的數量。

  在 42~44 行要抓取 imagePaths。我們還要初始化兩個列表來存放數據和類別標籤。

  46~55 行的循環用於建立數據和標籤列表。數據是由加載並將尺寸調整爲 32*32 像素的圖像組成的,標籤列表中存儲了每張圖相對應的標籤。

  在 59 行將所有像素縮放到 [0,1] 之間,並將列表轉換爲 NumPy 數組。

  現在來編碼標籤並劃分數據:

  63~65 行對標籤進行 one-hot 編碼處理。

  在 69 和 70 行用 scikit-learn 劃分數據————將數據的 75% 用來訓練,剩下的 25% 用來測試。

  接下來要初始化數據增強對象、編譯和訓練面部活性模型:

  73~75 行構造了數據增強對象,這個過程通過隨機的旋轉變換、縮放變換、平移變換、投影變換以及翻轉變換來生成圖像。

  在 79~83 行中建立並編譯了我們的 LivenessNet 模型。

  在 87~89 行着手訓練。考慮到模型較淺且數據集較小,因此這個過程相對而言會快一些。

  模型訓練好後,就可以評估結果並生成訓練圖了:

  在測試集上作出預測(93 行)。94 和 95 行生成了 classification_report,並將結果輸出在終端上。

  99~104 行將 LivenessNet 模型和標籤編碼器一起序列化到磁盤上。

  剩下的 107~117 行則爲後續的檢查生成了訓練歷史圖。

  訓練活體檢測器

  我們現在準備訓練活體檢測器了。

  確保你已經通過本教程的「Downloads」部分下載了源代碼和數據集,執行以下命令:

  圖 6:用 OpenCV、Keras 和深度學習訓練面部活體模型的圖。

  結果表明,我們的活體檢測器在驗證集上的準確率高達 99%!

  將各個部分組合在一起:用 OpenCV 做活體檢測

  圖 7:用 OpenCV 和深度學習做面部活性檢測。

  最後一步是將各個部分組合在一起:

  訪問網絡攝像頭/視頻流

  將面部檢測應用到每一幀

  對面部檢測的結果應用活體檢測器模型

  打開 liveness_demo.py 並插入以下代碼:

  2~11 行導入了需要的包。值得注意的是:

  會使用 VideoStream 來訪問相機饋送

  使用 img_to_array 來使幀採用兼容的數組形式

  用 load_model 來加載序列化的 Keras 模型

  爲了方便起見還要使用 imutils

  用 cv2 綁定 OpenCV

  解析 14~23 行命令行的參數:

  --model:用於活性檢測的預訓練 Keras 模型的路徑;

  --le:標籤編碼器的路徑;

  --detector:用來尋找面部 ROI 的 OpenCV 的深度學習面部檢測器路徑;

  --confidence:濾出弱檢測的最小概率閾值。

  現在要繼續初始化面部檢測器、LivenessNet 模型和標籤編碼器,以及視頻流:

  27~30 行加載 OpenCV 人臉檢測器。

  34 和 35 行加載序列化的預訓練模型(LivenessNet)和標籤編碼器。

  39 和 40 行實例化 VideoStream 對象,允許相機預熱兩秒。

  此時開始遍歷幀來檢測真實和虛假人臉:

  43 行開啓了無限的 while 循環塊,從這裏開始捕獲並調整各個幀的大小(46 和 47 行)。

  調整幀的大小後,抓取幀的維度,以便稍後進行縮放(50 行)。

  用 OpenCV 的 blobFromImage 函數可以生成 blob(51 和 52 行),然後將其傳到面部檢測器網絡,再繼續推理(56 和 57 行)。

  現在可以進行有意思的部分了——用 OpenCV 和深度學習做活性檢測:

  在 60 行開始循環遍歷面部檢測。在這個過程中,我們:

  濾出弱檢測(63~66 行);

  提取對應的面部邊界框,確保它們沒有超出幀(69~77 行);

  提取面部 ROI,用處理訓練數據的方式對面部 ROI 進行預處理(81~85 行);

  部署活性檢測器模型,確定面部圖片是「真實的」還是「僞造的」(89~91 行);

  當檢測出是真實面部時,直接在 91 行後面插入面部識別的代碼。僞代碼類似於:if label == "real": run_face_reconition();

  最後(在本例中)繪製出標籤文本並框出面部(94~98 行)。

  展示結果並清理:

  當捕獲按鍵時,在循環的每一次迭代中顯示輸出幀。無論用戶在什麼時候按下「q」(「退出」),都會跳出循環、釋放指針並關閉窗口(105~110 行)。

  在實時視頻中部署活體檢測器

  要繼續本教程,請確保你已經通過本教程的「Downloads」部分下載了源代碼和預訓練的活體檢測模型。

  打開終端並執行下列命令:

  在這裏可以看到我們的活性檢測器成功地分辨出真實面部和僞造面部。

  下面的視頻中有更長的演示:

  限制、改進和進一步工作

  本教程中的活體檢測器的主要限制在於數據集的限制——數據集中只有 311 張圖片(161 個「真實」類和 150 個「僞造」類)。

  這項工作第一個要擴展的地方就是要收集更多的訓練數據,更具體地說,不只是要有我或你自己的圖像(幀)。

  記住,這裏用的示例數據集只包括一個人(我)的面部。我是個白人(高加索人),而你收集的訓練數據中還應該有其他人種或其他膚色的面部。

  我們的活體檢測器只是針對屏幕上顯示的僞造面部訓練的——並沒有打印出來圖像或照片。因此,我的第三個建議是除了屏幕錄製得到的僞造面部外,還應該有通過其他方式僞造的面部資源。

  我最後要說的是,這裏的活體檢測並未涉及任何新技術。最好的活體檢測器會包含多種活性檢測的方法(請參考前文中提到的《What is liveness detection and why do we need it?》)。

  花些時間思考並評估你自己的項目、指南和需求——在某些情況下,你可能只需要基本的眨眼檢測啓發式。

  而在另一些情況中,你可能需要將基於深度學習的活體檢測和其他啓發式結合在一起。

  不要急於進行人臉識別和活體檢測——花點時間思考你的項目獨一無二的需求。這麼做可以確保你獲得更好、更準確的結果。

  總結

  你將在本教程中學習如何用 OpenCV 進行活體檢測。你現在就可以在自己的面部識別系統中應用這個活體檢測器,來發現僞造的面部並進行反面部欺騙。

  我們用 OpenCV、深度學習和 Python 創建了自己的活體檢測器。

  第一步是要收集真實面部和虛假面部的數據集。爲了完成這項任務,我們:

  首先用智能手機錄製了一段自己的視頻(即「真實」面部);

  將手機放在筆記本電腦或桌面上,重播同樣的視頻,用網絡攝像頭錄製重播的視頻(即「僞造」面部);

  在這兩段視頻上使用面部檢測器,來生成最終的活體檢測數據集。

  在構建數據集之後,我們實現了「LivenessNet」,它集成了 Keras 和深度學習 CNN。

  我們有意讓這個網絡儘可能淺,以確保:

  減少模型因數據集太小而導致的過擬合情況;

  模型可以實時運行(包括樹莓派)

  總體來說,我們的活體檢測器在驗證集上的準確率高達 99%。

  爲了演示完整的活體檢測流程,我們創建了一個 Python+OpenCV 的腳本,它可以加載我們的活體檢測器,並且可以將它應用在實時的視頻流上。

  正如演示所示,我們的活體檢測器可以區分真實面部和僞造面部。

  本文爲機器之心編譯,轉載請聯繫本公衆號獲得授權。

  ------------------------------------------------

相关文章