本教程將教授你如何使用 OpenCV 進(jìn)行活性檢測(cè)。通過(guò)學(xué)習(xí),你將能夠在人臉識(shí)別系統(tǒng)中創(chuàng)建一個(gè)可以發(fā)現(xiàn)偽造人臉并執(zhí)行反人臉欺騙的活體檢測(cè)器。
他們可能會(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)用中呢?
在本教程的第一部分,我們將對(duì)活體檢測(cè)進(jìn)行討論,包括「活體檢測(cè)是什么?」以及「為什么我們需要活體檢測(cè)來(lái)改進(jìn)人臉識(shí)別系統(tǒng)?」
我們將訓(xùn)練一個(gè)能夠區(qū)分真人臉和偽造人臉的深度神經(jīng)網(wǎng)絡(luò),來(lái)創(chuàng)建活體檢測(cè)器。
什么是活體檢測(cè), 我們?yōu)槭裁葱枰?/strong>
圖 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è)的方法有很多,包括:
頻率分析,如查看人臉圖片的傅里葉域(對(duì)其進(jìn)行傅里葉變換)。
變量聚焦分析,例如查看兩個(gè)連續(xù)幀之間像素值的變化。
基于啟發(fā)式的算法,包括眼球運(yùn)動(dòng)、嘴唇運(yùn)動(dòng)和眨眼檢測(cè)(https://www./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:///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è)主要的目錄:
(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)確率和損失曲線(xià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
圖 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./2018/03/12/python-argparse-command-line-arguments/):
「--input」:我們的輸入視頻文件的路徑。
「--output」:儲(chǔ)存每個(gè)裁剪出來(lái)的人臉圖像的輸出目錄的路徑。
「--detector」:人臉檢測(cè)器的路徑。我們將使用 OpenCV 的深度學(xué)習(xí)人臉檢測(cè)器(https://www./2018/02/26/face-detection-with-opencv-and-deep-learning/)。為了方便讀者使用,Caffe 模型可在本文的「下載」部分獲得。
「--confidence」:過(guò)濾弱人臉檢測(cè)結(jié)果的最小概率。默認(rèn)情況下,該值為 50%。
「--skip」:我們不需要檢測(cè)并存儲(chǔ)每個(gè)圖像,因?yàn)橄噜彽膸窍嗨频?。相反我們?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./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./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ù):
我們?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)練腳本
圖 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 行)。它們分別是:
「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./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)該更加直觀(guān)了。
該腳本接受四個(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./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...
precision recall f1-score support
fake 0.97 1.00 0.99 35
real 1.00 0.98 0.99 43
micro avg 0.99 0.99 0.99 78
macro avg 0.99 0.99 0.99 78
weighted avg 0.99 0.99 0.99 78
[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./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)的演示樣例。
局限性、改進(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)目需求。這樣的話(huà)將確保你獲得更好、更準(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./forms/321809846/submissions
via: https://www./2019/03/11/liveness-detection-with-opencv