参考博客:Dlib模型之驾驶员疲劳检测二(打哈欠)
因为人在疲倦时大概会产生两种状态: 眨眼:正常人的眼睛每分钟大约要眨动10-15次,每次眨眼大概0.2-0.4秒,如果疲倦时眨眼次数会增多,速度也会变慢。打哈欠:此时嘴会长大而且会保持一定的状态。因此检测人是否疲劳可以从眼睛的开合度,眨眼频率,以及嘴巴张合程度来判断一个人是否疲劳。
第一步:使用dlib.get_frontal_face_detector() 获得脸部位置检测器
第二步:使用dlib.shape_predictor获得脸部特征位置检测器
第三步:分别获取左右眼面部标志的索引
第四步:打开cv2 本地摄像头
第五步:从视频流进行循环,读取图片,并对图片做维度扩大,并进灰度化
第六步:使用detector(gray, 0) 进行脸部位置检测
第七步:循环脸部位置信息,使用predictor(gray, rect)获得脸部特征位置的信息
第八步:将脸部特征信息转换为数组array的格式
第九步:提取左眼和右眼坐标
第十步:构造函数计算左右眼的EAR值,使用平均值作为最终的EAR
第十一步:使用cv2.convexHull获得凸包位置,使用drawContours画出轮廓位置进行画图操作
第十二步:进行画图操作,用矩形框标注人脸
第十三步:分别计算左眼和右眼的评分求平均作为最终的评分,如果小于阈值,则加1,如果连续3次都小于阈值,则表示进行了一次眨眼活动
第十四步:进行画图操作,68个特征点标识
第十五步:进行画图操作,同时使用cv2.putText将眨眼次数进行显示
第十六步:统计总眨眼次数大于50次屏幕显示睡着。
原文链接:https://blog.csdn.net/cungudafa/article/details/103477960
常用来测量欧氏距离的函数方法有两个:np.linalg.norm()和dist.euclidean(),所以可以大概测量一下这两个运算速度的快慢:
from scipy.spatial import distance as dist
import numpy as np
import time
points = [[1, 3], [8, 7]]
points = [np.array(val) for val in points]
tic = time.clock()
for i in range(50000):
temp1 = (np.linalg.norm(points[0] - points[1]))
toc = time.clock()
print("np.linalg.norm: %.3fs" % (toc - tic))
tic = time.clock()
for i in range(50000):
temp2 = (dist.euclidean(points[0], points[1]))
toc = time.clock()
print("dist.euclidean: %.3fs " % (toc - tic))
np.linalg.norm: 0.284s
dist.euclidean: 0.596s
所以看出np.linalg.norm()函数的运算速度要比dist.euclidean()快一点。所以用前者来测量眼睛和嘴巴的欧氏距离。
import numpy as np
from imutils import face_utils #imutils是在OpenCV基础之上的一个封装
import imutils
import dlib
import cv2
#定义眼睛阈值和嘴阈值算式函数
def eye_detect_1(eye):
#np.linalg.norm计算时间要比dist.euclidean要快 因此使用Numpy的算法
A = np.linalg.norm(eye[1] - eye[5])
B = np.linalg.norm(eye[2] - eye[4])
C = np.linalg.norm(eye[0] - eye[3])
ear = (A + B) /(2.0 * C)
return ear
def mouth_detect_1(mouth):
A = np.linalg.norm(mouth[2] - mouth[10]) # 51, 59
B = np.linalg.norm(mouth[4] - mouth[8]) # 53, 57
C = np.linalg.norm(mouth[0] - mouth[6]) # 49, 55
mar = (A + B) / (2.0 * C)
return mar
#常数:眼睛长宽比/闪烁阈值
eye_Aspect_rato = 0.2
eye_Flicker_threshold = 3
#常数:嘴长宽比/闪烁阈值
mouth_Aspect_rato = 0.5
mouth_Flicker_threshold = 3
#计数常数
eCOUNTER = 0
eTOTAL = 0
mCOUNTER = 0
mTOTAL = 0
#脸部位置检测器
#返回值就是一个矩形
detect = dlib.get_frontal_face_detector()
#脸部特征位置检测器
predictor = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat')
#获得左右眼和嘴的标志索引
(lstart,lend) = face_utils.FACIAL_LANDMARKS_IDXS['left_eye']
(rstart,rend) = face_utils.FACIAL_LANDMARKS_IDXS['right_eye']
(mstart,mend) = face_utils.FACIAL_LANDMARKS_IDXS['mouth']
cap = cv2.VideoCapture(0)
while True:
ret,frame = cap.read()
frame = imutils.resize(frame)#, width=720)
gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
#返回值为脸部区域的矩形框,数组形式
rects = detect(gray,0)
#print(rects)
# #循环脸部信息
for rect in rects:
#检测器检测特征点
shape = predictor(gray,rect)
#将特征点转化为数组组,返回值是68个特征点坐标
shape = face_utils.shape_to_np(shape)
#print(shape)
#获取左右眼睛和嘴的坐标
lefteye = shape[lstart:lend]
righteye = shape[rstart:rend]
mouth = shape[mstart:mend]
#计算左右眼的ear值,嘴的mar值。
leftear = eye_detect_1(lefteye)
rightear = eye_detect_1(righteye)
ear = (leftear + rightear) / 2.0
mar = mouth_detect_1(mouth)
#cv2.convexHull为获取图像凸包位置的函数
lefteyehull = cv2.convexHull(lefteye)
righteyehull = cv2.convexHull(righteye)
mouthhull = cv2.convexHull(mouth)
#print(lefteyehull)
#cv2.drawContours是轮廓绘制函数
#第一个参数是指明在哪幅图像上绘制轮廓;image为三通道才能显示轮廓
#第二个参数是轮廓本身,在Python中是一个list;
#第三个参数指定绘制轮廓list中的哪条轮廓,如果是-1,则绘制其中的所有轮廓。
cv2.drawContours(frame,[lefteyehull],-1,(0,255,0),1)
cv2.drawContours(frame,[righteyehull],-1,(0,255,0),1)
cv2.drawContours(frame,[mouthhull],-1,(0,255,0),1)
# 进行画图操作,用矩形框标注人脸
# left = rect.left()
# top = rect.top()
# right = rect.right()
# bottom = rect.bottom()
# cv2.rectangle(frame, (left, top), (right, bottom), (0, 255, 0), 3)
#循环,满足条件的眨眼次数+1
if ear < eye_Aspect_rato: #阈值0.2
eCOUNTER += 1
#连续3帧都满足条件的计为一次闭眼
else:
if eCOUNTER >= eye_Flicker_threshold: #阈值3
eTOTAL += 1
eCOUNTER = 0
cv2.putText(frame,'Blinks:{}'.format(eTOTAL),(10,30),cv2.FONT_HERSHEY_SIMPLEX,0.7,(0,0,255),2)
cv2.putText(frame,'eCounter:{}'.format(eCOUNTER), (150, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
cv2.putText(frame,'Ear:{:.2f}'.format(ear), (300, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
if mar > mouth_Aspect_rato: #阈值0.5
mCOUNTER += 1
else:
if mCOUNTER >= mouth_Flicker_threshold: #阈值3
mTOTAL += 1
mCOUNTER = 0
cv2.putText(frame, 'Yawning:{}'.format(mTOTAL), (10,60), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
cv2.putText(frame, 'mCounter:{}'.format(mCOUNTER), (300,60), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
cv2.putText(frame, 'Mar:{:.2f}'.format(mar), (450, 60), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
#进行画图操作,68个特征点标识
#for (x,y) in shape: #shape为68特征点的坐标
#cv2.circle(frame,(x,y),1,(0,0,255),-1)
#假设1秒为60帧,则1帧为1/60秒,一个TOTAL为3帧为1/20秒
if eTOTAL >= 12 or mTOTAL >= 6:
cv2.putText(frame,"SLEEP!SLEEP!!",(360,360),cv2.FONT_HERSHEY_SIMPLEX,1,(84,255,200),2)
cv2.imshow('pilao',frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
#也许是因为我用的opencv-python是4.1.0 所以加上最后这两行总是报错,我删掉后就可以运行了
#cap.release()
#cv2.destroyAllWindows()