我们在开长途车的时候可能太累要打瞌睡,这是很危险的,很多高端车配备了瞌睡检测系统,那么是怎么实现的呢,今天小编带大家看看python怎么实现一个打瞌睡检测并报警的程序,先看看下面的效果图,
原理就是使用OpenCV检测视频流中的眨眼行为,并检测眼睛已经闭合了多长时间。如果眼睛已经闭合了超过了一定时间,系统便认为是打瞌睡并发出警报以唤醒他们并引起他们的注意。
▊ 在汽车上安装摄像头
我用于该项目的相机是Logitech C920。我喜欢这款相机,比较实惠。可以全1080p拍摄。即插即用几乎与我尝试过的所有设备(包括Raspberry Pi)兼容 。我拿了这台相机,并用一些双面胶带将其安装在仪表板的顶部,以防止在驱动过程中相机四处移动。然后将相机连接到我旁边座位上的MacBook Pro。
▊ 检测流程
▶首先,我们将设置一个相机来监视人脸流:
▶如果找到了脸部,我们将应用脸部界标检测并提取眼睛区域:
▶现在我们有了眼睛区域,我们可以计算眼睛的长宽比以确定眼睛是否闭合:
▶如果眼睛的纵横比表明眼睛已经闭合了足够长的时间,我们将发出警报以唤醒驾驶员:
▊ 具体实现代码
我们将使用OpenCV,dlib和Python来实现上面详述的打瞌睡检测算法。
使用OpenCV构建睡意检测器
我们新建一个新文件,命名为 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
我们将需要SciPy 软件包,以便我们可以在计算眼睛长宽比时计算出面部界标点之间的欧几里得距离(并非严格要求,但是如果您打算在计算机视觉,图像处理中进行任何工作,则应该安装SciPy ,或机器学习空间)。
我们还需要imutils软件包,这是我的一系列计算机视觉和图像处理功能,以简化 OpenCV的使用。
如果您还没有安装imutils在系统上,则可以安装/升级imutils通过:
$ pip install --upgrade imutils
playound库可通过以下方式方便地安装:
$ pip install playsound但是,如果您使用的是macOS(就像我在该项目中所做的那样),则还需要安装pyobjc,否则将出错:
$ pip install pyobjc为了检测和定位面部标志,我们需要 在第11行中导入 的dlib库。如果您需要在系统上安装dlib。
def sound_alarm(path): # play an alarm sound playsound.playsound(path)
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睁开眼睛时,眼睛纵横比的返回值将近似恒定。然后,该值将在眨眼期间迅速降低至零。
如果眼睛闭合,则眼睛的长宽比将再次保持近似恒定,但将比睁开眼睛时的长宽比 小得多。
左上:当眼睛睁开时,眼睛界标的可视化。右上:闭上眼睛时的眼睛地标。底部:绘制随时间变化的眼睛纵横比。眼睛纵横比的下降表示眨眼(Soukupová和Čech的图1)。
在 左上角,我们的眼睛完全张开,并绘制了眼睛的面部轮廓。然后在 右上角,我们的眼睛闭上了。然后, 底部绘制随时间变化的眼睛纵横比。
如我们所见,眼睛的纵横比是恒定的(表示眼睛睁开),然后迅速下降到零,然后再次增加,表明眨眼了。
在睡意检测器的情况下,我们将监视眼睛的宽高比,以查看该值是否 下降但 没有再次增加,从而暗示该人已经闭上了眼睛。
接下来,让我们分析命令行参数:
# 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("-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预先训练的面部标志检测器的路径。
- alarm 报警 :在这里,您可以选择指定要用作报警音频文件的路径。
-webcam 摄像头 :此整数控制内置网络摄像头/ USB摄像机的索引。
现在,我们的命令行参数已被解析,我们需要定义一些重要的变量:
# 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_CONSEC_FRAMES (第49行),我们会发出警报。
从实验上,我发现 EYE_AR_THRESH 的 0. 3 在各种情况下都可以很好地工作(尽管您可能需要针对自己的应用程序对其进行调整)。
我还设置了 EYE_AR_CONSEC_FRAMES 成为 48 ,这意味着如果一个人连续48帧闭上眼睛,我们将播放警报声。
你可以让打瞌睡探测器 更加敏感,通过减少的EYE_AR_CONSEC_FRAMES ,同样,您可以通过增加大课室探测器的 灵敏度来降低它的灵敏度。
第53行定义计数器 ,即眼睛长宽比低于的连续帧总数 EYE_AR_THRESH 。
如果 计数器 超过 EYE_AR_CONSEC_FRAMES ,然后我们将更新布尔值 ALARM_ON (第54行)。
dlib库附带了基于“基于直方图的直方图”的面部检测器以及一个面部界标预测器 -我们在以下代码块中将这两个实例化:
# 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生成的面部标志是可索引的列表,正如我在此处所述:
# 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"]使用这些索引,我们将能够轻松地通过数组切片提取眼睛区域。
现在我们准备开始打瞌睡检测器的核心:
# 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)
然后,我们暂停片刻,以使相机传感器预热(第70行)。
在 第73行,我们开始循环播放视频流中的帧。
第77行读取下一个帧 ,然后我们对其进行预处理,方法是将其调整为450像素的宽度并将其转换为灰度(第78和79行)。
第82行使用dlib的面部检测器来查找和定位图像中的面部。
下一步是应用面部标志检测来定位面部的每个重要区域:
# 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
对于每个检测到的脸,我们应用dlib的脸部界标检测器(第89行),并将结果转换为NumPy数组(第90行)。
使用NumPy数组切片,我们可以分别提取左眼和右眼的 (x,y)坐标(第94和95行)。
给定两只眼睛的 (x,y)坐标,然后我们在第96和97行上计算它们的眼睛纵横比 。
然后,我们可以 在我们的眼睛上看到每个眼睛区域帧通过使用 cv2。drawContours 下面的函数-当我们尝试调试脚本并希望确保正确检测和定位眼睛时,这通常很有用:
# 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)
# 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 cv2.putText(frame, "DROWSINESS ALERT!", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2) # otherwise, the eye aspect ratio is not below the blink # threshold, so reset the counter and alarm else: COUNTER = 0 ALARM_ON = False
# 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 \ --alarm alarm.wav
网友评论0