Python人臉檢測(cè)實(shí)戰(zhàn)之疲勞檢測(cè)
今天我們實(shí)現(xiàn)疲勞檢測(cè)。 如果眼睛已經(jīng)閉上了一段時(shí)間,我們會(huì)認(rèn)為他們開(kāi)始打瞌睡并發(fā)出警報(bào)來(lái)喚醒他們并引起他們的注意。我們測(cè)試一段視頻來(lái)展示效果。同時(shí)代碼中保留開(kāi)啟攝像頭的的代碼,取消注釋即可使用。

使用 OpenCV 構(gòu)建犯困檢測(cè)器
要開(kāi)始我們的實(shí)現(xiàn),打開(kāi)一個(gè)新文件,將其命名為 detect_drowsiness.py ,并插入以下代碼:
# import the necessary packages from scipy.spatial import distance as dist from imutils.video import VideoStream from imutils import face_utils from threading import Thread import numpy as np import playsound import argparse import imutils import time import dlib import cv2
導(dǎo)入們所需的 Python 包。
我們還需要 imutils 包,我的一系列計(jì)算機(jī)視覺(jué)和圖像處理功能,以便更輕松地使用 OpenCV。
如果您的系統(tǒng)上還沒(méi)有安裝 imutils,您可以通過(guò)以下方式安裝/升級(jí) imutils:
pip install --upgrade imutils
還將導(dǎo)入 Thread 類,以便我們可以在與主線程不同的線程中播放我們的警報(bào),以確保我們的腳本不會(huì)在警報(bào)響起時(shí)暫停執(zhí)行。
為了真正播放我們的 WAV/MP3 鬧鐘,我們需要 playsound 庫(kù),這是一個(gè)純 Python 的跨平臺(tái)實(shí)現(xiàn),用于播放簡(jiǎn)單的聲音。
playsound 庫(kù)可以通過(guò) pip 方便地安裝:
pip install playsound
但是,如果您使用的是 macOS(就像我為這個(gè)項(xiàng)目所做的那樣),您還需要安裝 pyobjc,否則當(dāng)您實(shí)際嘗試播放聲音時(shí),您將收到與 AppKit 相關(guān)的錯(cuò)誤:
pip install pyobjc
接下來(lái),我們需要定義 sound_alarm 函數(shù),該函數(shù)播放音頻文件:
def sound_alarm(path): # play an alarm sound playsound.playsound(path)
定義 eye_aspect_ratio 函數(shù),該函數(shù)用于計(jì)算垂直眼睛界標(biāo)之間的距離與水平眼睛界標(biāo)之間的距離之比:
def eye_aspect_ratio(eye): # compute the euclidean distances between the two sets of # vertical eye landmarks (x, y)-coordinates A = dist.euclidean(eye[1], eye[5]) B = dist.euclidean(eye[2], eye[4]) # compute the euclidean distance between the horizontal # eye landmark (x, y)-coordinates C = dist.euclidean(eye[0], eye[3]) # compute the eye aspect ratio ear = (A + B) / (2.0 * C) # return the eye aspect ratio return ear
由于OpenCV不能直接繪制中文,我們還需定義繪制中文的方法:
def cv2ImgAddText(img, text, left, top, textColor=(0, 255, 0), textSize=20):
if (isinstance(img, np.ndarray)): # 判斷是否OpenCV圖片類型
img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
# 創(chuàng)建一個(gè)可以在給定圖像上繪圖的對(duì)象
draw = ImageDraw.Draw(img)
# 字體的格式
fontStyle = ImageFont.truetype(
"font/simsun.ttc", textSize, encoding="utf-8")
# 繪制文本
draw.text((left, top), text, textColor, font=fontStyle,stroke_width=2)
# 轉(zhuǎn)換回OpenCV格式
return cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)
接下來(lái),定義命令行參數(shù):
# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-p", "--shape-predictor", required=True,
help="path to facial landmark predictor")
ap.add_argument("-v", "--video", type=str, default="",
help="path to input video file")
ap.add_argument("-a", "--alarm", type=str, default="",
help="path alarm .WAV file")
ap.add_argument("-w", "--webcam", type=int, default=0,
help="index of webcam on system")
args = vars(ap.parse_args())
犯困檢測(cè)器需要一個(gè)命令行參數(shù),后跟兩個(gè)可選參數(shù),每個(gè)參數(shù)的詳細(xì)信息如下:
–shape-predictor :這是 dlib 的預(yù)訓(xùn)練面部標(biāo)志檢測(cè)器的路徑。 您可以使用本博文底部的“下載”部分將檢測(cè)器和本教程的源代碼一起下載。
–video:視頻文件。本文用視頻文件測(cè)試。
–alarm :您可以在此處選擇指定要用作警報(bào)的輸入音頻文件的路徑。
–webcam :此整數(shù)控制內(nèi)置網(wǎng)絡(luò)攝像頭/USB 攝像頭的索引。
定義了命令行參數(shù),我們還需要定義幾個(gè)重要的變量:
# define two constants, one for the eye aspect ratio to indicate # blink and then a second constant for the number of consecutive # frames the eye must be below the threshold for to set off the # alarm EYE_AR_THRESH = 0.3 EYE_AR_CONSEC_FRAMES = 48 # initialize the frame counter as well as a boolean used to # indicate if the alarm is going off COUNTER = 0 ALARM_ON = False
定義了 EYE_AR_THRESH。如果眼睛縱橫比低于此閾值,我們將開(kāi)始計(jì)算人閉上眼睛的幀數(shù)。
如果該人閉上眼睛的幀數(shù)超過(guò) EYE_AR_CONSEC_FRAMES,我們將發(fā)出警報(bào)。
在實(shí)驗(yàn)中,我發(fā)現(xiàn) 0.3 的 EYE_AR_THRESH 在各種情況下都能很好地工作(盡管您可能需要為自己的應(yīng)用程序自己調(diào)整它)。
我還將 EYE_AR_CONSEC_FRAMES 設(shè)置為 48 ,這意味著如果一個(gè)人連續(xù)閉眼 48 幀,我們將播放警報(bào)聲。
您可以通過(guò)降低 EYE_AR_CONSEC_FRAMES 來(lái)使疲勞檢測(cè)器更敏感——同樣,您可以通過(guò)增加它來(lái)降低疲勞檢測(cè)器的敏感度。
定義了 COUNTER,即眼睛縱橫比低于 EYE_AR_THRESH 的連續(xù)幀的總數(shù)。
如果 COUNTER 超過(guò) EYE_AR_CONSEC_FRAMES ,那么我們將更新布爾值 ALARM_ON。
dlib 庫(kù)附帶了一個(gè)基于定向梯度的人臉檢測(cè)器的直方圖以及一個(gè)人臉地標(biāo)預(yù)測(cè)器——我們?cè)谝韵麓a塊中實(shí)例化了這兩個(gè):
# initialize dlib's face detector (HOG-based) and then create
# the facial landmark predictor
print("[INFO] loading facial landmark predictor...")
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor(args["shape_predictor"])
dlib 產(chǎn)生的面部標(biāo)志是一個(gè)可索引的列表,見(jiàn)下圖:

因此,要從一組面部標(biāo)志中提取眼睛區(qū)域,我們只需要知道正確的數(shù)組切片索引:
# grab the indexes of the facial landmarks for the left and # right eye, respectively (lStart, lEnd) = face_utils.FACIAL_LANDMARKS_IDXS["left_eye"] (rStart, rEnd) = face_utils.FACIAL_LANDMARKS_IDXS["right_eye"]
使用這些索引,我們將能夠通過(guò)數(shù)組切片輕松提取眼睛區(qū)域。
我們現(xiàn)在準(zhǔn)備啟動(dòng)我們的睡意檢測(cè)器的核心:
# start the video stream thread
print("[INFO] starting video stream thread...")
vs = VideoStream(src=args["webcam"]).start()
time.sleep(1.0)
# loop over frames from the video stream
while True:
# grab the frame from the threaded video file stream, resize
# it, and convert it to grayscale
# channels)
frame = vs.read()
frame = imutils.resize(frame, width=450)
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# detect faces in the grayscale frame
rects = detector(gray, 0)
實(shí)例化 VideoStream。
暫停一秒鐘,讓相機(jī)傳感器預(yù)熱。
開(kāi)始遍歷視頻流中的幀。
讀取下一幀,然后我們通過(guò)將其大小調(diào)整為 450 像素的寬度并將其轉(zhuǎn)換為灰度進(jìn)行預(yù)處理。
應(yīng)用 dlib 的人臉檢測(cè)器來(lái)查找和定位圖像中的人臉。
下一步是應(yīng)用面部標(biāo)志檢測(cè)來(lái)定位面部的每個(gè)重要區(qū)域:
# loop over the face detections for rect in rects: # determine the facial landmarks for the face region, then # convert the facial landmark (x, y)-coordinates to a NumPy # array shape = predictor(gray, rect) shape = face_utils.shape_to_np(shape) # extract the left and right eye coordinates, then use the # coordinates to compute the eye aspect ratio for both eyes leftEye = shape[lStart:lEnd] rightEye = shape[rStart:rEnd] leftEAR = eye_aspect_ratio(leftEye) rightEAR = eye_aspect_ratio(rightEye) # average the eye aspect ratio together for both eyes ear = (leftEAR + rightEAR) / 2.0
循環(huán)遍歷檢測(cè)到的每個(gè)人臉——在我們的實(shí)現(xiàn)中(特別與司機(jī)睡意有關(guān)),我們假設(shè)只有一張臉——司機(jī)——但我把這個(gè) for 循環(huán)留在這里以防萬(wàn)一你想應(yīng)用多張臉視頻的技術(shù)。
對(duì)于每個(gè)檢測(cè)到的人臉,我們應(yīng)用 dlib 的面部標(biāo)志檢測(cè)器并將結(jié)果轉(zhuǎn)換為 NumPy 數(shù)組。
使用 NumPy 數(shù)組切片,我們可以分別提取左眼和右眼的 (x, y) 坐標(biāo)。
給定雙眼的 (x, y) 坐標(biāo),我們?nèi)缓笥?jì)算它們的眼睛縱橫比。
Soukupová 和 ?ech 建議將兩個(gè)眼睛的縱橫比平均在一起以獲得更好的估計(jì)。
然后,我們可以使用下面的 cv2.drawContours 函數(shù)可視化框架上的每個(gè)眼睛區(qū)域——這在我們嘗試調(diào)試腳本并希望確保正確檢測(cè)和定位眼睛時(shí)通常很有幫助:
# compute the convex hull for the left and right eye, then # visualize each of the eyes leftEyeHull = cv2.convexHull(leftEye) rightEyeHull = cv2.convexHull(rightEye) cv2.drawContours(frame, [leftEyeHull], -1, (0, 255, 0), 1) cv2.drawContours(frame, [rightEyeHull], -1, (0, 255, 0), 1)
最后,我們現(xiàn)在準(zhǔn)備檢查視頻流中的人是否出現(xiàn)犯困的癥狀:
# check to see if the eye aspect ratio is below the blink
# threshold, and if so, increment the blink frame counter
if ear < EYE_AR_THRESH:
COUNTER += 1
# if the eyes were closed for a sufficient number of
# then sound the alarm
if COUNTER >= EYE_AR_CONSEC_FRAMES:
# if the alarm is not on, turn it on
if not ALARM_ON:
ALARM_ON = True
# check to see if an alarm file was supplied,
# and if so, start a thread to have the alarm
# sound played in the background
if args["alarm"] != "":
t = Thread(target=sound_alarm,
args=(args["alarm"],))
t.deamon = True
t.start()
# draw an alarm on the frame
frame=cv2ImgAddText(frame,"醒醒,別睡!",10,30,(255, 0, 0),30)
# otherwise, the eye aspect ratio is not below the blink
# threshold, so reset the counter and alarm
else:
COUNTER = 0
ALARM_ON = False
檢查眼睛縱橫比是否低于“眨眼/閉合”眼睛閾值 EYE_AR_THRESH 。
如果是,我們?cè)黾?COUNTER ,即該人閉上眼睛的連續(xù)幀總數(shù)。
如果 COUNTER 超過(guò) EYE_AR_CONSEC_FRAMES,那么我們假設(shè)此人開(kāi)始打瞌睡。
進(jìn)行了另一次檢查,以查看警報(bào)是否已打開(kāi)——如果沒(méi)有,我們將其打開(kāi)。
處理播放警報(bào)聲音,前提是在執(zhí)行腳本時(shí)提供了 --alarm 路徑。我們特別注意創(chuàng)建一個(gè)單獨(dú)的線程來(lái)負(fù)責(zé)調(diào)用 sound_alarm 以確保我們的主程序在聲音播放完畢之前不會(huì)被阻塞。
繪制文本 DROWSINESS ALERT!在我們的框架上——同樣,這通常有助于調(diào)試,尤其是當(dāng)您不使用 playsound 庫(kù)時(shí)。
最后,第 136-138 行處理眼睛縱橫比大于 EYE_AR_THRESH 的情況,表示眼睛是睜開(kāi)的。如果眼睛是睜開(kāi)的,我們重置計(jì)數(shù)器并確保警報(bào)關(guān)閉。
我們的睡意檢測(cè)器中的最后一個(gè)代碼塊處理將輸出幀顯示到我們的屏幕上:
# draw the computed eye aspect ratio on the frame to help
# with debugging and setting the correct eye aspect ratio
# thresholds and frame counters
cv2.putText(frame, "EAR: {:.2f}".format(ear), (300, 30),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
# show the frame
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()
到這里編碼完成!??!
測(cè)試疲勞檢測(cè)器
運(yùn)行指令:
python detect_drowsiness.py --shape-predictor shape_predictor_68_face_landmarks.dat --video 12.mp4 --alarm alarm.mp3
運(yùn)行結(jié)果:

檢測(cè)到打瞌睡就會(huì)發(fā)出提示,并將提醒打印在視頻上面?
以上就是Python人臉檢測(cè)實(shí)戰(zhàn)之疲勞檢測(cè)的詳細(xì)內(nèi)容,更多關(guān)于Python 人臉疲勞檢測(cè)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Python基礎(chǔ)之?dāng)?shù)據(jù)類型詳解
python的數(shù)值類型包括整數(shù),浮點(diǎn)數(shù),復(fù)數(shù),集合,小數(shù)和分?jǐn)?shù),布爾值.它們都是python中的數(shù)值類型.如果是有過(guò)其他語(yǔ)言編寫(xiě)經(jīng)驗(yàn)的人,一定很好奇,浮點(diǎn)數(shù)和小數(shù)的區(qū)別是什么?文中有非常詳細(xì)的介紹,需要的朋友可以參考下2021-06-06
14個(gè)用Python實(shí)現(xiàn)的Excel常用操作總結(jié)
自從學(xué)了Python后就逼迫自己不用Excel,所有操作用Python實(shí)現(xiàn)。目的是鞏固Python,與增強(qiáng)數(shù)據(jù)處理能力。本文為大家總結(jié)了14個(gè)用Python實(shí)現(xiàn)的Excel常用操作,需要的可以參考一下2022-06-06
Python標(biāo)準(zhǔn)庫(kù)之itertools庫(kù)的使用方法
Python提供了一個(gè)非常棒的模塊用于創(chuàng)建自定義的迭代器,這個(gè)模塊就是 itertools。itertools 提供的工具相當(dāng)高效且節(jié)省內(nèi)存,下面這篇文章主要給大家介紹了關(guān)于Python標(biāo)準(zhǔn)庫(kù)之itertools庫(kù)使用的相關(guān)資料,需要的朋友可以參考下。2017-09-09
Python?代替?xftp?從?Linux?服務(wù)器下載文件的操作方法
我們經(jīng)常需要從Linux服務(wù)器上同步文件,但是xftp等工具都需要注冊(cè)了,這里用免費(fèi)的Python代碼來(lái)下載文件,還可以擴(kuò)展更多的自定義用法,這篇文章主要介紹了Python?代替?xftp?從?Linux?服務(wù)器下載文件,需要的朋友可以參考下2024-06-06
python Django 反向訪問(wèn)器的外鍵沖突解決
這篇文章主要介紹了python Django 反向訪問(wèn)器的外鍵沖突解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-05-05
Python 實(shí)現(xiàn) 貪吃蛇大作戰(zhàn) 代碼分享
本文給大家分享的是一個(gè)使用cocos2d-python游戲引擎庫(kù)制作出來(lái)的貪吃蛇大作戰(zhàn)的游戲代碼,基于Python 2.7 和 cocos2d 庫(kù),有需要的小伙伴可以參考下2016-09-09
Python利用smtplib實(shí)現(xiàn)郵件發(fā)送
在當(dāng)今數(shù)字時(shí)代,電子郵件已成為我們生活和工作中不可或缺的一部分,本篇文章將為你講解如何在Python發(fā)送郵件,并為你提供實(shí)現(xiàn)的多種方式,希望對(duì)大家有所幫助2023-06-06
基于python圖書(shū)館管理系統(tǒng)設(shè)計(jì)實(shí)例詳解
這篇文章主要介紹了基于python圖書(shū)館管理系統(tǒng)設(shè)計(jì)實(shí)例詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08
Python+Tkinter繪制一個(gè)數(shù)字時(shí)鐘
這篇文章主要為大家詳細(xì)介紹了Python使用Tkinter繪制一個(gè)數(shù)字時(shí)鐘,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01

