用python检测是否打瞌睡并报警提醒

我们在开长途车的时候可能太累要打瞌睡,这是很危险的,很多高端车配备了瞌睡检测系统,那么是怎么实现的呢,今天小编带大家看看python怎么实现一个打瞌睡检测并报警的程序,先看看下面的效果图,

用python检测是否打瞌睡并报警提醒


原理就是使用OpenCV检测视频流中的眨眼行为,并检测眼睛已经闭合了多长时间。如果眼睛已经闭合了超过了一定时间,系统便认为是打瞌睡并发出警报以唤醒他们并引起他们的注意。

▊ 在汽车上安装摄像头

我用于该项目的相机是Logitech C920。我喜欢这款相机,比较实惠。可以全1080p拍摄。即插即用几乎与我尝试过的所有设备(包括Raspberry Pi)兼容 。我拿了这台相机,并用一些双面胶带将其安装在仪表板的顶部,以防止在驱动过程中相机四处移动。然后将相机连接到我旁边座位上的MacBook Pro。

▊ 检测流程

▶首先,我们将设置一个相机来监视人脸流:

用python检测是否打瞌睡并报警提醒

▶如果找到了脸部,我们将应用脸部界标检测并提取眼睛区域:

用python检测是否打瞌睡并报警提醒

▶现在我们有了眼睛区域,我们可以计算眼睛的长宽比以确定眼睛是否闭合:

用python检测是否打瞌睡并报警提醒

▶如果眼睛的纵横比表明眼睛已经闭合了足够长的时间,我们将发出警报以唤醒驾驶员:
用python检测是否打瞌睡并报警提醒

▊ 具体实现代码

我们将使用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


第2-12行导入了我们所需的Python包。

我们将需要SciPy 软件包,以便我们可以在计算眼睛长宽比时计算出面部界标点之间的欧几里得距离(并非严格要求,但是如果您打算在计算机视觉,图像处理中进行任何工作,则应该安装SciPy ,或机器学习空间)。

我们还需要imutils软件包,这是我的一系列计算机视觉和图像处理功能,以简化 OpenCV的使用。

如果您还没有安装imutils在系统上,则可以安装/升级imutils通过:

$ pip install --upgrade imutils


我们还将导入 threading,因此我们可以在与主线程不同的线程中播放警报,以确保在警报响起时脚本不会暂停执行。
为了实际播放WAV / MP3警报,我们需要playound库,这是一个纯Python的跨平台实现,用于播放简单的声音。

playound库可通过以下方式方便地安装:

$ pip install playsound
但是,如果您使用的是macOS(就像我在该项目中所做的那样),则还需要安装pyobjc,否则将出错:

$ pip install pyobjc
为了检测和定位面部标志,我们需要 在第11行中导入 的dlib库。如果您需要在系统上安装dlib。

接下来,我们需要定义我们的 声音警报 函数 参数为音频文件路径,然后播放该文件:

def sound_alarm(path):
	# play an alarm sound
	playsound.playsound(path)


我们还需要定义 eye_aspect_ratio 用于计算垂直眼界标之间的距离与水平眼界标之间的距离之比的函数:
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
睁开眼睛时,眼睛纵横比的返回值将近似恒定。然后,该值将在眨眼期间迅速降低至零。

如果眼睛闭合,则眼睛的长宽比将再次保持近似恒定,但将比睁开眼睛时的长宽比 小得多。

用python检测是否打瞌睡并报警提醒

左上:当眼睛睁开时,眼睛界标的可视化。右上:闭上眼睛时的眼睛地标。底部:绘制随时间变化的眼睛纵横比。眼睛纵横比的下降表示眨眼(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


第48行定义了EYE_AR_THRESH 。如果眼睛的宽高比 低于此阈值,我们将开始计算该人闭上眼睛的帧数。

如果该人闭上眼睛的帧数超过 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生成的面部标志是可索引的列表,正如我在此处所述:
用python检测是否打瞌睡并报警提醒
因此,要从一组面部标志中提取眼睛区域,我们只需要知道正确的数组切片索引即可:

# 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)


在 第69行,我们实例化了视频流 使用提供的 - 摄像头 指数。

然后,我们暂停片刻,以使相机传感器预热(第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


我们在第85行上遍历每个检测到的面孔 -在我们的实现中(具体涉及驾驶员的困倦),我们假设只有 一张面孔。

对于每个检测到的脸,我们应用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


在 第111行,我们进行了检查,以查看眼睛的纵横比是否低于“眨眼/闭眼”眼睛阈值,EYE_AR_THRESH 。
如果是,我们增加计数器 ,即人闭眼的连续帧总数 。
如果 计数器 超过 EYE_AR_CONSEC_FRAMES (第116行),那么我们假设此人开始打瞌睡。
再次进行检查,这次是在 118和119行上,以查看警报是否打开-如果没有打开,我们将其打开。
第124-128行处理播放警报声,- 报警 执行脚本时提供了音频路径。我们特别注意创建一个 单独的线程来负责调用声音警报 以确保在声音播放完毕之前不会阻塞我们的主程序。
第131和132行绘制文本DROWSINESS警报!
最后,第136-138行,其中眼长宽比大于EYE_AR_THRESH时 ,说明眼睛是睁开的,的。我们会重置计数器 并确保警报已关闭。
打瞌睡检测器中的最终代码块处理显示输出 帧 到我们的屏幕:
	# 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


正如你可以从截屏看,一旦视频流运行起来,我 仔细地开始了我的公寓测试在停车场睡意探测器,以确保它确实工作正常。

经过几次测试后,我又转向一些偏僻的道路和停车场,因为那里的 交通很少,以继续测试睡意探测器。

请记住,睁开眼睛,甚至持续一秒钟都是很危险的,所以我采取了额外的特殊预防措施,以确保 实验期间唯一可能受到伤害的人就是我自己。

结果表明,我们的睡意检测器能够检测到何时有打瞌睡的危险,然后发出响亮的警报以引起我的注意。

睡意检测器甚至可以在各种条件下工作,包括在道路上行驶时直射阳光,以及在混凝土停车场中时的低照明。

{{collectdata}}

网友评论0