活體檢測:keras

日萌社

人工智能AI:Keras PyTorch MXNet TensorFlow PaddlePaddle 深度學習實戰(不定時更新)


 

1. 什麼是活體檢測?
--> 判斷捕捉到的人臉是真實人臉,還是僞造的人臉攻擊(如:彩色紙張打印人臉圖,電子設備屏幕中的人臉數字圖像 以及 面具 等)
2. 爲什麼需要活體檢測?
--> 在金融支付,門禁等應用場景,活體檢測一般是嵌套在人臉檢測與人臉識別or驗證中的模塊,用來驗證是否用戶真實本人
3. 活體檢測對應的計算機視覺問題:
--> 就是分類問題,可看成二分類(真 or 假);也可看成多分類(真人,紙張攻擊,屏幕攻擊,面具攻擊)

據《人民日報》報道,嘉興上外秀洲外國語學校402班科學小隊向都市快報《好奇實驗室》報料:他們在一次課外科學實驗中發現,只要用一張打印照片就能代替真人刷臉,騙過小區裏的快遞智能櫃,最終取出父母們的貨件。隨後,小朋友們還發來了幾段視頻佐證。

據《人民日報》報道,嘉興上外秀洲外國語學校402班科學小隊向都市快報《好奇實驗室》報料:他們在一次課外科學實驗中發現,只要用一張打印照片就能代替真人刷臉,騙過小區裏的快遞智能櫃,最終取出父母們的貨件。隨後,小朋友們還發來了幾段視頻佐證。

生物識別技術在驗證過程中出現的漏洞可能會讓不法分子破解各種人臉識別應用,包括蘋果的 Face ID。 在拉斯維加斯舉辦的 2019 世界黑帽(Black Hat)安全大會上,騰訊公司的研究人員演示了攻破蘋果 Face ID 的法寶:一款特製眼鏡。這幅眼鏡鏡片上貼有黑色膠帶,黑色膠帶中心還貼有白色膠帶。

在演示中,研究人員只需要將這款眼鏡戴在受害者的臉上即可解鎖 Face ID,訪問手機。但是,鑑於不法分子需要在不喚醒受害者的情況下把眼鏡戴在他/她的臉上,所以這種攻擊本身存在難度。 在攻破蘋果 Face ID 的過程中,研究人員利用了生物識別技術背後的「活體檢測」功能。活體檢測是篩選人們「真假」特徵的生物識別認證過程中的一部分,其原理是檢測背景噪聲、響應失真或聚焦模糊。蘋果公司在 iPhone 和 iPad Pro 的 Face ID 人臉識別系統中使用的就是這種活體檢測功能。

騰訊安全研究人員馬卓(Zhuo Ma)認爲,雖然以前的攻擊主要聚焦於製作假數據來欺騙生物識別技術,但這種類型的音頻或視頻攻擊由各種部分組成,包括竊取受害者設備的指紋信息、生成假音頻或視頻以及硬件層面的侵入破解等。 而在此次攻擊實驗中,研究人員決定聚焦於活體檢測(允許用戶掃一眼即可解鎖手機),從而希望在受害者意識不清醒的時候通過面部來欺騙 Face ID。 但是,馬卓還表示:「這種操作非常具有挑戰性,我們不能吵醒正在熟睡的受害者,同時 3D 系統的僞造也存在諸多困難... 所以我們需要找到一種成本低但成功率高的解決方案。」

利用活體檢測破解 FaceID 研究人員專門研究了活體檢測如何掃描用戶眼睛。他們發現,活體檢測對人眼的抽象化處理是在黑色區域(人眼)上嵌上白點(虹膜),即以黑區+白點的形式來模擬眼睛和虹膜。此外,如果用戶戴上眼鏡,活體檢測掃描眼睛的方式也會出現變化。 研究人員發現了蘋果 Face ID 的漏洞,即用戶戴着眼鏡時也能解鎖手機。當 Face ID 識別到用戶戴着眼鏡時,就會自動跳過對眼部區域 3D 信息的提取。

利用活體檢測破解 FaceID 所以,結合以上兩種因素,研究人員製作了一副眼鏡原型——X 眼鏡(X-glasses):眼鏡鏡片上貼有黑色膠帶,黑色膠帶又內嵌白色膠帶,以模仿眼睛的構造。他們將這款貼有雙重膠帶的眼鏡戴在熟睡的受害者臉上,欺騙 Face ID 和其他類似技術的注意力檢測機制,從而可以解鎖受害者的手機,並通過移動支付應用轉走受害者的錢。 研究人員認爲,針對蘋果 Face ID 的攻擊揭露了活體檢測和生物識別認證在安全和設計上所存在的漏洞。

石膏「人臉」竟可以破解四種流行旗艦手機的 AI 人臉識別解鎖功能,而 iPhone X 不爲所動。測試中被「假頭」破解的手機包括 LG G7 ThinQ、三星 S9、三星 Note 8 。 從商場到工作場所,人臉識別無處不在,好像我們的臉每天都在被掃描。但智能手機應該保護用戶數據,使其免於泄露,而不是侵犯隱私。

用於測試的 3D 打印頭部是由英國伯明翰的 Backface 公司製作的。這家公司利用 50 個攝像頭同時拍照就能生成一幅完整的 3D 圖像,然後將生成的圖像導入電腦,用編輯軟件進行處理,任何錯誤都能得到修復。 接下來,Backface 用一臺以石膏粉爲原料的 3D 打印機打出模型。然後進行面部修復和上色。利用這種方法幾天之內就能做出一個真人頭部大小的模型,總花費僅 300 英鎊。 在這之後,你就得到了一個幾乎完美的人頭複製體。

安卓手機抵抗攻擊的性能也存在差異。如,首次打開這部全新的 G7 時,LG 曾提醒用戶不要打開人臉識別。「人臉識別爲二級解鎖方法,會降低您手機的安全性,」LG 手機播報道,提醒用戶類似的人臉也可以解鎖你的手機。難怪開始試驗的時候,3D 打印頭部輕鬆就解開了 G7。 但在拍攝期間,LG 似乎更新了人臉識別程序,大大增加了破解難度。一位 LG 發言人表示:「通過 LG 推薦的第二個識別步驟和高級識別,可以通過設置在設備上改進人臉識別功能。LG 試圖通過不斷加強設備穩定性和安全性來改進手機。」他們補充道,人臉識別被視爲次於 PIN、指紋等其他方式的「二級解鎖功能」。

三星 S9 在用戶註冊時也有類似提醒。「您的手機可能會被與您長相類似的人或物解鎖,」該手機提醒道。「如果僅使用人臉識別,安全性會低於使用手勢密碼、PIN 及密碼。」但奇怪的是,在設置該設備時,首先出現的解鎖選項是人臉和虹膜識別。虹膜識別不會被「假頭」模糊的眼睛欺騙,但人臉識別被欺騙了,儘管需要先調整角度和照明。

Note 8 具有「快速識別」功能,根據製造商的表述,「快速識別」的安全性比普通識別還要差。在此次實驗中,這並不重要,因爲無論怎麼設置,「假頭」都可以解鎖手機,只是普通解鎖需要花更多時間調整角度和照明。S9 和 LG 的慢速解鎖功能也是如此,而且事實證明,後者更難攻破。三星發言人表示:「人臉識別是爲了更方便地打開手機,類似於『滑動解鎖』。我們提供最高級別的生物特徵識別——指紋和虹膜,利用它們解鎖手機,併爲 Samsung Pay、Secure Folder 等功能提供驗證。」

全球銷量排名第二的華爲手機安全性如何?或許是因爲記者們的偏好,在福布斯的測試中並沒有出現華爲手機。不過更早些時候,德國媒體的同行們使用了另一種方式破解了 Mate 20 Pro 的 3D 結構光人臉識別解鎖功能:蓄鬍子。 這兩位長相相似,都留着鬍子的用戶,其中一人錄入了自己的面部信息,隨後在手機息屏狀態下,另一人接過手機之後直接解鎖成功了。大鬍子的存在阻礙了人臉識別的掃描?

不過,iPhone X 似乎不那麼容易被破解:蘋果公司在人臉識別方面投資很大,他們甚至和好萊塢電影工作室合作,製造仿真面具來測試 Face ID,他們的努力得到了回報,模型是無法解鎖 iPhone X 的。 作爲最貴的旗艦手機系列,蘋果公司自 2017 年起在 iPhone X 中使用了「TrueDepth 攝像機系統」(隱藏於屏幕上方的「齊劉海」部分)。 在識別時,手機會使用其中的傳感器、攝像頭和點陣投影儀,投射出 3 萬多個點,以形成一張完整的 3D「模型」來識別用戶臉部。此外,iPhone X 還採用了定製化的 AI 芯片 Neural Engine 來處理工作負載。 對於 Face ID 的自信甚至讓蘋果拋棄了一直使用的指紋解鎖功能。蘋果稱,同爲生物識別技術,TouchID 的解鎖錯誤率是五萬分之一,而 FaceID 則是一百萬分之一。

什麼是活體檢測,是指計算機判別檢測的人臉是真實的人臉,還是僞造的人臉攻擊,比如合法用戶圖片、提前拍攝的視頻等。傳統方法將其視爲一個“活體”VS“假體”的二分類問題,當然也可看成多分類問題,如真人、圖片攻擊、視頻回放攻擊、面具攻擊等) 目前主要研究機構:OULU大學(有多篇論文)、MSU(多篇論文Department of Computer Science and Engineering,Michigan State University)、HKBU(和OULU合作)、IDIAP(提供了很多數據集)、國內(曠視、百度、商湯、虹軟等都有提供) 目前攻擊方法有打印照片、視頻回放、面具攻擊,反攻擊也是針對這幾種方式,例如用戶配合動作檢測、語音校驗、顏色紋理、光流、遠程心率rPPG,還有一些藉助外部設備,如紅外攝像頭、深度攝像頭。 應該針對具體情境制定方案,嵌入式還是移動端,是否需要減少交互增強用戶體驗(靜默活體檢測)、是否允許額外設備支持等。

3D攝像頭:拍攝人臉,得到相應的人臉區域的3D數據,並基於這些數據做進一步的分析, 最終判斷出這個人臉是來自活體還是非活體。這裏非活體的來源是比較廣泛的,包括手機和Pad等介質的照片和視頻、各種打印的不同材質的照片(包含各種情形的彎曲、摺疊、剪裁、挖洞等情形)等。關鍵是,基於活體和非活體的3D人臉數據,如何選擇最具有區分度的特徵來訓練分類器,利用訓練好的分類器來區分活體和非活體。 光流法:利用圖像序列中的像素強度數據的時域變化和相關性來確定各自像素位置的“運動”,從圖像序列中得到各個像素點的運行信息,採用高斯差分濾波器、LBP特徵和支持向量機進行數據統計分析。同時,光流場對物體運動比較敏感,利用光流場可以統一檢測眼球移動和眨眼。這種活體檢測方式可以在用戶無配合的情況下實現盲測。

由左側兩張對比圖可以看出,活體的光流特徵,顯示爲不規則的向量特徵,而照片的光流特徵,則是規則有序的向量特徵,以此即可區分活體和照片。

靜默活體檢測:相對於動態活體檢測方法,靜默活體檢測是指,不需要用戶做任何動作,自然面對攝像頭3、4秒鐘即可。由於真實人臉並不是絕對靜止的, 存在微表情,如眼皮眼球的律動、眨眼、嘴脣及周邊面頰的伸縮等,可通過此類特徵反欺騙。

紅外活體檢測:利用額外設備紅外攝像頭不管是可見光還是紅外光,其本質都是電磁波。我們最終看到的圖像長什麼樣,與材質表面的反射特性有關。真實的人臉和紙片、屏幕、立體面具等攻擊媒介的反射特性都是不同的,所以成像也不同,而這種差異在紅外波反射方面會更加明顯,比如說,一塊屏幕在紅外成像的畫面裏,就只有白花花的一片,連人臉都沒了,攻擊完全不可能得逞。

紋理分析(Texture analysis):計算面部區域的局部二值模式(Local Binary Patterns),使用SVM分類真臉和假臉。 頻率分析(Frequency analysis):分析臉部的頻譜。

可變聚焦分析(Variable focusing analysis):評估兩個連續幀中的像素變化。 啓發式算法(Heuristic-based algorithms):包括眼睛動作,嘴脣動作,眨眼檢測。這些算法嘗試跟蹤眼睛的移動以及眨眼,保證用戶不是舉着別人的照片(因爲靜止的圖片中的人不會眨眼也不會動嘴脣)。

光流算法:審查3D物體和2D平面的光流特性變化。

3D臉部形狀:和蘋果iPhone臉部識別系統類似,使得系統能區別真臉和打印出來的別人圖片。 可變聚焦分析(Variable focusing analysis):評估兩個連續幀中的像素變化。


# 命令行參數
# 	python gather_examples.py --input videos/real.mov --output dataset/real --detector face_detector --skip 1
# 	python gather_examples.py --input videos/fake.mp4 --output dataset/fake --detector face_detector --skip 4

import numpy as np
import argparse
import cv2
import os

"""
這個腳本從輸入的視頻文件中提取了面部 ROI,幫助我們創建了深度學習面部活體數據集
	1.從訓練(視頻)數據集中檢測並提取面部 ROI,根據這些幀,我們後續將在這些圖像上訓練基於深度學習的活體檢測器。
		"./videos/fake.mp4":僞造面部圖像的視頻
		"./videos/real.mov":真實面部圖像
	
	2.dataset/:我們的數據集目錄中包含兩類圖像:
		1."./dataset/fake":在播放我的面部視頻時通過錄制屏幕得到的僞造圖像,即fake.mp4 中的面部 ROI;
		2."./dataset/real":手機直接拍攝我的面部視頻得到的真實圖像,即real.mov 中的面部 ROI。
	
	3.因爲「真」視頻比「假」視頻長,因此我們得把跳過幀的值設置得更長,來平衡每一類輸出的面部 ROI 數量。
	  在執行這個腳本之後,你的圖像數量應該如下:
			僞造面部:150 張圖片;
			真實面部:161 張圖片;
			總數:311 張圖片。
	4.構造參數解析並解析參數		
		1.--input:輸入視頻文件的路徑
		2.--output:輸出目錄的路徑,截取的每一張面部圖像都存儲在這個目錄中。
		3.--detector:面部檢測器的路徑。我們將使用 OpenCV 的深度學習面部檢測器。
				"./face_detector" 該路徑下包含以下兩個文件:
					deploy.prototxt:檢測人臉的網絡模型文件(Caffe中deploy.prototxt)
					res10_300x300_ssd_iter_140000.caffemodel:預訓練的權重參數文件(300x300代表該檢測人臉的模型的輸入應爲300x300)
		4.--confidence:過濾弱面部檢測的最小概率,默認值爲 50%。
		5.--skip:我們不需要檢測和存儲每一張圖像,因爲相鄰的幀是相似的。因此我們在檢測時會跳過 N 個幀。你可以使用這個參數並更改默認值16。
"""
# 構造參數解析並解析參數
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--input", type=str, required=False, default="./videos/real.mov", help="path to input video")
ap.add_argument("-o", "--output", type=str, required=False, default="./dataset/real",help="path to output directory of cropped faces")
# ap.add_argument("-i", "--input", type=str, required=False, default="./videos/fake.mp4", help="path to input video")
# ap.add_argument("-o", "--output", type=str, required=False, default="./dataset/fake", help="path to output directory of cropped faces")
ap.add_argument("-d", "--detector", type=str, required=False, default="./face_detector", 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())

# 從磁盤加載序列化的面部檢測器
print("[INFO] loading face detector...")
#deploy.prototxt:檢測人臉的網絡模型文件(Caffe中deploy.prototxt)
protoPath = os.path.sep.join([args["detector"], "deploy.prototxt"])
#res10_300x300_ssd_iter_140000.caffemodel:預訓練的權重參數文件(300x300代表該檢測人臉的模型的輸入應爲300x300)
modelPath = os.path.sep.join([args["detector"], "res10_300x300_ssd_iter_140000.caffemodel"])
""" 加載了 OpenCV 的深度學習面部檢測器 """
# 所加載的預訓練模型和權重參數文件用於人臉檢測
net = cv2.dnn.readNetFromCaffe(protoPath, modelPath)

# 打開一個指向視頻文件流的指針,並初始化到目前爲止已讀取和保存的幀總數
vs = cv2.VideoCapture(args["input"])
read = 0 #讀取的幀的數量
saved = 0 #執行循環時保存的幀的數量

# 循環播放視頻文件流中的幀
while True:
	# 從文件中抓取幀
	(grabbed, frame) = vs.read()
	# 如果沒有抓取到幀,那麼我們已經到達視頻流的盡頭
	if not grabbed:
		break
	# 增加到目前爲止讀取的總幀數
	read += 1
	# 檢查我們是否應該處理此幀
	# 我們不需要檢測和存儲每一張圖像,因爲相鄰的幀是相似的。因此我們在檢測時會跳過 N 個幀。你可以使用這個參數並更改默認值16。
	if read % args["skip"] != 0:
		continue

	# 抓取框架尺寸
	(h, w) = frame.shape[:2]
	# blobFromImage主要是用來對圖片進行預處理。包含兩個主要過程:
	#	1,整體像素值減去平均值(mean)
	#	2,通過縮放係數(scalefactor)對圖片像素值進行縮放
	# 預處理由OpenCV的blobFromImage函數:
	# 	1.將輸入圖片大小調整爲300×300像素,並執行均值減去。
	#	  因爲預訓練的權重參數文件使用的爲:res10_300x300_ssd_iter_140000.caffemodel(其中300x300代表該檢測人臉的模型的輸入應爲300x300)
	# 	2.圖片RGB三個通道分別減去(B=104.0, G=177.0, R=123.0),即每個通道減去指定的均值
	# 	3.縮放係數1.0 可以對圖片像素值進行縮放,此處使用1.0代表了並沒有做真正的縮放,像素值除以1.0結果值仍不變
	#爲了進行面部檢測,根據圖像創建一個 blob。爲了適應 Caffe 面部識別器,這個 blob 是 300*300 的,之後還要縮放邊界框。
	blob = cv2.dnn.blobFromImage(cv2.resize(frame, (300, 300)), 1.0, (300, 300), (104.0, 177.0, 123.0))
	# 通過網絡傳遞blob並獲得檢測和預測
	net.setInput(blob)
	# 執行面部檢測以定位圖像中所有面部的位置。通過深度學習面部識別器執行了 blob 的前向傳輸。
	detections = net.forward()

	"""
	這裏判斷代碼假設了視頻的每一幀中只有一張面部。這有助於減少假陽性。
	如果你要處理的視頻中不止有一張面部,建議根據需要調整邏輯。
	"""
	# 確保至少發現一張臉
	if len(detections) > 0:
		"""
		idx = int(detections[0, 0, 0, 1])  #比如 1.0,提取第1個可疑人臉的目標標籤
		confidence = detections[0, 0, 0, 2] #比如 0.9984427,提取第1個可疑人臉的置信度
		box = detections[0, 0, 0, 3:7] #比如 [0.5462329  0.12488028 0.6709176  0.3542412 ] 提取第1個可疑人臉的4個位置信息值
		"""
		# 表示可以一次性檢測出200個可能爲人臉目標,[0, 0, :, 2]取出的爲該200個可能爲人臉目標的置信度
		# print(detections[0, 0, :, 2].size) #200
		# 我們假設每個圖像只有一張臉,所以找到概率最大的邊界框,抓取了概率最高的面部檢測索引。
		# 從包含200個置信度值的列表中通過argmax取出最大置信度值對應的下標索引值
		i = np.argmax(detections[0, 0, :, 2])
		# 通過最大置信度值對應的下標索引值 取出對應的 置信度。
		confidence = detections[0, 0, i, 2]

		#確保檢測出來的人臉具有最大置信度,確保我們的面部檢測 ROI 滿足最小閾值,從而減少假陽性。
		if confidence > args["confidence"]:
			# 提取最大置信度的人臉的4個位置信息值,計算面部邊界框的(x,y)座標
			box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
			(startX, startY, endX, endY) = box.astype("int")
			# 提取僅面部位置的ROI(感興趣區域)
			face = frame[startY:endY, startX:endX]
			# 面部位置圖片 的輸出路徑
			p = os.path.sep.join([args["output"], "{}.png".format(saved)])
			# 將檢測出來的人臉 寫入磁盤
			cv2.imwrite(p, face)
			saved += 1
			print("[INFO] saved {} to disk".format(p))

# 做一些清理
vs.release()
cv2.destroyAllWindows()

"""
dnn.blobFromImage
	作用:根據輸入圖像,創建維度N(圖片的個數),通道數C,高H和寬W次序的blobs
	原型:blobFromImage(image, scalefactor=None, size=None, mean=None, swapRB=None, crop=None, ddepth=None)
	參數:
		image:cv2.imread 讀取的圖片數據
		scalefactor: 縮放像素值,如 [0, 255] - [0, 1]
		size: 輸出blob(圖像)的尺寸,如 (netInWidth, netInHeight)
		mean: 從各通道減均值. 如果輸入 image 爲 BGR 次序,且swapRB=True,則通道次序爲 (mean-R, mean-G, mean-B).
		swapRB: 交換 3 通道圖片的第一個和最後一個通道,如 BGR - RGB
		crop: 圖像尺寸 resize 後是否裁剪. 如果crop=True,則,輸入圖片的尺寸調整resize後,一個邊對應與 size 的一個維度,
			   而另一個邊的值大於等於 size 的另一個維度;然後從 resize 後的圖片中心進行 crop. 
			   如果crop=False,則無需 crop,只需保持圖片的長寬比
		ddepth: 輸出 blob 的 Depth. 可選: CV_32F 或 CV_8U

blob = cv2.dnn.blobFromImage(image, scalefactor=1.0, size, mean, swapRB=True)
	在進行深度學習或者圖片分類時,blobFromImage主要是用來對圖片進行預處理。包含兩個主要過程:
		1,整體像素值減去平均值(mean)
		2,通過縮放係數(scalefactor)對圖片像素值進行縮放
	參數:	
		image:這個就是我們將要輸入神經網絡進行處理或者分類的圖片。
		scalefactor:當我們將圖片減去平均值之後,還可以對剩下的像素值進行一定的尺度縮放,它的默認值是1,如果希望減去平均像素之後的值,
					  全部縮小一半,那麼可以將scalefactor設爲1/2。
		size:這個參數是我們神經網絡在訓練的時候要求輸入的圖片尺寸。
		mean:需要將圖片整體減去的平均值,如果我們需要對RGB圖片的三個通道分別減去不同的值,那麼可以使用3組平均值,
			   如果只使用一組例如(B=106.13, G=115.97, R=124.96),那麼就默認對三個通道減去一樣的值。
			   減去平均值(mean):爲了消除同一場景下不同光照的圖片,對我們最終的分類或者神經網絡的影響,
			   我們常常對圖片的R、G、B通道的像素求一個平均值,然後將每個像素值減去我們的平均值,這樣就可以得到像素之間的相對值,
			   就可以排除光照的影響。
		swapRB:OpenCV中認爲我們的圖片通道順序是BGR,但是我平均值假設的順序是RGB,所以如果需要交換R和G,那麼就要使swapRB=true
"""
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:
	"""
	靜態方法build接受 4 個參數:
		width:圖片/體積的寬度;
		height:圖片的高度;
		depth:圖像的通道數量(處理的是 RGB 圖像,通道數量爲3);
		classes:類的數量。總共有兩類:「真」和「假」。
	"""
	@staticmethod
	def build(width, height, depth, classes):
		#初始化模型和輸入形狀,以及通道尺寸 height和width,通道順序默認爲"channels last"
		model = Sequential()
		# height和width爲通道尺寸,位於最後的depth爲通道數,相當於"channels last"
		inputShape = (height, width, depth)
		chanDim = -1 #代表 "channels last"

		#如果使用通道順序爲"channels first",請更新輸入形狀和通道尺寸
		if K.image_data_format() == "channels_first":
			#位於首位置的depth爲通道數,相當於"channels first",height和width爲通道尺寸
			inputShape = (depth, height, width)
			chanDim = 1 #代表 "channels_first"

		# CONV => RELU => BN => CONV => RELU => BN => POOL => dropout
		model.add(Conv2D(16, (3, 3), padding="same", input_shape=inputShape))
		model.add(Activation("relu"))
		#BatchNormalization 可以不設置 axis
		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))

		# CONV => RELU => BN => CONV => RELU => BN => POOL => dropout
		model.add(Conv2D(32, (3, 3), padding="same"))
		model.add(Activation("relu"))
		# BatchNormalization 可以不設置 axis
		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))

		# Flatten => FC(Dense) => RELU => BN => dropout
		model.add(Flatten())
		model.add(Dense(64))
		model.add(Activation("relu"))
		# BatchNormalization 可以不設置 axis
		model.add(BatchNormalization())
		model.add(Dropout(0.5))

		# softmax分類器:FC(Dense(標籤類型數量)) => softmax
		model.add(Dense(classes))
		model.add(Activation("softmax"))

		# 返回構建的網絡架構
		return model
# 命令行參數:python train_liveness.py --dataset dataset --model liveness.model --le le.pickle

# 設置matplotlib後端,以便可以將圖形保存在後臺
import matplotlib
#在PyCharm中不顯示繪圖:use("Agg")
matplotlib.use("Agg")

from 項目二.day05.liveness_detection_opencv.pyimage.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

# 構造參數解析並解析參數
ap = argparse.ArgumentParser()
#輸入數據集的路徑
ap.add_argument("-d", "--dataset", required=False, default="./dataset", help="path to input dataset")
#輸出活體檢測模型的文件
ap.add_argument("-m", "--model", type=str, required=False, default="./liveness.model", help="path to trained model")
#輸出序列化標籤編碼器的文件
ap.add_argument("-l", "--le", type=str, required=False, default="./le.pickle", 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())

INIT_LR = 1e-4 # 初始化初始學習率
BS = 8 #批處理大小
EPOCHS = 1

#在我們的數據集目錄中獲取圖像列表,然後初始化數據列表(即圖像)和類圖像
print("[INFO] loading images...")
#./dataset/fake 和 ./dataset/real 兩個目錄下所有的圖片數據
imagePaths = list(paths.list_images(args["dataset"]))
# print("imagePaths",imagePaths)
data = [] #存放數據
labels = [] #存放類別標籤

# 遍歷 ./dataset/fake 和 ./dataset/real 兩個目錄下所有的圖片數據
for imagePath in imagePaths:
	# 從文件名中提取類標籤
	label = imagePath.split(os.path.sep)[-2]
	# 加載圖像
	image = cv2.imread(imagePath)
	# 並將其大小調整爲固定的32x32像素,而忽略寬高比
	image = cv2.resize(image, (32, 32))
	# 分別更新數據和標籤列表,循環用於建立數據和標籤列表。
	data.append(image) #數據是由加載並將尺寸調整爲 32*32 像素的圖像組成的
	labels.append(label) #標籤列表中存儲了每張圖相對應的標籤。

# 將數據轉換爲NumPy數組,然後通過將所有像素強度縮放到[0,1]範圍進行預處理。
# 將所有像素縮放到 [0,1] 之間,並將列表轉換爲 NumPy 數組。
data = np.array(data, dtype="float") / 255.0
#Label編碼器類:將標籤(當前爲字符串)編碼爲整數,然後一鍵編碼
le = LabelEncoder()
# print("labels",labels) #labels ['fake',。。。,'fake', 'real',。。。,'real']
#fit_transform擬合變換:把字符串的標籤 轉換爲 整數值的標籤
labels = le.fit_transform(labels)
# print("labels",labels) #[0 0 。。。 0 0 1 1 。。。1 1]
# print("classes=len(le.classes_)",len(le.classes_)) #2
# print("target_names=le.classes_)",le.classes_) #['fake' 'real']
#把每個整數值的標籤 進行 one-hot編碼化。將類向量(整數)轉換爲二進制類矩陣。num_classes=2 表示爲2種類型的標籤
labels = np_utils.to_categorical(labels, 2)
# print("labels",labels) #[[1. 0.] [1. 0.] 。。。[1. 0.] [1. 0.]   [0. 1.] [0. 1.] 。。。 [0. 1.] [0. 1.]]

# 使用75%的數據進行訓練,其餘25%進行測試,將數據劃分爲訓練和測試分組
(trainX, testX, trainY, testY) = train_test_split(data, labels, test_size=0.25, random_state=42)


"""
stratify=labels 的作用:
	保持測試集與整個數據集裏的labels中標籤分類的比例一致。
	例子:
		整個數據集有1000條樣本,也有1000個labels,並且labels分兩類(即0和1),其中labels爲0有300條樣本,labels爲1有700條樣本,
		即labels分兩類的比例爲3:7。那麼現在把整個數據集進行split切分,因爲test_size = 0.2,所以訓練集分到800條樣本,測試集分到200條樣本。
		因爲stratify=labels,則訓練集和測試集中的labels分兩類的比例均爲3:7,結果就是在訓練集中labels有240個0和560個1,
		測試集中labels有60個0和140個1。
	同理,若將訓練集進一步分出一個驗證集:
		(trainX, valX, trainY, valY) = train_test_split(trainX, trainY, test_size=0.20, stratify=trainY, random_state=42)
		則訓練集和驗證集中的樣本數分別爲640和160,且由於stratify=trainY,驗證集與訓練集中的標籤分類0和1的比例均爲3:7,
		則驗證集中將被分到48個0和112個1。
"""

# 構建用於數據增強的訓練圖像生成器
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")
"""
ImageDataGenerator()
	keras.preprocessing.image模塊中的圖片生成器,同時也可以在batch中對數據進行增強,擴充數據集大小,增強模型的泛化能力。
	比如進行旋轉,變形,歸一化等等。
		rotation_range(): 旋轉範圍
		width_shift_range(): 水平平移範圍
		height_shift_range(): 垂直平移範圍
		zoom_range(): 縮放範圍
		fill_mode: 填充模式, constant, nearest, reflect
		horizontal_flip(): 水平反轉
		vertical_flip(): 垂直翻轉
"""

# 初始化優化器和模型
print("[INFO] compiling model...")
# decay衰變率:初始化的學習率 / EPOCHS
opt = Adam(lr=INIT_LR, decay=INIT_LR / EPOCHS)
#len(le.classes_)=2 表示有2種類型的數據/標籤
model = LivenessNet.build(width=32, height=32, depth=3, classes=len(le.classes_))
model.compile(loss="binary_crossentropy", optimizer=opt, metrics=["accuracy"])

# 訓練網絡
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)

# 評估網絡
print("[INFO] evaluating network...")
predictions = model.predict(testX, batch_size=BS)
# print("predictions",predictions) # 比如 [[0.52001303 0.479987  ] 。。。[0.5353358  0.46466425]]
#classification_report:建立文字報告,顯示主要的分類指標
#target_names=le.classes_:['fake' 'real']
print(classification_report(testY.argmax(axis=1), predictions.argmax(axis=1), target_names=le.classes_))
print("testY.argmax(axis=1)",testY.argmax(axis=1))
print("predictions.argmax(axis=1)",predictions.argmax(axis=1))
"""
print(classification_report(testY.argmax(axis=1), predictions.argmax(axis=1), target_names=le.classes_))
警告:
	UndefinedMetricWarning: Precision and F-score are ill-defined and being set to 0.0 in labels with no predicted samples. 
	'precision', 'predicted', average, warn_for)
  	UndefinedMetricWarning:精度和F分數定義不明確,在沒有預測樣本的標籤中設置爲0.0。
分析:
	該爲警告,不是錯誤。
	比如真實標籤的類型是0和1,但是預測的標籤僅有其中一種(或僅有0或僅有1),那麼就會報出該警告。
"""
# 將活體檢測的模型網絡保存到磁盤文件中
print("[INFO] serializing network to '{}'...".format(args["model"]))
model.save(args["model"])

# 將標籤編碼器保存到磁盤文件中
f = open(args["le"], "wb")
f.write(pickle.dumps(le))
f.close()

# 繪製訓練損失和準確性
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["accuracy"], label="train_accuracy")
plt.plot(np.arange(0, EPOCHS), H.history["val_accuracy"], label="val_accuracy")
plt.title("Training Loss and Accuracy on Dataset")
plt.xlabel("Epoch #")
plt.ylabel("Loss/Accuracy")
plt.legend(loc="lower left")
plt.savefig(args["plot"])
# 命令行參數:python train_liveness.py --dataset dataset --model liveness.model --le le.pickle

# 設置matplotlib後端,以便可以將圖形保存在後臺
import matplotlib
matplotlib.use("Agg")

from 項目二.day05.liveness_detection_opencv.pyimage.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

# 構造參數解析並解析參數
ap = argparse.ArgumentParser()
#輸入數據集的路徑
# ap.add_argument("-d", "--dataset", required=False, default="./NewData/Detectedface", help="path to input dataset")
ap.add_argument("-d", "--dataset", required=False, default="./NewData/raw", help="path to input dataset")
#輸出活體檢測模型的文件
ap.add_argument("-m", "--model", type=str, required=False, default="./liveness.model", help="path to trained model")
#輸出序列化標籤編碼器的文件
ap.add_argument("-l", "--le", type=str, required=False, default="./le.pickle", 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())

INIT_LR = 1e-4 # 初始化初始學習率
BS = 8 #批處理大小
EPOCHS = 20

#在我們的數據集目錄中獲取圖像列表,然後初始化數據列表(即圖像)和類圖像
print("[INFO] loading images...")
#./dataset/fake 和 ./dataset/real 兩個目錄下所有的圖片數據
imagePaths = list(paths.list_images(args["dataset"]))
# print("imagePaths",imagePaths)
data = [] #存放數據
labels = [] #存放類別標籤

# 遍歷 ./dataset/fake 和 ./dataset/real 兩個目錄下所有的圖片數據
for imagePath in imagePaths:
	# 從文件名中提取類標籤
	label = imagePath.split(os.path.sep)[-3]
	# 加載圖像
	image = cv2.imread(imagePath)
	# 並將其大小調整爲固定的32x32像素,而忽略寬高比
	image = cv2.resize(image, (32, 32))
	# 分別更新數據和標籤列表,循環用於建立數據和標籤列表。
	data.append(image) #數據是由加載並將尺寸調整爲 32*32 像素的圖像組成的
	labels.append(label) #標籤列表中存儲了每張圖相對應的標籤。

# 將數據轉換爲NumPy數組,然後通過將所有像素強度縮放到[0,1]範圍進行預處理。
# 將所有像素縮放到 [0,1] 之間,並將列表轉換爲 NumPy 數組。
data = np.array(data, dtype="float") / 255.0
#Label編碼器類:將標籤(當前爲字符串)編碼爲整數,然後一鍵編碼
le = LabelEncoder()
# print("labels",labels) #labels ['fake',。。。,'fake', 'real',。。。,'real']
#fit_transform擬合變換:把字符串的標籤 轉換爲 整數值的標籤
labels = le.fit_transform(labels)
# print("labels",labels) #[0 0 。。。 0 0 1 1 。。。1 1]
print("classes=len(le.classes_)",len(le.classes_)) #2
print("target_names=le.classes_)",le.classes_) #['fake' 'real']
#把每個整數值的標籤 進行 one-hot編碼化。將類向量(整數)轉換爲二進制類矩陣。num_classes=2 表示爲2種類型的標籤
labels = np_utils.to_categorical(labels, 2)
print("labels",labels) #[[1. 0.] [1. 0.] 。。。[1. 0.] [1. 0.]   [0. 1.] [0. 1.] 。。。 [0. 1.] [0. 1.]]

# 使用75%的數據進行訓練,其餘25%進行測試,將數據劃分爲訓練和測試分組
(trainX, testX, trainY, testY) = train_test_split(data, labels, test_size=0.25, random_state=42)
"""
stratify=labels 的作用:
	保持測試集與整個數據集裏的labels中標籤分類的比例一致。
	例子:
		整個數據集有1000條樣本,也有1000個labels,並且labels分兩類(即0和1),其中labels爲0有300條樣本,labels爲1有700條樣本,
		即labels分兩類的比例爲3:7。那麼現在把整個數據集進行split切分,因爲test_size = 0.2,所以訓練集分到800條樣本,測試集分到200條樣本。
		因爲stratify=labels,則訓練集和測試集中的labels分兩類的比例均爲3:7,結果就是在訓練集中labels有240個0和560個1,
		測試集中labels有60個0和140個1。
	同理,若將訓練集進一步分出一個驗證集:
		(trainX, valX, trainY, valY) = train_test_split(trainX, trainY, test_size=0.20, stratify=trainY, random_state=42)
		則訓練集和驗證集中的樣本數分別爲640和160,且由於stratify=trainY,驗證集與訓練集中的標籤分類0和1的比例均爲3:7,
		則驗證集中將被分到48個0和112個1。
"""

# 構建用於數據增強的訓練圖像生成器
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")
"""
ImageDataGenerator()
	keras.preprocessing.image模塊中的圖片生成器,同時也可以在batch中對數據進行增強,擴充數據集大小,增強模型的泛化能力。
	比如進行旋轉,變形,歸一化等等。
		rotation_range(): 旋轉範圍
		width_shift_range(): 水平平移範圍
		height_shift_range(): 垂直平移範圍
		zoom_range(): 縮放範圍
		fill_mode: 填充模式, constant, nearest, reflect
		horizontal_flip(): 水平反轉
		vertical_flip(): 垂直翻轉
"""

# 初始化優化器和模型
print("[INFO] compiling model...")
# decay衰變率:初始化的學習率 / EPOCHS
opt = Adam(lr=INIT_LR, decay=INIT_LR / EPOCHS)
#len(le.classes_)=2 表示有2種類型的數據/標籤
model = LivenessNet.build(width=32, height=32, depth=3, classes=len(le.classes_))
model.compile(loss="binary_crossentropy", optimizer=opt, metrics=["accuracy"])

# 訓練網絡
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)

# 評估網絡
print("[INFO] evaluating network...")
predictions = model.predict(testX, batch_size=BS)
# print("predictions",predictions) # 比如 [[0.52001303 0.479987  ] 。。。[0.5353358  0.46466425]]
#classification_report:建立文字報告,顯示主要的分類指標
#target_names=le.classes_:['fake' 'real']
print(classification_report(testY.argmax(axis=1), predictions.argmax(axis=1), target_names=le.classes_))

# 將活體檢測的模型網絡保存到磁盤文件中
print("[INFO] serializing network to '{}'...".format(args["model"]))
model.save(args["model"])

# 將標籤編碼器保存到磁盤文件中
f = open(args["le"], "wb")
f.write(pickle.dumps(le))
f.close()

# 繪製訓練損失和準確性
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["accuracy"], label="train_accuracy")
plt.plot(np.arange(0, EPOCHS), H.history["val_accuracy"], label="val_accuracy")
plt.title("Training Loss and Accuracy on Dataset")
plt.xlabel("Epoch #")
plt.ylabel("Loss/Accuracy")
plt.legend(loc="lower left")
plt.savefig(args["plot"])
# 命令行參數:python liveness_demo.py --model liveness.model --le le.pickle --detector face_detector

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

# 構造參數解析並解析參數
ap = argparse.ArgumentParser()
#用於活體檢測的預訓練 Keras 模型的文件
ap.add_argument("-m", "--model", type=str, required=False, default="./liveness.model", help="path to trained model")
#序列化的標籤編碼器的文件
ap.add_argument("-l", "--le", type=str, required=False, default="./le.pickle", help="path to label encoder")
#用來尋找面部 ROI 的 OpenCV 的深度學習面部檢測器
"""
面部檢測器的路徑。我們將使用 OpenCV 的深度學習面部檢測器。
"./face_detector" 該路徑下包含以下兩個文件:
	deploy.prototxt:檢測人臉的網絡模型文件(Caffe中deploy.prototxt)
	res10_300x300_ssd_iter_140000.caffemodel:預訓練的權重參數文件(300x300代表該檢測人臉的模型的輸入應爲300x300)
"""
ap.add_argument("-d", "--detector", type=str, required=False, default="./face_detector", help="path to OpenCV's deep learning face detector")
#過濾弱面部檢測的最小概率,默認值爲 50%。
ap.add_argument("-c", "--confidence", type=float, default=0.5, help="minimum probability to filter weak detections")
args = vars(ap.parse_args())

# 從磁盤加載序列化的面部檢測器
print("[INFO] loading face detector...")
#deploy.prototxt:檢測人臉的網絡模型文件(Caffe中deploy.prototxt)
protoPath = os.path.sep.join([args["detector"], "deploy.prototxt"])
#res10_300x300_ssd_iter_140000.caffemodel:預訓練的權重參數文件(300x300代表該檢測人臉的模型的輸入應爲300x300)
modelPath = os.path.sep.join([args["detector"], "res10_300x300_ssd_iter_140000.caffemodel"])
# 所加載的預訓練模型和權重參數文件用於人臉檢測
net = cv2.dnn.readNetFromCaffe(protoPath, modelPath)

# 從磁盤加載活體檢測器模型和標籤編碼器
print("[INFO] loading liveness detector...")
model = load_model(args["model"]) #用於活體檢測的預訓練 Keras 模型的文件 liveness.model
le = pickle.loads(open(args["le"], "rb").read()) #序列化的標籤編碼器的文件 le.pickle

# 初始化視頻流並允許相機傳感器預熱
print("[INFO] starting video stream...")
vs = VideoStream(src=0).start()
time.sleep(2.0)

# 循環播放視頻流中的幀
while True:
	# 視頻流中抓取幀
	frame = vs.read()
	# 將其調整爲最大寬度爲600像素
	frame = imutils.resize(frame, width=600)

	# 抓取幀尺寸並將其轉換爲blob
	(h, w) = frame.shape[:2]
	# blobFromImage主要是用來對圖片進行預處理。包含兩個主要過程:
	#	1,整體像素值減去平均值(mean)
	#	2,通過縮放係數(scalefactor)對圖片像素值進行縮放
	# 預處理由OpenCV的blobFromImage函數:
	# 	1.將輸入圖片大小調整爲300×300像素,並執行均值減去。
	#	  因爲預訓練的權重參數文件使用的爲:res10_300x300_ssd_iter_140000.caffemodel(其中300x300代表該檢測人臉的模型的輸入應爲300x300)
	# 	2.圖片RGB三個通道分別減去(B=104.0, G=177.0, R=123.0),即每個通道減去指定的均值
	# 	3.縮放係數1.0 可以對圖片像素值進行縮放,此處使用1.0代表了並沒有做真正的縮放,像素值除以1.0結果值仍不變
	#爲了進行面部檢測,根據圖像創建一個 blob。爲了適應 Caffe 面部識別器,這個 blob 是 300*300 的,之後還要縮放邊界框。
	blob = cv2.dnn.blobFromImage(cv2.resize(frame, (300, 300)), 1.0, (300, 300), (104.0, 177.0, 123.0))
	# 通過網絡傳遞blob並獲得檢測和預測
	net.setInput(blob)
	# 執行面部檢測以定位圖像中所有面部的位置。通過深度學習面部識別器執行了 blob 的前向傳輸。
	detections = net.forward()
	"""
	for i in range(0, detections.shape[2]) 循環遍歷每個檢測出來的可能目標的人臉
		detections.shape:(1, 1, 200, 7)
			detections.shape[2]:表示有200個可能爲人臉的目標
			detections.shape[3]:每個人臉目標對應的7個值,第2個值爲目標標籤,第3個值爲目標是否爲人臉的置信度,
								   第4到第7個值一共4個值爲人臉座標位置信息
		idx = int(detections[0, 0, 0, 1])  #1.0 提取第1個可疑人臉的目標標籤
		confidence = detections[0, 0, 0, 2] #0.9984427 提取第1個可疑人臉的置信度
		box = detections[0, 0, 0, 3:7] #[0.5462329  0.12488028 0.6709176  0.3542412 ] 提取第1個可疑人臉的4個位置信息值
	"""
	# 循環檢測
	for i in range(0, detections.shape[2]):
		# 提取與預測相關的置信度(即概率)
		confidence = detections[0, 0, i, 2]
		#確保檢測出來的人臉具有最大置信度,確保我們的面部檢測 ROI 滿足最小閾值,從而減少假陽性。
		if confidence > args["confidence"]:
			# 計算面部邊界框的(x,y)座標並提取面部ROI
			box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
			(startX, startY, endX, endY) = box.astype("int")
			#提取對應的面部邊界框,確保它們沒有超出幀;
			# 確保檢測到的邊界框不在框架的尺寸範圍內
			startX = max(0, startX)
			startY = max(0, startY)
			endX = min(w, endX)
			endY = min(h, endY)
			# 提取面部ROI,然後以與訓練數據完全相同的方式進行預處理
			face = frame[startY:endY, startX:endX]
			# 因爲活體檢測模型的input_shape在訓練數據時設置的輸入形狀即爲(32, 32),因此此處預測時也需要設置輸入形狀爲(32, 32)
			face = cv2.resize(face, (32, 32))
			face = face.astype("float") / 255.0
			face = img_to_array(face)
			face = np.expand_dims(face, axis=0)
			# print("classes=len(le.classes_)",len(le.classes_)) #2
			# print("target_names=le.classes_)",le.classes_) # [b'fake' b'real']

			# 通過訓練有素的活動檢測器模型傳遞面部ROI
			# 活體檢測模型預測的標籤爲“真實real”還是“僞造fake”,或者“ClientRaw原始”還是“ImposterRaw冒名頂替”
			# e-01 表示10的負1次方。
			preds = model.predict(face)[0]
			# print("preds",preds) #model.predict(face) 返回的爲 二維類型的數據,比如 [[9.9996865e-01  3.1292140e-05]]
			# print("preds", preds)#model.predict(face) 取出第一維數據,比如 [9.999951e-01  4.936377e-06]
			# print("preds", preds) #比如 [1.0848045e-05 9.9998915e-01]
			# 取出預測最大概率值的標籤的索引值
			j = np.argmax(preds) # print("j",j) # 比如 1
			# 根據 最大概率值的標籤的索引值 取出 最大概率值的標籤
			label = le.classes_[j] # print("label", label) #比如 b'real'

			# 在幀上繪製標籤和邊框
			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)

	# 顯示輸出幀並等待按鍵
	cv2.imshow("Frame", frame)
	key = cv2.waitKey(1) & 0xFF
	# 如果按下“ q”鍵,則退出循環
	if key == ord("q"):
		break

# 做一些清理
cv2.destroyAllWindows()
vs.stop()

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章