软件杯 题目: 基于深度学习的疲劳驾驶检测 深度学习

news2024/11/14 16:33:16

文章目录

  • 0 前言
  • 1 课题背景
  • 2 实现目标
  • 3 当前市面上疲劳驾驶检测的方法
  • 4 相关数据集
  • 5 基于头部姿态的驾驶疲劳检测
    • 5.1 如何确定疲劳状态
    • 5.2 算法步骤
    • 5.3 打瞌睡判断
  • 6 基于CNN与SVM的疲劳检测方法
    • 6.1 网络结构
    • 6.2 疲劳图像分类训练
    • 6.3 训练结果
  • 7 最后

0 前言

🔥 优质竞赛项目系列,今天要分享的是

基于深度学习的驾驶疲劳检测

该项目较为新颖,适合作为竞赛课题方向,学长非常推荐!

🧿 更多资料, 项目分享:

https://gitee.com/dancheng-senior/postgraduate

在这里插入图片描述

1 课题背景

关于对疲劳驾驶的研究不在少数, 不少学者从人物面部入手展开。 人类的面部包含着许多不同的特征信息, 例如其中一些比较明显的特征如打哈欠、 闭眼、
揉眼等表情特征可用来作为判断驾驶员是否处于疲劳状态的依据。 随着计算机技术的不断发展, 尤其是在人工智能相关技术勃发的今天,
借助计算机可以快速有效的识别出图片中人脸特征, 对处于当前时刻驾驶员的精神状态做出判断, 并将疲劳预警信息传达给司机, 以保证交通的安全运行,
减少伤亡事故的发生。

2 实现目标

经查阅相关文献,疲劳在人体面部表情中表现出大致三个类型:打哈欠(嘴巴张大且相对较长时间保持这一状态)、眨眼(或眼睛微闭,此时眨眼次数增多,且眨眼速度变慢)、点头(瞌睡点头)。本实验从人脸朝向、位置、瞳孔朝向、眼睛开合度、眨眼频率、瞳孔收缩率等数据入手,并通过这些数据,实时地计算出驾驶员的注意力集中程度,分析驾驶员是否疲劳驾驶和及时作出安全提示。

3 当前市面上疲劳驾驶检测的方法

学长通过对疲劳驾驶在不同方法下研究进展的分析, 可以更清晰的认识的到当下对该问题较为有效的判定方法。 根据研究对象的不同对检测方法进行分类,
具体分类方法如图

在这里插入图片描述

基于驾驶员面部特征的检测方法是根据人在疲劳时面部变化来分析此时的精神状态。 人在瞌睡、 疲劳时面部表情与清醒时有着明显的区别。
通过装置在车辆中的摄像头对驾驶员人脸图片的采集, 利用计算机图像处理和模式识别, 可以有效检测驾驶员的疲
劳特征信息, 比较直观的特征有: 打哈欠, 眨眼, 低头等。

4 相关数据集

学长收集的疲劳检测数据集

驾驶疲劳人脸数据库图片来源分为 3 部分, 每部分均包含疲劳、 轻度疲劳和非疲劳

在这里插入图片描述

5 基于头部姿态的驾驶疲劳检测

5.1 如何确定疲劳状态

  • 思路一:可利用姿态估计结果(如Pitch的读数)来判断是否点头及点头幅度

  • 思路二:或用鼻尖处30号点的前后移动值(或是方差,方差表示一个单位时间数据的偏离程度,程度越大,则表示发生点头动作的概率越大、点头幅度越大)

在这里插入图片描述

5.2 算法步骤

  • 第一步:2D人脸关键点检测;

  • 第二步:3D人脸模型匹配;

  • 第三步:求解3D点和对应2D点的转换关系;

  • 第四步:根据旋转矩阵求解欧拉角。

    import cv2
    import dlib
    import numpy as np
    from imutils import face_utils
    """
    思路:
        第一步:2D人脸关键点检测;第二步:3D人脸模型匹配;
        第三步:求解3D点和对应2D点的转换关系;第四步:根据旋转矩阵求解欧拉角。
    """
    
    # 加载人脸检测和姿势估计模型(dlib)
    
    face_landmark_path = 'D:/myworkspace/JupyterNotebook/fatigue_detecting/model/shape_predictor_68_face_landmarks.dat'
    
    """
    只要知道世界坐标系内点的位置、像素坐标位置和相机参数就可以搞定旋转和平移矩阵(OpenCV自带函数solvePnp())
    """
    
    # 世界坐标系(UVW):填写3D参考点,该模型参考http://aifi.isr.uc.pt/Downloads/OpenGL/glAnthropometric3DModel.cpp
    
    object_pts = np.float32([[6.825897, 6.760612, 4.402142],  #33左眉左上角
                             [1.330353, 7.122144, 6.903745],  #29左眉右角
                             [-1.330353, 7.122144, 6.903745], #34右眉左角
                             [-6.825897, 6.760612, 4.402142], #38右眉右上角
                             [5.311432, 5.485328, 3.987654],  #13左眼左上角
                             [1.789930, 5.393625, 4.413414],  #17左眼右上角
                             [-1.789930, 5.393625, 4.413414], #25右眼左上角
                             [-5.311432, 5.485328, 3.987654], #21右眼右上角
                             [2.005628, 1.409845, 6.165652],  #55鼻子左上角
                             [-2.005628, 1.409845, 6.165652], #49鼻子右上角
                             [2.774015, -2.080775, 5.048531], #43嘴左上角
                             [-2.774015, -2.080775, 5.048531],#39嘴右上角
                             [0.000000, -3.116408, 6.097667], #45嘴中央下角
                             [0.000000, -7.415691, 4.070434]])#6下巴角
    
    # 相机坐标系(XYZ):添加相机内参
    
    K = [6.5308391993466671e+002, 0.0, 3.1950000000000000e+002,
         0.0, 6.5308391993466671e+002, 2.3950000000000000e+002,
         0.0, 0.0, 1.0]# 等价于矩阵[fx, 0, cx; 0, fy, cy; 0, 0, 1]
    
    # 图像中心坐标系(uv):相机畸变参数[k1, k2, p1, p2, k3]
    
    D = [7.0834633684407095e-002, 6.9140193737175351e-002, 0.0, 0.0, -1.3073460323689292e+000]
    
    # 像素坐标系(xy):填写凸轮的本征和畸变系数
    
    cam_matrix = np.array(K).reshape(3, 3).astype(np.float32)
    dist_coeffs = np.array(D).reshape(5, 1).astype(np.float32)
    
    
    
    # 重新投影3D点的世界坐标轴以验证结果姿势
    
    reprojectsrc = np.float32([[10.0, 10.0, 10.0],
                               [10.0, 10.0, -10.0],
                               [10.0, -10.0, -10.0],
                               [10.0, -10.0, 10.0],
                               [-10.0, 10.0, 10.0],
                               [-10.0, 10.0, -10.0],
                               [-10.0, -10.0, -10.0],
                               [-10.0, -10.0, 10.0]])
    
    # 绘制正方体12轴
    
    line_pairs = [[0, 1], [1, 2], [2, 3], [3, 0],
                  [4, 5], [5, 6], [6, 7], [7, 4],
                  [0, 4], [1, 5], [2, 6], [3, 7]]
    
    def get_head_pose(shape):
    
        # 填写2D参考点,注释遵循https://ibug.doc.ic.ac.uk/resources/300-W/
    
        """
          17左眉左上角/21左眉右角/22右眉左上角/26右眉右上角/36左眼左上角/39左眼右上角/42右眼左上角/
          45右眼右上角/31鼻子左上角/35鼻子右上角/48左上角/54嘴右上角/57嘴中央下角/8下巴角
        """
    
        # 像素坐标集合
    
        image_pts = np.float32([shape[17], shape[21], shape[22], shape[26], shape[36],
                                shape[39], shape[42], shape[45], shape[31], shape[35],
                                shape[48], shape[54], shape[57], shape[8]])
        """
        用solvepnp或sovlepnpRansac,输入3d点、2d点、相机内参、相机畸变,输出r、t之后
        用projectPoints,输入3d点、相机内参、相机畸变、r、t,输出重投影2d点
        计算原2d点和重投影2d点的距离作为重投影误差
        """
    
        # solvePnP计算姿势——求解旋转和平移矩阵:
    
        # rotation_vec表示旋转矩阵,translation_vec表示平移矩阵,cam_matrix与K矩阵对应,dist_coeffs与D矩阵对应。
    
        _, rotation_vec, translation_vec = cv2.solvePnP(object_pts, image_pts, cam_matrix, dist_coeffs)
    
        # projectPoints重新投影误差
    
    
        reprojectdst, _ = cv2.projectPoints(reprojectsrc, rotation_vec, translation_vec, cam_matrix,dist_coeffs)
    
        reprojectdst = tuple(map(tuple, reprojectdst.reshape(8, 2)))# 以8行2列显示
        
        # 计算欧拉角calc euler angle
        # 参考https://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html#decomposeprojectionmatrix
        rotation_mat, _ = cv2.Rodrigues(rotation_vec)#罗德里格斯公式(将旋转矩阵转换为旋转向量)
        pose_mat = cv2.hconcat((rotation_mat, translation_vec))# 水平拼接,vconcat垂直拼接
        # eulerAngles –可选的三元素矢量,包含三个以度为单位的欧拉旋转角度
        _, _, _, _, _, _, euler_angle = cv2.decomposeProjectionMatrix(pose_mat)# 将投影矩阵分解为旋转矩阵和相机矩阵
        
        return reprojectdst, euler_angle
    
    
    def main():
        # return
        cap = cv2.VideoCapture(0)
        if not cap.isOpened():
            print("Unable to connect to camera.")
            return
        # 检测人脸
        detector = dlib.get_frontal_face_detector()
        # 检测第一个人脸的关键点
        predictor = dlib.shape_predictor(face_landmark_path)
    
        while cap.isOpened():
            ret, frame = cap.read()
            if ret:
                face_rects = detector(frame, 0)
        
                if len(face_rects) > 0:
                    # 循环脸部位置信息,使用predictor(gray, rect)获得脸部特征位置的信息
                    shape = predictor(frame, face_rects[0])
                    # 将脸部特征信息转换为数组array的格式
                    shape = face_utils.shape_to_np(shape)
                    # 获取头部姿态
                    reprojectdst, euler_angle = get_head_pose(shape)
                    pitch = format(euler_angle[0, 0])
                    yaw = format(euler_angle[1, 0])
                    roll = format(euler_angle[2, 0])
                    print('pitch:{}, yaw:{}, roll:{}'.format(pitch, yaw, roll))
                    
                    # 标出68个特征点
                    for (x, y) in shape:
                        cv2.circle(frame, (x, y), 1, (0, 0, 255), -1)
                        
                    # 绘制正方体12轴
                    for start, end in line_pairs:
                        cv2.line(frame, reprojectdst[start], reprojectdst[end], (0, 0, 255))
                    # 显示角度结果
                    cv2.putText(frame, "X: " + "{:7.2f}".format(euler_angle[0, 0]), (20, 20), cv2.FONT_HERSHEY_SIMPLEX,0.75, (0, 0, 255), thickness=2)
                    cv2.putText(frame, "Y: " + "{:7.2f}".format(euler_angle[1, 0]), (20, 50), cv2.FONT_HERSHEY_SIMPLEX,0.75, (0, 0, 255), thickness=2)
                    cv2.putText(frame, "Z: " + "{:7.2f}".format(euler_angle[2, 0]), (20, 80), cv2.FONT_HERSHEY_SIMPLEX,0.75, (0, 0, 255), thickness=2)    
        
                # 按q退出提示
                cv2.putText(frame, "Press 'q': Quit", (20, 450),cv2.FONT_HERSHEY_SIMPLEX, 0.7, (84, 255, 159), 2)
                # 窗口显示 show with opencv
                cv2.imshow("Head_Posture", frame)
        
                if cv2.waitKey(1) & 0xFF == ord('q'):
                    break
        # 释放摄像头 release camera
        cap.release()
        # do a bit of cleanup
        cv2.destroyAllWindows()
    
    
    if __name__ == '__main__':
        main()
    
    
    

在这里插入图片描述

5.3 打瞌睡判断

头部姿态判断打瞌睡得到实时头部姿态的旋转角度过后,为头部旋转角度的3个参数Yaw,Pitch和Roll的示意图,驾驶员在打瞌睡时,显然头部会做类似于点头和倾斜的动作.而根据一般人的打瞌睡时表现出来的头部姿态,显然很少会在Yaw上有动作,而主要集中在Pitch和Roll的行为.设定参数阈值为0.3,在一个时间段内10
s内,当I PitchI≥20°或者|Rolll≥20°的时间比例超过0.3时,就认为驾驶员处于打瞌睡的状态,发出预警。

在这里插入图片描述



    from scipy.spatial import distance as dist
    from imutils.video import FileVideoStream
    from imutils.video import VideoStream
    from imutils import face_utils
    import numpy as np # 数据处理的库 numpy
    import argparse
    import imutils
    import time
    import dlib
    import cv2
    import math
    import time
    from threading import Thread,
     
    # 世界坐标系(UVW):填写3D参考点,该模型参考http://aifi.isr.uc.pt/Downloads/OpenGL/glAnthropometric3DModel.cpp
    object_pts = np.float32([[6.825897, 6.760612, 4.402142],  #33左眉左上角
                             [1.330353, 7.122144, 6.903745],  #29左眉右角
                             [-1.330353, 7.122144, 6.903745], #34右眉左角
                             [-6.825897, 6.760612, 4.402142], #38右眉右上角
                             [5.311432, 5.485328, 3.987654],  #13左眼左上角
                             [1.789930, 5.393625, 4.413414],  #17左眼右上角
                             [-1.789930, 5.393625, 4.413414], #25右眼左上角
                             [-5.311432, 5.485328, 3.987654], #21右眼右上角
                             [2.005628, 1.409845, 6.165652],  #55鼻子左上角
                             [-2.005628, 1.409845, 6.165652], #49鼻子右上角
                             [2.774015, -2.080775, 5.048531], #43嘴左上角
                             [-2.774015, -2.080775, 5.048531],#39嘴右上角
                             [0.000000, -3.116408, 6.097667], #45嘴中央下角
                             [0.000000, -7.415691, 4.070434]])#6下巴角
    
    # 相机坐标系(XYZ):添加相机内参
    K = [6.5308391993466671e+002, 0.0, 3.1950000000000000e+002,
         0.0, 6.5308391993466671e+002, 2.3950000000000000e+002,
         0.0, 0.0, 1.0]# 等价于矩阵[fx, 0, cx; 0, fy, cy; 0, 0, 1]
    # 图像中心坐标系(uv):相机畸变参数[k1, k2, p1, p2, k3]
    D = [7.0834633684407095e-002, 6.9140193737175351e-002, 0.0, 0.0, -1.3073460323689292e+000]
    
    # 像素坐标系(xy):填写凸轮的本征和畸变系数
    cam_matrix = np.array(K).reshape(3, 3).astype(np.float32)
    dist_coeffs = np.array(D).reshape(5, 1).astype(np.float32)


    # 重新投影3D点的世界坐标轴以验证结果姿势
    reprojectsrc = np.float32([[10.0, 10.0, 10.0],
                               [10.0, 10.0, -10.0],
                               [10.0, -10.0, -10.0],
                               [10.0, -10.0, 10.0],
                               [-10.0, 10.0, 10.0],
                               [-10.0, 10.0, -10.0],
                               [-10.0, -10.0, -10.0],
                               [-10.0, -10.0, 10.0]])
    # 绘制正方体12轴
    line_pairs = [[0, 1], [1, 2], [2, 3], [3, 0],
                  [4, 5], [5, 6], [6, 7], [7, 4],
                  [0, 4], [1, 5], [2, 6], [3, 7]]
    
    def get_head_pose(shape):# 头部姿态估计
        # (像素坐标集合)填写2D参考点,注释遵循https://ibug.doc.ic.ac.uk/resources/300-W/
        # 17左眉左上角/21左眉右角/22右眉左上角/26右眉右上角/36左眼左上角/39左眼右上角/42右眼左上角/
        # 45右眼右上角/31鼻子左上角/35鼻子右上角/48左上角/54嘴右上角/57嘴中央下角/8下巴角
        image_pts = np.float32([shape[17], shape[21], shape[22], shape[26], shape[36],
                                shape[39], shape[42], shape[45], shape[31], shape[35],
                                shape[48], shape[54], shape[57], shape[8]])
        # solvePnP计算姿势——求解旋转和平移矩阵:
        # rotation_vec表示旋转矩阵,translation_vec表示平移矩阵,cam_matrix与K矩阵对应,dist_coeffs与D矩阵对应。
        _, rotation_vec, translation_vec = cv2.solvePnP(object_pts, image_pts, cam_matrix, dist_coeffs)
        # projectPoints重新投影误差:原2d点和重投影2d点的距离(输入3d点、相机内参、相机畸变、r、t,输出重投影2d点)
        reprojectdst, _ = cv2.projectPoints(reprojectsrc, rotation_vec, translation_vec, cam_matrix,dist_coeffs)
        reprojectdst = tuple(map(tuple, reprojectdst.reshape(8, 2)))# 以8行2列显示
    
        # 计算欧拉角calc euler angle
        # 参考https://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html#decomposeprojectionmatrix
        rotation_mat, _ = cv2.Rodrigues(rotation_vec)#罗德里格斯公式(将旋转矩阵转换为旋转向量)
        pose_mat = cv2.hconcat((rotation_mat, translation_vec))# 水平拼接,vconcat垂直拼接
        # decomposeProjectionMatrix将投影矩阵分解为旋转矩阵和相机矩阵
        _, _, _, _, _, _, euler_angle = cv2.decomposeProjectionMatrix(pose_mat)
        
        pitch, yaw, roll = [math.radians(_) for _ in euler_angle]

        pitch = math.degrees(math.asin(math.sin(pitch)))
        roll = -math.degrees(math.asin(math.sin(roll)))
        yaw = math.degrees(math.asin(math.sin(yaw)))
        print('pitch:{}, yaw:{}, roll:{}'.format(pitch, yaw, roll))
    
        return reprojectdst, euler_angle# 投影误差,欧拉角
    
    def eye_aspect_ratio(eye):
        # 垂直眼标志(X,Y)坐标
        A = dist.euclidean(eye[1], eye[5])# 计算两个集合之间的欧式距离
        B = dist.euclidean(eye[2], eye[4])
        # 计算水平之间的欧几里得距离
        # 水平眼标志(X,Y)坐标
        C = dist.euclidean(eye[0], eye[3])
        # 眼睛长宽比的计算
        ear = (A + B) / (2.0 * C)
        # 返回眼睛的长宽比
        return ear
     
    def mouth_aspect_ratio(mouth):# 嘴部
        A = np.linalg.norm(mouth[2] - mouth[9])  # 51, 59
        B = np.linalg.norm(mouth[4] - mouth[7])  # 53, 57
        C = np.linalg.norm(mouth[0] - mouth[6])  # 49, 55
        mar = (A + B) / (2.0 * C)
        return mar
    
    # 定义常数
    # 眼睛长宽比
    # 闪烁阈值
    EYE_AR_THRESH = 0.2
    EYE_AR_CONSEC_FRAMES = 3
    # 打哈欠长宽比
    # 闪烁阈值
    MAR_THRESH = 0.5
    MOUTH_AR_CONSEC_FRAMES = 3
    # 瞌睡点头
    HAR_THRESH = 0.3
    NOD_AR_CONSEC_FRAMES = 3
    # 初始化帧计数器和眨眼总数
    COUNTER = 0
    TOTAL = 0
    # 初始化帧计数器和打哈欠总数
    mCOUNTER = 0
    mTOTAL = 0
    # 初始化帧计数器和点头总数
    hCOUNTER = 0
    hTOTAL = 0
    
    # 初始化DLIB的人脸检测器(HOG),然后创建面部标志物预测
    print("[INFO] loading facial landmark predictor...")
    # 第一步:使用dlib.get_frontal_face_detector() 获得脸部位置检测器
    detector = dlib.get_frontal_face_detector()
    # 第二步:使用dlib.shape_predictor获得脸部特征位置检测器
    predictor = dlib.shape_predictor('D:/myworkspace/JupyterNotebook/fatigue_detecting/model/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"]
    
    # 第四步:打开cv2 本地摄像头
    cap = cv2.VideoCapture(0)
     
    # 从视频流循环帧
    while True:
        # 第五步:进行循环,读取图片,并对图片做维度扩大,并进灰度化
        ret, frame = cap.read()
        frame = imutils.resize(frame, width=720)
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        # 第六步:使用detector(gray, 0) 进行脸部位置检测
        rects = detector(gray, 0)
        
        # 第七步:循环脸部位置信息,使用predictor(gray, rect)获得脸部特征位置的信息
        for rect in rects:
            shape = predictor(gray, rect)
            
            # 第八步:将脸部特征信息转换为数组array的格式
            shape = face_utils.shape_to_np(shape)
            
            # 第九步:提取左眼和右眼坐标
            leftEye = shape[lStart:lEnd]
            rightEye = shape[rStart:rEnd]
            # 嘴巴坐标
            mouth = shape[mStart:mEnd]        
            
            # 第十步:构造函数计算左右眼的EAR值,使用平均值作为最终的EAR
            leftEAR = eye_aspect_ratio(leftEye)
            rightEAR = eye_aspect_ratio(rightEye)
            ear = (leftEAR + rightEAR) / 2.0
            # 打哈欠
            mar = mouth_aspect_ratio(mouth)
     
            # 第十一步:使用cv2.convexHull获得凸包位置,使用drawContours画出轮廓位置进行画图操作
            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)
            mouthHull = cv2.convexHull(mouth)
            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), 1)    
     
            '''
                分别计算左眼和右眼的评分求平均作为最终的评分,如果小于阈值,则加1,如果连续3次都小于阈值,则表示进行了一次眨眼活动
            '''
            # 第十三步:循环,满足条件的,眨眼次数+1
            if ear < EYE_AR_THRESH:# 眼睛长宽比:0.2
                COUNTER += 1
               
            else:
                # 如果连续3次都小于阈值,则表示进行了一次眨眼活动
                if COUNTER >= EYE_AR_CONSEC_FRAMES:# 阈值:3
                    TOTAL += 1
                # 重置眼帧计数器
                COUNTER = 0
                
            # 第十四步:进行画图操作,同时使用cv2.putText将眨眼次数进行显示
            cv2.putText(frame, "Faces: {}".format(len(rects)), (10, 30),cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)     
            cv2.putText(frame, "COUNTER: {}".format(COUNTER), (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)
            cv2.putText(frame, "Blinks: {}".format(TOTAL), (450, 30),cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255,255,0), 2)
            
            '''
                计算张嘴评分,如果小于阈值,则加1,如果连续3次都小于阈值,则表示打了一次哈欠,同一次哈欠大约在3帧
            '''
            # 同理,判断是否打哈欠    
            if mar > MAR_THRESH:# 张嘴阈值0.5
                mCOUNTER += 1
                cv2.putText(frame, "Yawning!", (10, 60),cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
            else:
                # 如果连续3次都小于阈值,则表示打了一次哈欠
                if mCOUNTER >= MOUTH_AR_CONSEC_FRAMES:# 阈值:3
                    mTOTAL += 1
                # 重置嘴帧计数器
                mCOUNTER = 0
            cv2.putText(frame, "COUNTER: {}".format(mCOUNTER), (150, 60),cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2) 
            cv2.putText(frame, "MAR: {:.2f}".format(mar), (300, 60),cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
            cv2.putText(frame, "Yawning: {}".format(mTOTAL), (450, 60),cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255,255,0), 2)
            """
            瞌睡点头
            """
            # 第十五步:获取头部姿态
            reprojectdst, euler_angle = get_head_pose(shape)
            
            har = euler_angle[0, 0]# 取pitch旋转角度
            if har > HAR_THRESH:# 点头阈值0.3
                hCOUNTER += 1
            else:
                # 如果连续3次都小于阈值,则表示瞌睡点头一次
                if hCOUNTER >= NOD_AR_CONSEC_FRAMES:# 阈值:3
                    hTOTAL += 1
                # 重置点头帧计数器
                hCOUNTER = 0
            
            # 绘制正方体12轴
            for start, end in line_pairs:
                cv2.line(frame, reprojectdst[start], reprojectdst[end], (0, 0, 255))
            # 显示角度结果
            cv2.putText(frame, "X: " + "{:7.2f}".format(euler_angle[0, 0]), (10, 90), cv2.FONT_HERSHEY_SIMPLEX,0.75, (0, 255, 0), thickness=2)# GREEN
            cv2.putText(frame, "Y: " + "{:7.2f}".format(euler_angle[1, 0]), (150, 90), cv2.FONT_HERSHEY_SIMPLEX,0.75, (255, 0, 0), thickness=2)# BLUE
            cv2.putText(frame, "Z: " + "{:7.2f}".format(euler_angle[2, 0]), (300, 90), cv2.FONT_HERSHEY_SIMPLEX,0.75, (0, 0, 255), thickness=2)# RED    
            cv2.putText(frame, "Nod: {}".format(hTOTAL), (450, 90),cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255,255,0), 2)

            # 第十六步:进行画图操作,68个特征点标识
            for (x, y) in shape:
                cv2.circle(frame, (x, y), 1, (0, 0, 255), -1)
    
        print('嘴巴实时长宽比:{:.2f} '.format(mar)+"\t是否张嘴:"+str([False,True][mar > MAR_THRESH]))
        print('眼睛实时长宽比:{:.2f} '.format(ear)+"\t是否眨眼:"+str([False,True][COUNTER>=1]))
        
        # 确定疲劳提示:眨眼50次,打哈欠15次,瞌睡点头15次
        if TOTAL >= 50 or mTOTAL>=15 or hTOTAL>=15:
            cv2.putText(frame, "SLEEP!!!", (100, 200),cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 255), 3)
            
        # 按q退出
        cv2.putText(frame, "Press 'q': Quit", (20, 500),cv2.FONT_HERSHEY_SIMPLEX, 0.7, (84, 255, 159), 2)
        # 窗口显示 show with opencv
        cv2.imshow("Frame", frame)
        
        # if the `q` key was pressed, break from the loop
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
            
    # 释放摄像头 release camera
    cap.release()
    # do a bit of cleanup
    cv2.destroyAllWindows()



在这里插入图片描述
在这里插入图片描述

6 基于CNN与SVM的疲劳检测方法

6.1 网络结构

学长将卷积神经网络作为特征提取器, 支持向量机作为分类识别器并通过串联将两者结合 , 构造理想的深度识别模型, 提高对驾驶员疲劳的识别准确率。
本次课题主要以实现提高识别精度为目的, 设计使用的特征提取网络结构中卷积层、 池化层以及全连接层个数均为两层;
在网络的结尾处添加一层支持向量机作为识别分类器;

在这里插入图片描述
根据对卷积神经网络的描述, 这里设计使用的网络结构为: 输入层、 二层卷积层、 二层池化层、 二层全连接层以及 SVM
分类器组成的卷积神经网络对采集数据进行实验。

可将网络视为三个部分, 数据输入部分即网络输入层, 为特征提取部分由卷积层和池化层构成, SVM 为分类识别部分; 三部分网络串联出整体识别框架,
且相互间约束不大, 为后续优化工作提供了条件。

6.2 疲劳图像分类训练

网络的训练由于数据量较大进行实验时将数据分为多个批次, 每个批次中含有 20张图像, 经过前向、 反向传播后更新网络参数, 训练出误差合适的网络。 测试时,
图像由网络进行识别, 根据得到的识别正确率来验证网络的可行性。

在这里插入图片描述

疲劳驾驶检测需对网络进行训练, 在保证网络训练准确率达到一定精度后即可对图像进行判别; 疲劳驾驶网络训练算法过程如下:

  • Step1: 网络初始化: 初始化网络学习率η, 在数值范围[0, 1]中随机初始化网络参数权值及偏置值; 设置网络结构: 卷积核大小为 5×5, 每批次样本数量 20;
  • Step2: 随机选择数据库内面部表情图像并依次输入网络, 网络按照送入每一批次的图像进行训练;
  • Step3: 网络将训练得到的输出值同图像期望值进行比较, 计算出输出误差;
  • Step4: 根据反向传播原理将误差反向传播计算, 并调整网络参数权值和偏置值;
  • Step5: 判断迭代次数, 达到期望的迭代步数后转到 Step6, 否则转到 Step3;
  • Step6: 将 CNN 提取到的图像特征传入 SVM 中进行训练;
  • Step7: 结束。

6.3 训练结果

学长将对建立起的数据集进行实验, 实验中分别在每一批次下对识别正确和错误个数进行统计, 然后同批次中图片数量相比, 得出最终的准确率和损失率(错误率) 。

在这里插入图片描述
在这里插入图片描述

模型测试结果

在这里插入图片描述
在这里插入图片描述

7 最后

🧿 更多资料, 项目分享:

https://gitee.com/dancheng-senior/postgraduate

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1696636.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

RT-DRET在实时目标检测上超越YOLO8

导读 目标检测作为计算机视觉的核心任务之一&#xff0c;其研究已经从基于CNN的架构发展到基于Transformer的架构&#xff0c;如DETR&#xff0c;后者通过简化流程实现端到端检测&#xff0c;消除了手工设计的组件。尽管如此&#xff0c;DETR的高计算成本限制了其在实时目标检测…

一文了解 - GPS/DR组合定位技术

GPS Global Position System 全球定位系统这个大家都很熟悉&#xff0c; 不做太多介绍。 DR Dead Reckoning 车辆推算定位法&#xff0c; 一种常用的辅助的车辆定位技术。 DR系统的优点&#xff1a; 不需要发射和接收信号&#xff1b; 不受电磁波干扰。 DR系统的缺点&#x…

Leetcode 剑指 Offer II 079.子集

题目难度: 中等 原题链接 今天继续更新 Leetcode 的剑指 Offer&#xff08;专项突击版&#xff09;系列, 大家在公众号 算法精选 里回复 剑指offer2 就能看到该系列当前连载的所有文章了, 记得关注哦~ 题目描述 给定一个整数数组 nums &#xff0c;数组中的元素 互不相同 。返…

Java——接口后续

1.Comparable 接口 在Java中&#xff0c;我们对一个元素是数字的数组可以使用sort方法进行排序&#xff0c;如果要对一个元素是对象的数组按某种规则排序&#xff0c;就会用到Comparable接口 当实现Comparable接口后&#xff0c;sort会自动调用Comparable接口里的compareTo 方法…

【Shell】sed编辑器实例

sed是用来解析和转换文本的工具&#xff0c;它使用简单&#xff0c;是简洁的程序设计语言。 sed编辑器 &#xff08;一&#xff09; sed编辑器基础1. 简介2. sed的模式空间 &#xff08;二&#xff09;基本的sed编辑命令&#xff08;三&#xff09;sed命令实例1. 向文件中添加或…

leetcode-189. 旋转数组 原地递归算法(非官方的三种方法)

Problem: 189. 轮转数组 思路 首先&#xff0c;很明显&#xff0c;题目要求的操作等同于将数组的后k%n个元素移动到前面来。 然后我们思考原地操作的方法&#xff1a; &#xff08;为了方便讲解&#xff0c;我们先假设k<n/2&#xff09; 1.我们将数组划分为 [A&#xff0c;B…

MCU最小系统电路设计

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” 何为最小系统 最小系统板就是一个最精简的电路&#xff0c;精简到只能维持MCU最基本的正常工作 最小系统包括哪些模块 电源模块 MircoUSB接口 在这个图片当中&#xff0c;我…

ubuntu22.04 vsc命令行复制粘贴时下划线消失

vscode 在ubuntu的terminal中下划线不显示解决方案 CtrlShiftP&#xff0c;打开搜索&#xff0c;Perferences:Open User Settings 设置Editor:Font Family 为 ‘Ubuntu Mono’, monospace 保存&#xff0c;效果如图&#xff1a;

SpringBoot使用rsa-encrypt-body-spring-boot实现接口加解密

废话不多说&#xff0c;直接上代码 引入依赖 <dependency><groupId>cn.shuibo</groupId><artifactId>rsa-encrypt-body-spring-boot</artifactId><version>1.0.1.RELEASE</version> </dependency>配置文件 rsa:encrypt:# 是…

JAVA -- > 初识JAVA

初始JAVA 第一个JAVA程序详解 public class Main {public static void main(String[] args) {System.out.println("Hello world");} }1.public class Main: 类型,作为被public修饰的类,必须与文件名一致 2.public static 是JAVA中main函数准写法,记住该格式即可 …

【how2j java应用】

[Log4j] 演示如何使用log4j进行日志输出 1.导入jar包 2.使用Log4j 3.代码说明 LOG4J 配置讲解 在src目录下添加log4j.properties文件 说明 log4j.xml 除了使用log4j.properties&#xff0c;也可以使用xml格式进行配置。 [junit] 通过main方法来进行测试&#xff1a;如果…

5.20Git

版本控制工具Git&#xff0c;其他的工具还有SVN 共享代码&#xff0c;追溯记录&#xff0c;存储.c文件 Git实现的功能&#xff1a;回溯&#xff08;以前某个时间节点的数据情况&#xff09;共享&#xff08;大家共享修改&#xff09; Git&#xff1a;80% SVN&#xff…

MySQL——MySQL目录结构

MySQL安装完成后&#xff0c;会在磁盘上生成一个目录&#xff0c;该目录被称为MySQL的安装目录。在MySQL的安装目录中包含了启动文件、配置文件、数据库文件和命令文件等。 下面对 MySQL 的安装目录进行详细讲解 (1)bin 目录 : 用于放置一些可执行文件,如 mysql.exe、mysqld. …

数组-下一个排列

一、题目描述 二、解题思路 1.反向遍历当前排列&#xff0c;比如 排列A[a,b,c,d,e,f...] &#xff0c;当遍历到e时&#xff0c;说明以 a,b,c,d,e为前缀的排列中不存在A排列的下一个排列。 2.把e&#xff08;位置设为idx&#xff09;和后面的元素作比较&#xff1a; 2.1 如果有…

网络模型—BIO、NIO、IO多路复用、信号驱动IO、异步IO

一、用户空间和内核空间 以Linux系统为例&#xff0c;ubuntu和CentOS是Linux的两种比较常见的发行版&#xff0c;任何Linux发行版&#xff0c;其系统内核都是Linux。我们在发行版上操作应用&#xff0c;如Redis、Mysql等其实是无法直接执行访问计算机硬件(如cpu&#xff0c;内存…

LabVIEW步开发进电机的串口控制程序

LabVIEW步开发进电机的串口控制程序 为了提高电机控制的精确度和自动化程度&#xff0c;开发一种基于LabVIEW的实时、自动化电机串口控制程序。利用LabVIEW软件的图形化编程特性&#xff0c;通过串口实时控制电机的运行参数&#xff0c;实现电机性能的精准控制与评估。 系统组…

Spring MVC+mybatis 项目入门:旅游网(三)用户注册——控制反转以及Hibernate Validator数据验证

个人博客&#xff1a;Spring MVCmybatis 项目入门:旅游网&#xff08;三&#xff09;用户注册 | iwtss blog 先看这个&#xff01; 这是18年的文章&#xff0c;回收站里恢复的&#xff0c;现阶段看基本是没有参考意义的&#xff0c;技术老旧脱离时代&#xff08;2024年辣铁铁&…

《Ai学习笔记》自然语言处理 (Natural Language Processing):机器阅读理解-基础概念解析01

自然语言处理 (Natural Language Processing)&#xff1a; NLP四大基本任务 序列标注&#xff1a; 分词、词性标注 分类任务&#xff1a; 文本分类、情感分析 句子关系&#xff1a;问答系统、对话系统 生成任务&#xff1a;机器翻译、文章摘要 机器阅读理解的定义 Machi…

创建带有公共头部的Electron窗口

创建带有公共头部的Electron窗口 创建一个公共头部的html文件 1.我们在项目根目录创建一个名为app-header的文件夹 2.在app-header创建一个文件名为header.html的文件 结构如下&#xff1a; 基本结构和脚本如下 <body> <div class"header"><div c…

Node Video Pro v6.40.0,媲美电脑的专业手机剪辑APP

软件介绍 Node Video Pro&#xff0c;作为一款高端的视频编辑与共享平台&#xff0c;为用户提供了一套完备的视频制作工具集。该应用集成了视频剪辑、视觉特效增强、以及音乐背景选择等功能&#xff0c;允许用户在移动设备上实施专业级别的视频处理。它所具备的高级功能配合其…