危险驾驶之疲劳检测

参考博客:Dlib模型之驾驶员疲劳检测二(打哈欠)

文章目录

  • 疲劳检测
    • 原理
    • 检测工具
    • 代码思路
    • 代码修改
    • 代码
    • 检测结果

疲劳检测

原理

因为人在疲倦时大概会产生两种状态: 眨眼:正常人的眼睛每分钟大约要眨动10-15次,每次眨眼大概0.2-0.4秒,如果疲倦时眨眼次数会增多,速度也会变慢。打哈欠:此时嘴会长大而且会保持一定的状态。因此检测人是否疲劳可以从眼睛的开合度,眨眼频率,以及嘴巴张合程度来判断一个人是否疲劳。

检测工具

  • dlib :一个很经典的用于图像处理的开源库,shape_predictor_68_face_landmarks.dat是一个用于人脸68个关键点检测的dat模型库,使用这个模型库可以很方便地进行人脸检测,并进行简单的应用。
  • 各个版本dlib库网盘链接,选择自己需要的版本
    https://pan.baidu.com/s/1V_J49iuSQpMWtxTC02LTeg
    提取码:jeu2

代码思路

第一步:使用dlib.get_frontal_face_detector() 获得脸部位置检测器
第二步:使用dlib.shape_predictor获得脸部特征位置检测器
第三步:分别获取左右眼面部标志的索引
第四步:打开cv2 本地摄像头
第五步:从视频流进行循环,读取图片,并对图片做维度扩大,并进灰度化
第六步:使用detector(gray, 0) 进行脸部位置检测
第七步:循环脸部位置信息,使用predictor(gray, rect)获得脸部特征位置的信息
第八步:将脸部特征信息转换为数组array的格式
第九步:提取左眼和右眼坐标
第十步:构造函数计算左右眼的EAR值,使用平均值作为最终的EAR
第十一步:使用cv2.convexHull获得凸包位置,使用drawContours画出轮廓位置进行画图操作
第十二步:进行画图操作,用矩形框标注人脸
第十三步:分别计算左眼和右眼的评分求平均作为最终的评分,如果小于阈值,则加1,如果连续3次都小于阈值,则表示进行了一次眨眼活动
第十四步:进行画图操作,68个特征点标识
第十五步:进行画图操作,同时使用cv2.putText将眨眼次数进行显示
第十六步:统计总眨眼次数大于50次屏幕显示睡着。

原文链接:https://blog.csdn.net/cungudafa/article/details/103477960

代码修改

常用来测量欧氏距离的函数方法有两个:np.linalg.norm()和dist.euclidean(),所以可以大概测量一下这两个运算速度的快慢:

from scipy.spatial import distance as dist
import numpy as np
import time
points = [[1, 3], [8, 7]]
points = [np.array(val) for val in points]
tic = time.clock()
for i in range(50000):
    temp1 = (np.linalg.norm(points[0] - points[1]))
toc = time.clock()
print("np.linalg.norm: %.3fs" % (toc - tic))
tic = time.clock()
for i in range(50000):
    temp2 = (dist.euclidean(points[0], points[1]))
toc = time.clock()
print("dist.euclidean: %.3fs " % (toc - tic))
np.linalg.norm: 0.284s
dist.euclidean: 0.596s 

所以看出np.linalg.norm()函数的运算速度要比dist.euclidean()快一点。所以用前者来测量眼睛和嘴巴的欧氏距离。

代码

import numpy as np
from imutils import face_utils #imutils是在OpenCV基础之上的一个封装
import imutils
import dlib
import cv2
#定义眼睛阈值和嘴阈值算式函数
def eye_detect_1(eye):
    #np.linalg.norm计算时间要比dist.euclidean要快  因此使用Numpy的算法
    A = np.linalg.norm(eye[1] - eye[5])
    B = np.linalg.norm(eye[2] - eye[4])
    C = np.linalg.norm(eye[0] - eye[3])
    ear = (A + B) /(2.0 * C)
    return ear
def mouth_detect_1(mouth):
    A = np.linalg.norm(mouth[2] - mouth[10])  # 51, 59
    B = np.linalg.norm(mouth[4] - mouth[8])  # 53, 57
    C = np.linalg.norm(mouth[0] - mouth[6])  # 49, 55
    mar = (A + B) / (2.0 * C)
    return mar
#常数:眼睛长宽比/闪烁阈值
eye_Aspect_rato = 0.2
eye_Flicker_threshold = 3
#常数:嘴长宽比/闪烁阈值
mouth_Aspect_rato = 0.5
mouth_Flicker_threshold = 3
#计数常数
eCOUNTER = 0
eTOTAL = 0
mCOUNTER = 0
mTOTAL = 0
#脸部位置检测器
#返回值就是一个矩形
detect = dlib.get_frontal_face_detector()
#脸部特征位置检测器
predictor = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat')
#获得左右眼和嘴的标志索引
(lstart,lend) = face_utils.FACIAL_LANDMARKS_IDXS['left_eye']
(rstart,rend) = face_utils.FACIAL_LANDMARKS_IDXS['right_eye']
(mstart,mend) = face_utils.FACIAL_LANDMARKS_IDXS['mouth']

cap = cv2.VideoCapture(0)
while True:
    ret,frame = cap.read()
    frame = imutils.resize(frame)#, width=720)
    gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
    #返回值为脸部区域的矩形框,数组形式
    rects = detect(gray,0)
    #print(rects)
    # #循环脸部信息
    for rect in rects:
        #检测器检测特征点
        shape = predictor(gray,rect)
        #将特征点转化为数组组,返回值是68个特征点坐标
        shape = face_utils.shape_to_np(shape)
        #print(shape)
        #获取左右眼睛和嘴的坐标
        lefteye = shape[lstart:lend]
        righteye = shape[rstart:rend]
        mouth = shape[mstart:mend]
        #计算左右眼的ear值,嘴的mar值。
        leftear = eye_detect_1(lefteye)
        rightear = eye_detect_1(righteye)
        ear = (leftear + rightear) / 2.0
        mar = mouth_detect_1(mouth)
        #cv2.convexHull为获取图像凸包位置的函数
        lefteyehull = cv2.convexHull(lefteye)
        righteyehull = cv2.convexHull(righteye)
        mouthhull = cv2.convexHull(mouth)
        #print(lefteyehull)
        #cv2.drawContours是轮廓绘制函数
        #第一个参数是指明在哪幅图像上绘制轮廓;image为三通道才能显示轮廓
        #第二个参数是轮廓本身,在Python中是一个list;
        #第三个参数指定绘制轮廓list中的哪条轮廓,如果是-1,则绘制其中的所有轮廓。
        cv2.drawContours(frame,[lefteyehull],-1,(0,255,0),1)
        cv2.drawContours(frame,[righteyehull],-1,(0,255,0),1)
        cv2.drawContours(frame,[mouthhull],-1,(0,255,0),1)
        # 进行画图操作,用矩形框标注人脸
        # left = rect.left()
        # top = rect.top()
        # right = rect.right()
        # bottom = rect.bottom()
        # cv2.rectangle(frame, (left, top), (right, bottom), (0, 255, 0), 3)

        #循环,满足条件的眨眼次数+1
        if ear < eye_Aspect_rato: #阈值0.2
            eCOUNTER += 1
        #连续3帧都满足条件的计为一次闭眼
        else:
            if eCOUNTER >= eye_Flicker_threshold: #阈值3
                eTOTAL += 1
            eCOUNTER = 0

        cv2.putText(frame,'Blinks:{}'.format(eTOTAL),(10,30),cv2.FONT_HERSHEY_SIMPLEX,0.7,(0,0,255),2)
        cv2.putText(frame,'eCounter:{}'.format(eCOUNTER), (150, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
        cv2.putText(frame,'Ear:{:.2f}'.format(ear), (300, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)

        if mar > mouth_Aspect_rato:  #阈值0.5
            mCOUNTER += 1
        else:
            if mCOUNTER >= mouth_Flicker_threshold: #阈值3
                mTOTAL += 1
            mCOUNTER = 0

        cv2.putText(frame, 'Yawning:{}'.format(mTOTAL), (10,60), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
        cv2.putText(frame, 'mCounter:{}'.format(mCOUNTER), (300,60), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
        cv2.putText(frame, 'Mar:{:.2f}'.format(mar), (450, 60), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)

    #进行画图操作,68个特征点标识
        #for (x,y) in shape: #shape为68特征点的坐标
            #cv2.circle(frame,(x,y),1,(0,0,255),-1)
    #假设1秒为60帧,则1帧为1/60秒,一个TOTAL为3帧为1/20秒
    if eTOTAL >= 12 or mTOTAL >= 6:
        cv2.putText(frame,"SLEEP!SLEEP!!",(360,360),cv2.FONT_HERSHEY_SIMPLEX,1,(84,255,200),2)

    cv2.imshow('pilao',frame)

    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
   #也许是因为我用的opencv-python是4.1.0 所以加上最后这两行总是报错,我删掉后就可以运行了
   #cap.release()
   #cv2.destroyAllWindows()

检测结果

危险驾驶之疲劳检测_第1张图片
危险驾驶之疲劳检测_第2张图片

你可能感兴趣的:(python,opencv,计算机视觉)