当前位置: 首页 > news >正文

从特征到应用:用 dlib+OpenCV 实现实时疲劳检测(基于眼睛纵横比)

在前两篇内容中,我们完成了人脸检测、68 点关键点定位与面部特征可视化。本文将基于这些技术,聚焦一个实用场景 ——实时疲劳检测,通过计算眼睛纵横比(EAR)判断用户是否闭眼,并在持续闭眼时触发警报,可应用于驾驶安全、学习专注度监控等场景。

一、核心原理:眼睛纵横比(EAR)如何判断闭眼?

疲劳检测的核心逻辑是通过眼睛开合状态判断是否困倦,而眼睛纵横比(Eye Aspect Ratio,简称 EAR)是衡量眼睛开合程度的经典指标,由研究者 Tereza Soukupová 在 2016 年提出。

1. EAR 的计算逻辑

眼睛的 6 个关键点(如右眼 36-41、左眼 42-47)分布如下图所示,EAR 通过计算 “垂直方向距离之和” 与 “水平方向距离” 的比值,量化眼睛的开合程度:

          1    2\  /
0 ----------+---------- 3/  \5    4
  • 垂直距离:关键点 1 与 5 的距离(A)、关键点 2 与 4 的距离(B);
  • 水平距离:关键点 0 与 3 的距离(C);
  • EAR 公式EAR = (A + B) / (2 * C)

2. EAR 的判断标准

  • 睁眼状态:EAR 值通常在 0.25~0.35 之间(具体数值需根据摄像头距离、人脸大小微调);
  • 闭眼状态:EAR 值会急剧下降至 0.2 以下,甚至接近 0;
  • 疲劳判断:若 EAR 持续低于阈值(如 0.3)超过一定帧数(如 50 帧,约 1 秒,因摄像头通常为 30~60 帧 / 秒),则判定为疲劳状态。

二、完整代码实现与逐模块解析

1. 完整代码

import numpy as np
import dlib
import cv2
from sklearn.metrics.pairwise import euclidean_distances  # 计算欧式距离
from PIL import Image, ImageDraw, ImageFont  # 处理中文显示# 模块1:计算眼睛纵横比(EAR)
def eye_aspect_ratio(eye):# 计算垂直方向两个距离:A(1-5)、B(2-4)A = euclidean_distances(eye[1].reshape(1, 2), eye[5].reshape(1, 2))B = euclidean_distances(eye[2].reshape(1, 2), eye[4].reshape(1, 2))# 计算水平方向距离:C(0-3)C = euclidean_distances(eye[0].reshape(1, 2), eye[3].reshape(1, 2))# 计算EAR(避免除以0,加微小值)ear = ((A + B) / 2.0) / (C + 1e-6)return ear# 模块2:OpenCV添加中文(解决cv2.putText不支持中文的问题)
def cv2AddChineseText(img, text, position, textColor=(255, 0, 0), textSize=50):if isinstance(img, np.ndarray):  # 若为OpenCV的numpy数组格式,转换为PIL图像img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))draw = ImageDraw.Draw(img)  # 创建绘图对象# 加载中文字体(需确保系统有simsun.ttc字体,Windows默认有,Linux/mac需手动安装)fontStyle = ImageFont.truetype("simsun.ttc", textSize, encoding="utf-8")draw.text(position, text, textColor, font=fontStyle)  # 绘制中文return cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)  # 转回OpenCV格式# 模块3:绘制眼睛凸包(可视化眼睛区域)
def drawEye(eye, frame):eyeHull = cv2.convexHull(eye)  # 计算眼睛关键点的凸包cv2.drawContours(frame, [eyeHull], -1, (0, 255, 0), -1)  # 填充式绘制凸包(绿色)# 模块4:初始化与实时检测
if __name__ == "__main__":COUNTER = 0  # 闭眼持续帧数计数器EAR_THRESHOLD = 0.3  # EAR阈值(低于此值判定为闭眼)FATIGUE_FRAMES = 50  # 疲劳判定帧数(持续闭眼超过此帧数触发警报)# 初始化人脸检测器与关键点预测器detector = dlib.get_frontal_face_detector()predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")# 初始化摄像头(0表示默认摄像头,外接摄像头可改为1)cap = cv2.VideoCapture(0)# 设置摄像头分辨率(可选,根据硬件调整)cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)while True:# 读取一帧图像ret, frame = cap.read()if not ret:  # 摄像头读取失败(如未连接),退出循环print("Error: 无法读取摄像头图像")break# 水平翻转图像(可选,解决摄像头镜像问题,更符合人眼习惯)frame = cv2.flip(frame, 1)# 1. 检测人脸faces = detector(frame, 0)  # 0表示不上采样,平衡速度与精度# 2. 遍历每个人脸,处理眼睛关键点for face in faces:# 2.1 预测68点关键点shape_dlib = predictor(frame, face)shape = np.array([[p.x, p.y] for p in shape_dlib.parts()])# 2.2 提取左右眼关键点(索引对应68点分布)right_eye = shape[36:42]  # 右眼:36-41(左闭右开)left_eye = shape[42:48]   # 左眼:42-47(左闭右开)# 2.3 计算左右眼EAR,并取平均值(减少单眼误差)right_ear = eye_aspect_ratio(right_eye)left_ear = eye_aspect_ratio(left_eye)avg_ear = (right_ear + left_ear) / 2.0  # 最终EAR值# 2.4 绘制眼睛凸包(可视化,便于观察检测效果)drawEye(right_eye, frame)drawEye(left_eye, frame)# 2.5 疲劳判断逻辑if avg_ear < EAR_THRESHOLD:COUNTER += 1  # 闭眼帧数+1# 持续闭眼超过FATIGUE_FRAMES,触发警报if COUNTER >= FATIGUE_FRAMES:frame = cv2AddChineseText(frame, "!!!危险:请勿疲劳用眼!!!", position=(200, 200), textColor=(0, 0, 255),  # 红色警报文字textSize=40)else:COUNTER = 0  # 睁眼时,计数器清零# 2.6 在图像上显示当前EAR值(便于调试阈值)ear_text = f"EAR: {avg_ear[0][0]:.2f}"  # 格式化EAR值(保留2位小数)frame = cv2AddChineseText(frame, ear_text, position=(50, 50), textSize=30)# 3. 显示实时画面cv2.imshow("Real-Time Fatigue Detection", frame)# 4. 退出逻辑:按下ESC键(ASCII码27)关闭窗口if cv2.waitKey(1) & 0xFF == 27:break# 释放资源(摄像头+窗口)cap.release()cv2.destroyAllWindows()

2. 核心模块解析

(1)eye_aspect_ratio():EAR 计算函数

该函数是疲劳检测的 “核心算法”,关键逻辑如下:

  • 欧式距离计算:使用sklearn.metrics.pairwise.euclidean_distances计算两点间距离(也可手动用np.linalg.norm(eye[1]-eye[5])实现,效果一致);
  • 维度处理eye是形状为 (6,2) 的 numpy 数组(6 个关键点,每个点含 x/y 坐标),需用reshape(1,2)将单个点转换为 (1,2) 的二维数组,适配euclidean_distances的输入要求;
  • 防除零处理:在分母C后加1e-6(极小值),避免因水平距离为 0 导致程序报错。
(2)cv2AddChineseText():中文显示函数

OpenCV 的cv2.putText()不支持中文字体,需通过 PIL 库转换图像格式实现中文显示,关键步骤:

  1. 格式转换:将 OpenCV 的 BGR 格式 numpy 数组,转为 PIL 的 RGB 格式图像;
  2. 字体加载:使用系统自带的 “宋体” 字体(simsun.ttc),Windows 默认路径为C:/Windows/Fonts/simsun.ttc,Linux/mac 需手动安装该字体并指定路径(如/Library/Fonts/simsun.ttc);
  3. 格式转回:绘制中文后,再将 PIL 图像转回 OpenCV 的 BGR 格式,确保后续处理正常。
(3)实时检测逻辑
  • 摄像头初始化cv2.VideoCapture(0)调用默认摄像头,cap.set()可调整分辨率(越高越清晰,但占用资源越多);
  • 镜像翻转cv2.flip(frame, 1)实现水平翻转,解决摄像头 “左右颠倒” 的问题,让操作更自然;
  • 疲劳判定COUNTER记录持续闭眼帧数,当COUNTER >= FATIGUE_FRAMES时,用红色中文显示警报信息,直观提醒用户。

三、参数调试与效果优化

1. 关键参数调整

代码中的两个核心参数需根据实际场景微调,以达到最佳检测效果:

参数名称作用调整建议
EAR_THRESHOLDEAR 阈值,低于此值判定为闭眼- 若频繁误判 “睁眼为闭眼”:适当降低阈值(如 0.28);- 若频繁漏判 “闭眼为睁眼”:适当提高阈值(如 0.32);- 建议在光线充足环境下调试,先记录睁眼时的 EAR 值,取其 0.8~0.9 倍作为阈值。
FATIGUE_FRAMES疲劳判定帧数,超过此帧数触发警报- 摄像头为 30 帧 / 秒时:50 帧≈1.7 秒,适合驾驶场景(反应稍快);- 摄像头为 60 帧 / 秒时:100 帧≈1.7 秒,需按帧率等比例调整;- 若希望更灵敏:减少帧数(如 30 帧≈1 秒);若避免误报:增加帧数(如 80 帧≈2.7 秒)。

2. 环境优化建议

  • 光线条件:避免逆光或过暗环境,光线不足会导致关键点检测偏差,建议在室内正常照明或室外阴影处使用;
  • 人脸姿态:确保人脸正面朝向摄像头,侧脸或低头时眼睛关键点可能检测失败,可配合 “人脸跟踪” 功能优化(后续文章会介绍);
  • 硬件性能:若画面卡顿,可降低摄像头分辨率(如 800x600),或减少detector的上采样次数(如保持detector(frame, 0),不改为 1)。

五、总结

本文从 “理论原理” 到 “代码实现”,完整讲解了基于 EAR 的实时疲劳检测系统,核心是将眼睛关键点的几何特征(EAR)转化为实用的状态判断指标。相比传统的 “基于像素亮度的闭眼检测”,该方法抗干扰能力更强,精度更高,且可移植到嵌入式设备(如树莓派),实现更广泛的应用。

至此,我们已完成从 “人脸检测→关键点定位→特征可视化→实用应用” 的完整技术链。后续文章将继续探索更复杂的计算机视觉场景,如人脸识别、表情分类等,敬请关注!

http://www.dtcms.com/a/409955.html

相关文章:

  • 【买机器人,上BFT】北京大学联合项目 论文解读 | H2R:一种用于机器人视频预训练的人机数据增强技术
  • 音频库管理在数字媒体中的应用探索
  • BLE配网协议 - 分包、组包和重传功能指南
  • MediaHuman YouTube to MP3 多语便携版:高效音频提取工具
  • Java 数组的定义与使用
  • 自建站有哪些站点wordpress前台压缩图片
  • R语言中的获取函数与替换函数
  • 微前端中iframe集成方式与使用微前端框架方式对比
  • 【NOI】在信奥赛中 什么是函数交互题?
  • 电脑上怎么做网站963中华室内设计网
  • Python调用优云智算安装的ComfyUI服务器
  • ACA云原生工程师认证知识点脑图
  • 用 Flink 打造事件驱动流式应用从 DataStream 到 ProcessFunction
  • MySQL学习笔记05:MySQL 索引原理与优化实战指南
  • 【提示工程】Ch2(续)-提示技术(Prompt Technique)
  • 嵌入式软件知识点汇总(day2)
  • QT中QStackedWidget控件功能及应用
  • 网络爬虫(上)
  • 论文精读(六):微服务系统服务依赖发现技术综述
  • 农业推广网站建设企业商城网站建设价格
  • 教师做班级网站手机网站打开微信号
  • 司法审计师:在数字与法律之间行走的“侦探”
  • google drive 怎么断点续传下载?
  • 基于STM32单片机的温湿度臭氧二氧化碳检测OneNET物联网云平台设计
  • LeetCode 面试经典 150_哈希表_快乐数(45_202_C++_简单)(哈希表;快慢指针)
  • K8S部署的ELK分片问题解决,报错:unexpected error while indexing monitoring document
  • Atlas Mapper 教程系列 (7/10):单元测试与集成测试
  • 众智FlagOS 1.5发布:统一开源大模型系统软件栈,更全面、AI赋能更高效
  • 理解 mvcc
  • 【网络编程】TCP 粘包处理:手动序列化反序列化与报头封装的完整方案