Python⼈脸检测实战之疲劳检测
本⽂主要介绍了实现疲劳检测:如果眼睛已经闭上了⼀段时间,我们会认为他们开始打瞌睡并发出警报来唤醒他们并引起他们的注意,感兴趣的朋友可以了解⼀下。
今天我们实现疲劳检测。 如果眼睛已经闭上了⼀段时间,我们会认为他们开始打瞌睡并发出警报来唤醒他们并引起他们的注意。我们测试
⼀段视频来展⽰效果。同时代码中保留开启摄像头的的代码,取消注释即可使⽤。杨戬身世
使⽤ OpenCV 构建犯困检测器
要开始我们的实现,打开⼀个新⽂件,将其命名为 detect_drowsiness.py ,并插⼊以下代码:
1 2 3 4 5 6 7 8 9 10 11 12# 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
导⼊们所需的 Python 包。
我们还需要 imutils 包,我的⼀系列计算机视觉和图像处理功能,以便更轻松地使⽤ OpenCV。
如果您的系统上还没有安装 imutils,您可以通过以下⽅式安装/升级 imutils:
python入门教程视屏>小程序功能
1pip install --upgrade imutils
还将导⼊ Thread 类,以便我们可以在与主线程不同的线程中播放我们的警报,以确保我们的脚本不会在警报响起时暂停执⾏。
为了真正播放我们的 WAV/MP3 闹钟,我们需要 playsound 库,这是⼀个纯 Python 的跨平台实现,⽤于播放简单的声⾳。playsound 库可以通过 pip ⽅便地安装:
1pip install playsound
但是,如果您使⽤的是 macOS(就像我为这个项⽬所做的那样),您还需要安装 pyobjc,否则当您实际尝试播放声⾳时,您将收到与AppKit 相关的错误:
1pip install pyobjc
接下来,我们需要定义 sound_alarm 函数,该函数播放⾳频⽂件:
1 2 3def sound_alarm(path):
# play an alarm sound
playsound.playsound(path)
定义 eye_aspect_ratio 函数,该函数⽤于计算垂直眼睛界标之间的距离与⽔平眼睛界标之间的距离之⽐:
1 2 3 4 5 6 7 8 9 10 11 12def 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不能直接绘制中⽂,我们还需定义绘制中⽂的⽅法:
1 2 3 4 5 6 7 8 9 10 11 12def 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))    # 创建⼀个可以在给定图像上绘图的对象
draw =ImageDraw.Draw(img)
# 字体的格式
fontStyle =uetype(
"", textSize, encoding="utf-8")
# 绘制⽂本
<((left, top), text, textColor, font=fontStyle,stroke_width=2)
颜html代码大全# 转换回OpenCV格式
return cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)
接下来,定义命令⾏参数:
1 2 3 4 5 6 7 8 9 10 11# 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())
犯困检测器需要⼀个命令⾏参数,后跟两个可选参数,每个参数的详细信息如下:
–shape-predictor :这是 dlib 的预训练⾯部标志检测器的路径。 您可以使⽤本博⽂底部的“下载”部分将检测器和本教程的源代码⼀起下载。
–video:视频⽂件。本⽂⽤视频⽂件测试。
–alarm :您可以在此处选择指定要⽤作警报的输⼊⾳频⽂件的路径。
–webcam :此整数控制内置⽹络摄像头/USB 摄像头的索引。
定义了命令⾏参数,我们还需要定义⼏个重要的变量:
1 2 3 4 5 6 7 8 9 10# 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。如果眼睛纵横⽐低于此阈值,我们将开始计算⼈闭上眼睛的帧数。
如果该⼈闭上眼睛的帧数超过 EYE_AR_CONSEC_FRAMES,我们将发出警报。
在实验中,我发现 0.3 的 EYE_AR_THRESH 在各种情况下都能很好地⼯作(尽管您可能需要为⾃⼰的应⽤程序⾃⼰调整它)。我还将 EYE_AR_CONSEC_FRAMES 设置为 48 ,这意味着如果⼀个⼈连续闭眼 48 帧,我们将播放警报声。
您可以通过降低 EYE_AR_CONSEC_FRAMES 来使疲劳检测器更敏感——同样,您可以通过增加它来降低疲劳检测器的敏感度。定义了 COUNTER,即眼睛纵横⽐低于 EYE_AR_THRESH 的连续帧的总数。
如果 COUNTER 超过 EYE_AR_CONSEC_FRAMES ,那么我们将更新布尔值 ALARM_ON。
dlib 库附带了⼀个基于定向梯度的⼈脸检测器的直⽅图以及⼀个⼈脸地标预测器——我们在以下代码块中实例化了这两个:
1 2 3 4 5# initialize dlib's face detector (HOG-based) and then create # the facial landmark predictor
print("[INFO] loading facial ") detector =_frontal_face_detector()
predictor =dlib.shape_predictor(args["shape_predictor"])
dlib 产⽣的⾯部标志是⼀个可索引的列表,见下图:
wxpython button字体大小因此,要从⼀组⾯部标志中提取眼睛区域,我们只需要知道正确的数组切⽚索引:
1 2 3 4# 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"]
使⽤这些索引,我们将能够通过数组切⽚轻松提取眼睛区域。我们现在准备启动我们的睡意检测器的核⼼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14# start the video stream thread
print("[INFO] starting video ")
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 =size(frame, width=450)
gray =cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# detect faces in the grayscale frame
rects =detector(gray, 0)
实例化 VideoStream。
暂停⼀秒钟,让相机传感器预热。
开始遍历视频流中的帧。
读取下⼀帧,然后我们通过将其⼤⼩调整为 450 像素的宽度并将其转换为灰度进⾏预处理。应⽤ dlib 的⼈脸检测器来查和定位图像中的⼈脸。
下⼀步是应⽤⾯部标志检测来定位⾯部的每个重要区域:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15# 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
循环遍历检测到的每个⼈脸——在我们的实现中(特别与司机睡意有关),我们假设只有⼀张脸——司机——但我把这个 for 循环留在这⾥以防万⼀你想应⽤多张脸视频的技术。
对于每个检测到的⼈脸,我们应⽤ dlib 的⾯部标志检测器并将结果转换为 NumPy 数组。
使⽤ NumPy 数组切⽚,我们可以分别提取左眼和右眼的 (x, y) 坐标。
给定双眼的 (x, y) 坐标,我们然后计算它们的眼睛纵横⽐。
Soukupová 和 Čech 建议将两个眼睛的纵横⽐平均在⼀起以获得更好的估计。
然后,我们可以使⽤下⾯的 cv2.drawContours 函数可视化框架上的每个眼睛区域——这在我们尝试调试脚本并希望确保正确检测和定位眼睛时通常很有帮助:
1 2 3 4 5 6# compute the convex hull for the left and right eye, then # visualize each of the eyes
leftEyeHull =vexHull(leftEye)
rightEyeHull =vexHull(rightEye)
cv2.drawContours(frame, [leftEyeHull], -1, (0, 255, 0), 1) cv2.drawContours(frame, [rightEyeHull], -1, (0, 255, 0), 1)
最后,我们现在准备检查视频流中的⼈是否出现犯困的症状:
1 2 3 4 5 6 7 8 9 10# 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