Python人臉檢測實戰(zhàn)之疲勞檢測
今天我們實現(xiàn)疲勞檢測。 如果眼睛已經(jīng)閉上了一段時間,我們會認為他們開始打瞌睡并發(fā)出警報來喚醒他們并引起他們的注意。我們測試一段視頻來展示效果。同時代碼中保留開啟攝像頭的的代碼,取消注釋即可使用。
使用 OpenCV 構(gòu)建犯困檢測器
要開始我們的實現(xiàn),打開一個新文件,將其命名為 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 包,我的一系列計算機視覺和圖像處理功能,以便更輕松地使用 OpenCV。
如果您的系統(tǒng)上還沒有安裝 imutils,您可以通過以下方式安裝/升級 imutils:
pip install --upgrade imutils
還將導(dǎo)入 Thread 類,以便我們可以在與主線程不同的線程中播放我們的警報,以確保我們的腳本不會在警報響起時暫停執(zhí)行。
為了真正播放我們的 WAV/MP3 鬧鐘,我們需要 playsound 庫,這是一個純 Python 的跨平臺實現(xiàn),用于播放簡單的聲音。
playsound 庫可以通過 pip 方便地安裝:
pip install playsound
但是,如果您使用的是 macOS(就像我為這個項目所做的那樣),您還需要安裝 pyobjc,否則當您實際嘗試播放聲音時,您將收到與 AppKit 相關(guān)的錯誤:
pip install pyobjc
接下來,我們需要定義 sound_alarm 函數(shù),該函數(shù)播放音頻文件:
def sound_alarm(path): # play an alarm sound playsound.playsound(path)
定義 eye_aspect_ratio 函數(shù),該函數(shù)用于計算垂直眼睛界標之間的距離與水平眼睛界標之間的距離之比:
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)建一個可以在給定圖像上繪圖的對象 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)
接下來,定義命令行參數(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())
犯困檢測器需要一個命令行參數(shù),后跟兩個可選參數(shù),每個參數(shù)的詳細信息如下:
–shape-predictor :這是 dlib 的預(yù)訓(xùn)練面部標志檢測器的路徑。 您可以使用本博文底部的“下載”部分將檢測器和本教程的源代碼一起下載。
–video:視頻文件。本文用視頻文件測試。
–alarm :您可以在此處選擇指定要用作警報的輸入音頻文件的路徑。
–webcam :此整數(shù)控制內(nèi)置網(wǎng)絡(luò)攝像頭/USB 攝像頭的索引。
定義了命令行參數(shù),我們還需要定義幾個重要的變量:
# 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。如果眼睛縱橫比低于此閾值,我們將開始計算人閉上眼睛的幀數(shù)。
如果該人閉上眼睛的幀數(shù)超過 EYE_AR_CONSEC_FRAMES,我們將發(fā)出警報。
在實驗中,我發(fā)現(xiàn) 0.3 的 EYE_AR_THRESH 在各種情況下都能很好地工作(盡管您可能需要為自己的應(yīng)用程序自己調(diào)整它)。
我還將 EYE_AR_CONSEC_FRAMES 設(shè)置為 48 ,這意味著如果一個人連續(xù)閉眼 48 幀,我們將播放警報聲。
您可以通過降低 EYE_AR_CONSEC_FRAMES 來使疲勞檢測器更敏感——同樣,您可以通過增加它來降低疲勞檢測器的敏感度。
定義了 COUNTER,即眼睛縱橫比低于 EYE_AR_THRESH 的連續(xù)幀的總數(shù)。
如果 COUNTER 超過 EYE_AR_CONSEC_FRAMES ,那么我們將更新布爾值 ALARM_ON。
dlib 庫附帶了一個基于定向梯度的人臉檢測器的直方圖以及一個人臉地標預(yù)測器——我們在以下代碼塊中實例化了這兩個:
# 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)生的面部標志是一個可索引的列表,見下圖:
因此,要從一組面部標志中提取眼睛區(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"]
使用這些索引,我們將能夠通過數(shù)組切片輕松提取眼睛區(qū)域。
我們現(xiàn)在準備啟動我們的睡意檢測器的核心:
# 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)
實例化 VideoStream。
暫停一秒鐘,讓相機傳感器預(yù)熱。
開始遍歷視頻流中的幀。
讀取下一幀,然后我們通過將其大小調(diào)整為 450 像素的寬度并將其轉(zhuǎn)換為灰度進行預(yù)處理。
應(yīng)用 dlib 的人臉檢測器來查找和定位圖像中的人臉。
下一步是應(yīng)用面部標志檢測來定位面部的每個重要區(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)遍歷檢測到的每個人臉——在我們的實現(xiàn)中(特別與司機睡意有關(guān)),我們假設(shè)只有一張臉——司機——但我把這個 for 循環(huán)留在這里以防萬一你想應(yīng)用多張臉視頻的技術(shù)。
對于每個檢測到的人臉,我們應(yīng)用 dlib 的面部標志檢測器并將結(jié)果轉(zhuǎn)換為 NumPy 數(shù)組。
使用 NumPy 數(shù)組切片,我們可以分別提取左眼和右眼的 (x, y) 坐標。
給定雙眼的 (x, y) 坐標,我們?nèi)缓笥嬎闼鼈兊难劬v橫比。
Soukupová 和 ?ech 建議將兩個眼睛的縱橫比平均在一起以獲得更好的估計。
然后,我們可以使用下面的 cv2.drawContours 函數(shù)可視化框架上的每個眼睛區(qū)域——這在我們嘗試調(diào)試腳本并希望確保正確檢測和定位眼睛時通常很有幫助:
# 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)在準備檢查視頻流中的人是否出現(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 。
如果是,我們增加 COUNTER ,即該人閉上眼睛的連續(xù)幀總數(shù)。
如果 COUNTER 超過 EYE_AR_CONSEC_FRAMES,那么我們假設(shè)此人開始打瞌睡。
進行了另一次檢查,以查看警報是否已打開——如果沒有,我們將其打開。
處理播放警報聲音,前提是在執(zhí)行腳本時提供了 --alarm 路徑。我們特別注意創(chuàng)建一個單獨的線程來負責調(diào)用 sound_alarm 以確保我們的主程序在聲音播放完畢之前不會被阻塞。
繪制文本 DROWSINESS ALERT!在我們的框架上——同樣,這通常有助于調(diào)試,尤其是當您不使用 playsound 庫時。
最后,第 136-138 行處理眼睛縱橫比大于 EYE_AR_THRESH 的情況,表示眼睛是睜開的。如果眼睛是睜開的,我們重置計數(shù)器并確保警報關(guān)閉。
我們的睡意檢測器中的最后一個代碼塊處理將輸出幀顯示到我們的屏幕上:
# 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()
到這里編碼完成!??!
測試疲勞檢測器
運行指令:
python detect_drowsiness.py --shape-predictor shape_predictor_68_face_landmarks.dat --video 12.mp4 --alarm alarm.mp3
運行結(jié)果:
檢測到打瞌睡就會發(fā)出提示,并將提醒打印在視頻上面?
以上就是Python人臉檢測實戰(zhàn)之疲勞檢測的詳細內(nèi)容,更多關(guān)于Python 人臉疲勞檢測的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
解決keras GAN訓(xùn)練是loss不發(fā)生變化,accuracy一直為0.5的問題
這篇文章主要介紹了解決keras GAN訓(xùn)練是loss不發(fā)生變化,accuracy一直為0.5的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-07-07Python如何利用Har文件進行遍歷指定字典替換提交的數(shù)據(jù)詳解
這篇文章主要給大家介紹了關(guān)于Python如何利用Har文件進行遍歷指定字典替換提交的數(shù)據(jù)的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11python中的psutil模塊詳解(cpu、內(nèi)存、磁盤情況、結(jié)束指定進程)
這篇文章主要介紹了python中的psutil(cpu、內(nèi)存、磁盤情況、結(jié)束指定進程),本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-04-04詳解如何用django實現(xiàn)redirect的幾種方法總結(jié)
這篇文章主要介紹了如何用django實現(xiàn)redirect的幾種方法總結(jié),詳細的介紹3種實現(xiàn)方式,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-11-11Python數(shù)據(jù)可視化處理庫PyEcharts柱狀圖,餅圖,線性圖,詞云圖常用實例詳解
這篇文章主要介紹了Python數(shù)據(jù)可視化處理庫PyEcharts柱狀圖、餅圖、線性圖常用實例詳解,需要的朋友可以參考下2020-02-02PyTorch中model.zero_grad()和optimizer.zero_grad()用法
這篇文章主要介紹了PyTorch中model.zero_grad()和optimizer.zero_grad()用法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-06-06