当前位置:   article > 正文

使用opencv进行疲劳监测_基于opencv的疲劳驾驶检测

基于opencv的疲劳驾驶检测

中国有大概600万长途货车司机,在我老家也有很多人从事这一工作,这个工作辛苦且高危,就在今年春节前几天,邻村有个30多岁的货车司机因为疲劳驾驶,直接追尾等红灯的大货车,不幸离世,这让我不禁想起,如果疲劳检测系统能够普及,也许可以挽回很多生命。本篇文章讲一下如何用opencv检测眼睛的闭合状态来进行疲劳监测报警。

宋丹丹:问把大象关冰箱分几步?

赵本山:几步?

宋丹丹:三步!一、把门打开 二、把大象塞进去 三、把门关上

这里的代码也是分三步,很直接、很清晰。

1、打开摄像头,获取每一帧图片,检测人脸位置

2、在检测到人脸的基础上,提取人脸特征点(68个),取出眼睛对应坐标。

3、计算眼睛的高宽比,也就是闭合程度,根据闭合程度和持续时间决定是否报警。

在实际项目中,我们使用c++实现,这里为实验方便,使用python代码做演示。顺便提一点,在实际项目中,我们还结合了嘴巴张开的程度,即打哈欠的动作判断。

先上代码:

  1. # USAGE
  2. # python detect_drowsiness.py --shape-predictor shape_predictor_68_face_landmarks.dat
  3. # python detect_drowsiness.py --shape-predictor shape_predictor_68_face_landmarks.dat --alarm alarm.wav
  4. # import the necessary packages
  5. from scipy.spatial import distance as dist
  6. from imutils.video import VideoStream
  7. from imutils import face_utils
  8. from threading import Thread
  9. import numpy as np
  10. import playsound
  11. import argparse
  12. import imutils
  13. import time
  14. import dlib
  15. import cv2
  16. def sound_alarm(path):
  17. # play an alarm sound
  18. playsound.playsound(path)
  19. def eye_aspect_ratio(eye):
  20. # compute the euclidean distances between the two sets of
  21. # vertical eye landmarks (x, y)-coordinates
  22. A = dist.euclidean(eye[1], eye[5])
  23. B = dist.euclidean(eye[2], eye[4])
  24. # compute the euclidean distance between the horizontal
  25. # eye landmark (x, y)-coordinates
  26. C = dist.euclidean(eye[0], eye[3])
  27. # compute the eye aspect ratio
  28. ear = (A + B) / (2.0 * C)
  29. # return the eye aspect ratio
  30. return ear
  31. # construct the argument parse and parse the arguments
  32. ap = argparse.ArgumentParser()
  33. ap.add_argument("-p", "--shape-predictor", required=True,
  34. help="path to facial landmark predictor")
  35. ap.add_argument("-a", "--alarm", type=str, default="",
  36. help="path alarm .WAV file")
  37. ap.add_argument("-w", "--webcam", type=int, default=0,
  38. help="index of webcam on system")
  39. args = vars(ap.parse_args())
  40. # define two constants, one for the eye aspect ratio to indicate
  41. # blink and then a second constant for the number of consecutive
  42. # frames the eye must be below the threshold for to set off the
  43. # alarm
  44. EYE_AR_THRESH = 0.3
  45. EYE_AR_CONSEC_FRAMES = 48
  46. # initialize the frame counter as well as a boolean used to
  47. # indicate if the alarm is going off
  48. COUNTER = 0
  49. ALARM_ON = False
  50. # initialize dlib's face detector (HOG-based) and then create
  51. # the facial landmark predictor
  52. print("[INFO] loading facial landmark predictor...")
  53. detector = dlib.get_frontal_face_detector()
  54. predictor = dlib.shape_predictor(args["shape_predictor"])
  55. # grab the indexes of the facial landmarks for the left and
  56. # right eye, respectively
  57. (lStart, lEnd) = face_utils.FACIAL_LANDMARKS_IDXS["left_eye"]
  58. (rStart, rEnd) = face_utils.FACIAL_LANDMARKS_IDXS["right_eye"]
  59. # start the video stream thread
  60. print("[INFO] starting video stream thread...")
  61. vs = VideoStream(src=args["webcam"]).start()
  62. time.sleep(1.0)
  63. # loop over frames from the video stream
  64. while True:
  65. # grab the frame from the threaded video file stream, resize
  66. # it, and convert it to grayscale
  67. # channels)
  68. frame = vs.read()
  69. frame = imutils.resize(frame, width=500)
  70. gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
  71. # detect faces in the grayscale frame
  72. rects = detector(gray, 0)
  73. # loop over the face detections
  74. for rect in rects:
  75. # determine the facial landmarks for the face region, then
  76. # convert the facial landmark (x, y)-coordinates to a NumPy
  77. # array
  78. shape = predictor(gray, rect)
  79. shape = face_utils.shape_to_np(shape)
  80. # extract the left and right eye coordinates, then use the
  81. # coordinates to compute the eye aspect ratio for both eyes
  82. leftEye = shape[lStart:lEnd]
  83. rightEye = shape[rStart:rEnd]
  84. leftEAR = eye_aspect_ratio(leftEye)
  85. rightEAR = eye_aspect_ratio(rightEye)
  86. # average the eye aspect ratio together for both eyes
  87. ear = (leftEAR + rightEAR) / 2.0
  88. # compute the convex hull for the left and right eye, then
  89. # visualize each of the eyes
  90. leftEyeHull = cv2.convexHull(leftEye)
  91. rightEyeHull = cv2.convexHull(rightEye)
  92. cv2.drawContours(frame, [leftEyeHull], -1, (0, 255, 0), 1)
  93. cv2.drawContours(frame, [rightEyeHull], -1, (0, 255, 0), 1)
  94. # check to see if the eye aspect ratio is below the blink
  95. # threshold, and if so, increment the blink frame counter
  96. if ear < EYE_AR_THRESH:
  97. COUNTER += 1
  98. # if the eyes were closed for a sufficient number of
  99. # then sound the alarm
  100. if COUNTER >= EYE_AR_CONSEC_FRAMES:
  101. # if the alarm is not on, turn it on
  102. if not ALARM_ON:
  103. ALARM_ON = True
  104. # check to see if an alarm file was supplied,
  105. # and if so, start a thread to have the alarm
  106. # sound played in the background
  107. if args["alarm"] != "":
  108. t = Thread(target=sound_alarm,
  109. args=(args["alarm"],))
  110. t.deamon = True
  111. t.start()
  112. # draw an alarm on the frame
  113. cv2.putText(frame, "DROWSINESS ALERT!", (10, 30),
  114. cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
  115. # otherwise, the eye aspect ratio is not below the blink
  116. # threshold, so reset the counter and alarm
  117. else:
  118. COUNTER = 0
  119. ALARM_ON = False
  120. # draw the computed eye aspect ratio on the frame to help
  121. # with debugging and setting the correct eye aspect ratio
  122. # thresholds and frame counters
  123. cv2.putText(frame, "EAR: {:.2f}".format(ear), (300, 30),
  124. cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
  125. # show the frame
  126. cv2.imshow("Frame", frame)
  127. key = cv2.waitKey(1) & 0xFF
  128. # if the `q` key was pressed, break from the loop
  129. if key == ord("q"):
  130. break
  131. # do a bit of cleanup
  132. cv2.destroyAllWindows()
  133. vs.stop()

注:如果运行时提示有些module找不到,请使用pip install安装即可

看效果:

 

代码解析:

首先,看68个人脸特征点的分布

根据上图的标识,我们要获取到左右眼的特征点坐标 :

  1. (lStart, lEnd) = face_utils.FACIAL_LANDMARKS_IDXS["left_eye"]
  2. (rStart, rEnd) = face_utils.FACIAL_LANDMARKS_IDXS["right_eye"]

其中。(lStart,lEnd) =37,42+1。(rStart,rEnd)= 43,48+1。通过列表切片操作即可取出左右眼的相关坐标。

接下来,根据下图分析,人眼的眨眼动作有如下特点:眼睛的睁开的横宽比随时间的变化如图表所示,睁开时会保持固定且在0.25左右,然后迅速下降到0.1以下,再然后迅速上升到0.25左右,完成一次眨眼动作。

 根据之前获取的眼睛坐标,计算眼睛睁开的横宽比,方法如下,求直角坐标系中的两点距离就不用讲了。

  1. def eye_aspect_ratio(eye):
  2. # compute the euclidean distances between the two sets of
  3. # vertical eye landmarks (x, y)-coordinates
  4. A = dist.euclidean(eye[1], eye[5])
  5. B = dist.euclidean(eye[2], eye[4])
  6. # compute the euclidean distance between the horizontal
  7. # eye landmark (x, y)-coordinates
  8. C = dist.euclidean(eye[0], eye[3])
  9. # compute the eye aspect ratio
  10. ear = (A + B) / (2.0 * C)
  11. # return the eye aspect ratio
  12. return ear

讲2个眼睛的横宽比进行平均。

  1. # loop over the face detections
  2. for rect in rects:
  3. # determine the facial landmarks for the face region, then
  4. # convert the facial landmark (x, y)-coordinates to a NumPy
  5. # array
  6. shape = predictor(gray, rect)
  7. shape = face_utils.shape_to_np(shape)
  8. # extract the left and right eye coordinates, then use the
  9. # coordinates to compute the eye aspect ratio for both eyes
  10. leftEye = shape[lStart:lEnd]
  11. rightEye = shape[rStart:rEnd]
  12. leftEAR = eye_aspect_ratio(leftEye)
  13. rightEAR = eye_aspect_ratio(rightEye)
  14. # average the eye aspect ratio together for both eyes
  15. ear = (leftEAR + rightEAR) / 2.0

接下来,根据我们设定的阈值进行逻辑判断:如果ear连续小于阈值的帧数大于48则报警,只要出现一次ear小于阈值,则计数清零,起到滤波作用。逻辑比较清晰简单。

  1. # check to see if the eye aspect ratio is below the blink
  2. # threshold, and if so, increment the blink frame counter
  3. if ear < EYE_AR_THRESH:
  4. COUNTER += 1
  5. # if the eyes were closed for a sufficient number of
  6. # then sound the alarm
  7. if COUNTER >= EYE_AR_CONSEC_FRAMES:
  8. # if the alarm is not on, turn it on
  9. if not ALARM_ON:
  10. ALARM_ON = True
  11. # check to see if an alarm file was supplied,
  12. # and if so, start a thread to have the alarm
  13. # sound played in the background
  14. if args["alarm"] != "":
  15. t = Thread(target=sound_alarm,
  16. args=(args["alarm"],))
  17. t.deamon = True
  18. t.start()
  19. # draw an alarm on the frame
  20. cv2.putText(frame, "DROWSINESS ALERT!", (10, 30),
  21. cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
  22. # otherwise, the eye aspect ratio is not below the blink
  23. # threshold, so reset the counter and alarm
  24. else:
  25. COUNTER = 0
  26. ALARM_ON = False
  27. # draw the computed eye aspect ratio on the frame to help
  28. # with debugging and setting the correct eye aspect ratio
  29. # thresholds and frame counters
  30. cv2.putText(frame, "EAR: {:.2f}".format(ear), (300, 30),
  31. cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)

 

 

 

参考链接:https://www.pyimagesearch.com/2017/05/08/drowsiness-detection-opencv/

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/237788
推荐阅读
相关标签
  

闽ICP备14008679号