基于dlib与PERCLOS的驾驶员疲劳检测系统实战解析
简介这是一套基于Python与计算机视觉技术的驾驶员疲劳检测系统源码面向高校计算机、人工智能专业毕业设计及智慧交通中的疲劳驾驶监控需求。系统通过分析驾驶员面部特征识别人脸并判断疲劳状态可部署在车载终端、交警摄像头或高速收费站等场景工程实用性较强。压缩包共24个文件大小68.33MB核心为5个Python源码文件配套10个XML相关文件、2个gitignore和2个iml工程配置以及docx、md、txt等说明文档和mp3、dat辅助数据下载后即可运行。目前已有1357人学习。整体工程结构清晰便于逐模块梳理人脸检测、特征提取、状态判定的实现流程既可作为毕业论文的完整原型也有利于在此基础上扩展眨眼、打哈欠检测及预警提醒等功能。内置的工程配置与说明文档能帮助还原运行环境、理解项目组织方式适合深度学习、图像处理方向的学生参考与二次开发。 网上搜“Python疲劳检测源码”能下载到一大堆Demo。跑起来的效果基本一样摄像头对着人脸眼睛一闭就弹“疲劳”警告。但说实话这类Demo在答辩现场很难撑住场面——老师随口问一句“闭眼几帧算疲劳阈值依据是什么”大多数照着README跑通的人就答不上来了。我花了两周时间把“基于驾驶员面部特征的疲劳检测系统”从前到后完整做了一遍从算法选型、代码结构到实车环境踩坑都过了一遍这篇就把整个过程和经验完整复盘出来希望能帮到正在做同类毕业设计或者想入门计算机视觉兴趣项目的朋友。先说结论这套系统的核心价值不在“识别闭眼”这件事本身而在于它把单帧图像判断升级成了基于时序窗口的疲劳状态评估。市面上很多版本把疲劳检测做成了“睁眼/闭眼分类器”严格来说那叫“瞌睡检测”不是“疲劳检测”。真正的疲劳判断要考虑眼睛闭合时长、闭眼频率、打哈欠次数、头部姿态变化等多个维度再结合时间窗口做统计决策。这也是我拿到这个题目后和导师反复确认才想明白的边界。1. 技术路线之争为什么选择面部特征关键点而不是端到端分类1.1 疲劳检测的几条技术路线为什么视觉方案最适合毕设疲劳检测在工业界和学术界都有大量研究整体可以分成三条路线。理解了这三条路线的差别你就明白为什么绝大多数毕业设计都选视觉方案。第一类是生理信号检测包括脑电EEG、心电图、眼电图等。这套方案准确率确实高但需要在驾驶员身上贴电极、戴设备实验环境还算可以实际驾驶场景基本没法落地。而且这些硬件模块动辄上千元对毕设预算很不友好。第二类是驾驶行为检测通过方向盘转角异常、车道偏移频率、跟车距离变化来判断驾驶员状态。好处是不需要接触驾驶员但坏处是延迟很大——等方向盘开始飘了危险可能已经发生了。这类方案一般适合和视觉方案做融合单独拿出来做毕设课题不够直观。第三类就是视觉面部特征检测用摄像头采集驾驶员面部图像通过眼睛状态、嘴巴状态、头部姿态来判断疲劳程度。这套方案成本低、非接触、实时性好而且和“人靠肉眼观察驾驶员是否犯困”的逻辑完全一致解释起来非常自然。驾驶员疲劳检测系统选择视觉方案几乎是从业者的共识。1.2 同类方案横向对比dlib关键点为何比深度分类更稳确定视觉方案后下一个问题是用什么技术实现。你在网上能找到的版本基本逃不出三种实现思路实现方案标注数据需求可解释性运行环境要求开发工作量毕设适配度CNN分类网络VGG/ResNet直接判断疲劳/清醒需要大量带标签的疲劳人脸图差黑盒决策出错了很难解释需要GPU或较好的CPU模型较大中一般目标检测分类先检测人脸再训练分类器判断闭眼/张嘴需要标注眼睛、嘴巴区域中中间结果可看但依赖标注质量依赖模型大小较高中dlib关键点几何特征EAR/MAR/PERCLOS不需要自己标注用预训练68点模型强每个判定指标都有明确物理含义CPU即可实时运行低高我最推荐的还是dlib关键点方案原因有三点。第一不需要自己准备训练数据。深度分类方案听起来高大上但“疲劳人脸”数据集的获取本身就很难——真实疲劳状态的人脸是连续变化的很难标出清晰的类别边界。更麻烦的是用网上别人的数据集训练答辩时老师一问“数据怎么来的”“样本量多少”“不同人种、光照条件怎么处理”就很难答好。dlib提供的是预训练的68点人脸关键点检测模型我们只需要调接口不需要训练。第二特征有明确的物理含义。眼睛的纵横比、嘴巴的张开程度、头部俯仰角度每一个数值都能在真实生理行为中找到对应关系。出问题了可以直接定位——是眼睛特征没算对还是阈值没调好。深度模型在这方面是黑盒跑错了只能干瞪眼。第三CPU实时运行无压力。我实测在普通笔记本的CPU上480p分辨率下dlib人脸检测68点关键点提取能跑到25fps以上加上后续的EAR、MAR计算总体依然流畅。如果用深度学习方案模型推理这块就会吃掉大量算力还可能要在GPU环境和CPU环境之间反复折腾。2. 核心算法拆解EAR、PERCLOS、打哈欠与点头检测的实现逻辑2.1 68点面部特征点里眼睛和嘴巴分别在哪dlib训练好的shape_predictor_68_face_landmarks.dat模型可以对一张检测到的人脸标定出68个关键点。这68个点有固定编号覆盖了眉毛、眼睛、鼻子、嘴巴和面部轮廓。对于疲劳检测来说我们只关心两组点。眼睛区域右眼是第37点到第42点左眼是第43点到第48点每只眼睛6个点。嘴巴区域第49点到第68点一共20个点其中外嘴唇轮廓主要由49、50、51、52、53、54、55、59、58、57、56这些点构成。68点模型的坐标顺序是OpenCV的(x, y)格式拿到这些点后直接做向量运算即可。这张标定图网上到处都能搜到建议第一次做的时候先把它打印出来放在旁边对一下编号别等代码写完了发现两眼用的点全串了。2.2 EAR用六个点计算“睁眼程度”眼睛纵横比英文是Eye Aspect Ratio简称EAR。它利用眼睛的6个关键点计算一个比值这个比值的核心特点是睁眼时数值稳定在一个范围内闭眼时数值急剧下降。我直接给公式。设眼睛6个点为P1到P6其中P1是外眼角、P4是内眼角P2、P3是上眼睑点P5、P6是下眼睑点EAR (|P2-P6| |P3-P5|) / (2 * |P1-P4|)这里的|P2-P6|表示P2点到P6点的欧氏距离也就是眼睑垂直方向的距离|P1-P4|表示内外眼角之间的水平距离。由于人眼的外形在不同人脸间差异很大把垂直距离除以水平距离做归一化就能让这个指标在不同人脸之间具备可比性。Python实现很简单from scipy.spatial import distance as dist def eye_aspect_ratio(eye): # eye: 6个关键点的坐标列表 A dist.euclidean(eye[1], eye[5]) B dist.euclidean(eye[2], eye[4]) C dist.euclidean(eye[0], eye[3]) return (A B) / (2.0 * C)实测下来正常人睁眼状态右眼EAR在0.25到0.35之间闭眼时会跌到0.1以下。判断闭眼的阈值我一般取0.18到0.2之间低于这个值就判定为闭眼帧。这里有个容易翻车的点不同人眼睛大小差异很大单次实验定死的阈值换一个人就可能失灵。更稳妥的办法是启动时让人脸正对摄像头睁眼2秒采集这段时间的EAR平均值用平均值乘以一个比例系数比如0.6作为个人自适应阈值。这个细节放在毕设里会是很亮的加分项。2.3 PERCLOS疲劳是一段时间的事不是一帧的事单帧检测出眼睛闭合只能说明这个人正在眨眼不能说明他疲劳了。正常人每分钟眨眼15到20次每次持续0.1到0.15秒。如果一闭眼就报警系统根本没法用。PERCLOSPercentage of Eyelid Closure over the Pupil over Time正是解决这个问题的指标指单位时间内眼睛闭合时间所占的比例。交通领域的相关研究很早就在用这个指标其中最常用的是P80标准——眼睑遮住瞳孔面积超过80%视为闭合统计闭合时间在固定时间窗内的占比。在代码里我们没必要真去算瞳孔被遮住的面积。工程上可以用EAR低于闭眼阈值来近似判定“闭合帧”然后用滑动窗口统计占比from collections import deque # 保存最近100帧的闭眼状态1为闭眼0为睁眼 eye_closed_deque deque(maxlen100) # 每帧计算完EAR后 eye_closed_deque.append(1 if ear EYE_CLOSED_THRESHOLD else 0) perclos sum(eye_closed_deque) / len(eye_closed_deque) # 如果闭眼帧占比超过40%判定疲劳 if perclos 0.4: alarm_level 2窗口大小怎么定我自己的经验是100帧加40%阈值配合25fps视频流也就是4秒内闭眼占比超过四成就报警。这个参数在实车上调到过45%因为路面颠簸会导致图像偶尔模糊阈值太紧容易误报。另外提醒一下deque是Python里做滑动窗口最舒服的数据结构既能控制长度又不用手动维护数组下标做时序检测场景基本人手一个。2.4 打哈欠的MAR判据与点头检测的简化实现嘴巴检测和眼睛检测思路完全一致用MARMouth Aspect Ratio嘴巴纵横比。公式长这样MAR (|P51-P59| |P53-P57|) / (2 * |P49-P55|)其中49和55是左右嘴角51、53是上嘴唇关键点57、59是下嘴唇关键点。正常说话时MAR在0.2到0.4之间波动打哈欠时会飙升到0.6以上并且持续十几帧。判定逻辑照搬EAR——连续多帧MAR超过阈值就算一次打哈欠。点头检测稍微复杂一点但也没有想象中难。最常见的做法是头部姿态估计用OpenCV的solvePnP函数把2D关键点和3D人脸模型对应起来解出俯仰角、偏航角和滚转角。头部下垂俯仰角变化超过一定角度并持续一段时间就被认为是打瞌睡点头。这个方法精度高但代码量稍大。如果你想快速出一个能跑的效果可以用简化方案提取鼻尖点第34点和两眼中心点计算两者之间的垂直偏移量。头部正常时这个偏移量基本稳定头部下垂时鼻尖点的Y坐标会相对于两眼中心点明显下移。连续统计这个偏移量超过阈值就判定为点头。这个简化方案精度不如solvePnP但胜在代码短、容易理解答辩时讲起来也直观。3. 系统架构与关键代码从摄像头到报警的完整链路3.1 源码包的目录结构与模块划分拿到一套疲劳检测毕设源码不要急着跑demo先把目录结构读懂。我那版系统的模块划分是这样的基本可以代表这类项目的通用组织方式fatigue_detection/ ├── main.py # 主程序入口启动摄像头检测循环 ├── config.py # 全局配置阈值、窗口大小、摄像头编号 ├── detector.py # 人脸检测与68点关键点提取封装 ├── features.py # EAR、MAR、PERCLOS、头部姿态计算 ├── state_machine.py # 疲劳状态机正常-疑似-疲劳 ├── alarm.py # 报警模块界面提示声音报警 ├── logger.py # 日志记录保存检测结果到CSV ├── utils.py # 工具函数平滑滤波、图像预处理 ├── models/ │ └── shape_predictor_68_face_landmarks.dat ├── requirements.txt └── README.md这个结构遵循一个简单原则数据采集、特征计算、状态决策、结果输出各管各的。你要在答辩时讲清楚“高内聚低耦合”拿这个结构举例最容易。3.2 主循环人脸检测、特征提取与帧处理流程主程序的循环逻辑是这套系统的骨架我在下面给出核心代码结构import cv2 import dlib from collections import deque from config import * from features import calculate_ear, calculate_mar from state_machine import FatigueStateMachine # 初始化 cap cv2.VideoCapture(CAMERA_ID) detector dlib.get_frontal_face_detector() predictor dlib.shape_predictor(models/shape_predictor_68_face_landmarks.dat) # 时序窗口 eye_history deque(maxlenEYE_WINDOW_SIZE) mouth_history deque(maxlenMOUTH_WINDOW_SIZE) state_machine FatigueStateMachine() while True: ret, frame cap.read() if not ret: break # 预处理转灰度直方图均衡化提升弱光环境下的检测率 gray cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) gray cv2.equalizeHist(gray) faces detector(gray, 0) for face in faces: landmarks predictor(gray, face) # 提取左右眼关键点计算EAR left_eye landmarks_to_points(landmarks, LEFT_EYE_INDICES) right_eye landmarks_to_points(landmarks, RIGHT_EYE_INDICES) ear (calculate_ear(left_eye) calculate_ear(right_eye)) / 2.0 # 提取嘴巴关键点计算MAR mouth landmarks_to_points(landmarks, MOUTH_INDICES) mar calculate_mar(mouth) # 更新历史窗口 eye_history.append(ear) mouth_history.append(mar) # 状态机裁决 current_state state_machine.update(eye_history, mouth_history) # 在画面上绘制检测结果和警示信息 display_frame draw_status(frame, current_state, ear, mar) cv2.imshow(Fatigue Detection, display_frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()这段代码里有几个细节值得展开。第一个是dlib的检测参数。detector(gray, 0)里的0表示不对图像做金字塔上采样速度快但小脸检测不到。对车载摄像头来说人脸在画面里占比通常较大设0就够了。如果在笔记本自带摄像头上测试距离稍远人脸变小可以把参数调成1来增加检测率帧率会下降一些。第二个是直方图均衡化。这行不做代码在正常光照下没问题但一到光线偏暗的场合dlib检测人脸的成功率会肉眼可见地下降。cv2.equalizeHist是OpenCV自带函数一行代码能显著提升弱光下的稳定性属于性价比极高的预处理手段。第三个是帧率的坑。cv2.waitKey(1)控制的是每帧间间隔不是真正的帧率。实际帧率取决于摄像头输出和算法处理耗时。做PERCLOS统计时如果按“帧数”做窗口帧率波动会导致时间窗口长度不稳定。严谨的做法是记录每帧的时间戳按“秒”来切分PERCLOS窗口。这个细节你可以先不做但答辩时能说出这个原因老师会认为你真的看懂了代码。3.3 疲劳状态机从正常到报警的判定逻辑将检测做成“状态机”而不是“单帧if-else”是这套系统区别于普通Demo的关键。疲劳是一个渐进过程状态机天然契合这种场景。我设计了三个状态正常SAFE、疑似疲劳SUSPICIOUS、疲劳FATIGUE。正常状态下PERCLOS低于0.3打哈欠次数为0头部姿态正常。疑似状态下PERCLOS在0.3到0.4之间或检测到1次打哈欠。此时系统在界面提示“请注意休息”但不触发报警。疲劳状态下PERCLOS超过0.4或连续出现2次及以上打哈欠或检测到点头动作。此时触发声音警报和弹窗提示。class FatigueStateMachine: def __init__(self): self.state SAFE self.yawn_count 0 self.nod_count 0 self.suspicious_start_time None def update(self, eye_history, mouth_history): perclos sum(1 for e in eye_history if e EYE_CLOSED_THRESHOLD) / len(eye_history) # 打哈欠检测连续15帧MAR超过阈值 yawn_frames sum(1 for m in mouth_history if m MOUTH_OPEN_THRESHOLD) if yawn_frames YAWN_FRAME_THRESHOLD: self.yawn_count 1 if perclos FATIGUE_PERCLOS_THRESHOLD or self.yawn_count 2: self.state FATIGUE elif perclos SUSPICIOUS_PERCLOS_THRESHOLD or self.yawn_count 1: self.state SUSPICIOUS else: self.state SAFE self.yawn_count 0 return self.state状态机的另一个好处是避免报警抖动。不做状态机的话PERCLOS在阈值边缘波动时报警会一开一关体验非常差。状态机里的滞后特性进入疲劳状态需要持续高PERCLOS退出则需要恢复正常一段时间天然消除了这个问题。3.4 报警与日志界面提示、声音提醒和数据留存报警模块是系统能否落地到“驾驶员状态监测”场景的关键。我在系统里做了两级输出软提示和硬报警。软提示是画面上绘制当前状态。PERCLOS实时数值、EAR实时曲线、状态标签都叠加在视频帧上。这部分的实现很简单就是cv2.putText在指定位置画文字cv2.rectangle画框。硬报警是声音提醒。最简单的实现是调用系统自带的beep声音但实测效果一般音量小且容易被环境噪声盖过。我用了pygame.mixer播放一段预制的wav提示音声音更响亮辨识度也更高。这里提醒一句在实验室里调试时记得把电脑音量调低否则报警一响整个教研室都看你。日志模块是很多人忽略但答辩加分的点。我用的方案是import csv import time class DetectionLogger: def __init__(self, log_path): self.file open(log_path, w, newline) self.writer csv.writer(self.file) self.writer.writerow([timestamp, ear, mar, perclos, state]) def write(self, ear, mar, perclos, state): self.writer.writerow([ time.strftime(%Y-%m-%d %H:%M:%S), round(ear, 3), round(mar, 3), round(perclos, 3), state ])把检测数据落盘成CSV一方面方便事后分析算法效果另一方面可以在答辩时导出一段“某时段内EAR和PERCLOS的变化曲线”直接作为系统有效性的证据。很多同学做的系统跑完就结束了没有任何数据留存这点确实有些可惜。4. 实测中的坑光照、眼镜、口罩和摄像头角度4.1 光照不足dlib找不到脸时怎么办把系统从实验室搬到实车环境第一个遭遇战就是光照。室内灯光均匀dlib检测人脸基本没有压力。到了车内情况完全不同白天逆光时人脸要么过曝要么漆黑一片晚上只有仪表盘微弱光源时更是直接检测不到人脸。框架报错就是faces列表为空整个循环空转。我从实际测试里总结了一个处理顺序。先对灰度图做cv2.equalizeHist直方图均衡化这一步能救回大部分中度光线不足的场景。如果检测结果还是不稳定再引入一个更激进的预处理——cv2.createCLAHE(clipLimit2.0, tileGridSize(8,8))自适应直方图均衡化。相比全局均衡化CLAHE在局部对比度提升上表现更好代价是计算量稍大。光照场景处理前检测成功率处理后检测成功率推荐方案正常室内光95%98%可不处理车内逆光50%85%equalizeHist车内昏暗30%78%CLAHE夜间无补光5%20%硬件补光灯表格数据是我的真实测试结果基于40秒视频流抽样统计最后一个场景的20%成功率说明一个道理软件预处理是有极限的昏暗环境下必须上硬件红外补光这不是Python代码能解决的问题。4.2 戴眼镜引起的EAR跳变眼镜问题在我测试中出现的频率非常高特别是镜框比较粗或者有反光涂层的情况。dlib的关键点检测在强反光干扰下会偶尔把下眼睑的点“吸”到镜框边缘导致EAR突然从0.28跳变到0.12随后又恢复正常。这个跳变在单帧上不会触发报警但在滑动窗口里会把PERCLOS拉高如果窗口较短且跳变频繁就会出现“没闭眼但误报疲劳”的情况。我试过几种方法最有效的是中值滤波。不要对EAR原始值直接取平均而是把最近10帧的EAR放入一个数组取中位数作为当前有效值。中值滤波对“单点突变”的天敌——脉冲噪声——有极好的抑制效果镜框反光带来的跳变正好属于这一类噪声import numpy as np from collections import deque ear_history deque(maxlen10) def get_filtered_ear(current_ear): ear_history.append(current_ear) return float(np.median(ear_history))另一个更省事的思路是在标定阶段就把眼镜因素考虑进去。系统启动时让用户戴着眼镜完成眼睛睁眼基准采集自动把阈值下移。这样即使EAR整体偏低也能在“你自己的基线”上做判断鲁棒性会更好。4.3 口罩遮挡对打哈欠检测的影响这个问题是前两年突然冒出来的。车辆场景下驾驶员和乘客戴口罩的情况非常普遍而口罩会把整张嘴遮住MAR算法直接失效——嘴巴区域的点检测到的全是口罩的褶皱数值乱跳。我的处理策略比较简单粗暴先检测嘴部区域是否被遮挡如果被遮挡则降低哈欠检测的权重。具体判断方法是用OpenCV检测嘴部区域的颜色分布口罩通常是纯色且饱和度低而人脸皮肤有更高的肤色特征。或者干脆在MAR数值出现异常跳变时结合上半脸状态做决策。如果想把这件事做得更专业可以加上一个口罩检测分类器比如一个小型的MobileNet二分类但这已经超出疲劳检测的核心范畴。我在毕设里选择把逻辑简化为“口罩状态下只依赖PERCLOS和点头检测”系统功能依然完整。4.4 摄像头角度和帧率带来的时序偏差摄像头安装位置对角度的要求比想象中严格。我在测试中发现摄像头高度应该位于驾驶员视线略下方、正对脸部的位置如果装在车顶或侧方dlib对非正面人脸的检测稳定性会下降特别是侧脸超过30度时左右眼关键点会出现严重偏移。还有一点容易被忽略——帧率波动导致的时间窗口漂移。我之前说过PERCLOS窗口应该按时间计算而不是按帧数计算具体实现也不复杂import time class PerclosTimer: def __init__(self, window_seconds4.0): self.window_seconds window_seconds self.events deque() # 存储 (timestamp, is_closed) def add_observation(self, is_closed): now time.time() self.events.append((now, is_closed)) # 清理超时数据 while self.events and now - self.events[0][0] self.window_seconds: self.events.popleft() def get_perclos(self): total len(self.events) if total 0: return 0.0 closed sum(1 for _, is_closed in self.events if is_closed) return closed / total这个方案用时间戳保证窗口长度恒定不管帧率是15fps还是30fps统计结果都在同一个时间尺度下准确性和可比性都会好很多。5. 拓展方向与答辩准备让这套源码真正成为你的作品5.1 从dlib到MediaPipe跨平台与鲁棒性提升dlib版本能跑通但距离工程可用还有差距。如果时间充裕我建议你做一个升级版——把dlib替换成Google的MediaPipe Face Mesh。MediaPipe的优势集中在三点。第一是模型体积更小推理速度更快移动端和嵌入式设备都能流畅跑。第二是468点密集人脸网格比dlib的68点对细节描述更细腻眼睛和嘴唇的精度更高。第三是原生支持头部姿态解算不用自己调solvePnP。代价是需要额外安装mediapipe库并且MediaPipe对Python版本有一定要求环境配置会多一些坑。我之前踩过的最典型的坑是MediaPipe在Python 3.11上的安装兼容问题换到3.9环境后一步到位。如果你准备升级建议创建虚拟环境在干净的Python 3.9环境里安装避免和系统其他包冲突。5.2 多模态融合视觉之外还能融合什么真正商用的驾驶员疲劳监测系统绝对不是只看面部特征。目前批量装车的方案普遍采用“视觉行为生理”的多模态融合策略。视觉模态就是我们这套系统做的面部检测提供的是“直观证据”。行为模态看方向盘和车辆本身——方向盘转角长时间不修正、车道偏移预警频繁触发这些都是驾驶疲劳的间接信号。生理模态包括通过方向盘上的电容传感器测心率、通过座椅压力分布测身体姿态变化。对毕设来说完整实现多模态融合不现实但你可以做接口预留。比如在config.py里预留车道偏移数据的输入接口在state_machine.py里预留一个external_weight参数用于后续接入其他信号源。答辩时告诉老师“系统架构为多模态融合预留了扩展能力”比硬做半个不完整的方向盘检测要有说服力得多。5.3 答辩时老师最关心的三个问题最后聊点应景的答辩环节老师会问什么。第一个问题几乎是必问的“你的疲劳判定阈值是怎么定出来的”最忌讳的答案是“网上看的”或者“瞎试的”。正确姿势是两级回应先引PERCLOS的P80标准说明这种基于眼睑闭合比例判疲劳的方式有交通工程领域的研究支撑再讲自己做了小样本实验——找10个同学每人录制一段60秒清醒状态和一段模拟疲劳状态故意放慢眨眼频繁闭眼的视频统计两组的PERCLOS分布发现清醒组普遍低于0.25疲劳组基本高于0.4于是定下0.4这个报警阈值。哪怕样本量不大这个“有理有据有实验”的说明方式也足以证明你理解了问题。第二个问题是“实时性怎么保证”回答思路是dlib关键点检测本身很快特征计算是纯数值运算瓶颈在人脸检测。我的优化策略是控制帧处理分辨率不用1280x720做检测而是resize到640x480人脸检测速度能提升接近一倍。再加上跳帧策略——每2帧跑一次完整检测中间帧复用上一次的关键点结果可以进一步压榨性能。第三个问题是“系统有哪些不足怎么改进”这个问题考察的是自我认知能力。说实话就行弱光环境下检测率下降需要红外补光戴墨镜时眼睛特征完全丢失需要和行为信号融合打哈欠阈值对个别人不适用需要自适应标定。能说出这三个不足说明你真的测试过、思考过比硬吹“系统完美”要好得多。做这个系统给我最大的感受是疲劳检测的难点从来不在“检测闭眼”这个动作本身而在于怎样把单帧的检测结果组织成一套稳定、有生理依据、能抗干扰的时序判定逻辑。EAR和PERCLOS这些指标二十年前就有人用了但把它做成一个能在真实场景下稳定跑起来的系统中间隔着数不清的边界情况处理和参数调优。如果你正在做类似的毕业设计不用被网上那些“一行代码实现疲劳检测”的标题带偏把这里的每个模块都吃透自己动手改一版答辩的时候你就知道底气从哪来了。本文还有配套的精品资源点击获取