0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

如何使用OpenCV進(jìn)行活性檢測(cè)

新機(jī)器視覺(jué) ? 來(lái)源:AI科技評(píng)論 ? 作者:AI科技評(píng)論 ? 2021-03-20 09:57 ? 次閱讀

本文來(lái)自著名的計(jì)算機(jī)視覺(jué)教學(xué)網(wǎng)站「pyimagesearch」,文章作者為 Adrian Rosebrock。在本文中,Adrian 將就「如何鑒別圖像/視頻中的真實(shí)人臉和偽造人臉」這一問(wèn)題進(jìn)行深入的分析,并介紹使用基于 OpenCV 的模型進(jìn)行活體檢測(cè)的具體方法。雷鋒網(wǎng) AI 科技評(píng)論編譯如下。

教程將教授你如何使用 OpenCV 進(jìn)行活性檢測(cè)。通過(guò)學(xué)習(xí),你將能夠在人臉識(shí)別系統(tǒng)中創(chuàng)建一個(gè)可以發(fā)現(xiàn)偽造人臉并執(zhí)行反人臉欺騙的活體檢測(cè)器。

在過(guò)去一年中,本文作者已經(jīng)寫(xiě)過(guò)了多篇人臉識(shí)別教程,包括:

基于 OpenCV 的人臉識(shí)別(閱讀地址:https://www.pyimagesearch.com/2018/09/24/opencv-face-recognition/)

使用 dlib、Python深度學(xué)習(xí)進(jìn)行人臉識(shí)別(閱讀地址:https://www.pyimagesearch.com/2018/06/18/face-recognition-with-opencv-python-and-deep-learning/)

基于樹(shù)莓派的人臉識(shí)別(閱讀地址:https://www.pyimagesearch.com/2018/06/25/raspberry-pi-face-recognition/)

然而,在我收到的一些郵件以及這些關(guān)于人臉識(shí)別的博文的評(píng)論中,我經(jīng)常被問(wèn)到的一個(gè)問(wèn)題是:

我如何才能區(qū)分真人臉和偽造人臉?

試想一下,如果一個(gè)居心叵測(cè)的用戶(hù)有目的地試圖騙過(guò)你的人臉識(shí)別系統(tǒng),會(huì)發(fā)生什么?

他們可能會(huì)嘗試向執(zhí)行人臉識(shí)別的攝像頭展示一張照片或一段視頻(例如在本文頂部的圖像)。而這張照片和視頻中的人并不是這些用戶(hù)本人。

在這種情況下,人臉識(shí)別系統(tǒng)完全有可能把展示到它們面前的圖片識(shí)別為正確的人臉,這最終會(huì)導(dǎo)致未授權(quán)的用戶(hù)躲過(guò)人臉識(shí)別系統(tǒng)!

如何才能區(qū)分出這些「?jìng)卧斓摹古c「真實(shí)的/合法的」人臉呢?如何才能將反人臉欺騙算法應(yīng)用到你的人臉識(shí)別應(yīng)用中呢?

答案是使用 OpenCV 進(jìn)行活體檢測(cè),這正是本文要討論的內(nèi)容。

那么,如何將基于 OpenCV 的活體檢測(cè)功能結(jié)合到你自己的人臉識(shí)別系統(tǒng)中呢?本文接下來(lái)將給出答案。

基于 OpenCV 的活體檢測(cè)

在本教程的第一部分,我們將對(duì)活體檢測(cè)進(jìn)行討論,包括「活體檢測(cè)是什么?」以及「為什么我們需要活體檢測(cè)來(lái)改進(jìn)人臉識(shí)別系統(tǒng)?」

在這里,我們首先回顧一下接下來(lái)將用來(lái)進(jìn)行活體檢測(cè)的數(shù)據(jù)集,內(nèi)容包括:

如何構(gòu)建用于活體檢測(cè)的數(shù)據(jù)集

我們的真假人臉的圖像示例

我們還將回顧針對(duì)活體檢測(cè)器項(xiàng)目的項(xiàng)目結(jié)構(gòu)。

我們將訓(xùn)練一個(gè)能夠區(qū)分真人臉和偽造人臉的深度神經(jīng)網(wǎng)絡(luò),來(lái)創(chuàng)建活體檢測(cè)器。

因此,我們需要:

1.構(gòu)建圖像數(shù)據(jù)集

2.實(shí)現(xiàn)一個(gè)能夠進(jìn)行活體檢測(cè)的卷積神經(jīng)網(wǎng)絡(luò)(我們稱(chēng)之為「LivenessNet」)

3.訓(xùn)練活體檢測(cè)網(wǎng)絡(luò)

4.創(chuàng)建一個(gè)能夠使用我們訓(xùn)練好的活體檢測(cè)模型并將其應(yīng)用于實(shí)時(shí)視頻的 Python+OpenCV 的腳本

接下來(lái),讓我們進(jìn)入正題吧!

什么是活體檢測(cè), 我們?yōu)槭裁葱枰?/p>

圖 1:基于 OpenCV 的活體檢測(cè)。左圖是一個(gè)我的活體(真人)的視頻,而在右圖中可以看到我正拿著我的 iPhone(屏幕上有偽造人臉/用來(lái)欺騙人臉識(shí)別系統(tǒng)的人臉圖片)。

人臉識(shí)別系統(tǒng)正變得越來(lái)越普及。從 iphone / 智能手機(jī)上的人臉識(shí)別系統(tǒng),到中國(guó)用來(lái)進(jìn)行大規(guī)模監(jiān)控的人臉識(shí)別系統(tǒng)——人臉識(shí)別變得無(wú)處不在。

然而,人臉識(shí)別系統(tǒng)卻很容易被「具有欺騙性的」和「不真實(shí)」的人臉?biāo)夼?/p>

通過(guò)將一個(gè)人的照片(無(wú)論是打印出來(lái)的,或者是顯示在智能手機(jī)屏幕上的,等等)展示給用于人臉識(shí)別的攝像頭,可以很容易地騙過(guò)人臉識(shí)別系統(tǒng)。

為了使人臉識(shí)別系統(tǒng)變得更加安全,我們需要檢測(cè)出這種偽造的/不真實(shí)的人臉——而「活體檢測(cè)」就是被用來(lái)指代這種算法的術(shù)語(yǔ)。

活體檢測(cè)的方法有很多,包括:

紋理分析,包括在人臉區(qū)域上計(jì)算局部二值模式(LBP,https://www.pyimagesearch.com/2015/12/07/local-binary-patterns-with-python-opencv/)和使用支持向量機(jī)(SVM)將人臉?lè)诸?lèi)為真實(shí)的或偽造的。

頻率分析,如查看人臉圖片的傅里葉域(對(duì)其進(jìn)行傅里葉變換)。

變量聚焦分析,例如查看兩個(gè)連續(xù)幀之間像素值的變化。

基于啟發(fā)式的算法,包括眼球運(yùn)動(dòng)、嘴唇運(yùn)動(dòng)和眨眼檢測(cè)(https://www.pyimagesearch.com/2017/04/24/eye-blink-detection-opencv-python-dlib/)。這類(lèi)算法試圖跟蹤眼球運(yùn)動(dòng)和眨眼,以確保用戶(hù)展示的并非另一個(gè)人的照片(因?yàn)檎掌粫?huì)眨眼或移動(dòng)嘴唇)。

光流算法,即查看由三維物體和二維平面產(chǎn)生的光流的差異和特性。

三維人臉形狀,類(lèi)似于蘋(píng)果 iPhone 所使用的人臉識(shí)別系統(tǒng),使人臉識(shí)別系統(tǒng)能夠區(qū)分真實(shí)的人臉和打印出來(lái) / 照片中的 / 圖像中的另一個(gè)人的人臉。

將上述方法結(jié)合起來(lái),使人臉識(shí)別系統(tǒng)工程師能夠選擇適合其特定應(yīng)用程序的活體檢測(cè)模型。

在 Chakraborty 和 Das 于 2014 年發(fā)表的論文「An Overview of Face liveness Detection」(論文閱讀地址:https://arxiv.org/pdf/1405.2227.pdf)中,對(duì)活體檢測(cè)算法進(jìn)行了全面的回顧。

在本文的教程中,我們將活體檢測(cè)作為一個(gè)二分類(lèi)問(wèn)題來(lái)看待。

給定一張輸入圖像,我們將訓(xùn)練一個(gè)能夠識(shí)別出真假人臉的卷積神經(jīng)網(wǎng)絡(luò)。

在開(kāi)始訓(xùn)練我們的活體檢測(cè)模型前, 我們先查看一下使用的數(shù)據(jù)集。

我們的活體檢測(cè)視頻

圖 2:一個(gè)收集到的真實(shí)人臉和偽造/欺騙性人臉的例子。左邊的視頻是一個(gè)我的人臉的合法錄像。右邊是我的筆記本電腦將左邊的這段視頻錄下來(lái)的視頻。

為了簡(jiǎn)化我們的例子,我們?cè)诒疚闹袠?gòu)建的活體檢測(cè)器將重點(diǎn)關(guān)注區(qū)分屏幕上的真實(shí)人臉和欺騙性的人臉。

該算法可以很被容易地?cái)U(kuò)展到其他類(lèi)型的欺騙性人臉中,包括打印機(jī)或高分辨率打印出來(lái)的人臉等。

為了構(gòu)建活體檢測(cè)數(shù)據(jù)集,我將:

1. 使用我的 iPhone,把它調(diào)成人像 / 自拍模式。

2. 錄制一段大約 25 秒的我自己在辦公室內(nèi)走來(lái)走去的視頻。

3. 將我的 iPhone 朝向我的桌面重放這個(gè) 25 秒的視頻,我從桌面的角度錄制了這段重放的視頻;

4. 這會(huì)產(chǎn)生兩個(gè)示例視頻。一個(gè)被用作「真實(shí)」人臉,另一個(gè)被用作「?jìng)卧?欺騙性」人臉。

5. 最終,我將人臉檢測(cè)技術(shù)同時(shí)應(yīng)用在了這兩組視頻上,以提取兩類(lèi)人臉各自的 ROI。

我在本文的「下載」部分為大家提供了真實(shí)人臉視頻和偽造人臉視頻。

你可以直接使用這些視頻開(kāi)始構(gòu)建數(shù)據(jù)集,但是我建議你收集更多的數(shù)據(jù),從而幫助提升你的活體檢測(cè)器的魯棒性和準(zhǔn)確率。

通過(guò)測(cè)試,我確認(rèn)這個(gè)模型會(huì)偏向于檢測(cè)出我自己的人臉——因?yàn)樗械哪P投际鞘褂梦易约旱囊曨l訓(xùn)練的,因此這個(gè)模型依舊是有意義的。此外,由于我是白種人,我并不奢望該數(shù)據(jù)集被用于檢測(cè)其它膚色的人臉時(shí)有同樣出色的表現(xiàn)。

理想情況下,你可以使用包含多個(gè)種族的人臉的數(shù)據(jù)來(lái)訓(xùn)練一個(gè)模型。如果讀者想要獲得更多關(guān)于改進(jìn)活體檢測(cè)模型的建議,請(qǐng)務(wù)必參考下面的「局限性和進(jìn)一步工作」章節(jié)。

在接下來(lái)的教程中,你將學(xué)習(xí)到如何利用我記錄下來(lái)的數(shù)據(jù)集,并使用 OpenCV 和深度學(xué)習(xí)技術(shù)得到一個(gè)真正的活體檢測(cè)器。

項(xiàng)目架構(gòu)

在繼續(xù)閱讀的過(guò)程中,讀者可以使用「下載」部分提供的鏈接獲取代碼、數(shù)據(jù)集以及活體檢測(cè)模型,并解壓存檔。

當(dāng)你導(dǎo)航到項(xiàng)目的目錄時(shí),你會(huì)注意到如下所示的架構(gòu):

$ tree --dirsfirst --filelimit 10

├── dataset

│ ├── fake [150 entries]

│ └── real [161 entries]

├── face_detector

│ ├── deploy.prototxt

│ └── res10_300x300_ssd_iter_140000.caffemodel

├── pyimagesearch

│ ├── __init__.py

│ └── livenessnet.py

├── videos

│ ├── fake.mp4

│ └── real.mov

├── gather_examples.py

├── train_liveness.py

├── liveness_demo.py

├── le.pickle

├── liveness.model

└── plot.png

6 directories, 12 files

在我們的項(xiàng)目中有四個(gè)主要的目錄:

「dataset/」:我們的數(shù)據(jù)集目錄包含兩類(lèi)圖像:

(1)攝像頭對(duì)著正在播放我的人臉視頻的手機(jī)屏幕所拍下的偽造圖像。

(2)我用手機(jī)自拍的視頻中的真實(shí)圖像。

「face_detector/ 」:包括我們用來(lái)定位人臉 ROI 的、預(yù)訓(xùn)練好的 Caffe 人臉檢測(cè)器。

「pyimagesearch/」:該模塊包含我們的 LivenessNet 類(lèi)。

「videos/」:我提供了兩個(gè)用來(lái)訓(xùn)練 LivenessNet 分類(lèi)器的輸入視頻。

現(xiàn)在,我們將仔細(xì)回顧三個(gè) Python 腳本。在本文的最后,你可以在自己的數(shù)據(jù)和輸入視頻上運(yùn)行這些腳本。按照在本教程中出現(xiàn)的順序,這三個(gè)腳本依次是:

1. 「gather_examples.py」:該腳本從輸入的視頻文件中抓取人臉的 ROI,并幫助我們創(chuàng)建一個(gè)深度學(xué)習(xí)人臉活體檢測(cè)數(shù)據(jù)集。

2.「train_liveness.py」:如文件名所示,該腳本將訓(xùn)練我們的 LivenessNet 分類(lèi)器。我們將使用 Keras 和 TensorFlow 來(lái)訓(xùn)練模型。訓(xùn)練的過(guò)程會(huì)產(chǎn)生以下幾個(gè)文件:

(1)le.pickle:我們的類(lèi)標(biāo)簽編碼器。

(2)liveness.model:我們用來(lái)訓(xùn)練人臉活體檢測(cè)器的一系列 Keras 模型。

(3)plot.png:訓(xùn)練歷史示意圖顯示出了模型的準(zhǔn)確率和損失曲線,我們可以根據(jù)它評(píng)估我們的模型(即過(guò)擬合 / 欠擬合)。

3.「liveness_demo.py」:我們的演示腳本將啟動(dòng)你的網(wǎng)絡(luò)攝像頭拍下視頻幀,來(lái)進(jìn)行實(shí)時(shí)人臉活體檢測(cè)。

從我們的訓(xùn)練(視頻)數(shù)據(jù)集中檢測(cè)并提取人臉的 ROI

30a50fd8-88ef-11eb-8b86-12bb97331649.jpg

圖 3:為了建立一個(gè)活體檢測(cè)數(shù)據(jù)集,首先需要檢測(cè)出視頻中的人臉 ROI 區(qū)域

現(xiàn)在我們可以回顧一下我們初始化的數(shù)據(jù)集和項(xiàng)目架構(gòu),讓我們看看如何從輸入的視頻中提取真實(shí)和偽造的人臉圖像。

這個(gè)腳本的最終目標(biāo)是向兩個(gè)目錄中填充數(shù)據(jù):

1. 「dataset/fake/」:包含「fake.mp4」文件中的人臉 ROI 區(qū)域。

2. 「dataset/real/」:包含「real.mov」文件中的人臉 ROI 區(qū)域。

基于這些幀,我們接下來(lái)將在圖像上訓(xùn)練一個(gè)基于深度學(xué)習(xí)的活體檢測(cè)器。

打開(kāi)「gather_examples.py」文件并插入下列代碼:

# import the necessary packages

import numpy as np

import argparse

import cv2

import os

# construct the argument parse and parse the arguments

ap = argparse.ArgumentParser()

ap.add_argument("-i", "--input", type=str, required=True,

help="path to input video")

ap.add_argument("-o", "--output", type=str, required=True,

help="path to output directory of cropped faces")

ap.add_argument("-d", "--detector", type=str, required=True,

help="path to OpenCV's deep learning face detector")

ap.add_argument("-c", "--confidence", type=float, default=0.5,

help="minimum probability to filter weak detections")

ap.add_argument("-s", "--skip", type=int, default=16,

help="# of frames to skip before applying face detection")

args = vars(ap.parse_args())

第 2-5 行引入了我們需要的安裝包。除了內(nèi)置的 Python 模塊,該腳本僅僅需要用到 OpenCV 和 NumPy 。

第 8-19 行代碼將解析我們的命令行參數(shù)(https://www.pyimagesearch.com/2018/03/12/python-argparse-command-line-arguments/):

「--input」:我們的輸入視頻文件的路徑。

「--output」:儲(chǔ)存每個(gè)裁剪出來(lái)的人臉圖像的輸出目錄的路徑。

「--detector」:人臉檢測(cè)器的路徑。我們將使用 OpenCV 的深度學(xué)習(xí)人臉檢測(cè)器(https://www.pyimagesearch.com/2018/02/26/face-detection-with-opencv-and-deep-learning/)。為了方便讀者使用,Caffe 模型可在本文的「下載」部分獲得。

「--confidence」:過(guò)濾弱人臉檢測(cè)結(jié)果的最小概率。默認(rèn)情況下,該值為 50%。

「--skip」:我們不需要檢測(cè)并存儲(chǔ)每個(gè)圖像,因?yàn)橄噜彽膸窍嗨频摹O喾次覀儠?huì)在兩次人臉檢測(cè)任務(wù)之間跳過(guò) N 個(gè)幀。你可以使用此參數(shù)修改默認(rèn)的 N 值(16)。

讓我們繼續(xù)加載人臉檢測(cè)器并初始化我們的視頻流:

# load our serialized face detector from disk

print("[INFO] loading face detector...")

protoPath = os.path.sep.join([args["detector"], "deploy.prototxt"])

modelPath = os.path.sep.join([args["detector"],

"res10_300x300_ssd_iter_140000.caffemodel"])

net = cv2.dnn.readNetFromCaffe(protoPath, modelPath)

# open a pointer to the video file stream and initialize the total

# number of frames read and saved thus far

vs = cv2.VideoCapture(args["input"])

read = 0

saved = 0

第 23-26 行加載了 OpenCV 的深度學(xué)習(xí)人臉檢測(cè)器(相關(guān)閱讀:https://www.pyimagesearch.com/2018/02/26/face-detection-with-opencv-and-deep-learning/)。

之后,我們?cè)诘?30 行打開(kāi)了我們的視頻流。

我們還在循環(huán)執(zhí)行時(shí),為讀取到的幀數(shù)以及保存下來(lái)的幀數(shù)初始化了兩個(gè)變量(第 31 和第 32 行)。

接下來(lái),我們將創(chuàng)建一個(gè)循環(huán)來(lái)處理這些幀:

# loop over frames from the video file stream

while True:

# grab the frame from the file

(grabbed, frame) = vs.read()

# if the frame was not grabbed, then we have reached the end

# of the stream

if not grabbed:

break

# increment the total number of frames read thus far

read += 1

# check to see if we should process this frame

if read % args["skip"] != 0:

continue

我們?cè)诘?35 行開(kāi)始「while」循環(huán)。

接著,我們抓取到了一個(gè)幀并對(duì)其進(jìn)行驗(yàn)證(第 37-42 行)。

這時(shí),由于我們已經(jīng)讀取了一個(gè)幀,我們將增加「read」計(jì)數(shù)器的計(jì)數(shù)(第 48 行)。如果需要跳過(guò)這個(gè)特定的幀,我們將不做任何操作繼續(xù)進(jìn)入下一輪循環(huán)(第 48 和 49 行)。

讓我們繼續(xù)檢測(cè)人臉:

# grab the frame dimensions and construct a blob from the frame

(h, w) = frame.shape[:2]

blob = cv2.dnn.blobFromImage(cv2.resize(frame, (300, 300)), 1.0,

(300, 300), (104.0, 177.0, 123.0))

# pass the blob through the network and obtain the detections and

# predictions

net.setInput(blob)

detections = net.forward()

# ensure at least one face was found

if len(detections) > 0:

# we're making the assumption that each image has only ONE

# face, so find the bounding box with the largest probability

i = np.argmax(detections[0, 0, :, 2])

confidence = detections[0, 0, i, 2]

為了執(zhí)行人臉檢測(cè),我們需要根據(jù)圖像創(chuàng)建一個(gè)二進(jìn)制大對(duì)象數(shù)據(jù)(blob)(第 53 和 54 行)。為了適應(yīng)于 Caffe 人臉檢測(cè)器,這個(gè)「blob」的寬、高為 300*300。稍后還需要對(duì)邊界框進(jìn)行放縮,因此在第 52 行中,我們會(huì)抓取到幀的維度。

第 58 和 59 行通過(guò)深度學(xué)習(xí)人臉檢測(cè)器前饋傳遞這個(gè)「blob」。

我們的腳本假設(shè)在視頻的每一幀中只出現(xiàn)了一張人臉(第 62-65 行),這有助于防止假正例的產(chǎn)生。如果你正在處理包含多個(gè)面孔的視頻,我建議你相應(yīng)地調(diào)整邏輯。

因此,第 65 行獲取到了最高的人臉檢測(cè)索引的概率。第 66 行使用該索引提取出檢測(cè)的置信度(confidence)。

接下來(lái),讓我們過(guò)濾掉弱人臉檢測(cè)結(jié)果并將人臉的 ROI 寫(xiě)入磁盤(pán):

# ensure that the detection with the largest probability also

# means our minimum probability test (thus helping filter out

# weak detections)

if confidence > args["confidence"]:

# compute the (x, y)-coordinates of the bounding box for

# the face and extract the face ROI

box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])

(startX, startY, endX, endY) = box.astype("int")

face = frame[startY:endY, startX:endX]

# write the frame to disk

p = os.path.sep.join([args["output"],

"{}.png".format(saved)])

cv2.imwrite(p, face)

saved += 1

print("[INFO] saved {} to disk".format(p))

# do a bit of cleanup

vs.release()

cv2.destroyAllWindows()

第 71 行保證了我們的人臉檢測(cè) ROI 能夠滿(mǎn)足減少假正例所需要的最小閾值。

在此基礎(chǔ)上,我們提取出了人臉的 ROI 邊界框「box」坐標(biāo)以及相應(yīng)的人臉 ROI 數(shù)值(第 74-76 行)。

我們?yōu)槿四?ROI 生成了一個(gè)「路徑+文件名」,并在第 79-81 行中將其寫(xiě)入了磁盤(pán)。在這里,我們可以增加「saved」人的數(shù)量。

處理完成后,我們將在第 86 和 87 行中清空 OpenCV 的工作內(nèi)存。

構(gòu)建我們的活體檢測(cè)圖片數(shù)據(jù)集

圖 4:我們的 OpenCV 人臉活體檢測(cè)數(shù)據(jù)集。我們將使用 Keras 和 OpenCV 訓(xùn)練一個(gè)活體檢測(cè)模型的演示樣例。

請(qǐng)確保你使用了本教程「下載」部分的鏈接獲取到了源代碼以及輸入視頻的示例。

在此基礎(chǔ)上,請(qǐng)打開(kāi)一個(gè)終端并執(zhí)行下面的命令,從而提取出我們的「?jìng)卧?欺騙性」的類(lèi)別所需要的人臉圖像:

$ python gather_examples.py --input videos/real.mov --output dataset/real

--detector face_detector --skip 1

[INFO] loading face detector...

[INFO] saved datasets/fake/0.png to disk

[INFO] saved datasets/fake/1.png to disk

[INFO] saved datasets/fake/2.png to disk

[INFO] saved datasets/fake/3.png to disk

[INFO] saved datasets/fake/4.png to disk

[INFO] saved datasets/fake/5.png to disk

...

[INFO] saved datasets/fake/145.png to disk

[INFO] saved datasets/fake/146.png to disk

[INFO] saved datasets/fake/147.png to disk

[INFO] saved datasets/fake/148.png to disk

[INFO] saved datasets/fake/149.png to disk

類(lèi)似地,我們可以為獲取「真實(shí)」類(lèi)別的人臉圖像進(jìn)行相同的操作:

$ python gather_examples.py --input videos/fake.mov --output dataset/fake

--detector face_detector --skip 4

[INFO] loading face detector...

[INFO] saved datasets/real/0.png to disk

[INFO] saved datasets/real/1.png to disk

[INFO] saved datasets/real/2.png to disk

[INFO] saved datasets/real/3.png to disk

[INFO] saved datasets/real/4.png to disk

...

[INFO] saved datasets/real/156.png to disk

[INFO] saved datasets/real/157.png to disk

[INFO] saved datasets/real/158.png to disk

[INFO] saved datasets/real/159.png to disk

[INFO] saved datasets/real/160.png to disk

由于「真實(shí)」的人臉視頻比「?jìng)卧臁沟娜四樢曨l文件要更長(zhǎng)一些,我們將使用更長(zhǎng)的跳幀值來(lái)平衡每一類(lèi)輸出的人臉 ROI 的數(shù)量。

在執(zhí)行了上面的腳本后,你應(yīng)該已經(jīng)獲得了如下所示的圖像數(shù)據(jù):

偽造人臉:150 張圖像

真實(shí)人臉:161 張圖像

總計(jì):311 張圖像

實(shí)現(xiàn)我們的深度學(xué)習(xí)活體檢測(cè)器「LivenessNet」

圖 5:LivenessNet 的深度學(xué)習(xí)架構(gòu),這是一個(gè)被設(shè)計(jì)用來(lái)在圖像和視頻中檢測(cè)出活體人臉的卷積神經(jīng)網(wǎng)絡(luò)(CNN)。

接下來(lái),我們將實(shí)現(xiàn)基于深度學(xué)習(xí)技術(shù)的活體檢測(cè)器「LivenessNet」。

實(shí)質(zhì)上,「LivenessNet」就是一個(gè)簡(jiǎn)單的卷積神經(jīng)網(wǎng)絡(luò)。

出于以下兩點(diǎn)原因,我們將刻意讓該網(wǎng)絡(luò)保持盡可能淺的層數(shù)和盡可能少的參數(shù):

1. 為了降低在我們的小型數(shù)據(jù)集上發(fā)生過(guò)擬合現(xiàn)象的可能性。

2. 為了確保我們的活體檢測(cè)器能夠快速、實(shí)時(shí)運(yùn)行(即使在諸如樹(shù)莓派等計(jì)算資源受限的設(shè)備上)。

現(xiàn)在,我們將開(kāi)始實(shí)現(xiàn)「LivenessNet」,請(qǐng)打開(kāi)「livenessnet.py」文件并插入下列代碼:

# import the necessary packages

from keras.models import Sequential

from keras.layers.normalization import BatchNormalization

from keras.layers.convolutional import Conv2D

from keras.layers.convolutional import MaxPooling2D

from keras.layers.core import Activation

from keras.layers.core import Flatten

from keras.layers.core import Dropout

from keras.layers.core import Dense

from keras import backend as K

class LivenessNet:

@staticmethod

def build(width, height, depth, classes):

# initialize the model along with the input shape to be

# "channels last" and the channels dimension itself

model = Sequential()

inputShape = (height, width, depth)

chanDim = -1

# if we are using "channels first", update the input shape

# and channels dimension

if K.image_data_format() == "channels_first":

inputShape = (depth, height, width)

chanDim = 1

我們將從 Keras 包中引入所有需要的方法(第 2-10 行)。如果你想要進(jìn)一步了解每一個(gè)網(wǎng)絡(luò)層和函數(shù),請(qǐng)參閱「 Deep Learning for Computer Vision with Python」(網(wǎng)址為:https://www.pyimagesearch.com/deep-learning-computer-vision-python-book/)。

我們?cè)诘?12 行開(kāi)始定義「LivenessNet」類(lèi)。它包含一個(gè)靜態(tài)的構(gòu)造方法「build」(第 14 行)?!竍uild」方法將接受四個(gè)輸入?yún)?shù):

「width」:圖像/視頻的寬度

「height」:圖像的高度

「depth」:圖像的通道數(shù)(由于我們將使用 RGB 圖像,本例中 depth 為 3)。

「classes」:檢測(cè)類(lèi)別的數(shù)目。我們共有兩類(lèi)輸出人臉:「真實(shí)」人臉和「?jìng)卧臁谷四槨?/p>

我們?cè)诘?17 行將「model」初始化。

第 18 行定義了模型的「inputShape」,而第 23-25 行指定了通道的順序。

下面我們將向卷積神經(jīng)網(wǎng)絡(luò)中加入一些網(wǎng)絡(luò)層組件:

# first CONV => RELU => CONV => RELU => POOL layer set

model.add(Conv2D(16, (3, 3), padding="same",

input_shape=inputShape))

model.add(Activation("relu"))

model.add(BatchNormalization(axis=chanDim))

model.add(Conv2D(16, (3, 3), padding="same"))

model.add(Activation("relu"))

model.add(BatchNormalization(axis=chanDim))

model.add(MaxPooling2D(pool_size=(2, 2)))

model.add(Dropout(0.25))

# second CONV => RELU => CONV => RELU => POOL layer set

model.add(Conv2D(32, (3, 3), padding="same"))

model.add(Activation("relu"))

model.add(BatchNormalization(axis=chanDim))

model.add(Conv2D(32, (3, 3), padding="same"))

model.add(Activation("relu"))

model.add(BatchNormalization(axis=chanDim))

model.add(MaxPooling2D(pool_size=(2, 2)))

model.add(Dropout(0.25))

我們的卷積神經(jīng)網(wǎng)路表現(xiàn)出了 VGGNet 的一些特性。它非常淺,只有少許幾個(gè)學(xué)習(xí)到的卷積核。理想情況下,我們并不需要用通過(guò)一個(gè)深度網(wǎng)絡(luò)來(lái)區(qū)分真實(shí)人臉和偽造人臉。

第 28-36 行指定了第一個(gè)「CONV => RELU => CONV => RELU => POOL」(卷積層=> RELU 激活函數(shù)層=>卷積層=>RELU 激活函數(shù)層=>池化層)網(wǎng)絡(luò)層集合,其中也加入了批量歸一化層和 dropout 層。

第 39-46 行加上了另一個(gè)「CONV => RELU => CONV => RELU => POOL」(卷積層=>RELU 激活函數(shù)層=>卷積層=>RELU 激活函數(shù)層=>池化層)網(wǎng)絡(luò)層集合。

最后加入我們的「FC => RELU」(全連接=>RELU 激活函數(shù)層)層:

# first (and only) set of FC => RELU layers

model.add(Flatten())

model.add(Dense(64))

model.add(Activation("relu"))

model.add(BatchNormalization())

model.add(Dropout(0.5))

# softmax classifier

model.add(Dense(classes))

model.add(Activation("softmax"))

# return the constructed network architecture

return model

第 49-57 行由全連接層和 ReLU 激活函數(shù)層以及一個(gè) softmax 分類(lèi)器的頭組成。

在第 60 行中將該模型返回給訓(xùn)練腳本。

創(chuàng)建活體檢測(cè)器的訓(xùn)練腳本

31467b5c-88ef-11eb-8b86-12bb97331649.jpg

圖 6:訓(xùn)練 LivenessNet 的處理流程。同時(shí)使用「真實(shí)」人臉和「欺騙性/偽造」人臉圖像作為我們的數(shù)據(jù)集,我們可以使用 OpenCV、Keras 框架以及深度學(xué)習(xí)技術(shù)訓(xùn)練一個(gè)活體檢測(cè)模型。

給定我們的真實(shí)/欺騙性人臉圖像數(shù)據(jù)集以及對(duì) LivenessNet 實(shí)現(xiàn),我們現(xiàn)在已經(jīng)做好了訓(xùn)練該網(wǎng)絡(luò)的準(zhǔn)備:

請(qǐng)打開(kāi)「train_liveness.py」文件并插入下列代碼:

# set the matplotlib backend so figures can be saved in the background

import matplotlib

matplotlib.use("Agg")

# import the necessary packages

from pyimagesearch.livenessnet import LivenessNet

from sklearn.preprocessing import LabelEncoder

from sklearn.model_selection import train_test_split

from sklearn.metrics import classification_report

from keras.preprocessing.image import ImageDataGenerator

from keras.optimizers import Adam

from keras.utils import np_utils

from imutils import paths

import matplotlib.pyplot as plt

import numpy as np

import argparse

import pickle

import cv2

import os

# construct the argument parser and parse the arguments

ap = argparse.ArgumentParser()

ap.add_argument("-d", "--dataset", required=True,

help="path to input dataset")

ap.add_argument("-m", "--model", type=str, required=True,

help="path to trained model")

ap.add_argument("-l", "--le", type=str, required=True,

help="path to label encoder")

ap.add_argument("-p", "--plot", type=str, default="plot.png",

help="path to output loss/accuracy plot")

args = vars(ap.parse_args())

我們的人臉活體檢測(cè)腳本引入了大量的輸入(第 2-19 行)。它們分別是:

「matplotlib」:用以生成訓(xùn)練情況示意圖。在第 3 行中,我們將后端指定為「Agg」,從而使我們可以很容易地將示意圖寫(xiě)入磁盤(pán)。

「LivenessNet」:我們?cè)谇懊娴恼鹿?jié)定義過(guò)的活體檢測(cè)卷積神經(jīng)網(wǎng)絡(luò)。

「train_test_split」:從 scikit-learn 引入的函數(shù),它構(gòu)造了用于訓(xùn)練和測(cè)試的數(shù)據(jù)劃分。

「classification_report」:同樣從 scikit-learn 引入的函數(shù),它將生成一個(gè)描述我們模型性能的簡(jiǎn)短的統(tǒng)計(jì)報(bào)告。

「ImageDataGenerator」:用于進(jìn)行數(shù)據(jù)增強(qiáng),向我們提供批量的隨機(jī)突變?cè)鰪?qiáng)的圖像。

「Adam」:一個(gè)非常適用于該模型的優(yōu)化器(可選方案包括隨機(jī)梯度下降算法(SGD)、RMSprop 等)。

「paths」:從我的「imutils」包中引入,該模塊可以幫助我們收集磁盤(pán)上所有圖像文件的路徑。

「pyplot」:用來(lái)生成一個(gè)精美的訓(xùn)練過(guò)程示意圖。

「numpy」:一個(gè)用于 Python 的數(shù)字處理開(kāi)發(fā)庫(kù),它也是 OpenCV 的所需要的。

「argparse」:用于處理命令行參數(shù)(相關(guān)閱讀:https://www.pyimagesearch.com/2018/03/12/python-argparse-command-line-arguments/)

「pickle」:被用來(lái)將我們的標(biāo)簽編碼器序列化,從而寫(xiě)入磁盤(pán)。

「cv2」:用于綁定 OpenCV。

「os」:該模塊有很多功能,但在這里我們僅使用它來(lái)創(chuàng)建操作系統(tǒng)路徑分隔符。

輸入似乎有點(diǎn)多,但是在了解這些輸入的目的后,檢查腳本的其余部分就應(yīng)該更加直觀了。

該腳本接受四個(gè)命令行參數(shù):

「--dataset」:輸入數(shù)據(jù)集的路徑。本文前面通過(guò)「gather_examples.py」腳本創(chuàng)建了該數(shù)據(jù)集。

「--model」:我們的腳本會(huì)生成一個(gè)輸出模型文件,通過(guò)該參數(shù)指定其路徑。

「--le」:輸出序列化標(biāo)簽編碼器文件所需要提供的路徑。

「--plot」:訓(xùn)練腳本將生成一個(gè)訓(xùn)練過(guò)程示意圖。如果你想重寫(xiě)「plot.png」的缺省值,你應(yīng)該在命令行中指定該值。

下面的代碼塊將執(zhí)行一系列初始化工作,并創(chuàng)建我們的數(shù)據(jù):

# initialize the initial learning rate, batch size, and number of

# epochs to train for

INIT_LR = 1e-4

BS = 8

EPOCHS = 50

# grab the list of images in our dataset directory, then initialize

# the list of data (i.e., images) and class images

print("[INFO] loading images...")

imagePaths = list(paths.list_images(args["dataset"]))

data = []

labels = []

for imagePath in imagePaths:

# extract the class label from the filename, load the image and

# resize it to be a fixed 32x32 pixels, ignoring aspect ratio

label = imagePath.split(os.path.sep)[-2]

image = cv2.imread(imagePath)

image = cv2.resize(image, (32, 32))

# update the data and labels lists, respectively

data.append(image)

labels.append(label)

# convert the data into a NumPy array, then preprocess it by scaling

# all pixel intensities to the range [0, 1]

data = np.array(data, dtype="float") / 255.0

在第 35-37 行設(shè)置的訓(xùn)練參數(shù)包括學(xué)習(xí)率、批處理規(guī)模以及迭代的次數(shù)。

到這里,我們就獲取到了「iamgePaths」中的路徑,同時(shí)也初始化了兩個(gè)用來(lái)存放「data」和類(lèi)「labels」的列表(第 42-44 行)。

第 46-55 行的循環(huán)創(chuàng)建好了「data」和「labels」列表?!竏ata」列表由我們加載并重新調(diào)整尺寸到 32*32 像素的圖像組成。而每張圖像則都有一個(gè)對(duì)應(yīng)的標(biāo)簽被存儲(chǔ)在「labels」列表中。

每個(gè)像素的亮度都被放縮到了 [0,1] 區(qū)間內(nèi),我們?cè)诘?59 行將列表轉(zhuǎn)換成了 NumPy array 的形式。

接下來(lái),我們將對(duì)標(biāo)簽進(jìn)行編碼并對(duì)數(shù)據(jù)進(jìn)行劃分:

# encode the labels (which are currently strings) as integers and then

# one-hot encode them

le = LabelEncoder()

labels = le.fit_transform(labels)

labels = np_utils.to_categorical(labels, 2)

# partition the data into training and testing splits using 75% of

# the data for training and the remaining 25% for testing

(trainX, testX, trainY, testY) = train_test_split(data, labels,

test_size=0.25, random_state=42)

第 63-65 行將標(biāo)簽編碼成了獨(dú)熱向量。

我們使用 scikit-learn 對(duì)數(shù)據(jù)進(jìn)行劃分——將 75% 的數(shù)據(jù)用來(lái)做訓(xùn)練,其余 25% 的數(shù)據(jù)則被留作測(cè)試之用(第 69 和 70 行)。

接下來(lái),我們將初始化我們的數(shù)據(jù)增強(qiáng)對(duì)象,并編譯、訓(xùn)練我們的人臉活體檢測(cè)模型:

# construct the training image generator for data augmentation

aug = ImageDataGenerator(rotation_range=20, zoom_range=0.15,

width_shift_range=0.2, height_shift_range=0.2, shear_range=0.15,

horizontal_flip=True, fill_mode="nearest")

# initialize the optimizer and model

print("[INFO] compiling model...")

opt = Adam(lr=INIT_LR, decay=INIT_LR / EPOCHS)

model = LivenessNet.build(width=32, height=32, depth=3,

classes=len(le.classes_))

model.compile(loss="binary_crossentropy", optimizer=opt,

metrics=["accuracy"])

# train the network

print("[INFO] training network for {} epochs...".format(EPOCHS))

H = model.fit_generator(aug.flow(trainX, trainY, batch_size=BS),

validation_data=(testX, testY), steps_per_epoch=len(trainX) // BS,

epochs=EPOCHS)

第 73-75 行構(gòu)造了一個(gè)數(shù)據(jù)增強(qiáng)對(duì)象,它將通過(guò)隨機(jī)的旋轉(zhuǎn),縮放、平移、剪切和翻轉(zhuǎn)生成新的圖像。想要了解更多關(guān)于數(shù)據(jù)增強(qiáng)的知識(shí),請(qǐng)參閱下面這篇博文:https://www.pyimagesearch.com/2018/12/24/how-to-use-keras-fit-and-fit_generator-a-hands-on-tutorial/

我們?cè)诘?79-83 行構(gòu)建并編譯了「LivenessNet」模型。

接著,我們?cè)诘?87-89 行開(kāi)始訓(xùn)練模型。由于我們使用了淺層網(wǎng)絡(luò)和小型數(shù)據(jù)集,這個(gè)過(guò)程相對(duì)會(huì)比較快。

當(dāng)模型訓(xùn)練完成后,我們可以評(píng)估訓(xùn)練結(jié)果,并生成一個(gè)訓(xùn)練過(guò)程示意圖:

# evaluate the network

print("[INFO] evaluating network...")

predictions = model.predict(testX, batch_size=BS)

print(classification_report(testY.argmax(axis=1),

predictions.argmax(axis=1), target_names=le.classes_))

# save the network to disk

print("[INFO] serializing network to '{}'...".format(args["model"]))

model.save(args["model"])

# save the label encoder to disk

f = open(args["le"], "wb")

f.write(pickle.dumps(le))

f.close()

# plot the training loss and accuracy

plt.style.use("ggplot")

plt.figure()

plt.plot(np.arange(0, EPOCHS), H.history["loss"], label="train_loss")

plt.plot(np.arange(0, EPOCHS), H.history["val_loss"], label="val_loss")

plt.plot(np.arange(0, EPOCHS), H.history["acc"], label="train_acc")

plt.plot(np.arange(0, EPOCHS), H.history["val_acc"], label="val_acc")

plt.title("Training Loss and Accuracy on Dataset")

plt.xlabel("Epoch #")

plt.ylabel("Loss/Accuracy")

plt.legend(loc="lower left")

plt.savefig(args["plot"])

第 93 行給出了在測(cè)試集上得到的預(yù)測(cè)結(jié)果。以此為基礎(chǔ),生成了分類(lèi)結(jié)果報(bào)告「classification_report」并顯示在終端中(第 94 和 95 行)。

「LivenessNet」模型和標(biāo)簽編碼器在第 99-104 行被序列化并寫(xiě)入磁盤(pán)。

剩余的第 107-117 行生成了一個(gè)訓(xùn)練過(guò)程示意圖,以待查看。

訓(xùn)練我們的活體檢測(cè)器

現(xiàn)在,我們已經(jīng)為訓(xùn)練活體檢測(cè)器做好了準(zhǔn)備。

請(qǐng)確保你通過(guò)本教程「下載」部分的鏈接下載了源代碼和數(shù)據(jù)集。接著,請(qǐng)執(zhí)行下面你的命令:

$ python train.py --dataset dataset --model liveness.model --le le.pickle

[INFO] loading images...

[INFO] compiling model...

[INFO] training network for 50 epochs...

Epoch 1/50

29/29 [==============================] - 2s 58ms/step - loss: 1.0113 - acc: 0.5862 - val_loss: 0.4749 - val_acc: 0.7436

Epoch 2/50

29/29 [==============================] - 1s 21ms/step - loss: 0.9418 - acc: 0.6127 - val_loss: 0.4436 - val_acc: 0.7949

Epoch 3/50

29/29 [==============================] - 1s 21ms/step - loss: 0.8926 - acc: 0.6472 - val_loss: 0.3837 - val_acc: 0.8077

...

Epoch 48/50

29/29 [==============================] - 1s 21ms/step - loss: 0.2796 - acc: 0.9094 - val_loss: 0.0299 - val_acc: 1.0000

Epoch 49/50

29/29 [==============================] - 1s 21ms/step - loss: 0.3733 - acc: 0.8792 - val_loss: 0.0346 - val_acc: 0.9872

Epoch 50/50

29/29 [==============================] - 1s 21ms/step - loss: 0.2660 - acc: 0.9008 - val_loss: 0.0322 - val_acc: 0.9872

[INFO] evaluating network...

precisionrecallf1-score support

fake 0.971.000.9935

real 1.000.980.9943

micro avg 0.990.990.9978

macro avg 0.990.990.9978

weighted avg 0.990.990.9978

[INFO] serializing network to 'liveness.model'...

圖 6:使用 OpenCV、Keras 以及深度學(xué)習(xí)技術(shù)訓(xùn)練一個(gè)人臉活體檢測(cè)模型的訓(xùn)練過(guò)程示意圖。

如結(jié)果所示,我們?cè)隍?yàn)證集上實(shí)現(xiàn) 99% 的活體檢測(cè)準(zhǔn)確率。

整合一下:通過(guò) OpenCV 實(shí)現(xiàn)活體檢測(cè)

圖 7:使用 OpenCV 和深度學(xué)習(xí)技術(shù)實(shí)現(xiàn)人臉活體檢測(cè)

最后,我們需要做的是將以上內(nèi)容整合起來(lái):

1. 連接到我們的網(wǎng)絡(luò)攝像頭/視頻流

2.將人臉檢測(cè)應(yīng)用到每一幀上

3.對(duì)每一個(gè)檢測(cè)到的人臉應(yīng)用我們的活體檢測(cè)模型

請(qǐng)打開(kāi)「liveness_demo.py」文件并插入下列代碼:

# import the necessary packages

from imutils.video import VideoStream

from keras.preprocessing.image import img_to_array

from keras.models import load_model

import numpy as np

import argparse

import imutils

import pickle

import time

import cv2

import os

# construct the argument parse and parse the arguments

ap = argparse.ArgumentParser()

ap.add_argument("-m", "--model", type=str, required=True,

help="path to trained model")

ap.add_argument("-l", "--le", type=str, required=True,

help="path to label encoder")

ap.add_argument("-d", "--detector", type=str, required=True,

help="path to OpenCV's deep learning face detector")

ap.add_argument("-c", "--confidence", type=float, default=0.5,

help="minimum probability to filter weak detections")

args = vars(ap.parse_args())

第 2-11 行輸入了我們需要的安裝包。注意,我們將使用:

「VideoStream」:用來(lái)連接到我們的攝像頭。

「img_to_array」:令我們的視頻幀存儲(chǔ)在一個(gè)兼容的數(shù)組格式中。

「load_model」:加載我們的序列化 Keras 模型。

「imutils」:包含一些方便使用的工具函數(shù)。

「cv2」:綁定 OpenCV。

在第 14-23 行中,我們將解析命令行參數(shù):

「--model」:我們預(yù)訓(xùn)練好的用于活體檢測(cè)的 Keras 模型的路徑。

「--le」:標(biāo)簽編碼器的路徑。

「--detector」:用于計(jì)算出人臉 ROI 的 OpenCV 的深度學(xué)習(xí)人臉檢測(cè)器的路徑。

「--confidence」:過(guò)濾掉較弱的檢測(cè)結(jié)果的最小閾值概率。

接下來(lái),我們將初始化人臉檢測(cè)器、LivenessNet 模型 + 標(biāo)簽編碼器,以及我們的視頻流:

# load our serialized face detector from disk

print("[INFO] loading face detector...")

protoPath = os.path.sep.join([args["detector"], "deploy.prototxt"])

modelPath = os.path.sep.join([args["detector"],

"res10_300x300_ssd_iter_140000.caffemodel"])

net = cv2.dnn.readNetFromCaffe(protoPath, modelPath)

# load the liveness detector model and label encoder from disk

print("[INFO] loading liveness detector...")

model = load_model(args["model"])

le = pickle.loads(open(args["le"], "rb").read())

# initialize the video stream and allow the camera sensor to warmup

print("[INFO] starting video stream...")

vs = VideoStream(src=0).start()

time.sleep(2.0)

在第 27-30 行加載 OpenCV 的人臉檢測(cè)器。

在此基礎(chǔ)上,我們加載了序列化、預(yù)訓(xùn)練的 LivenessNet 模型以及標(biāo)簽編碼器(第 34-35 行)。

在第 39 和 40 行中,我們的「VideoStream」對(duì)象被實(shí)例化,并且允許攝像頭預(yù)熱 2 秒。

至此,是時(shí)候開(kāi)始循環(huán)輸入視頻幀來(lái)檢測(cè)「真實(shí)」人臉和「?jìng)卧?欺騙性」人臉了:

# loop over the frames from the video stream

while True:

# grab the frame from the threaded video stream and resize it

# to have a maximum width of 600 pixels

frame = vs.read()

frame = imutils.resize(frame, width=600)

# grab the frame dimensions and convert it to a blob

(h, w) = frame.shape[:2]

blob = cv2.dnn.blobFromImage(cv2.resize(frame, (300, 300)), 1.0,

(300, 300), (104.0, 177.0, 123.0))

# pass the blob through the network and obtain the detections and

# predictions

net.setInput(blob)

detections = net.forward()

第 43 行開(kāi)啟了一個(gè)無(wú)限的「while」循環(huán)代碼塊,我們首先讀取某一幀然后對(duì)其進(jìn)行放縮(第46 和 47 行)。

重新定義圖像尺寸后,獲取幀的尺寸,以便稍后執(zhí)行縮放(第 50 行)。

使用 OpenCV 的「blobFromImage」函數(shù)(https://www.pyimagesearch.com/2017/11/06/deep-learning-opencvs-blobfromimage-works/)生成一個(gè)「blob」(第 51 行和 52 行),然后讓其通過(guò)人臉檢測(cè)網(wǎng)絡(luò),完成推理過(guò)程(第 56 和 57 行)。

現(xiàn)在,讓我們進(jìn)入有趣的部分——使用 OpenCV 和深度學(xué)習(xí)進(jìn)行活體檢測(cè)。

# loop over the detections

for i in range(0, detections.shape[2]):

# extract the confidence (i.e., probability) associated with the

# prediction

confidence = detections[0, 0, i, 2]

# filter out weak detections

if confidence > args["confidence"]:

# compute the (x, y)-coordinates of the bounding box for

# the face and extract the face ROI

box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])

(startX, startY, endX, endY) = box.astype("int")

# ensure the detected bounding box does fall outside the

# dimensions of the frame

startX = max(0, startX)

startY = max(0, startY)

endX = min(w, endX)

endY = min(h, endY)

# extract the face ROI and then preproces it in the exact

# same manner as our training data

face = frame[startY:endY, startX:endX]

face = cv2.resize(face, (32, 32))

face = face.astype("float") / 255.0

face = img_to_array(face)

face = np.expand_dims(face, axis=0)

# pass the face ROI through the trained liveness detector

# model to determine if the face is "real" or "fake"

preds = model.predict(face)[0]

j = np.argmax(preds)

label = le.classes_[j]

# draw the label and bounding box on the frame

label = "{}: {:.4f}".format(label, preds[j])

cv2.putText(frame, label, (startX, startY - 10),

cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)

cv2.rectangle(frame, (startX, startY), (endX, endY),

(0, 0, 255), 2)

在第 60 行中,我們開(kāi)始循環(huán)進(jìn)行人臉檢測(cè)。在循環(huán)過(guò)程中,我們做到了:

過(guò)濾掉較弱的檢測(cè)結(jié)果(第 63-66 行)。

抽取出人臉邊界「box」的坐標(biāo),并確保它們不會(huì)超出檢測(cè)框的尺寸范圍(第 69-77 行)。

抽取出人臉 ROI,并使用訓(xùn)練數(shù)據(jù)所采取的同樣方式對(duì)其進(jìn)行預(yù)處理。(第 81-85 行)

使用活體檢測(cè)模型來(lái)判別輸入的人臉是「真實(shí)的」還是「?jìng)卧?欺騙性」的(第 89-91 行)。

在第 91 行中,你可以插入自己的代碼執(zhí)行人臉識(shí)別任務(wù),但是只能在真實(shí)圖像上進(jìn)行。偽代碼與「if label == "real": run_face_reconition()」類(lèi)似,可以直接添加到第 91 行之后。

最后(在這個(gè)演示樣例中),我們會(huì)繪制出「label」文本和一個(gè)包圍人臉的「rectangle」(第 94-98 行)。

接下來(lái),我們將展示檢測(cè)結(jié)果并清理內(nèi)存:

# show the output frame and wait for a key press

cv2.imshow("Frame", frame)

key = cv2.waitKey(1) & 0xFF

# if the `q` key was pressed, break from the loop

if key == ord("q"):

break

# do a bit of cleanup

cv2.destroyAllWindows()

vs.stop()

當(dāng)捕獲到鍵盤(pán)按鍵時(shí),循環(huán)過(guò)程中的每一輪迭代都會(huì)顯示出輸出幀(第 101-102 行)。只要用戶(hù)按下「q」(quit)鍵,檢測(cè)器就停止循環(huán)、開(kāi)啟指示器并關(guān)閉窗口(第 105-110 行)。

將我們的活體檢測(cè)器應(yīng)用到實(shí)時(shí)視頻上

如果讀者想跟著我們的活體檢測(cè)演示完成整個(gè)執(zhí)行過(guò)程,請(qǐng)確保你已經(jīng)通過(guò)本文「下載」部分的鏈接下載到了源代碼和預(yù)訓(xùn)練好的活體檢測(cè)模型。

接著,請(qǐng)打開(kāi)一個(gè)終端并執(zhí)行下面的命令:

$ python liveness_demo.py --model liveness.model --le le.pickle

--detector face_detector

Using TensorFlow backend.

[INFO] loading face detector...

[INFO] loading liveness detector...

[INFO] starting video stream...

在這里,你可以看到我們的活體檢測(cè)器成功地將真實(shí)人臉和偽造人臉區(qū)分了開(kāi)來(lái)。

我在下面的視頻匯中展示了一段更長(zhǎng)的演示樣例。

視頻觀看地址:https://youtu.be/MPedzm6uOMA

局限性、改進(jìn)和未來(lái)的工作

我們的活性檢測(cè)器的主要限制實(shí)際上是數(shù)據(jù)集比較有限——總共只有 311 張圖像(包含「真實(shí)」類(lèi)的 161 張圖像和「?jìng)卧臁诡?lèi)的 150張圖像)。

這項(xiàng)工作的一項(xiàng)擴(kuò)展,就是收集額外的訓(xùn)練數(shù)據(jù),更具體地說(shuō),可以收集來(lái)自于你、我之外的人的圖像/視頻幀數(shù)據(jù)。

請(qǐng)記住,這里使用的示例數(shù)據(jù)集只包含一個(gè)人(我自己)的人臉。我是白人/白種人,而你應(yīng)該收集其他種族和膚色的人臉訓(xùn)練數(shù)據(jù)。

我們的活體檢測(cè)器只在屏幕上具有欺騙性攻擊的人臉進(jìn)行訓(xùn)練,而沒(méi)有對(duì)打印出來(lái)的圖像或照片進(jìn)行訓(xùn)練。因此,我的第三個(gè)建議是,除了簡(jiǎn)單的屏幕錄制回放之外,還要收集其它種類(lèi)的圖像/人臉資源。

最后,我想說(shuō):想實(shí)現(xiàn)活體檢測(cè)并沒(méi)有什么捷徑。

最好的活體檢測(cè)器應(yīng)該包含多種活體檢測(cè)方法(請(qǐng)參閱上面的「什么是活體檢測(cè), 我們?yōu)槭裁葱枰俊挂还?jié))。

你需要花點(diǎn)時(shí)間來(lái)考慮和評(píng)估你自己的項(xiàng)目、指導(dǎo)方針和需求。在某些情況下,你可能只需要用到基本的眨眼檢測(cè)啟發(fā)方法。

而在其他情況下,你需要將基于深度學(xué)習(xí)的活體檢測(cè)與其它啟發(fā)式方法相結(jié)合。

不要急于進(jìn)行人臉識(shí)別和活體檢測(cè)。你克制一下自己,花點(diǎn)時(shí)間考慮自己獨(dú)特的項(xiàng)目需求。這樣的話將確保你獲得更好、更準(zhǔn)確的結(jié)果。

總結(jié)

通過(guò)學(xué)習(xí)本教程,你就可以掌握如何使用 OpenCV 進(jìn)行活體檢測(cè)。

現(xiàn)在通過(guò)使用活體檢測(cè)器,你就可以檢測(cè)出偽造的人臉,并在你自己的人臉識(shí)別系統(tǒng)中執(zhí)行反人臉欺騙過(guò)程。

在活體檢測(cè)器的創(chuàng)建過(guò)程中,我們用到了 OpenCV、深度學(xué)習(xí)技術(shù)以及 Python 語(yǔ)言。

首先,我們需要收集自己的「真實(shí) vs 偽造」人臉數(shù)據(jù)集。為了完成該任務(wù),我們要做到:

1. 用智能手機(jī)錄制一段我們自己的視頻(即「真實(shí)」人臉)。

2. 將我們的智能手機(jī)屏幕展示給筆記本電腦/桌面電腦的攝像頭,重放在上一步中錄制的同一個(gè)視頻,然后使用你的網(wǎng)絡(luò)攝像頭錄下視頻回放(即「?jìng)卧臁谷四槪?/p>

3. 將人臉檢測(cè)技術(shù)同時(shí)應(yīng)用到上面兩個(gè)視頻集合中,以構(gòu)建最終的活體檢測(cè)數(shù)據(jù)集。

在構(gòu)建好數(shù)據(jù)集后,我們實(shí)現(xiàn)了「LivenessNet」——它是一個(gè)用 Keras 和深度學(xué)習(xí)技術(shù)實(shí)現(xiàn)的卷積神經(jīng)網(wǎng)絡(luò)。

我們特意將網(wǎng)絡(luò)設(shè)計(jì)得很淺,這是為了確保:

1. 在我們的小型數(shù)據(jù)集上減少過(guò)擬合現(xiàn)象。

2. 模型能夠?qū)崟r(shí)運(yùn)行(包括在樹(shù)莓派等硬件上)。

總的來(lái)說(shuō),該活體檢測(cè)器在我們的驗(yàn)證集上的表現(xiàn),準(zhǔn)確率高達(dá) 99% 。

為了演示完整的活體檢測(cè)工作流程,我們創(chuàng)建了一個(gè) Python + OpenCV 的腳本,該腳本加載了我們的活體檢測(cè)程序并將其應(yīng)用在了實(shí)時(shí)視頻流上。

正如我們的演示樣例所示,我們的活體檢測(cè)器能夠區(qū)分真假人臉。

希望你喜歡這篇使用 OpenCV 進(jìn)行活體檢測(cè)的文章!

資源下載鏈接:https://www.getdrip.com/forms/321809846/submissions

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

責(zé)任編輯:lq

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 算法
    +關(guān)注

    關(guān)注

    23

    文章

    4552

    瀏覽量

    92030
  • 人臉識(shí)別
    +關(guān)注

    關(guān)注

    76

    文章

    3998

    瀏覽量

    81364
  • OpenCV
    +關(guān)注

    關(guān)注

    29

    文章

    622

    瀏覽量

    41091

原文標(biāo)題:干貨 | 史上最全 OpenCV 活體檢測(cè)教程!

文章出處:【微信號(hào):vision263com,微信公眾號(hào):新機(jī)器視覺(jué)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    opencv圖像識(shí)別有什么算法

    OpenCV(Open Source Computer Vision Library)是一個(gè)開(kāi)源的計(jì)算機(jī)視覺(jué)和機(jī)器學(xué)習(xí)軟件庫(kù),提供了大量的圖像處理和計(jì)算機(jī)視覺(jué)相關(guān)的算法。以下是一些常見(jiàn)的OpenCV
    的頭像 發(fā)表于 07-16 10:40 ?375次閱讀

    opencv-python和opencv一樣嗎

    不一樣。OpenCV(Open Source Computer Vision Library)是一個(gè)開(kāi)源的計(jì)算機(jī)視覺(jué)和機(jī)器學(xué)習(xí)軟件庫(kù),它提供了大量的圖像和視頻處理功能。OpenCV
    的頭像 發(fā)表于 07-16 10:38 ?453次閱讀

    opencv的主要功能有哪些

    OpenCV提供了豐富的圖像處理功能,包括圖像的讀取、顯示、保存、轉(zhuǎn)換等。此外,OpenCV還支持圖像的濾波、邊緣檢測(cè)、形態(tài)學(xué)操作、圖像金字塔等高級(jí)圖像處理技術(shù)。 特征檢測(cè)與描述:
    的頭像 發(fā)表于 07-16 10:35 ?803次閱讀

    使用OpenCV進(jìn)行儀表指針刻度的識(shí)別與讀取

    首先說(shuō)一下模板匹配,它是OpenCV自帶的一個(gè)算法,可以根據(jù)一個(gè)模板圖到目標(biāo)圖上去尋找對(duì)應(yīng)位置,如果模板找的比較好那么效果顯著,這里說(shuō)一下尋找模板的技巧,模板一定要標(biāo)準(zhǔn)、精準(zhǔn)且特征明顯。
    發(fā)表于 02-22 13:54 ?1521次閱讀
    使用<b class='flag-5'>OpenCV</b><b class='flag-5'>進(jìn)行</b>儀表指針刻度的識(shí)別與讀取

    在核桃派上實(shí)現(xiàn)USB攝像頭的OpenCV顏色檢測(cè)

    在前幾周的文章中已經(jīng)介紹過(guò)如何在核桃派上用OpenCV讀取圖像并顯示到pyqt5的窗口上,這里在上一篇文章的基礎(chǔ)上,給開(kāi)發(fā)板加入顏色檢測(cè)功能,嘗試將圖像中所有藍(lán)色的東西都用一個(gè)框標(biāo)記出來(lái)。
    的頭像 發(fā)表于 01-20 17:50 ?809次閱讀
    在核桃派上實(shí)現(xiàn)USB攝像頭的<b class='flag-5'>OpenCV</b>顏色<b class='flag-5'>檢測(cè)</b>

    【EASY EAI Nano】RV1126實(shí)時(shí)讀取攝像頭并進(jìn)行yolo檢測(cè)顯示

    實(shí)現(xiàn)了三個(gè)并行模塊,分別是 攝像頭讀取,使用opencv轉(zhuǎn)換到適合大小 yolo檢測(cè) 托管到Qt進(jìn)行現(xiàn)實(shí) 檢測(cè)的DEMO從每幀10次改到每幀2次,可以看到還是具備一定實(shí)時(shí)性。
    發(fā)表于 01-14 18:53

    OpenCV4.8 CUDA編程代碼教程

    OpenCV4支持通過(guò)GPU實(shí)現(xiàn)CUDA加速執(zhí)行,實(shí)現(xiàn)對(duì)OpenCV圖像處理程序的加速運(yùn)行,當(dāng)前支持加速的模塊包括如下。
    的頭像 發(fā)表于 12-05 09:56 ?846次閱讀
    <b class='flag-5'>OpenCV</b>4.8 CUDA編程代碼教程

    OpenCV對(duì)openEuler提供上游原生支持

    ),是一個(gè)多平臺(tái)的開(kāi)源計(jì)算機(jī)視覺(jué)庫(kù)。它提供了豐富的功能,包括圖像處理,特征檢測(cè),目標(biāo)識(shí)別,機(jī)器學(xué)習(xí)等,在計(jì)算機(jī)視覺(jué)領(lǐng)域,OpenCV憑借其強(qiáng)大的功能、性能和廣泛的應(yīng)用場(chǎng)景,已經(jīng)建立起絕對(duì)的領(lǐng)導(dǎo)
    的頭像 發(fā)表于 11-20 09:14 ?683次閱讀

    基于OpenVINO+OpenCV的OCR處理流程化實(shí)現(xiàn)

    預(yù)處理主要是基于OpenCV、場(chǎng)景文字檢測(cè)與識(shí)別基于OpenVINO框架 + PaddleOCR模型完成。直接按圖索驥即可得到最終結(jié)果。 OpenCV預(yù)處理主要是完成偏斜矯正、背景矯正等操作,然后使用場(chǎng)景文字
    的頭像 發(fā)表于 11-07 11:21 ?618次閱讀
    基于OpenVINO+<b class='flag-5'>OpenCV</b>的OCR處理流程化實(shí)現(xiàn)

    對(duì)于形狀近似矩形但邊緣有規(guī)則起伏的情況,可以使用OpenCV庫(kù)中的approxPolyDP函數(shù)進(jìn)行多邊形擬合和矩形檢測(cè)。

    對(duì)于形狀近似矩形但邊緣有規(guī)則起伏的情況,可以使用OpenCV庫(kù)中的approxPolyDP函數(shù)進(jìn)行多邊形擬合和矩形檢測(cè)。 approxPolyDP函數(shù)通過(guò)在給定的點(diǎn)集上使用動(dòng)態(tài)規(guī)劃算法,計(jì)算出近似
    發(fā)表于 11-01 09:23

    OpenCV基礎(chǔ)知識(shí)入門(mén)

    OpenCV是計(jì)算機(jī)視覺(jué)中最受歡迎的庫(kù),最初由intel使用C和C ++進(jìn)行開(kāi)發(fā)的,現(xiàn)在也可以在python中使用。該庫(kù)是一個(gè)跨平臺(tái)的開(kāi)源庫(kù),是免費(fèi)使用的。OpenCV庫(kù)是一個(gè)高度優(yōu)化的庫(kù),主要關(guān)注實(shí)時(shí)應(yīng)用程序。
    的頭像 發(fā)表于 10-29 11:29 ?661次閱讀
    <b class='flag-5'>OpenCV</b>基礎(chǔ)知識(shí)入門(mén)

    Android系統(tǒng)下OpenCV的人臉檢測(cè)模塊的設(shè)計(jì)

    電子發(fā)燒友網(wǎng)站提供《Android系統(tǒng)下OpenCV的人臉檢測(cè)模塊的設(shè)計(jì).pdf》資料免費(fèi)下載
    發(fā)表于 10-23 09:37 ?0次下載
    Android系統(tǒng)下<b class='flag-5'>OpenCV</b>的人臉<b class='flag-5'>檢測(cè)</b>模塊的設(shè)計(jì)

    openCV邊緣檢測(cè)原理是什么?

    openCV是通過(guò)什么原理來(lái)實(shí)現(xiàn)邊緣檢測(cè)
    發(fā)表于 10-10 06:21

    OpenCV 如何加載圖片

    之前也寫(xiě)過(guò)一些 OpenCV 的文章,最近正好在考慮 寫(xiě)一個(gè) OpenCV相關(guān)的工具,目前還是在開(kāi)發(fā)過(guò)程中,邊開(kāi)發(fā)邊更新。預(yù)計(jì)會(huì)持續(xù)一段時(shí)間。目前的想法是把 OpenCV 的 API 在這個(gè)工具上都
    的頭像 發(fā)表于 10-09 15:01 ?605次閱讀

    【幸狐 Core3566 模組試用體驗(yàn)】基于openCV的貓臉識(shí)別

    OpenCV中,目標(biāo)檢測(cè)使用的函數(shù)是cv2.CascadeClassifier.detectMultiScale(),它可以檢測(cè)圖像中所有的目標(biāo)。其完整定義如下: def detectMultiScale
    發(fā)表于 09-24 23:50