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