import matplotlib.pyplot as plt
import dlib
import numpy as np
import glob
import re
# 初始化人脸检测器
detector = dlib.get_frontal_face_detector()
# 初始化面部关键点检测器
sp = dlib.shape_predictor("D:\\LBJAVAscript\\shape_predictor_68_face_landmarks.dat")
# 初始化人脸识别模型
facerec = dlib.face_recognition_model_v1("D:\\LBJAVAscript\\dlib_face_recognition_resnet_model_v1.dat")
# 存储候选人脸部描述向量
descriptors = []
photo_locations = []
# 遍历所有图片
for photo in glob.glob('D:\\LBJAVAscript\\faces*.jpg'):
photo_locations.append(photo)
img = plt.imread(photo)
img = np.array(img)
# 检测人脸
dets = detector(img, 1)
for k, d in enumerate(dets):
# 检测每张照片中人脸的特征
shape = sp(img, d)
face_descriptor = facerec.compute_face_descriptor(img, shape)
v = np.array(face_descriptor)
descriptors.append(v)
# 对待识别的人脸进行相同的处理
img = plt.imread('D:\\test_photo10.jpg')
img = np.array(img)
```
2.2 疲劳检测算法
疲劳检测算法主要通过监控驾驶员的眼睛状态来判断其疲劳程度。通过计算眼睛的EAR值(眼睛纵横比),并与设定的阈值进行比较,可以有效地识别出驾驶员是否处于疲劳状态。
2.3 YOLOV5算法
YOLOV5 是一种先进的目标检测算法,适用于实时场景下的物体检测任务。在本项目中,YOLOV5 被用来检测驾驶员的行为,如使用手机、吸烟、喝水等,进一步增强系统的安全性和实用性。
3 效果展示
3.1 眨眼检测
系统能够准确检测驾驶员眨眼的动作,确保在驾驶员出现短暂失神时及时提醒。
3.2 打哈欠检测
通过分析驾驶员的面部表情,特别是嘴部的开合情况,系统可以判断驾驶员是否在打哈欠,这通常是疲劳的一个重要标志。
3.3 使用手机检测
系统能够识别驾驶员是否在驾驶过程中使用手机,这是一种非常危险的行为,系统会在检测到时立即发出警告。
3.4 抽烟检测
抽烟不仅影响健康,也可能分散驾驶员的注意力。系统可以通过识别手部动作和物品来判断驾驶员是否在抽烟,并及时提醒。
3.5 喝水检测
在长时间驾驶中,适当饮水是有益的,但频繁的操作可能增加事故风险。系统能够检测驾驶员喝水的行为,并在必要时提供建议。
dets = detector(img, 1)
# 计算输入人脸与已有人脸间的差异程度(例如使用欧式距离)
differences = []
for k, d in enumerate(dets):
shape = sp(img, d)
face_descriptor = facerec.compute_face_descriptor(img, shape)
d_test = np.array(face_descriptor)
# 计算输入人脸与所有已有人脸描述向量的欧氏距离
for i in descriptors:
distance = np.linalg.norm(i - d_test)
differences.append(distance)
# 按照欧式距离排序,欧式距离最小的即为匹配的人脸
candidate_count = len(photo_locations)
candidates_dict = dict(zip(photo_locations, differences))
candidates_dict_sorted = sorted(candidates_dict.items(), key=lambda x: x[1])
# 设置matplotlib以正确显示中文
plt.rcParams['font.family'] = ['sans-serif']
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['figure.figsize'] = (20.0, 70.0)
ax = plt.subplot(candidate_count + 1, 4, 1)
ax.set_title("输入的人脸")
ax.imshow(img)
for i, (photo, distance) in enumerate(candidates_dict_sorted):
img = plt.imread(photo)
face_name = ""
photo_name = re.search(r'([^\]*).jpg$', photo)
if photo_name:
face_name = photo_name[1]
ax = plt.subplot(candidate_count + 1, 4, i + 2)
ax.set_xticks([])
ax.set_yticks([])
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.spines['left'].set_visible(False)
if i == 0:
ax.set_title("最匹配的人脸\n" + face_name + "\n差异度:" + str(distance))
else:
ax.set_title(face_name + "\n差异度:" + str(distance))
ax.imshow(img)
plt.show()
2.2 疲劳检测算法
该系统通过Dlib库中的68个人脸关键点检测模型(shape_predictor_68_face_landmarks.dat)对视频中的人脸进行检测,返回人脸特征点坐标、人脸框及人脸角度等信息。系统利用这些关键点来评估驾驶员的疲劳状态,具体算法如下:
- 初始化Dlib的人脸检测器(HOG),并创建面部标志物预测器;
- 使用dlib.get_frontal_face_detector() 获取脸部位置检测器;
- 使用dlib.shape_predictor 获取脸部特征位置检测器;
- 获取左眼和右眼面部标志的索引;
- 开启cv2本地摄像头。
Dlib库的68个特征点模型如下所示:
眼睛检测算法
当人眼处于睁开状态时,EAR(Eye Aspect Ratio)指标会在特定区间内波动。而当眼睛闭合时,EAR值会迅速降低,理论上接近零。若EAR值降至某一预设阈值之下,表明眼睛已闭合。若EAR值快速下降并再次回升超过此阈值,即可判定发生了一次眨眼行为。为了准确统计眨眼次数,需设定连续帧数来识别单次眨眼。由于眨眼过程非常迅速,通常仅需1至3帧即可完成。以下展示了用于识别的眼部关键点:
EAR的计算公式如下所示:
若相邻两帧之间眼睛宽高比的变化幅度(EAR)超过0.2,则可能指示驾驶员存在疲劳驾驶的情况。在68个面部特征点中,编号37至42对应左眼,43至48对应右眼。
右眼的开合程度可通过以下数学表达式进行量化:
眼睛从完全睁开到开始闭合的过程定义为进入闭眼期,而从闭合到重新睁开则标志着睁眼期的开始。通过计算最长闭眼持续时间(可用帧数表示),并记录闭眼与睁眼转换的次数,可以评估闭眼频率。设定一定时间内的闭眼次数及闭眼时间的临界值,以此判断驾驶员是否处于疲劳状态。
以下是用于实现上述疲劳检测逻辑的相关Python代码示例:
# 疲劳检测功能,主要针对眼睛和嘴巴的活动程度
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
import argparse
import imutils
import time
import dlib
import cv2
import math
from threading import Thread
def eye_aspect_ratio(eye):
A = dist.euclidean(eye[1], eye[5])
B = dist.euclidean(eye[2], eye[4])
C = dist.euclidean(eye[0], eye[3])
ear = (A + B) / (2.0 * C)
return ear
除了眼睛的状态,打哈欠也是一种重要的疲劳迹象。打哈欠检测算法基于MAR(Mouth Aspect Ratio)来实现,利用Dlib库提取出嘴部的6个特征点(51、59、53、57的垂直坐标和49、55的水平坐标)。当人开口说话时,这些点间的垂直距离会增加,导致MAR值显著升高;相反,当嘴闭合时,MAR值会迅速下降。
下图展示了用于计算MAR值的关键点布局:
MAR值的计算方法如下:
通过分析MAR值的变化趋势,可以判断驾驶员是否正在打哈欠,以及打哈欠的持续时间。为了确保准确性,通常会采用双阈值策略来区分正常的谈话或唱歌与真正的打哈欠行为。例如,在一分钟内,如果打哈欠的频率超过总帧数的10%,则认为驾驶员进行了一个深打哈欠或至少两次浅打哈欠,这时系统将发出疲劳警告。
下面是执行打哈欠检测的部分代码片段:
# 继续疲劳检测,特别关注嘴部活动
def mouth_aspect_ratio(mouth):
A = np.linalg.norm(mouth[2] - mouth[10])
B = np.linalg.norm(mouth[4] - mouth[8])
C = np.linalg.norm(mouth[0] - mouth[6])
mar = (A + B) / (2.0 * C)
return mar
此外,还存在一种基于头部运动的疲劳检测方法——点头检测算法,它通过监测驾驶员头部的上下移动来评估其疲劳水平。这种方法同样依赖于对面部特征点的精确追踪,以捕捉头部细微的动作变化。
基于HPE算法的点头检测
该算法包括以下步骤:首先进行2D人脸关键点检测,接着匹配3D人脸模型,然后计算3D点与相应2D点之间的转换关系,最后通过旋转矩阵计算欧拉角。在检测过程中,需运用世界坐标系(UVW)、相机坐标系(XYZ)、图像中心坐标系(uv)以及像素坐标系(xy)。
一个物体相对于相机的姿态可以通过旋转矩阵(R)和平移矩阵(T)来描述。其中,平移矩阵表示物体相对于相机的空间位置关系,而旋转矩阵则表示空间姿态关系。因此,坐标系之间的转换是必不可少的。具体关系如下图所示:
四个坐标系及其相互关系如下:
- 世界坐标系转至相机坐标系:

- 相机坐标系转至像素坐标系:

- 像素坐标系与世界坐标系的关系:

- 图像中心坐标系转至像素坐标系:

获得旋转矩阵后,接下来计算欧拉角:

设定参数阈值为0.3,在例如10秒的时间段内,如果低头角度(Pitch)的绝对值大于等于20°或头部侧倾角度(Roll)的绝对值大于等于20°的时间比例超过0.3,则判定驾驶员可能处于困倦状态,并触发警报。
2.3 YOLOv5算法简介
为了检测玩手机、抽烟和喝水这三种行为,我们选择了当前最先进的YOLOv5卷积神经网络。2020年6月9日,Ultralytics公司发布了YOLOv5,距离上一版YOLOv4的发布不足50天。此次发布的YOLOv5完全基于PyTorch实现,不仅带来了更快的实时目标检测能力,而且其模型大小仅为YOLOv4的1/9,大大提高了效率。
YOLOv5在官方测试中,每张图片的推理时间最快可达0.007秒,即每秒可处理140帧图像(FPS)。YOLOv5属于one-stage目标检测架构,意味着它可以直接从输入图像中生成位置和类别的预测,无需经过候选区域的生成过程。YOLO是“你只需看一次”的缩写,强调了其高效的检测流程。
YOLOv5共有四种不同性能的版本,具体表现如下图所示:

YOLOv5的网络架构如下:

效果展示
3.1 眨眼检测
3.2 打哈欠检测
3.3 使用手机检测
3.4 抽烟检测
3.5 喝水检测
项目分享:欢迎各位自行获取,仅供学习参考之用,获取方式详见本文末尾。