各位同学好,今天和大家分享一下如何使用 mediapipe+opencv 实现眨眼计数器。先放张图看效果。

下图左侧为视频图像,右侧为平滑后的人眼开合比曲线。以左眼为例,若眼眶上下边界的距离与左右边界的距离的比值小于26%,就认为是眨眼。当眨眼成功计数一次后,接下来的10帧不再计算眨眼次数,防止重复。

不眨眼时:

眨眼时:


1. 安装工具包

pip install opencv_python==4.2.0.34  # 安装opencv
pip install mediapipe  # 安装mediapipe
# pip install mediapipe --user  #有user报错的话试试这个
pip install cvzone  # 安装cvzone# 导入工具包
import cv2
import cvzone
from cvzone.FaceMeshModule import FaceMeshDetector  # 导入脸部关键点检测方法
from cvzone.PlotModule import LivePlot  # 导入实时绘图模块

2. 脸部关键点检测

(1)cvzone.FaceMeshModule.FaceMeshDetector()  人脸关键点检测方法

参数:

staticMode: 默认为 False,将输入图像视为视频流。它将尝试在第一个输入图像中检测人脸,并在成功检测后进一步定位468个关键点的坐标。在随后的图像中,一旦检测到所有 maxFaces 张脸并定位了相应的关键点的坐标,它就会跟踪这些坐标,而不会调用另一个检测,直到它失去对任何一张脸的跟踪。这减少了延迟,非常适合处理视频帧。如果设置为 True,则在每个输入图像上运行脸部检测,用于处理一批静态的、可能不相关的图像。

maxFaces: 最多检测几张脸,默认为 2

minDetectionCon=0.5: 脸部关键点检测模型的最小置信值(0-1之间),超过阈值则检测成功。默认为 0.5

minTrackCon=0.5: 关键点坐标跟踪模型的最小置信值 (0-1之间),用于将手部坐标视为成功跟踪,不成功则在下一个输入图像上自动调用手部检测。将其设置为更高的值可以提高解决方案的稳健性,但代价是更高的延迟。如果 mode 为 True,则忽略这个参数,手部检测将在每个图像上运行。默认为 0.5

(2)cvzone.FaceMeshModule.FaceMeshDetector.findFaceMesh()  人脸关键点检测方法

参数:

img: 需要检测关键点的帧图像,格式为BGR

draw: 是否需要在原图像上绘制关键点及连线

返回值:

img: 返回绘制了关键点及连线后的图像

faces: 检测到的脸部信息,三维列表,包含每张脸的468个关键点

代码如下:

import cv2
import cvzone
from cvzone.FaceMeshModule import FaceMeshDetector  # 导入脸部关键点检测方法#(1)读取视频文件
filepath = 'D:/deeplearning/video/eyes.mp4'
cap = cv2.VideoCapture(filepath)#(2)配置
# 接收脸部检测方法,设置参数
detector = FaceMeshDetector(maxFaces=1)  # 最多只检测一张脸#(3)图像处理
while True:# 原视频较短,循环播放if cap.get(cv2.CAP_PROP_POS_FRAMES) == cap.get(cv2.CAP_PROP_FRAME_COUNT):  # 如果当前帧等于总帧数,即视频播放到了结尾cap.set(cv2.CAP_PROP_POS_FRAMES, 0)  # 让当前帧为0,重置视频从头开始# 返回帧图像是否读取成功success,读取的帧图像imgsuccess, img = cap.read()#(4)关键点检测,绘制人脸网状检测结果img, faces = detector.findFaceMesh(img) # img为绘制关键点后的图像,faces为关键点坐标# 查看结果print(faces)  # faces是三维数组,包含每张脸所有关键点的坐标[[[mark1],[mark2],..,[markn]], [face2], [face3]...]#(5)显示图像cv2.imshow('img', img)  # 传入窗口名和帧图像# 每帧图像滞留10毫秒后消失,按下键盘上的ESC键退出程序if cv2.waitKey(10) & 0xFF == 27:break# 释放视频资源
cap.release()
cv2.destroyAllWindows()

效果图如下,只检测一张人脸的468个关键点,并在输出栏中打印各个关键点的坐标。


2. 判断是否眨眼

(1)计算人眼开合比

首先考虑到人脸距离摄像机的远近对眼眶上下及左右边界的距离有影响,人脸靠摄像机越近,眼眶间的距离就越小。因此,不能单单靠计算眼眶上下边界的距离来判断是否眨眼

以左眼的开闭来判断是否眨眼,先根据人脸关键点坐标编号找出左眼关键点的编号。运用 cvzone.FaceMeshModule.FaceMeshDetector.findDistance() 来计算两个关键点之间的距离,等同于勾股定理计算两点间的距离。参数,两个点的坐标;返回值,两点之间的线段长度 length线段信息 info 包括(两个端点的坐标,以及连线中点的坐标)

计算得到左眼上下框之间的距离 lengthver,以及左眼左右框之间的距离 lengthhor。计算上下框距离除以左右框的距离,得到开合比 ratio

(2)实时绘图

cvzone.PlotModule LivePlot()  实时绘图方法

参数:

w:绘图框的宽,默认为 640

h:绘图框的高,默认为 480

yLimit:y坐标的刻度范围,默认为 [0, 100]

interval:图像上每个点的间隔,默认为 0.001

invert:曲线图上下翻转,默认为 False

cvzone.PlotModule LivePlot.update()  在图像上实时添加新的坐标点

参数:

y:新的坐标点的y轴坐标(x轴坐标为实时的时间点)

color:图像上的坐标点的颜色,默认是(255, 0, 255)

返回值:绘制好坐标点的当前帧的图像

cvzone.stackImages()  合并两张图像

参数:

imgList:列表形式,需要合并的图片,如:[img1, img2, ...]

cols:列方向排序,图片合并后排几列

scale:改变合并后的图像的size,图像大小是原来的几倍。

返回值:合并后的图像

接着上面的代码补充:

import cv2
import cvzone
from cvzone.FaceMeshModule import FaceMeshDetector  # 导入脸部关键点检测方法
from cvzone.PlotModule import LivePlot  # 导入实时绘图模块#(1)读取视频文件
filepath = 'D:/deeplearning/video/eyes1.mp4'
cap = cv2.VideoCapture(filepath)#(2)配置
# 接收脸部检测方法,设置参数
detector = FaceMeshDetector(maxFaces=1)  # 最多只检测一张脸# 人眼关键点所在的索引
idList = [22, 23, 24, 26, 110, 130, 157, 158, 159, 160, 161, 243]  # 左眼 # 接收实时绘图方法,图宽640高360,y轴刻度范围,间隔默认0.01,上下翻转默认False
plotY = LivePlot(640, 360, [20,40], invert=True)#(3)图像处理
while True:# 原视频较短,循环播放if cap.get(cv2.CAP_PROP_POS_FRAMES) == cap.get(cv2.CAP_PROP_FRAME_COUNT):  # 如果当前帧等于总帧数,即视频播放到了结尾cap.set(cv2.CAP_PROP_POS_FRAMES, 0)  # 让当前帧为0,重置视频从头开始# 返回帧图像是否读取成功success,读取的帧图像imgsuccess, img = cap.read()#(4)关键点检测,不绘制关键点及连线img, faces = detector.findFaceMesh(img, draw=False) # img为绘制关键点后的图像,faces为关键点坐标# 如果检测到关键点了就接下去处理if faces:face = faces[0]  # face接收一张脸的所有关键点信息,且faces是三维列表# 遍历所有的眼部关键点for id in idList:cv2.circle(img, tuple(face[id]), 4, (0,255,255), cv2.FILLED)  # 以关键点为圆心半径为5画圆# 由于人脸距离摄像机的距离的远近会影响眼部上下边界之间的距离,因此通过开合比例来判断是否眨眼leftUp = tuple(face[159])  # 左眼最上边界的关键点坐标leftDown = tuple(face[23])  # 左眼最下边界的关键点坐标leftLeft = tuple(face[130])  # 左眼最左边界的关键点坐标leftRight = tuple(face[243])  # 左眼最右边界的关键点坐标# 计算上下边界以及左右边界的距离,函数返回距离length,以及线段两端点和中点坐标,这里用不到lengthver, __ = detector.findDistance(leftUp, leftDown)  # 上下边界距离lengthhor, __ = detector.findDistance(leftLeft, leftRight)  # 左右边界距离# 在上下及左右关键点之间各画一条线段,端点坐标是元组cv2.line(img, leftUp, leftDown, (255,0,255), 2)cv2.line(img, leftLeft, leftRight, (255,0,255), 2)# 计算竖直距离与水平距离的比值ratio = 100*lengthver/lengthhorprint('水平距离:', lengthhor, '垂直距离:', lengthver, '百分比%:', ratio)# 以时间为x轴,百分比为y轴,绘制实时变化曲线imgPlot = plotY.update(ratio, (0,0,255))  # 参数:每个时刻的y值,线条颜色#(5)眨眼计数器# 重塑图像的宽和高,保证图像size和曲线图size一致img = cv2.resize(img, (640,360))  # 将变化曲线和原图像组合起来,2代表排成两列,最后一个1代表比例不变imgStack = cvzone.stackImages([img, imgPlot], 2, 1)#(6)显示图像cv2.imshow('img', imgStack)  # 传入窗口名和帧图像# 每帧图像滞留20毫秒后消失,按下键盘上的ESC键退出程序if cv2.waitKey(20) & 0xFF == 27:break# 释放视频资源
cap.release()
cv2.destroyAllWindows()

效果图如下,右侧为每一帧图像的人眼开合百分比,下侧输出栏打印每一帧的眼眶的水平、竖直距离,及比值。


3. 眨眼计数器

(1)平滑曲线

由于每一帧计算一次人眼开合比,曲线变化幅度较大,不宜判断。因此对曲线进行平滑操作,如果帧数小于10帧,更新的坐标是每帧的人眼闭合比;如果帧数大于10帧,那就每10帧求一次均值,将这个均值更新到图像上。例如,第11帧图像的人眼闭合比为,第2至第11帧的人眼闭合比的均值

如下面代码中的第(5)步,变量 ratioList 中总是存放10帧的人眼闭合比值。如,计算第12帧图像的闭合比时,就将列表中第2帧的闭合比值删除,计算第3帧到第12帧的均值

(2)避免重复计数

如下面代码中的第(6)步,如果判断了一次眨眼之后,那么接下来的10帧都是属于该次眨眼的过程,只有10帧过后才能进行下次一眨眼计数。

代码如下:

import cv2
import cvzone
from cvzone.FaceMeshModule import FaceMeshDetector  # 导入脸部关键点检测方法
from cvzone.PlotModule import LivePlot  # 导入实时绘图模块#(1)读取视频文件
filepath = 'D:/deeplearning/video/eyes1.mp4'
cap = cv2.VideoCapture(filepath)#(2)配置
# 接收脸部检测方法,设置参数
detector = FaceMeshDetector(maxFaces=1)  # 最多只检测一张脸# 人眼关键点所在的索引
idList = [22, 23, 24, 26, 110, 130, 157, 158, 159, 160, 161, 243]  # 左眼 # 接收实时绘图方法,图宽640高360,y轴刻度范围,间隔默认0.01,上下翻转默认False
plotY = LivePlot(640, 360, [20,40], invert=True)ratioList = []  # 存放实时的人眼开合百分比blinkCounter = 0  # 眨眼计数器默认=0counter = 0  # 代表当前帧没计算过眨眼次数colorblink = (255,0,255)  # 不眨眼时计数器的颜色#(3)图像处理
while True:# 原视频较短,循环播放if cap.get(cv2.CAP_PROP_POS_FRAMES) == cap.get(cv2.CAP_PROP_FRAME_COUNT):  # 如果当前帧等于总帧数,即视频播放到了结尾cap.set(cv2.CAP_PROP_POS_FRAMES, 0)  # 让当前帧为0,重置视频从头开始# 返回帧图像是否读取成功success,读取的帧图像imgsuccess, img = cap.read()#(4)关键点检测,不绘制关键点及连线img, faces = detector.findFaceMesh(img, draw=False) # img为绘制关键点后的图像,faces为关键点坐标# 如果检测到关键点了就接下去处理if faces:face = faces[0]  # face接收一张脸的所有关键点信息,且faces是三维列表# 遍历所有的眼部关键点for id in idList:cv2.circle(img, tuple(face[id]), 4, (0,255,255), cv2.FILLED)  # 以关键点为圆心半径为5画圆# 由于人脸距离摄像机的距离的远近会影响眼部上下边界之间的距离,因此通过开合比例来判断是否眨眼leftUp = tuple(face[159])  # 左眼最上边界的关键点坐标leftDown = tuple(face[23])  # 左眼最下边界的关键点坐标leftLeft = tuple(face[130])  # 左眼最左边界的关键点坐标leftRight = tuple(face[243])  # 左眼最右边界的关键点坐标# 计算上下边界以及左右边界的距离,函数返回距离length,以及线段两端点和中点坐标,这里用不到lengthver, __ = detector.findDistance(leftUp, leftDown)  # 上下边界距离lengthhor, __ = detector.findDistance(leftLeft, leftRight)  # 左右边界距离# 在上下及左右关键点之间各画一条线段,端点坐标是元组cv2.line(img, leftUp, leftDown, colorblink, 2)cv2.line(img, leftLeft, leftRight, colorblink, 2)# 计算竖直距离与水平距离的比值ratio = 100*lengthver/lengthhorratioList.append(ratio)  # 存放每帧图像的人眼开合比#(5)平滑实时变化曲线# 每10帧图像的人眼开合百分比取平均值,在图像上更新一个值if len(ratioList) > 10:ratioList.pop(0)  # 删除最前面一个元素ratioAvg = sum(ratioList)/len(ratioList)  # 超过10帧后,保证每10个元素取平均print(ratioAvg)#(6)眨眼计数器# 当开合比低于26%并且距前一次计算眨眼已超过10帧时,认为是眨眼if ratioAvg < 26 and counter == 0:blinkCounter += 1  # 眨眼次数加1colorblink = (0,255,0)  # 眨眼时改变当前帧的颜色counter = 1  # 当前帧计算了一次眨眼# 保证在一次眨眼期间只在10帧中计算一次,不再多余的计算眨眼次数if counter != 0:  # 代表前面某10帧已经计算过一次眨眼counter += 1  # 累计不计算眨眼次数的帧数+1if counter > 10:  # 如果距前一次计算眨眼次数超过10帧了,那么下一次可以计算眨眼counter = 0colorblink = (255,0,255)  # 不眨眼时计数器颜色回到原颜色# 以时间为x轴,百分比为y轴,绘制实时变化曲线imgPlot = plotY.update(ratioAvg, colorblink)  # 参数:每个时刻的y值,线条颜色# 将计数显示在图上cvzone.putTextRect(img, f'BlinkCount:{blinkCounter}',  # 显示内容是字符串类型(850,80), 4, 3, colorR=colorblink) # 设置文本显示位置、颜色、线条# 重塑图像的宽和高,保证图像size和曲线图size一致img = cv2.resize(img, (640,360))  # 将变化曲线和原图像组合起来,2代表排成两列,最后一个1代表比例不变imgStack = cvzone.stackImages([img, imgPlot], 2, 1)#(7)显示图像cv2.imshow('img', imgStack)  # 传入窗口名和帧图像# 每帧图像滞留20毫秒后消失,按下键盘上的ESC键退出程序if cv2.waitKey(20) & 0xFF == 27:break# 释放视频资源
cap.release()
cv2.destroyAllWindows()

效果图如下:眨眼时,曲线和计数器变成绿色

【机器视觉案例】(11) 眨眼计数器,人脸关键点检测,附python完整代码相关推荐

  1. 【MediaPipe】(3) AI视觉,人脸识别,附python完整代码

    各位同学好,今天和大家分享一下如何使用MediaPipe完成人脸实时跟踪检测,先放张图看效果,FPS值为14,右侧的输出为:每帧图像是人脸的概率,检测框的左上角坐标及框的宽高. 有需要的可以使用 cv ...

  2. 【机器视觉案例】(5) AI视觉,手势调节物体尺寸,附python完整代码

    各位同学好,今天和大家分享一下如何使用opencv+mediapipe完成远程手势调节图片尺寸的案例.先放张图看效果.当拇指和食指竖起时,根据食指间的连线的长度自由缩放图片尺寸.图片的中点始终位于指尖 ...

  3. 【机器视觉案例】(5) AI视觉,远程手势控制虚拟计算器,附python完整代码

    各位同学好,今天和大家分享一下如何使用MediaPipe+Opencv完成虚拟计算器,先放张图看效果.FPS值为29,食指和中指距离小于规定阈值则认为点击按键,为避免重复数字出现,规定每20帧可点击一 ...

  4. 【机器视觉案例】(13) 脸部和摄像机间的距离测量,自适应文本大小,附python完整代码

    各位同学好,今天和大家分享一下如何使用 opencv+Mediapipe 测量人脸和摄像机镜头之间的距离,并创建一块根据人脸距离变化的文本框.先放张图看效果. 左图是视频图像,显示人脸和摄像机之间的距 ...

  5. 【机器视觉案例】(15) 虚拟答题板,手部关键点识别,附python完整代码

    各位同学好,今天和大家分享一下如何使用 opencv+Mediapipe 制作虚拟问答,先放张图看效果. 当食指在某个答案框内部,并且食指指尖和中指指尖之间的距离小于预设值,那么就认为是点击该答案框, ...

  6. 【机器视觉案例】(6) AI视觉,距离测量,自制AI小游戏,附python完整代码

    各位同学好,今天和大家分享一下如何使用 opencv + mediapipe 创建一个AI视觉小游戏,先放图看效果. 游戏规则,用手按下屏幕上的圆形按钮,每按一次后松开,按钮就随机出现在屏幕上的一个位 ...

  7. 【机器视觉案例】(16) 自制视觉小游戏--桌上冰球,附python完整代码

    大家好,今天和各位分享一下如何使用 mediapipe+opencv 制作桌上冰球的交互式小游戏.先放张图看效果. 规则如下:左手控制白色球拍:右手控制紫色球拍:球拍只能上下移动:红色圆形就是冰球:球 ...

  8. 【机器视觉案例】(10) AI视觉搭积木,手势移动虚拟物体,附python完整代码

    各位同学好,今天和大家分享一下如何使用 opencv+mediapipe 完成手势移动虚拟物体,可自定义各种形状的物体,通过手势搭积木.先放张图看效果. 规则:当食指在某个物体内部,并且中指指尖和食指 ...

  9. 【机器视觉案例】(9) AI视觉,手势控制电脑键盘,附python完整代码

    各位同学好,今天和大家分享一下如何使用 opencv+mediapipe 完成远程手势控制电脑键盘.感兴趣的可以看一下我前面一篇手势控制电脑鼠标:https://blog.csdn.net/dgvv4 ...

最新文章

  1. 收藏一波:常用正则表达式公式总结
  2. hihocoder 1490 Tree Restoration
  3. tip use view.isineditmode() in your custom views to skip code when shown in eclipse
  4. Pycharm增加新安装Python的路径
  5. jquery input值改变事件_前端技术--JQuery
  6. JAVA面试常考系列二
  7. 2018年全球智能手机销售收入增至5220亿美元 但销量却下降了
  8. ARM 指令集版本和ARM 版本z
  9. centos php7 无法加载mysqli_Linux下安装PHP7+MySQL
  10. 【渝粤教育】国家开放大学2018年春季 0104-21T酒店安全管理 参考试题
  11. CMake Error at CMakeLists.txt:52 (PROJECT): No CMAKE_CXX_COMPILER could be found.
  12. tp 数据库查询排序_ThinkPHP对查询的数据随机排序
  13. 滚动条插件better-scroll(BScroll)的使用
  14. Windows中使用pip下载任何包都报错
  15. SSM整合练习:记账管理
  16. 2018 ACM-ICPC南京网络赛 Magical Girl Haze(分层最短路)
  17. (附源码)ssm校园交流网站 毕业设计 261624
  18. SEO的外链与描文本该如何添加?
  19. 微信小程序 web-view 在ios显示空白页面
  20. 打开envi出现警告

热门文章

  1. Toast 位置的改变 和 Toast的简单用法
  2. react 子组件获取变量属性值
  3. php+文件+加密+原理,PHP的加密方式及原理
  4. 2022-2028年中国出版业投资分析及前景预测报告(全卷)
  5. C++ 笔记(16)— 类和对象(类定义、类实例对象定义、访问类成员、类成员函数、类 public/private/protected 成员、类对象引用和指针)
  6. SharePreference工具类
  7. jQuery-1.9.1源码分析系列(四) 缓存系统
  8. log4cxx第三篇----使用多个logger
  9. sql server 2008学习10 存储过程
  10. DataGridView打印类