• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

Dlib模型的疲劳检测3

武飞扬头像
陈子迩
帮助1

目录

前言

一、背景

(1)环境搭建

(2)下载开源数据集

(3)Head Pose Estimation 如何理解?

fatigue_detecting: 基于图像驾驶员疲劳检测技术研究


提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

经过查阅文献,基于人脸表面特征的疲劳检测,主要分为三个部分,打哈欠、眨眼、点头。本实验从人脸朝向、位置、瞳孔朝向、眼睛开合度、眨眼频率、瞳孔收缩率等数据入手,并通过这些数据,实时地计算出驾驶员的注意力集中程度,分析驾驶员是否疲劳驾驶和及时作出安全提示。

一、背景

(1)环境搭建

题主使用的环境配置:python3.9.13 cuda11.3 anaconda3   

所需库:

pip install numpy

pip install matplotlib

pip installl imutils

pip install scipy

pip install dlib

其中 dlib下载方法(本文仅提供py3.9版本下载)

首先安装

pip install cmake

pip install boost

下载dlib-19.23.0-cp39-cp39-win_amd64.whl

下载后在对应文件夹下执行(这个大家应该都会吧(我自己是放在环境目录的backages文件夹里))

pip install dlib-19.23.0-cp39-cp39-win_amd64.whl

其他版本dlib中下载

(2)下载开源数据集

shape_predictor_68_face_landmarks.dat

官方下载地址:Links for dlib
码云下载:fatigue_detecting: 基于图像驾驶员疲劳检测技术研究

(3)Head Pose Estimation 如何理解?


一种比较经典的 Head Pose Estimation 算法的步骤一般为:2D人脸关键点检测;3D人脸模型匹配;求解3D点和对应2D点的转换关系;根据旋转矩阵求解欧拉角。

众所周知一个物体相对于相机的姿态可以使用旋转矩阵和平移矩阵来表示。

平移矩阵:物体相对于相机的空间位置关系矩阵,用T表示;
旋转矩阵:物体相对于相机的空间姿态关系矩阵,用R表示。
学新通

于是世界坐标系(UVW)、相机坐标系(XYZ)、图像中心坐标系(uv)和像素坐标系(xy)四兄弟闪亮登场。如果相机完美无瑕,老三可以回家洗洗睡觉,关系也相对简单。

 世界坐标系到相机坐标系:

学新通

相机坐标系到像素坐标系:

学新通
因此像素坐标系和世界坐标系的关系如下:

学新通

上式的求解可用DLT(Direct Linear Transform)算法结合最小二乘进行迭代求解,最小二乘的目标函数可为学新通带^的变量为预测值,其余为测量值。


可是相机也很无奈,她不完美,总有点瑕疵,比如径向和切向畸变,那关系就要稍微复杂一些,叫醒阿三继续推导:

 学新通


相机坐标系要先转换到图像中心坐标系:学新通
然后再被折磨一番(计算考虑畸变):


最后图像中心坐标系到像素坐标系:

 学新通

看来只要知道世界坐标系内点的位置、像素坐标位置和相机参数就可以搞定旋转和平移矩阵,可上面的关系分明是非线性的,这可怎么解啊?其实OpenCV已经给我们提供了求解PnP问题的函数solvePnp(),一步轻松到位。
得到旋转矩阵后,就可以开心地去见欧拉角了:

学新通

 世界坐标系中点的位置怎么得到呢?
总不能每时每刻都要测一下人脸各个点在空间的位置
从各种论文中发现,原来大牛们在算法里面内置了一个3D人脸模型,把关键点的空间位置都标出来,就充当真实脸的空间位置;可是大牛又觉得这样不太合理,一个3D人脸模型不能表示所有人的脸,对所有人采用一个模型得到的精度肯定不好,于是便有了3DMM(3D Morphable Model),对不同人可以拟合出对应的3D脸模型,这样关键点的空间位置就比较准确了,Head Pose Estimation 的精度提上去了。
学新通

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

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

(4)主要代码思路
第一步:2D人脸关键点检测;
第二步:3D人脸模型匹配;
第三步:求解3D点和对应2D点的转换关系;
第四步:根据旋转矩阵求解欧拉角。
 

  1.  
    # 参考https://github.com/lincolnhard/head-pose-estimation
  2.  
    import cv2
  3.  
    import dlib
  4.  
    import numpy as np
  5.  
    from imutils import face_utils
  6.  
    """
  7.  
    思路:
  8.  
    第一步:2D人脸关键点检测;第二步:3D人脸模型匹配;
  9.  
    第三步:求解3D点和对应2D点的转换关系;第四步:根据旋转矩阵求解欧拉角。
  10.  
    """
  11.  
     
  12.  
    # 加载人脸检测和姿势估计模型(dlib)
  13.  
    face_landmark_path = 'D:/myworkspace/JupyterNotebook/fatigue_detecting/model/shape_predictor_68_face_landmarks.dat'
  14.  
     
  15.  
    """
  16.  
    只要知道世界坐标系内点的位置、像素坐标位置和相机参数就可以搞定旋转和平移矩阵(OpenCV自带函数solvePnp())
  17.  
    """
  18.  
     
  19.  
    # 世界坐标系(UVW):填写3D参考点,该模型参考http://aifi.isr.uc.pt/Downloads/OpenGL/glAnthropometric3DModel.cpp
  20.  
    object_pts = np.float32([[6.825897, 6.760612, 4.402142], #33左眉左上角
  21.  
    [1.330353, 7.122144, 6.903745], #29左眉右角
  22.  
    [-1.330353, 7.122144, 6.903745], #34右眉左角
  23.  
    [-6.825897, 6.760612, 4.402142], #38右眉右上角
  24.  
    [5.311432, 5.485328, 3.987654], #13左眼左上角
  25.  
    [1.789930, 5.393625, 4.413414], #17左眼右上角
  26.  
    [-1.789930, 5.393625, 4.413414], #25右眼左上角
  27.  
    [-5.311432, 5.485328, 3.987654], #21右眼右上角
  28.  
    [2.005628, 1.409845, 6.165652], #55鼻子左上角
  29.  
    [-2.005628, 1.409845, 6.165652], #49鼻子右上角
  30.  
    [2.774015, -2.080775, 5.048531], #43嘴左上角
  31.  
    [-2.774015, -2.080775, 5.048531],#39嘴右上角
  32.  
    [0.000000, -3.116408, 6.097667], #45嘴中央下角
  33.  
    [0.000000, -7.415691, 4.070434]])#6下巴角
  34.  
     
  35.  
    # 相机坐标系(XYZ):添加相机内参
  36.  
    K = [6.5308391993466671e 002, 0.0, 3.1950000000000000e 002,
  37.  
    0.0, 6.5308391993466671e 002, 2.3950000000000000e 002,
  38.  
    0.0, 0.0, 1.0]# 等价于矩阵[fx, 0, cx; 0, fy, cy; 0, 0, 1]
  39.  
    # 图像中心坐标系(uv):相机畸变参数[k1, k2, p1, p2, k3]
  40.  
    D = [7.0834633684407095e-002, 6.9140193737175351e-002, 0.0, 0.0, -1.3073460323689292e 000]
  41.  
     
  42.  
    # 像素坐标系(xy):填写凸轮的本征和畸变系数
  43.  
    cam_matrix = np.array(K).reshape(3, 3).astype(np.float32)
  44.  
    dist_coeffs = np.array(D).reshape(5, 1).astype(np.float32)
  45.  
     
  46.  
     
  47.  
     
  48.  
    # 重新投影3D点的世界坐标轴以验证结果姿势
  49.  
    reprojectsrc = np.float32([[10.0, 10.0, 10.0],
  50.  
    [10.0, 10.0, -10.0],
  51.  
    [10.0, -10.0, -10.0],
  52.  
    [10.0, -10.0, 10.0],
  53.  
    [-10.0, 10.0, 10.0],
  54.  
    [-10.0, 10.0, -10.0],
  55.  
    [-10.0, -10.0, -10.0],
  56.  
    [-10.0, -10.0, 10.0]])
  57.  
    # 绘制正方体12轴
  58.  
    line_pairs = [[0, 1], [1, 2], [2, 3], [3, 0],
  59.  
    [4, 5], [5, 6], [6, 7], [7, 4],
  60.  
    [0, 4], [1, 5], [2, 6], [3, 7]]
  61.  
     
  62.  
    def get_head_pose(shape):
  63.  
    # 填写2D参考点,注释遵循https://ibug.doc.ic.ac.uk/resources/300-W/
  64.  
    """
  65.  
    17左眉左上角/21左眉右角/22右眉左上角/26右眉右上角/36左眼左上角/39左眼右上角/42右眼左上角/
  66.  
    45右眼右上角/31鼻子左上角/35鼻子右上角/48左上角/54嘴右上角/57嘴中央下角/8下巴角
  67.  
    """
  68.  
    # 像素坐标集合
  69.  
    image_pts = np.float32([shape[17], shape[21], shape[22], shape[26], shape[36],
  70.  
    shape[39], shape[42], shape[45], shape[31], shape[35],
  71.  
    shape[48], shape[54], shape[57], shape[8]])
  72.  
    """
  73.  
    用solvepnp或sovlepnpRansac,输入3d点、2d点、相机内参、相机畸变,输出r、t之后
  74.  
    用projectPoints,输入3d点、相机内参、相机畸变、r、t,输出重投影2d点
  75.  
    计算原2d点和重投影2d点的距离作为重投影误差
  76.  
    """
  77.  
    # solvePnP计算姿势——求解旋转和平移矩阵:
  78.  
    # rotation_vec表示旋转矩阵,translation_vec表示平移矩阵,cam_matrix与K矩阵对应,dist_coeffs与D矩阵对应。
  79.  
    _, rotation_vec, translation_vec = cv2.solvePnP(object_pts, image_pts, cam_matrix, dist_coeffs)
  80.  
    # projectPoints重新投影误差
  81.  
    reprojectdst, _ = cv2.projectPoints(reprojectsrc, rotation_vec, translation_vec, cam_matrix,dist_coeffs)
  82.  
     
  83.  
    reprojectdst = tuple(map(tuple, reprojectdst.reshape(8, 2)))# 以8行2列显示
  84.  
     
  85.  
    # 计算欧拉角calc euler angle
  86.  
    # 参考https://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html#decomposeprojectionmatrix
  87.  
    rotation_mat, _ = cv2.Rodrigues(rotation_vec)#罗德里格斯公式(将旋转矩阵转换为旋转向量)
  88.  
    pose_mat = cv2.hconcat((rotation_mat, translation_vec))# 水平拼接,vconcat垂直拼接
  89.  
    # eulerAngles –可选的三元素矢量,包含三个以度为单位的欧拉旋转角度
  90.  
    _, _, _, _, _, _, euler_angle = cv2.decomposeProjectionMatrix(pose_mat)# 将投影矩阵分解为旋转矩阵和相机矩阵
  91.  
     
  92.  
    return reprojectdst, euler_angle
  93.  
     
  94.  
     
  95.  
    def main():
  96.  
    # return
  97.  
    cap = cv2.VideoCapture(0)
  98.  
    if not cap.isOpened():
  99.  
    print("Unable to connect to camera.")
  100.  
    return
  101.  
    # 检测人脸
  102.  
    detector = dlib.get_frontal_face_detector()
  103.  
    # 检测第一个人脸的关键点
  104.  
    predictor = dlib.shape_predictor(face_landmark_path)
  105.  
     
  106.  
    while cap.isOpened():
  107.  
    ret, frame = cap.read()
  108.  
    if ret:
  109.  
    face_rects = detector(frame, 0)
  110.  
     
  111.  
    if len(face_rects) > 0:
  112.  
    # 循环脸部位置信息,使用predictor(gray, rect)获得脸部特征位置的信息
  113.  
    shape = predictor(frame, face_rects[0])
  114.  
    # 将脸部特征信息转换为数组array的格式
  115.  
    shape = face_utils.shape_to_np(shape)
  116.  
    # 获取头部姿态
  117.  
    reprojectdst, euler_angle = get_head_pose(shape)
  118.  
    pitch = format(euler_angle[0, 0])
  119.  
    yaw = format(euler_angle[1, 0])
  120.  
    roll = format(euler_angle[2, 0])
  121.  
    print('pitch:{}, yaw:{}, roll:{}'.format(pitch, yaw, roll))
  122.  
     
  123.  
    # 标出68个特征点
  124.  
    for (x, y) in shape:
  125.  
    cv2.circle(frame, (x, y), 1, (0, 0, 255), -1)
  126.  
     
  127.  
    # 绘制正方体12轴
  128.  
    for start, end in line_pairs:
  129.  
    cv2.line(frame, reprojectdst[start], reprojectdst[end], (0, 0, 255))
  130.  
    # 显示角度结果
  131.  
    cv2.putText(frame, "X: " "{:7.2f}".format(euler_angle[0, 0]), (20, 20), cv2.FONT_HERSHEY_SIMPLEX,0.75, (0, 0, 255), thickness=2)
  132.  
    cv2.putText(frame, "Y: " "{:7.2f}".format(euler_angle[1, 0]), (20, 50), cv2.FONT_HERSHEY_SIMPLEX,0.75, (0, 0, 255), thickness=2)
  133.  
    cv2.putText(frame, "Z: " "{:7.2f}".format(euler_angle[2, 0]), (20, 80), cv2.FONT_HERSHEY_SIMPLEX,0.75, (0, 0, 255), thickness=2)
  134.  
     
  135.  
    # 按q退出提示
  136.  
    cv2.putText(frame, "Press 'q': Quit", (20, 450),cv2.FONT_HERSHEY_SIMPLEX, 0.7, (84, 255, 159), 2)
  137.  
    # 窗口显示 show with opencv
  138.  
    cv2.imshow("Head_Posture", frame)
  139.  
     
  140.  
    if cv2.waitKey(1) & 0xFF == ord('q'):
  141.  
    break
  142.  
    # 释放摄像头 release camera
  143.  
    cap.release()
  144.  
    # do a bit of cleanup
  145.  
    cv2.destroyAllWindows()
  146.  
     
  147.  
     
  148.  
    if __name__ == '__main__':
  149.  
    main()
  150.  
     

点头如何判断是否是瞌睡?

主要参数:欧拉角

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


 

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhhfhgec
系列文章
更多 icon
同类精品
更多 icon
继续加载