基于Python OpenCV和 dlib实现眨眼检测

目录
  • 了解“眼睛纵横比”(EAR)
  • 使用面部标志和 OpenCV 检测眨眼
  • 眨眼检测结果
  • 总结

今天,我们使用面部标记和 OpenCV 检测视频流中的眨眼次数。

为了构建我们的眨眼检测器,我们将计算一个称为眼睛纵横比 (EAR) 的指标,该指标由 Soukupová 和 Čech 在他们 2016 年的论文《使用面部标记的实时眨眼检测》中介绍。

与计算眨眼的传统图像处理方法不同,传统的图像处理方法通常涉及以下某些组合:

  • 眼睛定位。
  • 阈值以找到眼白。
  • 确定眼睛的“白色”区域是否在一段时间内消失(表示眨眼)。
  • 眼睛纵横比是一个更优雅的解决方案,它涉及基于眼睛面部标志之间距离比的非常简单的计算。

这种眨眼检测方法要求快速、高效且易于实现。

今天我们通过四部分来实现眨眼检测:

第一部分,我们将讨论眼睛纵横比以及如何使用它来确定一个人在给定的视频帧中是否在眨眼。

然后,我们将编写 Python、OpenCV 和 dlib 代码来 (1) 执行面部标志检测和 (2) 检测视频流中的眨眼。

基于此实现,我们将应用我们的方法来检测示例网络摄像头流和视频文件中的眨眼。

最后,我将通过讨论改进眨眼检测器的方法来结束今天的博客文章。

了解“眼睛纵横比”(EAR)

在眨眼检测方面,我们只对两组面部结构感兴趣——眼睛。每只眼睛由 6 个 (x, y) 坐标表示,从眼睛的左角开始(就像您在看人一样),然后围绕该区域的其余部分顺时针旋转:

基于这张图片,我们应该了解关键点:

这些坐标的宽度和高度之间存在关系。根据 Soukupová 和 Čech 在 2016 年发表的论文《使用面部标志进行实时眨眼检测》中的工作,我们可以推导出反映这种关系的方程,称为眼睛纵横比 (EAR):

其中 p1, …, p6 是 2D 面部标志位置。

该方程的分子计算垂直眼睛界标之间的距离,而分母计算水平眼睛界标之间的距离,由于只有一组水平点但有两组垂直点,因此对分母进行适当加权。

为什么这个方程如此有趣?

好吧,正如我们将发现的那样,眼睛睁开时眼睛的纵横比大致恒定,但在眨眼时会迅速降至零。

使用这个简单的方程,我们可以避免使用图像处理技术,而只需依靠眼睛界标距离的比率来确定一个人是否在眨眼。

为了更清楚地说明这一点,请考虑 Soukupová 和 Čech 的下图:

在左上角,我们有一个完全睁开的眼睛——这里的眼睛纵横比会很大(r)并且随着时间的推移相对恒定。

然而,一旦人眨眼(右上),眼睛的纵横比就会急剧下降,接近于零。

下图绘制了视频剪辑的眼睛纵横比随时间变化的图表。 正如我们所看到的,眼睛纵横比是恒定的,然后迅速下降到接近零,然后再次增加,表明发生了一次眨眼。

在下一节中,我们将学习如何使用面部标志、OpenCV、Python 和 dlib 实现眨眼检测的眼睛纵横比。

使用面部标志和 OpenCV 检测眨眼

首先,打开一个新文件并将其命名为 detect_blinks.py 。 从那里,插入以下代码:

# import the necessary packages
from scipy.spatial import distance as dist
from imutils.video import FileVideoStream
from imutils.video import VideoStream
from imutils import face_utils
import numpy as np
import argparse
import imutils
import time
import dlib
import cv2

导入必要的库。

如果您的系统上没有安装 imutils(或者如果您使用的是旧版本),请确保使用以下命令安装/升级:

pip install --upgrade imutils 

如果没有安装dlib,请参考文章

 接下来,我们将定义我们的 eye_aspect_ratio 函数:

def eye_aspect_ratio(eye):
	# compute the euclidean distances between the two sets of
	# vertical eye landmarks (x, y)-coordinates
	A = dist.euclidean(eye[1], eye[5])
	B = dist.euclidean(eye[2], eye[4])
	# compute the euclidean distance between the horizontal
	# eye landmark (x, y)-coordinates
	C = dist.euclidean(eye[0], eye[3])
	# compute the eye aspect ratio
	ear = (A + B) / (2.0 * C)
	# return the eye aspect ratio
	return ear

此函数接受单个必需参数,即给定眼睛的面部标志的 (x, y) 坐标。

计算两组垂直眼睛界标之间的距离,然后计算水平眼睛界标之间的距离。

最后,结合了分子和分母以得出最终的眼睛纵横比。

然后将眼睛纵横比返回给调用函数。

让我们继续解析我们的命令行参数:

# construct the argument parse and parse the arguments
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())

我们的detect_blinks.py 脚本需要一个命令行参数,然后是第二个可选参数:

  1. –shape-predictor :这是 dlib 的预训练面部标志检测器的路径。 您可以使用本博文底部的“下载”部分将检测器以及源代码 + 示例视频下载到本教程中。
  2. –video :此可选开关控制驻留在磁盘上的输入视频文件的路径。 如果您想使用实时视频流,只需在执行脚本时省略此开关即可。

我们现在需要设置两个重要的常量,您可能需要为自己的实现进行调整,同时初始化另外两个重要的变量,所以一定要注意这个解释:

# 定义两个常量,一个为眼睛纵横比来表示
# 闪烁然后第二个常量为连续的次数
# 帧眼睛必须低于阈值
EYE_AR_THRESH = 0.3
EYE_AR_CONSEC_FRAMES = 3
# 初始化帧计数器和闪烁总数
COUNTER = 0
TOTAL = 0

在确定视频流中是否发生眨眼时,我们需要计算眼睛纵横比。

如果眼睛纵横比低于某个阈值,然后又高于阈值,那么我们将注册一个“眨眼”——EYE_AR_THRESH 就是这个阈值。我们默认它的值为 0.3,因为这对我的应用程序最有效,但您可能需要为自己的应用程序调整它。

然后我们有一个重要的常量,EYE_AR_CONSEC_FRAME——这个值被设置为 3 以指示眼睛纵横比小于 EYE_AR_THRESH 的三个连续帧必须发生,以便注册眨眼。

同样,根据管道的帧处理吞吐率,您可能需要为自己的实现提高或降低此数字。

第 44 和 45 行初始化两个计数器。 COUNTER 是眼睛纵横比小于 EYE_AR_THRESH 的连续帧的总数,而 TOTAL 是脚本运行时发生的眨眼总数。

现在我们的导入、命令行参数和常量都已经处理好了,我们可以初始化 dlib 的人脸检测器和面部标记检测器:

# 初始化dlib的人脸检测器(基于HOG)然后创建
# 面部标志预测器
print("[INFO] loading facial landmark predictor...")
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor(args["shape_predictor"])

初始化实际的面部标志预测器。

dlib 生成的面部标志遵循可索引的列表,如下:

因此,我们可以确定开始和结束数组切片索引值,以便为下面的左眼和右眼提取 (x, y) 坐标:

# 获取左侧和面部标志的索引
# 右眼,分别
(lStart, lEnd) = face_utils.FACIAL_LANDMARKS_IDXS["left_eye"]
(rStart, rEnd) = face_utils.FACIAL_LANDMARKS_IDXS["right_eye"]

使用这些索引,我们将能够毫不费力地提取眼睛区域。

接下来,我们需要决定是使用基于文件的视频流还是实时 USB/网络摄像头/Raspberry Pi 相机视频流:

# start the video stream thread
print("[INFO] starting video stream thread...")
vs = FileVideoStream(args["video"]).start()
fileStream = True
# vs = VideoStream(src=0).start()
# vs = VideoStream(usePiCamera=True).start()
# fileStream = False
time.sleep(1.0)
fps = 30    #保存视频的FPS,可以适当调整
size=(450,800)
videoWriter = cv2.VideoWriter('3.mp4',-1,fps,size)#最后一个是保存图片的尺寸

如果您使用的是文件视频流,则保留代码原样。

如果您想使用内置网络摄像头或 USB 摄像头,请取消注释# vs = VideoStream(src=0).start()。

对于 Raspberry Pi 摄像头模块,取消注释# vs = VideoStream(usePiCamera=True).start()。

定义帧数。

定义大小

定义视频写入对象

最后,我们到达了脚本的主循环:

# loop over frames from the video stream
while True:
	# 如果这是一个文件视频流,那么我们需要检查是否
	# 缓冲区中还有更多帧要处理
	if fileStream and not vs.more():
		break
	frame = vs.read()
	if frame is None:
        break
	frame = imutils.resize(frame, width=450)
	gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
	# 在灰度帧中检测人脸
	rects = detector(gray, 0)

遍历视频流中的帧。

如果我们正在访问一个视频文件流并且视频中没有更多的帧,我们就会中断循环。

从视频流中读取下一帧,然后调整其大小并将其转换为灰度。

然后我们通过 dlib 的内置人脸检测器检测灰度帧中的人脸。

我们现在需要遍历帧中的每个人脸,然后对每个人应用面部标志检测:

	# loop over the face detections
	for rect in rects:
		# 确定面部区域的面部标志,然后
		# 将面部标志 (x, y) 坐标转换为 NumPy数组
		shape = predictor(gray, rect)
		shape = face_utils.shape_to_np(shape)
		# 提取左右眼坐标,然后使用
		# 坐标来计算双眼的眼睛纵横比
		leftEye = shape[lStart:lEnd]
		rightEye = shape[rStart:rEnd]
		leftEAR = eye_aspect_ratio(leftEye)
		rightEAR = eye_aspect_ratio(rightEye)
		# 平均两只眼睛的眼睛纵横比
		ear = (leftEAR + rightEAR) / 2.0

确定面部区域的面部标志,将这些 (x, y) 坐标转换为 NumPy 数组。

使用本脚本前面的数组切片技术,我们可以分别提取左眼和右眼的 (x, y) 坐标。

然后,在第 96 和 97 行计算每只眼睛的眼睛纵横比。

按照 Soukupová 和 Čech 的建议,我们将两只眼睛的纵横比平均在一起以获得更好的眨眼估计(当然,假设一个人同时眨眼)。

我们的下一个代码块只是处理眼睛区域本身的面部标志的可视化:

		# 计算左眼和右眼的凸包,然后
		# 可视化每只眼睛
		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)

在这一点上,我们已经计算了我们的(平均)眼睛纵横比,但我们实际上还没有确定是否发生了眨眼——这将在下一节中解决:

		# 检查眼睛的纵横比是否低于眨眼
		# 阈值,如果是,则增加闪烁帧计数器
		if ear < EYE_AR_THRESH:
			COUNTER += 1
		# 否则,眼睛纵横比不低于眨眼
		# 临界点
		else:
			# 如果眼睛闭上足够多的次数
			# 然后增加闪烁的总数
			if COUNTER >= EYE_AR_CONSEC_FRAMES:
				TOTAL += 1
			# 重置眼框计数器
			COUNTER = 0

检查眼睛纵横比是否低于我们的眨眼阈值——如果是增加指示正在发生眨眼的连续帧的数量。

否则,处理眼睛纵横比不低于眨眼阈值的情况。

在这种情况下,再次检查以查看是否有足够数量的连续帧包含低于我们预定义阈值的眨眼率。

如果检查通过,我们增加闪烁的总次数。

然后我们重置连续闪烁的次数 COUNTER。

我们的最终代码块只是处理在我们的输出帧上绘制眨眼次数,以及显示当前眼睛纵横比:

		# 绘制帧上闪烁的总数以及
		# 计算出的帧的眼睛纵横比
		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)

	# show the frame
	cv2.imshow("Frame", frame)
	videoWriter.write(frame)
	key = cv2.waitKey(1) & 0xFF

	# if the `q` key was pressed, break from the loop
	if key == ord("q"):
		break
videoWriter.release()
# do a bit of cleanup
cv2.destroyAllWindows()
vs.stop()

眨眼检测结果

要将我们的眨眼检测器应用于示例视频,只需执行以下命令:

python detect_blinks.py --shape-predictor shape_predictor_68_face_landmarks.dat --video 11.mp4

测试结果:

测试视频链接:

眨眼检测

如果测试摄像头则,如下操作:

#vs = FileVideoStream(args["video"]).start()
#fileStream = True
vs = VideoStream(src=0).start()
# vs = VideoStream(usePiCamera=True).start()
fileStream = False

注释FileVideoStream,取消注释VideoStream。

执行命令:

python detect_blinks.py --shape-predictor shape_predictor_68_face_landmarks.dat

总结

在这篇博文中,我演示了如何使用 OpenCV、Python 和 dlib 构建眨眼检测器。

构建眨眼检测器的第一步是执行面部标志检测,以定位视频流中给定帧中的眼睛。

一旦我们有了双眼的面部标志,我们就计算每只眼睛的眼睛纵横比,这给了我们一个奇异值,将垂直眼睛标志点之间的距离与水平标志点之间的距离联系起来。

一旦我们有了眼睛纵横比,我们就可以确定一个人是否在眨眼——眼睛纵横比在睁眼时将保持大致恒定,然后在眨眼时迅速接近零,然后随着眼睛睁开再次增加.

为了改进我们的眨眼检测器,Soukupová 和 Čech 建议构建一个 13 维的眼睛纵横比特征向量(第 N 帧、N – 6 帧和 N + 6 帧),然后将该特征向量输入线性 SVM分类。

通过这篇博文你还学会了如何保存视频。

眨眼检测的一个应用场景是睡意检测。

完整的代码,提取码:k653

以上就是基于Python OpenCV和 dlib实现眨眼检测的详细内容,更多关于Python OpenCV dlib眨眼检测的资料请关注我们其它相关文章!

(0)

相关推荐

  • Python+Dlib+Opencv实现人脸采集并表情判别功能的代码

    一.dlib以及opencv-python库安装 介于我使用的是jupyter notebook,所以在安装dlib和opencv-python时是在 这个命令行安装的 dlib安装方法: 1.若可以,直接使用上图所示命令行输入以下命令: pip install cmake pip install boost pip install dlib 若安装了visual studio2019应该就可以直接pip install dlib,至少我是这样 由于很多在执行第三句时都会报错,所以这里提供第二种

  • 树莓派上利用python+opencv+dlib实现嘴唇检测的实现

    目录 1.安装相关库文件 2.代码部分 3.实验效果 树莓派上利用python+opencv+dlib实现嘴唇检测 项目的目标是在树莓派上运行python代码以实现嘴唇检测,本来以为树莓派的硬件是可以流畅运行实时检测的,但是实验的效果表明树莓派实时检测是不可行,后面还需要改进. 实验的效果如下: 1.安装相关库文件 这里需要用的库有opencv,numpy,dlib. 1.1 安装opencv pip3 install opencv-python 1.2 安装numpy 树莓派中自带了numpy

  • Python中人脸图像特征提取方法(HOG、Dlib、CNN)简述

    目录 人脸图像特征提取方法 (一)HOG特征提取 (二)Dlib库 (三)卷积神经网络特征提取(CNN) 人脸图像特征提取方法 (一)HOG特征提取 1.HOG简介 Histogram of Oriented Gridients,缩写为HOG,是目前计算机视觉.模式识别领域很常用的一种描述图像局部纹理的特征.它的主要思想是在一副图像中,局部目标的表象和形状能够被梯度或边缘的方向密度分布很好地描述.其本质为:梯度的统计信息,而梯度主要存在于边缘的地方. 2.实现方法 首先将图像分成小的连通区域,这

  • python中dlib库的详细安装方法

    一.下载dlib 下载自己需要的. 二.安装需要的库 准备安装dlib前安装的库: 更新 pip.setuptools.wheel pip install --upgrade pip pip install --upgrade setuptools pip install --upgrade wheel 可以使用镜像:-i pip install cmake -i pip install boost i 三.安装dlib win + R => cmd # pip install (前面下载的dl

  • python使用dlib进行人脸检测和关键点的示例

    #!/usr/bin/env python # -*- coding:utf-8-*- # file: {NAME}.py # @author: jory.d # @contact: dangxusheng163@163.com # @time: 2020/04/10 19:42 # @desc: 使用dlib进行人脸检测和人脸关键点 import cv2 import numpy as np import glob import dlib FACE_DETECT_PATH = '/home/b

  • 基于Python OpenCV和 dlib实现眨眼检测

    目录 了解"眼睛纵横比"(EAR) 使用面部标志和 OpenCV 检测眨眼 眨眼检测结果 总结 今天,我们使用面部标记和 OpenCV 检测视频流中的眨眼次数. 为了构建我们的眨眼检测器,我们将计算一个称为眼睛纵横比 (EAR) 的指标,该指标由 Soukupová 和 Čech 在他们 2016 年的论文<使用面部标记的实时眨眼检测>中介绍. 与计算眨眼的传统图像处理方法不同,传统的图像处理方法通常涉及以下某些组合: 眼睛定位. 阈值以找到眼白. 确定眼睛的"白

  • 基于python opencv单目相机标定的示例代码

    相机固定不动,通过标定版改动不同方位的位姿进行抓拍 import cv2 camera=cv2.VideoCapture(1) i = 0 while 1: (grabbed, img) = camera.read() cv2.imshow('img',img) if cv2.waitKey(1) & 0xFF == ord('j'): # 按j保存一张图片 i += 1 u = str(i) firename=str('./img'+u+'.jpg') cv2.imwrite(firename

  • 基于Python+OpenCV制作屏幕录制工具

    目录 应用平台 屏幕录制部分 计算视频最优fps及使用numpy计算中间帧数组 使用pynput监听键盘按键 如何保存MP4格式视频 源码 总结 最近有在使用屏幕录制软件录制桌面,在用的过程中突发奇想,使用python能不能做屏幕录制工具,也锻炼下自己的动手能力.接下准备写使用python如何做屏幕录制工具的系列文章: 录制屏幕制作视频 录制音频 合成视频,音频 基于pyqt5制作可视化窗口 大概上述四个部分,希望自己能够尽快完善,接下来开始使用python制作屏幕录制部分. 应用平台 wind

  • 基于Python OpenCV实现图像的覆盖

    目录 前言 1.导入相关库 2.使用OpenCV读取和显示图像 3.从物体的图像中去除背景 4.添加对象到背景图像 5.结果展示 前言 在本文中,我将展示如何将对象从一个图像添加到另一个图像.为此,我们需要: 1.背景图像; 2.对象 3.对象的mask(mask为黑色,其他空间为白色). 在我们的例子中,背景是一张大海的照片,对象是一杯咖啡.在这里,他们是: 1.导入相关库 现在,使用jupiter notebook创建一个新文件.首先,我们需要导入必要的模块: import cv2 # Op

  • Python OpenCV使用dlib进行多目标跟踪详解

    目录 1.使用dlib进行多目标跟踪 2.项目结构 3.dlib多对象跟踪的简单“朴素”方法 4.快速.高效的dlib多对象跟踪实现 5.完整代码 6.改进和建议 在本教程中,您将学习如何使用 dlib 库在实时视频中有效地跟踪多个对象. 我们当然可以使用 dlib 跟踪多个对象:但是,为了获得可能的最佳性能,我们需要利用多处理并将对象跟踪器分布在处理器的多个内核上. 正确利用多处理使我们能够将 dlib 多对象跟踪每秒帧数 (FPS) 提高 45% 以上! 1.使用 dlib 进行多目标跟踪

  • 基于python OpenCV实现动态人脸检测

    本文实例为大家分享了python动态人脸检测的具体代码,供大家参考,具体内容如下 直接上代码: 按Q退出 import cv2 import numpy as np cv2.namedWindow("test") cap = cv2.VideoCapture(0) #加载摄像头录制 # cap = cv2.VideoCapture("test.mp4") #打开视频文件 success, frame = cap.read() # classifier = cv2.C

  • 基于python+opencv调用电脑摄像头实现实时人脸眼睛以及微笑识别

    本文教大家调用电脑摄像头进行实时人脸+眼睛识别+微笑识别,供大家参考,具体内容如下 一.调用电脑摄像头进行实时人脸+眼睛识别 # 调用电脑摄像头进行实时人脸+眼睛识别,可直接复制粘贴运行 import cv2 face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades+'haarcascade_frontalface_default.xml') eye_cascade = cv2.CascadeClassifier(cv2.data.ha

  • python+OpenCV实现车牌号码识别

    基于python+OpenCV的车牌号码识别,供大家参考,具体内容如下 车牌识别行业已具备一定的市场规模,在电子警察.公路卡口.停车场.商业管理.汽修服务等领域已取得了部分应用.一个典型的车辆牌照识别系统一般包括以下4个部分:车辆图像获取.车牌定位.车牌字符分割和车牌字符识别 1.车牌定位的主要工作是从获取的车辆图像中找到汽车牌照所在位置,并把车牌从该区域中准确地分割出来 这里所采用的是利用车牌的颜色(黄色.蓝色.绿色) 来进行定位 #定位车牌 def color_position(img,ou

  • 基于python的opencv图像处理实现对斑马线的检测示例

    基本思路 斑马线检测通过opencv图像处理来进行灰度值转换.高斯滤波去噪.阈值处理.腐蚀和膨胀后对图像进行轮廓检测,通过判断车辆和行人的位置,以及他们之间的距离信息,当车速到超过一定阈值时并且与行人距离较近时,则会被判定车辆为未礼让行人. 结果示例 实验流程 先通过视频截取一张图片来进行测试,如果结果满意之后再嵌套到视频中,从而达到想要的效果. 1.预处理(灰度值转换.高斯滤波去噪.阈值处理.腐蚀和膨胀)> 根据自己的需求来修改一些值 #灰度值转换 imgGray = cv2.cvtColor

  • Python OpenCV 基于图像边缘提取的轮廓发现函数

    基础知识铺垫 在图像中,轮廓可以简单的理解为连接具有相同颜色的所有连续点(边界)的曲线,轮廓可用于形状分析和对象检测.识别等领域. 轮廓发现的原理:先通过阈值分割提取目标物体,再通过边缘检测提取目标物体轮廓. 一个轮廓就是一系列的点(像素),这些点构成了一个有序的点集合. 使用 cv2.findContours 函数可以用来检测图像的边缘. 函数原型说明 contours, hierarchy = cv2.findContours(image, mode, method[, contours[,

随机推荐