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

news2024/11/17 21:23:12

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

在这里插入图片描述

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

背景说明

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

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 np


def 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 ear


def mouth_aspect_ratio(mouth):
    # 默认二范数:求特征值,然后求最大特征值得算术平方根
    A = np.linalg.norm(mouth[2] - mouth[9])  # 51, 59(人脸68个关键点)
    B = np.linalg.norm(mouth[4] - mouth[7])  # 53, 57
    C = np.linalg.norm(mouth[0] - mouth[6])  # 49, 55
    mar = (A + B) / (2.0 * C)

    return mar


def nose_jaw_distance(nose, jaw):
    # 计算鼻子上一点"27"到左右脸边界的欧式距离
    face_left1 = dist.euclidean(nose[0], jaw[0])  # 27, 0
    face_right1 = dist.euclidean(nose[0], jaw[16])  # 27, 16
    # 计算鼻子上一点"30"到左右脸边界的欧式距离
    face_left2 = dist.euclidean(nose[3], jaw[2])  # 30, 2
    face_right2 = dist.euclidean(nose[3], jaw[14])  # 30, 14
    # 创建元组,用以保存4个欧式距离值
    face_distance = (face_left1, face_right1, face_left2, face_right2)

    return face_distance


def eyebrow_jaw_distance(leftEyebrow, jaw):
    # 计算左眉毛上一点"24"到左右脸边界的欧式距离(镜像对称)
    eyebrow_left = dist.euclidean(leftEyebrow[2], jaw[0])  # 24, 0
    eyebrow_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 = 0
    TOTAL_EYE = 0
    # 初始化张嘴帧计数器和总张嘴次数
    COUNTER_MOUTH = 0
    TOTAL_MOUTH = 0
    # 初始化摇头帧计数器和摇头次数
    distance_left = 0
    distance_right = 0
    TOTAL_FACE = 0
    # 初始化点头帧计数器和点头次数
    nod_flag = 0
    TOTAL_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 = True

    time.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 += 1
                    COUNTER_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 += 1
                distance_right = 0
                distance_left = 0

            # 两边之和是否小于或等于第三边+阈值,来判断是否点头
            # 根据左眉毛到左右脸边界的欧式距离与左右脸边界之间的欧式距离作比较,判断是否点头
            if eyebrow_left + eyebrow_right <= left_right + 3:
                nod_flag += 1
            if nod_flag != 0 and eyebrow_left + eyebrow_right >= left_right + 3:
                TOTAL_NOD += 1
                nod_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_utils

import DynamicRecognition

app = 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.coloradmin.cn/o/1506308.html

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

相关文章

Spring Boot整合MyBatis Plus配置多数据源

Spring Boot 专栏&#xff1a;https://blog.csdn.net/dkbnull/category_9278145.html Spring Cloud 专栏&#xff1a;https://blog.csdn.net/dkbnull/category_9287932.html GitHub&#xff1a;https://github.com/dkbnull/SpringBootDemo Gitee&#xff1a;https://gitee.com/…

Lego-loam 算法三维建图

运行环境 Linux&#xff1a;Ubuntu18.04ros&#xff1a;MelodicCeres Solver 2.0.0&#xff08;Ubuntu18.04安装Ceres&#xff09;PCL 1.8.1&#xff08;Ubuntu系统的PCL、Eigen卸载和安装&#xff09; 运行数据集 lego-loam 39/39 终端一&#xff1a;进入catkin_ws工作空间…

二,几何相交---4,BO算法---(1)接近性和可分离性

提了三个观点 1&#xff0c;如果一条直线&#xff08;比如竖直&#xff09;可以分开两个线段&#xff0c;则这两个线段不相交 2&#xff0c;只需要观察与隔离线相交的几个线段 3&#xff0c;从左向右扫描线只需要观察每个线段的两个端点和一些可能的相交点。

elasticsearch篇:RestClient操作

1. RestClient ES官方提供了各种不同语言的客户端&#xff0c;用来操作ES。这些客户端的本质就是组装DSL语句&#xff0c;通过http请求发送给ES。官方文档地址&#xff1a;Elasticsearch Clients | Elastic 其中的Java Rest Client又包括两种&#xff1a; Java Low Level Res…

十大排序算法(冒泡排序、插入排序、选择排序、希尔排序、堆排序、快排、归并排序、桶排序、计数排序、基数排序)

目录 一、冒泡排序&#xff1a; 二、插入排序&#xff1a; 三、选择排序&#xff1a; 四、希尔排序&#xff1a; 五、堆排序&#xff1a; 六、快速排序&#xff1a; 6.1挖坑法&#xff1a; 6.2左右指针法 6.3前后指针法&#xff1a; 七、归并排序&#xff1a; 八、桶…

中医把脉笔记

目录 寸关尺对应的五脏六腑自己给自己把脉把脉五布法定寸关尺分浮中沉分快慢辨阴阳看虚实 参考文章 寸关尺对应的五脏六腑 自己给自己把脉 up主道道总是睡不着的把脉教学视频 用中指按住小骨头下面一点&#xff0c;这是关脉&#xff0c;左手的关脉对应肝脏。 把脉五布法 定…

【脚本玩漆黑的魅影】冠军之路全自动练级

文章目录 原理全部代码 原理 没有新东西。 安全区选在了这里&#xff0c;上面是洞口&#xff0c;下面是治疗。 因为逛的区域大了点&#xff0c;这次用跑的。&#xff08;好像之前也能跑&#xff09; def chu_qu(): # 右逛c.press(B)press(UP, 0.4)c.release(B) def hui_qu()…

【Lazy ORM】 小工具 acw 本地客户端 你负责点击页面,他负责输出代码

介绍 wu-smart-acw-client 简称acw-client&#xff0c;是一个基于Lazy ORM定制的客户端代码生成小工具 Lazy ORM 小工具 acw 本地客户端 你负责点击页面&#xff0c;他负责输出代码安装 <dependency><groupId>top.wu2020</groupId><artifactId>wu-sma…

2024年腾讯云99元1年服务器_新老同享_续费99元一年

良心腾讯云推出99元一年服务器&#xff0c;新用户和老用户均可以购买&#xff0c;续费不涨价&#xff0c;续费也是99元&#xff0c;配置为轻量2核2G4M、50GB SSD盘、300GB月流量、4M带宽&#xff1a;优惠价格99元一年&#xff0c;续费99元&#xff0c;官方活动页面 txybk.com/g…

MySQL入门到中级知识汇总2024

文章目录 1.揭开MySQL的神秘面纱2. SQL的基本命令实操3. 数据库的备份与恢复4. MySQL常用的数据类型&#xff08;列类型&#xff09;5. 数据类型之小数类型的使用6. 表的创建7. 表的修改8. mysql事务9. mysql表类型和存储引擎10. mysql的视图11. mysql的管理 1.揭开MySQL的神秘…

OpenHarmony教程指南—性能示例(renderGroup接口)

性能示例 介绍 本示例使用renderGroup接口实现了在单一页面上存在大量应用动效的组件时&#xff0c;使用renderGroup方法来解决卡顿问题&#xff0c;从而提升绘制能力。 renderGroup原理说明&#xff1a;首次绘制组件时&#xff0c;若组件被标记为启用renderGroup状态&#…

考研常识 | 专业硕士与学术硕士的11个区别

专业硕士与学术硕士的11个区别 对于考研学子而言&#xff0c;了解专业学位与学术学位的区别&#xff0c;是报考的第一步。学术学位研究生一般都是全日制的&#xff0c;而专业学位研究生的学习方式还分为即全日制与非全日制两种。这篇文章将带大家认识全日制专业学位与全日制学术…

Visual C++ 2010学习版安装教程

1. 创建项目 点击 “创建新项目”&#xff0c;创建一个项目。 2. 创建 helloworld.c ⽂件 3. 在弹出的编辑框中&#xff0c;选中 “C文件(.cpp)”&#xff0c;将 下方 “源.cpp” 手动改为要新创建的文件名。 如&#xff1a;helloWorld.c 。注意&#xff0c;默认 cpp 后缀名&am…

vue+nodejs超市购物商城在线选品系统wtk87

在此基础上&#xff0c;结合现有超市货品信息管理体系的特点&#xff0c;运用新技术&#xff0c;构建了以 vue为基础的超市货品信息管理信息化管理体系。首先&#xff0c;以需求为依据&#xff0c;根据需求分析结果进行了系统的设计&#xff0c;并将其划分为管理员和用户二种角…

代码随想录算法训练营Day39 || leetCode 762.不同路径 || 63. 不同路径 II

62.不同路径 每一位的结果等于上方与左侧结果和 class Solution { public:int uniquePaths(int m, int n) {vector<vector<int>> dp(m,vector(n,0));for (int i 0; i < m; i) dp[i][0] 1;for (int j 0; j < n; j) dp[0][j] 1;for (int i 1; i < m; …

MySQL-----存储过程

▶ 介绍 存储过程是事先经过编译并存储在数据库中的一段SQL语句的集合&#xff0c;调用存储过程可以简化应用开发人员的很多工作&#xff0c;减少数据在数据库和应用服务器之间的传输&#xff0c;对于提高数据处理的效率是有好处的。 存储过程思想上很简单&#xff0c;…

Vue3 快速上手从0到1,两小时学会【附源码】

小伙伴们好&#xff0c;欢迎关注&#xff0c;一起学习&#xff0c;无限进步 以下内容为vue3的学习笔记 项目需要使用到的依赖 npm install axios npm install nanoid vue-router npm install pinia npm install mitt 源码&#xff1a;Gitee 运行 npm install npm run dev需要运…

蓝桥杯练习系统(算法训练)ALGO-982 最小距离

资源限制 内存限制&#xff1a;256.0MB C/C时间限制&#xff1a;1.0s Java时间限制&#xff1a;3.0s Python时间限制&#xff1a;5.0s 最小距离 问题描述 数轴上有n个数字&#xff0c;求最近的两个数&#xff0c;即min(abs(x-y)) 输入格式 第一行包含一个整数n。   …

IDEA编译安卓源码TVBox

因为电视x受限&#xff0c;无法观看电视直播&#xff0c;为了春晚不受影响&#xff0c;于是网络一顿搜索&#xff0c;试过多个APP&#xff0c;偶尔找到这款开源的TVBox&#xff0c;寒假在家&#xff0c;随便拿来练练手&#xff0c;学习安卓APP的编写&#xff0c;此文做以记录&a…

面向对象的编程语言是什么意思?——跟老吕学Python编程

面向对象的编程语言是什么意思&#xff1f;——跟老吕学Python编程 面向对象是什么意思&#xff1f;面向对象的定义面向对象的早期发展面向对象的背景1.审视问题域的视角2.抽象级别3.封装体4.可重用性 面向对象的特征面向对象的开发方法面向对象程序设计基本思想实现 面向对象的…