活体检测(点头,摇头,张嘴等动态识别)

活体检测(点头,摇头,张嘴等动态识别)

在这里插入图片描述

某本书里有一句话,等我去读、去拍案。
田间的野老,等我去了解、去惊识。
山风与发,冷泉与舌,
流云与眼,松涛与耳,
他们等着,在神秘的时间的两端等着,
等着相遇的一刹——
一旦相遇,就不一样了,永远不一样了。
—— 张晓风 《我还有一片风景要完成》

背景说明

为什么会写这篇文章,在这里,向大家做一个解释,也作为一个开场白,供大家参考,知道这篇文章会涉及那些内容:

1.对于人脸识别打卡,存在照片打卡,没有活体检测

2.目前的活体检测服务售价高,造成开发成本

3.自主集成度低,不能随意的修改变化,响应业务需求

因此,在这里写下这篇文章,但是,由于活体检测的算法以及核心代码是一位github的技术大佬,在这里向前辈致敬!

Live_Detection https://github.com/echo1118/Live_Detection

代码实现

from scipy.spatial import distance as dist
from imutils.video import FileVideoStream
from imutils.video import VideoStream
from imutils import face_utils
import argparse
import imutils
import time
import dlib
import cv2
import numpy as npdef eye_aspect_ratio(eye):# 计算眼睛的两组垂直关键点之间的欧式距离A = dist.euclidean(eye[1], eye[5])  # 1,5是一组垂直关键点B = dist.euclidean(eye[2], eye[4])  # 2,4是一组# 计算眼睛的一组水平关键点之间的欧式距离C = dist.euclidean(eye[0], eye[3])  # 0,3是一组水平关键点# 计算眼睛纵横比ear = (A + B) / (2.0 * C)# 返回眼睛纵横比return eardef mouth_aspect_ratio(mouth):# 默认二范数:求特征值,然后求最大特征值得算术平方根A = np.linalg.norm(mouth[2] - mouth[9])  # 51, 59(人脸68个关键点)B = np.linalg.norm(mouth[4] - mouth[7])  # 53, 57C = np.linalg.norm(mouth[0] - mouth[6])  # 49, 55mar = (A + B) / (2.0 * C)return mardef nose_jaw_distance(nose, jaw):# 计算鼻子上一点"27"到左右脸边界的欧式距离face_left1 = dist.euclidean(nose[0], jaw[0])  # 27, 0face_right1 = dist.euclidean(nose[0], jaw[16])  # 27, 16# 计算鼻子上一点"30"到左右脸边界的欧式距离face_left2 = dist.euclidean(nose[3], jaw[2])  # 30, 2face_right2 = dist.euclidean(nose[3], jaw[14])  # 30, 14# 创建元组,用以保存4个欧式距离值face_distance = (face_left1, face_right1, face_left2, face_right2)return face_distancedef eyebrow_jaw_distance(leftEyebrow, jaw):# 计算左眉毛上一点"24"到左右脸边界的欧式距离(镜像对称)eyebrow_left = dist.euclidean(leftEyebrow[2], jaw[0])  # 24, 0eyebrow_right = dist.euclidean(leftEyebrow[2], jaw[16])  # 24, 16# 计算左右脸边界之间的欧式距离left_right = dist.euclidean(jaw[0], jaw[16])  # 0, 16# 创建元组,用以保存3个欧式距离值eyebrow_distance = (eyebrow_left, eyebrow_right, left_right)return eyebrow_distance# 构造参数解析并解析参数
def Face_Recognize(file_path):EYE_AR_THRESH = 0.27  # 眨眼阈值EYE_AR_CONSEC_FRAMES =2  # 闭眼次数阈值# 张嘴阈值MAR_THRESH = 0.5# 初始化眨眼帧计数器和总眨眼次数COUNTER_EYE = 0TOTAL_EYE = 0# 初始化张嘴帧计数器和总张嘴次数COUNTER_MOUTH = 0TOTAL_MOUTH = 0# 初始化摇头帧计数器和摇头次数distance_left = 0distance_right = 0TOTAL_FACE = 0# 初始化点头帧计数器和点头次数nod_flag = 0TOTAL_NOD = 0# 初始化dlib的人脸检测器(基于HOG),然后创建面部界标预测器print("[Prepare000] 加载面部界标预测器...")# 表示脸部位置检测器detector = dlib.get_frontal_face_detector()# 表示脸部特征位置检测器predictor = dlib.shape_predictor("./static/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"]# 鼻子的索引(nStart, nEnd) = face_utils.FACIAL_LANDMARKS_IDXS["nose"]# 下巴的索引(jStart, jEnd) = face_utils.FACIAL_LANDMARKS_IDXS['jaw']# 左眉毛的索引(Eyebrow_Start, Eyebrow_End) = face_utils.FACIAL_LANDMARKS_IDXS['left_eyebrow']# 启动视频流线程print("[Prepare111] 启动视频流线程...")print("[Prompt information] 按Q键退出...")vs = FileVideoStream(file_path).start()fileStream = Truetime.sleep(1.0)# 循环播放视频流中的帧while True:# 如果这是一个文件视频流,那么我们需要检查缓冲区中是否还有更多的帧需要处理if fileStream and not vs.more():break# 从线程视频文件流中获取帧,调整大小并将其转换为灰度通道frame = vs.read()frame = imutils.resize(frame, width=600)gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)# 在灰度框中检测人脸rects = detector(gray, 0)# 循环人脸检测for rect in rects: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)# 提取嘴唇坐标,然后使用该坐标计算嘴唇纵横比Mouth = shape[mStart:mEnd]mouthMAR = mouth_aspect_ratio(Mouth)# 提取鼻子和下巴的坐标,然后使用该坐标计算鼻子到左右脸边界的欧式距离nose = shape[nStart:nEnd]jaw = shape[jStart:jEnd]NOSE_JAW_Distance = nose_jaw_distance(nose, jaw)# 提取左眉毛的坐标,然后使用该坐标计算左眉毛到左右脸边界的欧式距离leftEyebrow = shape[Eyebrow_Start:Eyebrow_End]Eyebrow_JAW_Distance = eyebrow_jaw_distance(leftEyebrow, jaw)# 对左右两只眼睛的纵横比取平均值ear = (leftEAR + rightEAR) / 2.0# 移植嘴唇纵横比mar = mouthMAR# 移植鼻子到左右脸边界的欧式距离face_left1 = NOSE_JAW_Distance[0]face_right1 = NOSE_JAW_Distance[1]face_left2 = NOSE_JAW_Distance[2]face_right2 = NOSE_JAW_Distance[3]# 移植左眉毛到左右脸边界的欧式距离,及左右脸边界之间的欧式距离eyebrow_left = Eyebrow_JAW_Distance[0]eyebrow_right = Eyebrow_JAW_Distance[1]left_right = Eyebrow_JAW_Distance[2]# 判断眼睛纵横比是否低于眨眼阈值,如果是,则增加眨眼帧计数器if ear < EYE_AR_THRESH:COUNTER_EYE += 1# 否则,眼睛的纵横比不低于眨眼阈值else:# 如果闭上眼睛的次数足够多,则增加眨眼的总次数if COUNTER_EYE >= EYE_AR_CONSEC_FRAMES:TOTAL_EYE += 1# 重置眼框计数器COUNTER_EYE = 0# 判断嘴唇纵横比是否高于张嘴阈值,如果是,则增加张嘴帧计数器if mar > MAR_THRESH:COUNTER_MOUTH += 1# 否则,嘴唇的纵横比低于或等于张嘴阈值else:# 如果张嘴帧计数器不等于0,则增加张嘴的总次数if COUNTER_MOUTH != 0:TOTAL_MOUTH += 1COUNTER_MOUTH = 0# 根据鼻子到左右脸边界的欧式距离,判断是否摇头# 左脸大于右脸if face_left1 >= face_right1 + 2 and face_left2 >= face_right2 + 2:distance_left += 1# 右脸大于左脸if face_right1 >= face_left1 + 2 and face_right2 >= face_left2 + 2:distance_right += 1# 左脸大于右脸,并且右脸大于左脸,判定摇头if distance_left != 0 and distance_right != 0:TOTAL_FACE += 1distance_right = 0distance_left = 0# 两边之和是否小于或等于第三边+阈值,来判断是否点头# 根据左眉毛到左右脸边界的欧式距离与左右脸边界之间的欧式距离作比较,判断是否点头if eyebrow_left + eyebrow_right <= left_right + 3:nod_flag += 1if nod_flag != 0 and eyebrow_left + eyebrow_right >= left_right + 3:TOTAL_NOD += 1nod_flag = 0# 画出画框上眨眼的总次数以及计算出的帧的眼睛纵横比cv2.putText(frame, "Blinks: {}".format(TOTAL_EYE), (10, 30),cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)# 画出张嘴的总次数以及计算出的帧的嘴唇纵横比cv2.putText(frame, "Mouth is open: {}".format(TOTAL_MOUTH), (10, 60),cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)# 画出摇头次数cv2.putText(frame, "shake one's head: {}".format(TOTAL_FACE), (10, 90),cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)# 画出点头次数cv2.putText(frame, "nod: {}".format(TOTAL_NOD), (10, 120),cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)# 活体检测cv2.putText(frame, "Live detection: wink(5)", (300, 30),cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)if TOTAL_EYE >= 5:  # 眨眼五次cv2.putText(frame, "open your mouth(3)", (300, 60),cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)if TOTAL_MOUTH >= 3:  # 张嘴三次cv2.putText(frame, "shake your head(2)", (300, 90),cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)if TOTAL_FACE >= 2:  # 摇头两次cv2.putText(frame, "nod(2)", (300, 120),cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)if TOTAL_NOD >= 2:  # 点头两次cv2.putText(frame, "Live detection: done", (300, 150),cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)# 展示窗口cv2.imshow("Frame", frame)key = cv2.waitKey(1) & 0xFF# Q键退出if key == ord("q"):break# 撕毁窗口cv2.destroyAllWindows()vs.stop()

这段Python代码实现了一个基于计算机视觉的人脸动作识别系统,主要用于实时分析视频流中的人脸行为,如眨眼、张嘴、摇头和点头等动作。

  1. 导入必要的库:

    • scipy.spatial.distance用于计算两个点之间的欧氏距离。
    • imutils.video.FileVideoStreamimutils.video.VideoStream用于处理视频流。
    • imutils.face_utils包含一些方便的人脸特征点操作函数。
    • 其他库包括OpenCV (cv2)、NumPy (numpy) 和时间 (time) 等,用于图像处理和程序控制。
  2. 定义了几个辅助函数:

    • eye_aspect_ratio(eye):计算眼睛的纵横比(EAR),这是评估眼睛是否闭合的关键指标。
    • mouth_aspect_ratio(mouth):计算嘴巴的纵横比(MAR),用来判断嘴巴是否张开。
    • nose_jaw_distance(nose, jaw):计算鼻子到左右脸颊边界的距离,用于估计头部运动。
    • eyebrow_jaw_distance(leftEyebrow, jaw):计算眉毛到左右脸颊边界的距离,辅助判断点头动作。
  3. 主函数Face_Recognize(file_path)接收一个视频文件路径作为输入:

    • 设置了几个阈值变量,如眨眼、张嘴的阈值等。
    • 使用dlib库加载预训练的人脸检测器和面部特征预测器模型。
    • 获取人脸特征点的索引范围(例如眼睛、嘴巴、鼻子、下巴和眉毛)。
  4. 开启视频流,读取每一帧图像并执行以下步骤:

    • 对每帧图像进行灰度处理,并使用dlib检测人脸。
    • 遍历检测到的人脸,利用面部特征预测器提取68个特征点。
    • 计算眼睛和嘴巴的纵横比以及其他几何特征。
    • 根据预先设定的阈值,累计眨眼和张嘴的动作次数。
    • 利用鼻子和下巴的位置变化判断摇头动作。
    • 根据眉毛位置判断点头动作。
  5. 将检测到的动作次数实时显示在视频帧上,并通过键盘事件监听用户按键,当按下q时退出程序。

这就是主要方法,在原来的方法上进行了修改,取消了摄像头捕获视频,采用API接口调用传参,更加灵活!但是,对于传参方式以及效率问题,目前也是在进行思考,是否采用实时视频流进行传输还是怎么弄,目前没有确定,现在,我们这里就是给大家一个基础的核心解决方法!就是前端传递视频地址到后端,后端对于视频画像进行解析,以此进行活体判断,当然,必然存在不足,目前本人技术尚浅,后期再做深入!

API接口

import base64
from flask import Flask, request
import numpy as np
import cv2
import imutils
import dlib
from imutils import face_utilsimport DynamicRecognitionapp = Flask(__name__)@app.route('/process_video', methods=['GET'])
def process_video():# 从前端获取视频文件地址file_path = request.args.get('file_path')# 视频分析DynamicRecognition.Face_Recognize(file_path)# 返回处理后的结果(可以是JSON格式)return {"result": "Processed successfully"}if __name__ == '__main__':app.run(debug=True)

效果测试

在这里插入图片描述

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/736185.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

21、状态模式(行为性模式)

版本一、get状态指针 #include <iostream> using namespace std;//前置声明 class Context;//状态 class State{ public://4个状态virtual void toUp (Context& context){ }virtual void toDown (Context& context){ }virtual void toLeft (Context& cont…

架构学习总结:企业架构=业务+数据+技术+应用架构

最近再次研读DAMA数据管理知识体系,结合工作对什么是企业架构?如何开展企业架构设计工作有一些新的认识,供大家参考。企业架构包括企业的业务架构、数据架构、技术架构和应用架构,要想做好企业的信息化数字化建设规划,这四个架构都不可缺少,这四个方面的内容共同组成了企…

[云原生] k8s配置资源管理

一、Secret的资源配置 1.1 Secret配置的相关说明 Secret 是用来保存密码、token、密钥等敏感数据的 k8s 资源&#xff0c;这类数据虽然也可以存放在 Pod 或者镜像中&#xff0c;但是放在 Secret 中是为了更方便的控制如何使用数据&#xff0c;并减少暴露的风险。 Secret 有…

【JavaSE】抽象类与接口

Object 类 类 java.lang.Object是类层次结构的根类&#xff0c;即所有类的父类。 除Object类之外的任何一个Java类&#xff0c;全部直接或间接的继承于Object类。由此&#xff0c;Object类也被称为根父类。Object类中声明的成员具有通用性&#xff0c;并且Object类中没有声明…

300W-500W-700W-1000W超薄制动电阻

EAK制动电阻&#xff0c;最大连续功率&#xff1a;300 W--1000W 制动电阻器&#xff0c;用于带有中低功率变频器 或作为充电电阻器的驱动器。 安装在变频器附近。 防护等级 IP 20 / IP 54 可根据要求提供更高的防护等级 测试电压 2.5 kV AC 可根据要求提供其他容量和安装…

SMART PLC自适应低通滤波器(收放卷线速度滤波)

一阶低通滤波器更多内容请参考信号处理专栏相关文章,常用链接如下: 1、SMART PLC 低通滤波器和模拟量采集应用 https://rxxw-control.blog.csdn.net/article/details/136595982https://rxxw-control.blog.csdn.net/article/details/1365959822、SMART PLC双线性变换和后向差…

Docker笔记-进入运行中的镜像,查看日志等操作

docker搭建好后&#xff0c;查看运行的docker镜像&#xff1a; docker ps -a 进入运行的容器&#xff0c;命令如下&#xff1a; docker exec -it <容器ID> /bin/bash # 或者&#xff0c;直接用容器里面的命令&#xff0c;比如mysql镜像 docker exec -it <容器ID>…

网络协议常见问题

网络协议常见问题 OSI&#xff08;Open Systems Interconnection&#xff09;模型OSI 封装 TCP/IP协议栈IP数据报的报头TCP头格式UDP头格式TCP (3-way shake)三次握手建立连接&#xff1a;为什么三次握手才可以初始化 Socket、序列号和窗口大小并建立 TCP 连接。每次建立TCP连接…

蓝桥杯单片机---第十二届省赛题目解析

文章目录 比赛题目一、代码相关定义、声明1.头文件声明2.变量声明 二、主要函数1.main函数2.按键扫描3.数码管显示4.电压模式1、2输出 & LED显示5.定时器中断6.消除85C显示 三、次要函数1.初始化函数Init2.按键函数Key3.LED函数Led4.数码管函数Seg5.iic函数中6.onewire函数…

【LeetCode】17.电话号码的字母组合

题目 链接&#xff1a;17. 电话号码的字母组合 - 力扣&#xff08;LeetCode&#xff09; 给定一个仅包含数字2-9的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按任意顺序返回 给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何…

Vue ECharts line3D点击空白处重置图表视角- 附完整示例

ECharts&#xff1a;一个基于 JavaScript 的开源可视化图表库。 目录 效果 一、介绍 1、官方文档&#xff1a;Apache ECharts 2、官方示例 二、准备工作 1、安装依赖包 2、示例版本 三、使用步骤 1、在单页面引入 echarts 2、指定容器并设置容器宽高 3、数据处理&…

leetcode 热题 100_反转链表

题解一&#xff1a; 迭代&#xff1a;逐步修改节点指针&#xff0c;注意在修改前要保存下一个节点指针。 class Solution {public ListNode reverseList(ListNode head) {ListNode pre null;while (head! null) {ListNode temp head.next;head.next pre;pre head;head te…

使用Canvas绘制一个自适应长度的折线图

要求x轴根据数据长度自适应 y轴根据数据最大值取长度值 <template><div ref"cvsContainer" class"cvs-container"><canvas ref"cvs" class"canvas"></canvas></div> </template><script set…

Chapter20-Ideal gases-CIE课本要点摘录、总结

20.1 Particles of a gas Brownian motion Fast modules 速率的数值大概了解下&#xff1a; average speed of the molecules:400m/s speed of sound:approximately 330m/s at STP&#xff08;standard temperature and pressure&#xff09; Standard Temperature and Pres…

计算表达式x*(2^i)的值math.ldexp(x, i)

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 计算表达式x*(2^i)的值 math.ldexp(x, i) [太阳]选择题 关于以下代码输出的结果说法正确的是&#xff1f; import math print("【执行】math.ldexp(3,2)") print(math.ldexp(3,2)) …

HTML5+CSS3+移动web——CSS基础

系列文章目录 HTML5CSS3移动web——HTML 基础-CSDN博客https://blog.csdn.net/ymxk2876721452/article/details/136070953?spm1001.2014.3001.5501HTML5CSS3移动web——列表、表格、表单-CSDN博客https://blog.csdn.net/ymxk2876721452/article/details/136221443?spm1001.2…

【Spring云原生系列】SpringBoot+Spring Cloud Stream:消息驱动架构(MDA)解析,实现异步处理与解耦合

&#x1f389;&#x1f389;欢迎光临&#xff0c;终于等到你啦&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;持续更新的专栏《Spring 狂野之旅&#xff1a;从入门到入魔》 &a…

wordpress博客趣主题个人静态网页模板

博客趣页面模板适合个人博客&#xff0c;个人模板等内容分享。喜欢的可以下载套用自己熟悉的开源程序建站。 博客趣主题具有最小和清洁的设计&#xff0c;易于使用&#xff0c;并具有有趣的功能。bokequ主题简约干净的设计、在明暗风格之间进行现场切换。 下载地址 清新个人…

【金三银四】Spring面试题

目录 1、什么是Spring2、说一下Spring的IOC3、Spring的AOP4、连接点&#xff1f;切入点&#xff1f;5、Spring AOP 是通过什么实现的6、Spring Bean的生命周期是怎么样的&#xff1f;7、Spring Bean的初始化过程是怎么样8、Spring的事务传播机制有哪些&#xff1f;9、Autowired…

割点原理及封装好的割点类

作者推荐 视频算法专题 预备知识 本分析针对&#xff1a;连通无向图G。 搜索树 节点的父子关系&#xff1a;任意 节点的邻接 节点除了已处理 节点&#xff0c;都是它的子 节点。 以任意一点为根开始DFS&#xff0c;计算所有 节点的父子关系。只保留个子 节点到父 节点形成…