《深度学习》Dlib库 人脸应用实例 疲劳监测

目录

一、了解项目

1、脸部关键点

2、实现方法

3、流程

初始化阶段:

视频处理循环:

显示与交互:

二、案例实现

1、完整代码

2、运行结果


一、了解项目

1、脸部关键点

2、实现方法

        通过眼睛的纵横比来判断眼睛是否闭合。从而判断人是否处于疲劳状态。

3、流程

  1. 初始化阶段
    • 导入必要的库:numpy、dlib、cv2(OpenCV)、sklearn.metrics.pairwise(用于计算欧氏距离)、PIL(用于添加中文文本)。
    • 定义辅助函数:
      • eye_aspect_ratio计算眼睛的纵横比(EAR),用于判断眼睛是否闭合。
      • cv2AddChineseText在图像上添加中文文本。
      • drawEye绘制眼睛的凸包轮廓。
    • 初始化变量:COUNTER用于统计闭眼持续的帧数,detectorpredictor分别为dlib的人脸检测器和关键点定位器。
    • 加载视频文件
  2. 视频处理循环
    • 从视频文件中逐帧读取图像。
    • 使用dlib的人脸检测器检测图像中的人脸。
    • 对每个人脸,使用关键点定位器获取68个面部特征点的坐标。
    • 根据特征点坐标,提取左右眼睛的坐标区域。
    • 计算左右眼睛的EAR值,并取平均值作为最终的EAR值。
    • 根据EAR值判断眼睛是否闭合(EAR值小于0.3认为眼睛闭合):
      • 如果眼睛闭合持续时间超过50帧,则在图像上添加“危险”提示。
      • 否则,将闭眼计数器清零。
    • 绘制左右眼睛的凸包轮廓。
    • 在图像上显示当前的EAR值。
  3. 显示与交互
    • 使用OpenCV的imshow函数显示处理后的视频帧。
    • 检测按键事件,如果按下ESC键(ASCII码为27),则退出循环。

二、案例实现

1、完整代码

import numpy as np
import dlib
import cv2
from sklearn.metrics.pairwise import euclidean_distances
from PIL import Image, ImageDraw, ImageFont


def eye_aspect_ratio(eye):   # 计算眼睛纵横比,传入的eye格式为数组类型,需要使用reshape重塑其形状,将其改变为1行2两列格式
    A = euclidean_distances(eye[1].reshape(1, 2), eye[5].reshape(1, 2))   # 计算关键点1到5的欧几里得距离
    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=(0,255,0),textSize=30):
    """向图片中添加中文"""
    if (isinstance(img,np.ndarray)):  # 判断是否0penCV图片类型
        img = Image.fromarray(cv2.cvtColor(img,cv2.COLOR_BGR2RGB))  # 实现array到image的转换
    draw = ImageDraw.Draw(img)  # 在img图片上创建一个绘图的对象
    # 字体的格式
    fontStyle = ImageFont.truetype("STXINGKA.TTF",textSize,encoding = "utf-8")
    draw.text(position,text,textColor,font=fontStyle)
    return cv2.cvtColor(np.asarray(img),cv2.COLOR_BGR2RGB)       # 转换回0penCV格式

def drawEye(eye):   # 绘制眼框凸包
    eyeHull = cv2.convexHull(eye)
    cv2.drawContours(frame,[eyeHull],-1,(0,255,0),-1)  # 最后的-1表示画笔宽度为-1,即填充轮廓


COUNTER = 0  # 闭眼持续次数统计,初始化为0
detector = dlib.get_frontal_face_detector()  # 构造脸部位置检测器
predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")  # 读取人脸关键点定位模型
cap = cv2.VideoCapture('笑容.mp4')  # 读取视频

while True:
    ret, frame = cap.read()   # 读取视频帧
    faces = detector(frame, 0)  # 获取人脸
    for face in faces:  # 循环遍历每一个人脸
        shape = predictor(frame,face)   # 获取关键点
        # 将关键点转换为坐标(x,y)的形式
        shape = np.array([[p.x, p.y] for p in shape.parts()])  # 获取关键点迭代器,再遍历出来每个关键点的坐标点
        rightEye = shape[36:42]  # 右眼坐标,关键点索引从36到41(不包含42)
        leftEye = shape[42:48]   # 左眼坐标,关键点索引从42到47(不包含48)
        rightEAR = eye_aspect_ratio(rightEye)  # 计算右眼纵横比
        leftEAR = eye_aspect_ratio(leftEye)  # 计算左眼纵横比
        ear =(leftEAR + rightEAR)/2.0   # 均值处理

        if ear < 0.3:  
            COUNTER += 1  # 每检测到一次,将+1
            if COUNTER >= 50:  # 如果持续50帧画面,警报
                frame = cv2AddChineseText(frame, "!!!!危险!!!!",(250,250))  # 绘制中文文本
        # 宽高比>0.3,则计数器清零、解除疲劳标志
        else:
            COUNTER = 0   # 闭眼次数清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
# 释放资源
cv2.destroyAllWindows()
cap.release()

2、运行结果