【深度学习】dlib 人脸关键点实时疲劳检测

文章目录


完整代码一览

c 复制代码
import numpy as np
import dlib
import cv2
from sklearn.metrics.pairwise import euclidean_distances
from PIL import Image, ImageDraw, ImageFont

# 计算眼睛EAR纵横比
def eye_aspect_ratio(eye):
    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 = euclidean_distances(eye[0].reshape(1,2), eye[3].reshape(1,2))
    ear = ((A + B) / 2.0) / C
    return ear

# 绘制中文文字
def cv2AddChineseText(img, text, position, textColor=(255, 0, 0), textSize=50):
    if isinstance(img, np.ndarray):
        img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
    draw = ImageDraw.Draw(img)
    fontStyle = ImageFont.truetype("simhei.ttf", textSize, encoding="utf-8")
    draw.text(position, text, textColor, fontStyle)
    return cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)

# 绘制眼睛轮廓
def drawEye(eye):
    eyeHull = cv2.convexHull(eye)
    cv2.drawContours(frame, [eyeHull], -1, (0, 255, 0), 2)

COUNTER = 0

detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")
cap = cv2.VideoCapture(0)

while True:
    ret, frame = cap.read()
    if not ret:
        break
    faces = detector(frame, 0)
    for face in faces:
        shape = predictor(frame, face)
        shape = np.array([[p.x, p.y] for p in shape.parts()])
        rightEye = shape[36:42]
        leftEye = shape[42:48]
        rightEAR = eye_aspect_ratio(rightEye)
        leftEAR = eye_aspect_ratio(leftEye)
        ear = (leftEAR + rightEAR) / 2.0

        if ear < 0.3:
            COUNTER += 1
            if COUNTER >= 30:
                frame = cv2AddChineseText(frame, "!!!!危险!!!!", (250,250))
        else:
            COUNTER = 0

        drawEye(leftEye)
        drawEye(rightEye)
        info = "EAR: {:.2f}".format(ear[0][0])
        frame = cv2AddChineseText(frame, info, (0,30))

    cv2.imshow("Frame", frame)
    if cv2.waitKey(1) == 27:
        break

cap.release()
cv2.destroyAllWindows()

环境准备与模型下载

c 复制代码
import numpy as np
import dlib
import cv2
from sklearn.metrics.pairwise import  euclidean_distances #计算欧氏距离
from PIL import Image, ImageDraw, ImageFont

dlib安装可以看博主的另外一篇文章了解,本文不过多赘述:

【深度学习】dlib 人脸关键点检测

核心原理:眼睛纵横比(EAR)

EAR(Eye Aspect Ratio)是衡量眼睛张开程度的一个指标。

  • 上眼睑左点:A
  • 上眼睑右点:B
  • 右眼角:C
  • 下眼睑右点:D
  • 下眼睑左点:E
  • 左眼角:F

EAR = (||A-E|| + ||B-D||) / (2 * ||F-C||)

分子是两只眼睛垂直方向的平均距离(约等于眼睛高度),

分母是水平方向的距离(约等于眼睛宽度)。

当眼睛睁开时,EAR 值较大(约 0.25~0.3);当眼睛闭合时,EAR 急剧减小(接近 0)。我们设定一个阈值(如 0.3),即可判断睁闭眼。

逐行代码详解

导入库

c 复制代码
import numpy as np #数组操作
import dlib #人脸检测
import cv2 #图像处理
from sklearn.metrics.pairwise import euclidean_distances #计算欧氏距离
from PIL import Image, ImageDraw, ImageFont

EAR 计算函数

c 复制代码
def eye_aspect_ratio(eye):
    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 = euclidean_distances(eye[0].reshape(1,2), eye[3].reshape(1,2))
    ear = ((A + B) / 2.0) / C
    return ear

EAR公式:(A+B)/(2*C)

eye 是包含 6 个点坐标的 NumPy 数组,形状为 (6, 2)。

按照 dlib 的索引顺序,0 和 3 是水平两端,1和5、2和4 是垂直两对。

计算三组距离,代入公式得到 EAR。注意返回的 ear 是一个 (1,1) 数组,后面取值时需用 ear00。

中文绘制函数

c 复制代码
def cv2AddChineseText(img, text, position, textColor=(255, 0, 0), textSize=50):
    if isinstance(img, np.ndarray):
        img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
    draw = ImageDraw.Draw(img)
    fontStyle = ImageFont.truetype("simhei.ttf", textSize, encoding="utf-8")
    draw.text(position, text, textColor, fontStyle)
    return cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)

将 OpenCV 的 BGR 图像转为 PIL 的 RGB 图像,绘制文字后再转回 BGR。

绘制眼睛轮廓

c 复制代码
def drawEye(eye):
    eyeHull = cv2.convexHull(eye)
    cv2.drawContours(frame, [eyeHull], -1, (0, 255, 0), 2)

cv2.convexHull 计算眼睛点的凸包,然后绘制绿色轮廓线,用于直观显示眼睛区域。

主程序

c 复制代码
COUNTER = 0
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")
cap = cv2.VideoCapture(0)

COUNTER 用于统计连续闭眼帧数。

初始化检测器、预测器,打开默认摄像头。

主循环

c 复制代码
while True:
    ret, frame = cap.read()
    if not ret:
        break
    faces = detector(frame, 0)

逐帧读取,检测人脸。

c 复制代码
    for face in faces:
        shape = predictor(frame, face)
        shape = np.array([[p.x, p.y] for p in shape.parts()])
        rightEye = shape[36:42]
        leftEye = shape[42:48]
        rightEAR = eye_aspect_ratio(rightEye)
        leftEAR = eye_aspect_ratio(leftEye)
        ear = (leftEAR + rightEAR) / 2.0

提取 68 点,分别截取左右眼的 6 个点(注意 dlib 中右眼是 36~41,左眼是 42~47)。

分别计算两眼的 EAR,取平均值作为最终的 ear。

c 复制代码
        if ear < 0.3:
            COUNTER += 1
            if COUNTER >= 30:
                frame = cv2AddChineseText(frame, "!!!!危险!!!!", (250,250))
        else:
            COUNTER = 0

如果 EAR 低于阈值 0.3,说明眼睛闭合,计数器加 1。

当连续闭眼超过 30 帧,判定为疲劳瞌睡,在画面中央显示红色警告文字。

一旦睁眼,计数器清零。

c 复制代码
        drawEye(leftEye)
        drawEye(rightEye)
        info = "EAR: {:.2f}".format(ear[0][0])
        frame = cv2AddChineseText(frame, info, (0,30))

绘制眼睛轮廓,并在屏幕左上角显示当前的 EAR 数值,方便调试。

c 复制代码
    cv2.imshow("Frame", frame)
    if cv2.waitKey(1) == 27:
        break

显示画面,按 ESC 退出。

相关推荐
非优秀程序员5 小时前
第12章 烧钱无底洞!!!不烧就死,烧了也不知道啥时候死|《互联网产品恩仇录》
人工智能
Zelman9456 小时前
第06章-交互与观察动作空间
人工智能
冬奇Lab6 小时前
开源项目第258期:Harness² —— Google Research 的「递归式 Agent Harness 优化」,模型权重不动,只改 Agent 的外壳
人工智能·开源
字节跳动视频云技术团队8 小时前
每两家头部手机厂商就有一家选它:火山引擎如何成为 AI 手机助手的“隐形底座”?
人工智能
泯泷8 小时前
像项目经理一样指挥 AI 团队:管理者模式
人工智能·llm·agent
京东云开发者8 小时前
双11囤Token火了:京东搜索量暴涨7倍,AI生产力成新刚需
人工智能
ahccqw9 小时前
OpenCV 车辆数量统计
人工智能·opencv·计算机视觉
人工智能AI技术9 小时前
LangGraph 本地 build 频频翻车,最终在服务器跑通流式对话
人工智能
Flynn Liang9 小时前
同一需求三套写法:用 Spring AI、LangChain4j、AgentScope Java 各实现一个带工具调用的 RAG 助手(实战对比)
java·人工智能·大模型·架构设计·spring ai·langchain4j·agentscope