为什么这个方程如此有趣?
好吧,正如我们将发现的那样,眼睛睁开时眼睛的纵横比大致恒定,但在眨眼时会迅速降至零。
使用这个简单的方程,我们可以避免使用图像处理技术,而只需依靠眼睛界标距离的比率来确定一个人是否在眨眼。
为了更清楚地说明这一点,请考虑 Soukupová 和 Čech 的下图:
在左上角,我们有一个完全睁开的眼睛——这里的眼睛纵横比会很大(r)并且随着时间的推移相对恒定。
然而,一旦人眨眼(右上),眼睛的纵横比就会急剧下降,接近于零。
下图绘制了视频剪辑的眼睛纵横比随时间变化的图表。 正如我们所看到的,眼睛纵横比是恒定的,然后迅速下降到接近零,然后再次增加,表明发生了一次眨眼。
在下一节中,我们将学习如何使用面部标志、OpenCV、Python 和 dlib 实现眨眼检测的眼睛纵横比。
==============================================================================
首先,打开一个新文件并将其命名为 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,请参考我的文章:
https://wanghao.blog.csdn.net/article/details/121470556
接下来,我们将定义我们的 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 脚本需要一个命令行参数,然后是第二个可选参数:
-
–shape-predictor :这是 dlib 的预训练面部标志检测器的路径。 您可以使用本博文底部的“下载”部分将检测器以及源代码 + 示例视频下载到本教程中。
-
–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:
如果眼睛闭上足够多的次数
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Python工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Python开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以扫码获取!!!(备注:Python)
片转存中…(img-2Hi4ZhVG-1713752263999)]
[外链图片转存中…(img-IbJiDPrd-1713752264000)]
[外链图片转存中…(img-WTigBnc6-1713752264001)]
[外链图片转存中…(img-HLZcjskh-1713752264002)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以扫码获取!!!(备注:Python)