前言
上次博客我们讲到了如何定位人脸,并且在人脸上进行关键点定位。其中包括5点定位和68点定位等,在定位之后呢,我们就可以使用定位信息来做一些相关操作,例如闭眼检测,这里就可以应用到驾驶员的疲劳检测上,或者是经常使用电脑的人,不闭眼可能会导致眼睛干涩等。
关键点讲解
我们本次博客主要讲解通过闭眼来检测疲劳驾驶,那么我们首先就要了解怎么让计算机来判断人是否闭了眼睛。我们通过上次的博客可以知道,我们首先要让计算机识别出来人脸,然后在识别出来的人脸上继续做关键点查找。我们这里用的是68关键点检测。
对于眼睛来讲,他每一个眼睛都有6个关键点。这里我们可以通过一种方式来判断是否进行了眨眼。
在眼睛的6个关键点中,我们可以发现当睁眼的时候,2和6点以及3和5点的欧氏距离较大。1和4点稍稍距离会增加一点,那么我们可以设定一个公式。
对应在图上就是2点和6点相减,3和5点相减。然后比上2倍的1和4点的差。其中都是绝对值。这样睁眼的时候EAR的数值就会较大,闭眼的时候EAR的数值就会较小。然后我们自己设定一个阈值,如果EAR的数值低于这个阈值超过了视频帧中的几帧。那么我们就认为该驾驶员正在闭眼。
经过了论文验证,说明该方法的准确度是非常可观的,且具有较强的鲁棒性。
代码详解
首先我们导入工具包,这里面也包括了计算欧氏距离的工具包。
from scipy.spatial import distance as dist from collections import OrderedDict import numpy as np import argparse import time import dlib import cv2
然后我们把68点关键点定位信息定位好。
FACIAL_LANDMARKS_68_IDXS = OrderedDict([ ("mouth", (48, 68)), ("right_eyebrow", (17, 22)), ("left_eyebrow", (22, 27)), ("right_eye", (36, 42)), ("left_eye", (42, 48)), ("nose", (27, 36)), ("jaw", (0, 17)) ])
这里"jaw", (0, 17)
表示的是下巴的位置的关键点标识分别是0-17点。
然后我们将需要的模型和视频导入到程序当中。关键点检测模型。
ap = argparse.ArgumentParser() ap.add_argument("-p", "--shape-predictor", required=True, help="path to facial landmark predictor") ap.add_argument("-v", "--video", type=str, default="", help="path to input video file") args = vars(ap.parse_args())
EYE_AR_THRESH = 0.3 EYE_AR_CONSEC_FRAMES = 3
这里这两个参数很重要,其中EYE_AR_THRESH这个表示EAR的阈值。如果高于这个阈值说明人这个时候是睁眼的,如果低于这个阈值的话,那么这个时候就要注意了,驾驶员可能在闭眼。而EYE_AR_CONSEC_FRAMES这个表示如果EAR数值超过了三帧及以上我们就可以把他认定为一次闭眼。为什么是三帧呢?因为如果一帧两帧的话可能是其他因素影响的。
COUNTER = 0 TOTAL = 0
然后我们又设定了两个计数器,如果小于阈值那么COUNTER
的数值就加一,知道COUNTER的数值大于等于3的时候,这个TOTAL就加一,就说明记录的闭眼了一次。
print("[INFO] loading facial landmark predictor...") detector = dlib.get_frontal_face_detector() predictor = dlib.shape_predictor(args["shape_predictor"])
这里我们就很熟悉了,一个是人脸定位器,一个是关键点检测器。这里分别调出来。
(lStart, lEnd) = FACIAL_LANDMARKS_68_IDXS["left_eye"] (rStart, rEnd) = FACIAL_LANDMARKS_68_IDXS["right_eye"]
然后我们通过关键点只取两个ROI区域,就是左眼区域和右眼区域。
print("[INFO] starting video stream thread...") vs = cv2.VideoCapture(args["video"])
随后我们将视频读进来。
while True: # 预处理 frame = vs.read()[1] if frame is None: break (h, w) = frame.shape[:2] width=1200 r = width / float(w) dim = (width, int(h * r)) frame = cv2.resize(frame, dim, interpolation=cv2.INTER_AREA) gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
将视频的展示框放大一点,这里很关键就是如果视频的框框设置的太小的话,可能无法检测到人脸。然后我们就把宽设置成了1200,然后对长度也同比例就行resize操作。最后转换成灰度图。
rects = detector(gray, 0)
这里面检测到人脸,将人脸框的四个坐标拿到手。注意就是必须要是对灰度图进行处理。
for rect in rects: # 获取坐标 shape = predictor(gray, rect) shape = shape_to_np(shape)
在这里进行人脸框遍历,然后检测68关键点。
def shape_to_np(shape, dtype="int"): # 创建68*2 coords = np.zeros((shape.num_parts, 2), dtype=dtype) # 遍历每一个关键点 # 得到坐标 for i in range(0, shape.num_parts): coords[i] = (shape.part(i).x, shape.part(i).y) return coords
这里就是提取关键点的坐标。
leftEye = shape[lStart:lEnd] rightEye = shape[rStart:rEnd] leftEAR = eye_aspect_ratio(leftEye) rightEAR = eye_aspect_ratio(rightEye)
然后我们把左眼和右眼分别求了一下EAR数值。这里的eye_aspect_ratio
函数就是计算EAR数值的。
def eye_aspect_ratio(eye): # 计算距离,竖直的 A = dist.euclidean(eye[1], eye[5]) B = dist.euclidean(eye[2], eye[4]) # 计算距离,水平的 C = dist.euclidean(eye[0], eye[3]) # ear值 ear = (A + B) / (2.0 * C) return ear
其中dist.euclidean
表示计算欧式距离,和公式中计算EAR数值一摸一样。
ear = (leftEAR + rightEAR) / 2.0 # 绘制眼睛区域 leftEyeHull = cv2.convexHull(leftEye) rightEyeHull = cv2.convexHull(rightEye) cv2.drawContours(frame, [leftEyeHull], -1, (0, 255, 0), 1) cv2.drawContours(frame, [rightEyeHull], -1, (0, 255, 0), 1)
然后对于左眼和右眼都进行了EAR求解然后求了一个平均值,然后根据凸包的概念,对眼睛区域进行了绘图。将左眼区域和右眼区域绘图出来。
if ear < EYE_AR_THRESH: COUNTER += 1 else: # 如果连续几帧都是闭眼的,总数算一次 if COUNTER >= EYE_AR_CONSEC_FRAMES: TOTAL += 1 # 重置 COUNTER = 0 # 显示 cv2.putText(frame, "Blinks: {}".format(TOTAL), (10, 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) cv2.imshow("Frame", frame) key = cv2.waitKey(10) & 0xFF if key == 27: break vs.release() cv2.destroyAllWindows()
最后进行了一次阈值判断,如果EAR连续三帧都小于0.3,那么我们就把TOTAL加一,这样记录一次闭眼的过程。然后最后将EAR数值和TOTAL的数值展示在视频当中。最后完成整体的训练。
结果展示
计算机视觉:驾驶员疲劳检测(二)+https://developer.aliyun.com/article/1384998