简介:这是一份关于基于计算机视觉的司机驾驶疲劳检测系统的学术论文PDF,面向计算机视觉、图像处理方向的研究者与学生,可用于课题调研、课程学习或项目开发参考。论文围绕人脸特征点与人眼特征点检测展开,对比了OpenCV Haar cascades与dlib两种工具的人眼定位效果,详细说明基于眼部纵横比(EAR)的疲劳判别算法:通过上下眼睑特征点距离计算眼睛张开程度,结合闭眼时间与频率判断驾驶员是否疲劳,并给出从视频流预处理到疲劳预警的完整实现流程。资源为单个PDF文件,大小约2.66MB,内容包含系统设计、算法原理、实现流程、结果分析及参考文献,可直接作为毕业设计、论文写作或相关项目开发的佐证资料。实验显示,系统检测成功率约90%,能有效识别疲劳状态。目前已有165人学习/下载,适合需要快速掌握疲劳驾驶检测技术路线、获取完整参考方案的学习者。
1. 一台摄像头就能抓疲劳:这个系统到底在解决什么
长途货运和网约车司机一天在方向盘前坐八小时以上,疲劳是安全事故里最隐蔽、也最致命的一个变量。基于计算机视觉的司机驾驶疲劳检测系统,做的正是用一颗普通摄像头盯住人脸,从眼睑开合程度、眨眼频率、打哈欠和头部姿态变化里判断司机是否进入疲劳状态,再分级报警。它不碰司机身体、不抢方向盘,是一个典型的计算机视觉落地项目,门槛不高但坑不少。新手可以用它作为计算机视觉入门后的第一个完整实战,熟手也能在阈值标定和工程化上做出可交付的产品。
2. 从摄像头到报警器:拆出系统五段链路与最小可跑通代码
2.1 五段链路与数据流向
一个完整的司机疲劳检测系统,在工程上可以拆成五段,任何一段做不好,后面的模型再强也白搭。
第一段是图像采集。车里光线变化剧烈,白天逆光、夜间只有仪表盘微光,摄像头需要具备一定的宽动态能力,或者直接配红外补光。第二段是人脸检测,也就是在画面里先找到“人脸在哪”。第三段是关键点定位,把眼睛、嘴巴、鼻尖这些位置标出来。第四段是疲劳特征提取,用关键点坐标去计算眨眼时长、PERCLOS、哈欠频率这类指标。第五段是判定与报警,把特征喂给阈值规则或分类器,输出“正常、提醒、疲劳”三级状态。
这五段链路里,真正需要模型的部分集中在前三段,后面两段反而决定了误报率。很多团队把精力花在不断换更重的人脸关键点模型上,忽略了判定逻辑,结果实车测试时被驾驶员低头看手机、打哈欠说话等正常动作搞出一堆误报,这就是没把系统当成一个整体来做。
2.2 最小可跑通代码:用 dlib 算 EAR 和 PERCLOS
环境上不复杂,Python 3.8 以上、opencv-python、dlib、numpy 就够了。至于新手纠结 visual studio code 和 pycharm 装哪个,对这个项目没有决定性影响,能跑通代码就行,我用 VS Code 多一些,因为远程调试方便。dlib 在 Windows 上需要编译,直接 pip 装经常失败,找一个预编译的 wheel 装上就行,后面会用一整个小节讲这个坑。
先写一个最简版本:检测人脸、定位关键点、计算眼睛纵横比 EAR,再用 30 帧窗口统计 PERCLOS。这版代码不涉及任何训练,跑通了就理解了这个系统的主干逻辑。
import dlib import cv2 import numpy as np # dlib 68 点关键点定义:42-47 是图像左侧那只眼睛,36-41 是右侧那只 LEFT_EYE = list(range(42, 48)) RIGHT_EYE = list(range(36, 42)) def eye_aspect_ratio(eye): # EAR 公式:纵向距离 / 横向距离,眼睛闭合时这个值会明显变小 vertical_a = np.linalg.norm(eye[1] - eye[5]) vertical_b = np.linalg.norm(eye[2] - eye[4]) horizontal = np.linalg.norm(eye[0] - eye[3]) return (vertical_a + vertical_b) / (2.0 * horizontal) detector = dlib.get_frontal_face_detector() predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat") cap = cv2.VideoCapture(0) fps = cap.get(cv2.CAP_PROP_FPS) frame_step = max(1, int(fps // 30)) # 把处理帧率压到 30fps 附近 frame_count = 0 closed_frames = 0 total_frames = 0 while True: ret, frame = cap.read() if not ret: break frame_count += 1 if frame_count % frame_step != 0: continue gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) faces = detector(gray, 0) for face in faces: landmarks = predictor(gray, face) left_eye = np.array([(landmarks.part(i).x, landmarks.part(i).y) for i in LEFT_EYE]) right_eye = np.array([(landmarks.part(i).x, landmarks.part(i).y) for i in RIGHT_EYE]) ear = (eye_aspect_ratio(left_eye) + eye_aspect_ratio(right_eye)) / 2.0 # 单帧闭眼判定,EAR 阈值先取 0.2 if ear < 0.2: closed_frames += 1 total_frames += 1 # 每 30 帧(约 1 秒)计算一次 PERCLOS if total_frames == 30: perclos = closed_frames / 30.0 if perclos > 0.2: print("疲劳预警: PERCLOS=%.2f" % perclos) closed_frames = 0 total_frames = 0 if cv2.waitKey(1) & 0xFF == ord("q"): break cap.release() cv2.destroyAllWindows()这段代码每一步都有讲究。detector 用的是 HOG 特征的人脸检测器,CPU 上单帧几十毫秒;predictor 负责定位 68 个关键点。EAR 的本质是把眼睛的“张开程度”变成一个几乎与人脸尺度无关的比值,因为计算公式里做了归一化,摄像头远近变化对结果影响很小。PERCLOS 是疲劳检测领域最经典的指标,定义是“闭眼帧数占总帧数的比例”,这里用 30 帧做窗口是因为 PERCLOS 统计本身就需要一个短时间窗,太短了单次眨眼的偶然性太大,太长了报警滞后明显。
注意这一版没有做任何平滑,画面抖动、司机转头、光线突变都会让 EAR 剧烈波动,所以它只能叫“最小可跑通”,离“能用”还差一个阈值标定和时序逻辑的距离。
2.3 为什么第一版用 dlib 而不是直接上 YOLO 和 CNN
很多第一次做计算机视觉项目的人,看到“疲劳检测”第一反应是“我要训一个 CNN 分类器”,这其实是一个方向误区。人脸检测本质上就是目标检测的一个子问题,YOLO 确实更现代,但在这个场景里,dlib 的 HOG 检测器配合 68 点关键点定位在 CPU 上就能跑实时,而 YOLOv8 的人脸检测加关键点版本在同等算力下帧率可能只有它的一半。
更关键的是,疲劳判定需要的关键特征——眼睑开合、嘴巴开合——对关键点的精度要求远高于对分类精度的要求。dlib 的 68 点关键点坐标足够稳定,计算 EAR 的微小抖动可以通过时序滤波消化。如果把任务换成“判断手是否离开方向盘”或者“识别司机是否在抽烟”,那才值得上 YOLO 这类检测模型。
第一版用 dlib 还有一层原因:它把特征提取变成了一个透明的几何计算过程。EAR 低于 0.2 就是闭眼,这个 0.2 是能解释、能调的;而 CNN 分类器输出的“疲劳概率”,你很难解释它到底学到了什么,改阈值也常常像在调一个黑匣子。我的经验是,第一版系统越透明越好,等你把数据、阈值、报警策略都跑顺了,再决定要不要把关键点检测替换成深度学习版本也不迟。斯坦福 cs231n 课程里那些 CNN 结构可以当作后置升级的参考,但别让模型升级打乱你验证整套链路的主线。
3. 疲劳特征与阈值标定:PERCLOS 之外,疲劳判定还能怎么融合
3.1 四个疲劳特征与参数表
PERCLOS 单独用,误报率会比较高。一个司机正常眨眼 15 次/分钟,眼皮偶尔耷拉一下并不代表疲劳;反过来,有人天生眼睛小,EAR 一直比常人低,单阈值很容易把他判定成“一直闭眼”。所以成熟方案一般至少融合四个特征。
| 特征 | 计算方式 | 疲劳信号 | 典型参数范围 | 弱点 |
|---|---|---|---|---|
| PERCLOS | 闭眼帧数 / 窗口总帧数 | 长时间眼睑闭合 | 窗口 30~60 秒,阈值 0.15~0.3 | 正常眨眼也计入 |
| 眨眼频率与时长 | 单位时间眨眼次数、单次闭眼时长 | 疲劳时眨眼变慢变长 | 单次闭合超 400~500ms 记一次微睡眠 | 对睁眼发呆不敏感 |
| 打哈欠频率 | 嘴巴张开程度的比例与持续时间 | 困倦前兆 | MAR 大于 0.5 且持续 1 秒以上计一次 | 说话、吃东西误报 |
| 头部姿态/点头 | 鼻尖点相对基准线的垂直位移 | 疲劳点头、瞌睡 | 500ms 内低头超过阈值 | 司机主动看仪表盘/导航时误报 |
这四个特征里,PERCLOS 和眨眼时长是核心,哈欠和头部姿态是辅助。辅助特征的作用不是独立报警,而是给主特征“加分”:当 PERCLOS 已经高于阈值但还没到报警线时,如果同时检测到打哈欠或者点头,就把疲劳分数拉高一个等级。这种设计能有效压低报警延迟,不至于非要等 30 秒统计窗口走完才报警。
3.2 指导性判定:把 EAR、哈欠、点头做成一个状态机
把多特征融合起来,我一般不用机器学习分类器,而是写一个显式状态机,每一帧更新状态,输出分级报警。原因很简单:规则是透明的,出误报时你能马上知道是哪条规则触发的;神经网络融合效果未必更好,还凭空多了一个调参的黑匣子。
下面是一个可直接参考的判定逻辑框架:
class FatigueStateMachine: def __init__(self): self.microsleep_count = 0 # 一个统计周期内的微睡眠次数 self.yawn_count = 0 # 一个统计周期内的哈欠次数 self.eye_close_start = None # 单次闭眼开始时间 self.last_event_time = 0 # 参数表,按实际标定结果调整 self.ear_close_thr = 0.2 self.eye_close_timeout = 0.5 # 单次闭眼超过 0.5s 记一次微睡眠 self.microsleep_win = 120 # 统计窗口 120 秒 self.microsleep_alarm = 2 # 窗口内 2 次微睡眠触发疲劳 self.yawn_mar_thr = 0.5 self.yawn_duration = 1.0 self.nod_distance_thr = 0.08 # 归一化头部下沉距离 def update(self, ear, mar, head_y, head_ref, now): # 单帧闭眼与微睡眠计时 if ear < self.ear_close_thr: if self.eye_close_start is None: self.eye_close_start = now else: if self.eye_close_start is not None: duration = now - self.eye_close_start if duration > self.eye_close_timeout: self.microsleep_count += 1 self.eye_close_start = None # 哈欠判定:嘴巴持续张开超过 1 秒 if mar > self.yawn_mar_thr: if now - self.last_yawn_start > self.yawn_duration: self.yawn_count += 1 # 头部下沉判定:鼻尖纵坐标相对基准明显下移 nod_ratio = (head_y - head_ref) / head_ref if head_ref else 0 if nod_ratio > self.nod_distance_thr: self.head_nod_count += 1 # 疲劳等级输出 fatigue_score = 0 if self.microsleep_count >= self.microsleep_alarm: fatigue_score += 2 if self.yawn_count >= 2: fatigue_score += 1 if self.head_nod_count >= 3: fatigue_score += 1 return fatigue_score # 0 正常,1 提醒,2 疲劳,3 强疲劳状态机的核心价值在于“时序”二字。单帧 EAR 小于 0.2 只能说明这一帧闭眼了,可能是正常眨眼;连续 0.5 秒闭眼才是微睡眠信号。同理,哈欠和点头都要有持续时间约束,否则司机说话张口、低头看挡位都会触发误报。
这里的每个参数都是可调的,而且不同驾驶员差异很大。ear_close_thr 建议按人标定:让司机正常平视前方 60 秒,取 EAR 均值的 60% 作为阈值。封闭窗口不宜太长,120 秒是经验值,太短会导致偶发闭眼直接触发报警,太长则失去预警意义。head_ref 是标定时记下的司机正常驾驶姿态下的参考值,简单方案用鼻尖点在第 30 帧到第 60 帧的均值。
3.3 阈值标定的玄学:为什么论文阈值不能直接抄
论文里给出的阈值,比如 PERCLOS 0.15、EAR 0.2,只能作为起点,不能直接抄到实车系统里。原因有三层。
第一层是传感器差异。论文实验通常用固定分辨率的工业相机,画面构图稳定;车载摄像头安装角度、分辨率、焦距都不一样,同一个 EAR 值反映的闭眼程度可能完全不同。第二层是人的差异。有些人天生睑裂小,清醒状态下 EAR 就在 0.18 附近;有些人眼皮厚,疲劳时 EAR 也不一定掉到 0.2 以下,单靠绝对阈值很容易误判。第三层是驾驶行为干扰。司机正常驾驶时会频繁看后视镜,头部转动让关键点检测产生偏移,眼睛区域的关键点位置会抖动,哪怕眼睛根本没闭,EAR 也可能瞬时低于阈值。
应对方案是两件事。一是给每个司机做“清醒基线”标定,上车后前 60 秒采集正常驾驶状态下的 EAR、MAR、头部姿态分布,用基线的分位数动态调整阈值。二是把阈值扫描放到自己的验证集上去做,而不是拿论文的结论硬套。这正好也解释了计算机视觉和机器学习区别于边界上容易被忽略的一点:机器学习关心的是模型在数据分布上的泛化,而计算机视觉项目还需要关心传感器安装位置、光学参数、人的个体差异对数据分布本身的影响。疲劳检测不是拿来一个模型调参就好用的视觉分类问题,它是一个“观测系统标定”问题。
4. 夜间、墨镜、颠簸:真实驾驶场景的五大踩坑排查手记
这一章的素材来自实车测试中被反复摩擦的五个问题,每条都按“现象 → 原因 → 解决”的顺序写。它们的共同特点是:在办公室对着屏幕跑永远发现不了。
4.1 夜间场景让检测集体翻车
现象:晚上在车内打开系统,画面里司机脸部一片死黑,人脸检测框时有时无,EAR 曲线疯狂上下跳。
原因:普通 RGB 摄像头的动态范围在车大灯、路灯、仪表盘混在一起的光线下根本不够用。摄像头自动增益一拉高,背景亮的地方过曝、脸的地方还是黑的。dlib 的 HOG 检测器对光照极其敏感,脸部对比度不足时直接漏检。
解决:换带红外补光的摄像头,850nm 或 940nm 红外 LED 阵列把脸照亮,然后用红外图像做人脸检测和关键点定位。此时摄像头需要支持自动切换或直接固定使用 IR 模式,同时关闭自动白平衡,避免红外画面被算法“纠正”成奇怪的偏色图。另外,增益上限要卡住,不能让传感器在夜间无限制提亮,否则拖影会毁掉关键点定位。
4.2 太阳镜和反光让关键点跑到镜框上
现象:司机戴太阳镜时,关键点经常漂移到镜框边缘,EAR 随之暴跌,系统疯狂误报“闭眼”。
原因:可见光方案下,太阳镜片反射的是周围环境的光,镜片上可能出现高亮区域,关键点检测器把镜框或高光边缘当成眼睛特征。红外方案下,部分太阳镜会滤掉红外光,眼睛区域在画面里直接变成黑色,关键点同样定位失败。
解决:优先选 940nm 红外光源,这种波长对很多深色太阳镜的穿透力比 850nm 好一些,但并不能保证覆盖所有镜片,所以另一个关键动作是加“关键点可信度约束”。简单做法是用脸框的相对坐标校验:眼睛关键点必须落在脸框上半部的合理区域内,超出区域即判定为“关键点丢失”,此时不更新眨眼状态,只标记该帧无效。系统宁可漏掉一帧,也不能把一个错误坐标计入闭眼统计。
4.3 颠簸路面上的关键点抖动
现象:车辆经过减速带或烂路时,摄像头随车身颠簸,人脸检测框小幅跳动,眼睛关键点每帧上下抖动 3~5 个像素,眨眼检测的误触发率明显上升。
原因:dlib 的关键点检测是逐帧独立的,没有任何时序先验。车身震动让同一双眼睛在不同帧里的坐标出现高频噪声,EAR 对这种噪声非常敏感,因为它的计算用的是纵向距离,抖动会直接放大。
解决:给 EAR 序列加一个轻量平滑,一阶指数平滑就够,EMA 半衰期设置在 100~150ms 左右。公式很简单:ear_smooth = alpha * ear_current + (1 - alpha) * ear_smooth。alpha 取 0.1 到 0.2 之间。同时可以把“闭眼判定”从单帧阈值改为连续 N 帧低于阈值,比如连续 4 帧 EAR < 0.2 才认定闭眼开始,这样单帧抖动就不会轻易触发状态翻转。代价是闭眼判定的延迟增加若干帧,但换来的是误报大幅下降。
4.4 帧率不足导致眨眼被直接吞掉
现象:在低算力设备上跑,系统只有 12~15fps,眨眼检测几乎失灵,单次眨眼需要 150~250ms,在 15fps 下只有 2~3 帧,如果这 2 帧恰好在处理间隔中被跳过,这一次眨眼就完全消失了。
原因:处理速度跟不上采集速度,丢帧发生在最不应该丢的地方。很多人以为降低分辨率就能解决,实际上人脸关键点检测在内部分辨率 320x240 时就够用,真正的瓶颈是每帧都要跑一次人脸检测。
解决:先做人脸框跟踪,而不是每帧全图检测。第一帧用 dlib 检测人脸,之后在上一帧人脸框周围扩大 1.5 倍作为 ROI,只在 ROI 内做关键点检测。如果连续 30 帧检测到的人脸位置都没大变化,就隔 5 帧做一次全图检测防止跟丢。另一种方案是按时间戳而不是帧数统计眨眼时长:记录每帧的时间戳,EAR 低于阈值的实际毫秒数用时间差计算,这样即使帧率波动,眨眼时长的统计依然可信。用时间窗口替代帧窗口,是疲劳检测工程化里最容易被忽略的一步。
4.5 实验室数据的“假阳性”幻觉
现象:用公开数据集测试,疲劳检测准确率 93%,装上实车跑一趟,半小时内误报 7 次,驾驶员直接关闭系统。
原因:公开数据集的摄像头视角、光照条件、受试者动作范围都高度受控。实车场景里的低头看导航、单手调整空调、和乘客聊天时侧头,这些“非疲劳但很像疲劳”的动作在数据集中几乎没有。模型或规则在真实分布上泛化不佳,产生系统性误报。
解决:从项目第一天就建自己的实车数据库,哪怕只有三个人、每段 10 分钟的视频,也比公开数据集更能暴露问题。录制时把场景打散:白天/夜间/地下车库、戴/不戴眼镜、平路/颠簸路。跑通系统后先做“负样本轰炸测试”,故意做看手机、打哈欠、扭头说话等正常动作,统计误报率,再针对高频误报场景加规则过滤。
5. 把系统从“demo 能跑”变成“可信检测”:验证与阈值选优
5.1 录自己的数据:一份带时间戳的标注样例
验证这套系统,需要一份属于你自己的带标注视频数据。录制时用与实车相同的摄像头位置,固定在仪表盘上方或后视镜附近,录制 10 到 20 分钟视频,让司机按规定动作执行:正常驾驶、频繁眨眼、闭眼 1 秒以上、打哈欠、低头看导航、和乘客说话。
标注格式我习惯用最简单的时间段 CSV,每一行表示一个事件:
start_ms,end_ms,label 12000,13500,microsleep 28000,32000,yawn 40500,42000,look_down标注的关键是“事件级”而不是“帧级”。因为验证 PERCLOS 这类窗口统计指标时,你关心的是某个 30 秒窗口内是否发生了疲劳事件,而不是某一帧是否闭眼。写一个 20 行左右的脚本,把视频按 1 秒一段切窗,窗口内包含 microsleep 或 yawn 事件就标记为 1,否则为 0。后面阈值扫描时用这个窗口级标签就够了。
5.2 三个指标先定死:准确率、误报率、端到端延迟
评估一个疲劳检测系统,不能只看准确率。以下几个指标要一起看:
| 指标 | 含义 | 合格线 | 说明 |
|---|---|---|---|
| 疲劳检出率(TPR) | 实际疲劳事件中成功报警的比例 | > 90% | 漏报是安全底线 |
| 误报率(FPR) | 正常驾驶中被错误报警的比例 | < 1 次/小时 | 过高会被司机放弃使用 |
| 报警延迟 | 疲劳事件开始到报警输出的时间差 | < 3 秒 | 延迟越大越失去预警意义 |
一个常见的评价陷阱是只算窗口级准确率。如果视频中疲劳事件只有 5%,一个永远不报警的系统准确率是 95%,看上去很好,但它没有任何价值。所以重点看 TPR 和误报率的组合,阈值扫描的价值也在这里。
5.3 阈值扫描:从 0.05 扫到 0.5 找 PERCLOS 最优点
把标注好的窗口标签和系统输出的 PERCLOS 分数放进同一份数组,然后用最简单的方法扫阈值:
import numpy as np # labels 窗口级标签,0 正常,1 疲劳 # scores 对应窗口的 PERCLOS 分数 labels = np.load("labels.npy") scores = np.load("perclos_scores.npy") best = (0, 0, 0) # (youden_index, thr, acc) for thr in np.arange(0.05, 0.51, 0.05): pred = (scores >= thr).astype(int) tp = ((pred == 1) & (labels == 1)).sum() fn = ((pred == 0) & (labels == 1)).sum() fp = ((pred == 1) & (labels == 0)).sum() tn = ((pred == 0) & (labels == 0)).sum() tpr = tp / (tp + fn) if (tp + fn) else 0 fpr = fp / (fp + tn) if (fp + tn) else 0 youden = tpr - fpr acc = (tp + tn) / len(labels) print("thr=%.2f acc=%.3f tpr=%.3f fpr=%.3f youden=%.3f" % (thr, acc, tpr, fpr, youden)) if youden > best[0]: best = (youden, thr, acc) print("最佳阈值: %.2f, Youden=%.3f, acc=%.3f" % (best[1], best[0], best[2]))Youden 指数(TPR - FPR)是挑阈值时最简单有效的准则,它同时兼顾了漏报和误报,适合安全类系统。如果你的业务更偏重“宁可误报也不能漏报”,就把目标函数改成 tpr * 0.8 + (1 - fpr) * 0.2 这类加权分数,然后从扫描结果里挑最高分对应的阈值。
这个脚本看起来简单,但有一个隐藏前提:scores 需要用“真实运行时的输出”,而不是离线重算的分数。我踩过坑,离线用完整视频帧逐帧算 PERCLOS,和人脸检测在低帧率下跑出来的分数差异很大,因为离线重算的帧率远高于实时系统。所以疲劳检测系统在阈值选优时,一定要记录线上运行时的 PERCLOS 日志,否则你优化的是一条线上根本不存在的曲线。
6. 进阶:从能跑出检测到扛得住八小时驾驶
一个能过验证的系统,离真正装车还有一段距离。最后这一段讲两个我后来才想明白的细节。
第一个是报警分级与状态复位。疲劳报警触发后,不能每 30 秒循环报警,否则司机会直接关掉系统。正确做法是:一级“提醒”只亮灯和低频提示音;二级“疲劳”加入语音提醒;三级“强疲劳”才触发持续报警并要求停车。每次报警后进入 3 分钟的静默期,期间只累计分数不重复报警。另外要接入车速信号,车速低于 20km/h 时把头部姿态检测灵敏度降低,因为低速时司机频繁看后视镜、看导航,误报率天然高。
第二个是摄像头安装的几何偏置问题。很多人以为摄像头装哪都一样,实际差异非常大。推荐位置是仪表盘正上方、挡风玻璃下沿,俯仰角向下 10~15 度,这样既能拍到正脸,又能避免方向盘和驾驶员手臂遮挡。如果装在 A 柱或后视镜侧面,人脸偏转角度大,关键点检测精度会明显下降。几何偏置不是玄学,它直接改变系统看到的数据分布,进而影响阈值;换个安装位置,之前扫描出来的最优阈值就可能不再成立。
我自己的习惯是,在正式交付前会专门做一轮“负样本轰炸”:让司机连续做低头看手机、打哈欠、大笑、揉眼睛、戴墨镜五个动作,把误报次数一条条记下来,每一个误报都要能解释出是哪个特征触发的,解释不了就加规则。这套负样本集最终和正样本同样重要,系统上线后的每一次阈值调整都会重跑一遍它。疲劳检测说到底不是一个模型精度问题,而是一个“在真实驾驶环境里稳定区分疲劳和正常动作”的工程问题,先把这条底线守住了,再谈更花哨的模型升级也不迟。这套流程走下来,你的系统才真正算能扛住八小时方向盘的考验。
希望这些从摄像头、阈值到排查的实战经验,能帮你在做基于计算机视觉的司机疲劳检测系统时少走一段弯路。
本文还有配套的精品资源,点击获取