使用MediaPipe Face Mesh 面部动作检测

一、技术选型

  1. OpenCV(Open Source Computer Vision Library)

    • 用于视频流捕捉、图像预处理和基本图像处理操作。
  2. MediaPipe

    • 提供高效的人脸检测与关键点提取功能(Face Mesh)。
  3. Python

    • 作为后端开发语言,整合上述库进行图像处理和动作识别。
  4. Flask/Django(可选)

    • 用于构建后端API服务,处理前端请求。

二、整体流程概述

  1. 视频流或图片获取

    • 前端通过摄像头捕捉视频流或图片,并将数据发送至后端。
  2. 图像预处理

    • 对接收到的图像数据进行解码、缩放和颜色空间转换。
  3. 人脸检测与关键点提取

    • 使用 MediaPipe 提取面部关键点(Face Mesh)。
  4. 动作识别

    • 根据关键点数据,分析用户的具体动作(如转头、眨眼、张嘴)。
  5. 结果返回

    • 将识别结果以 JSON 格式返回前端。

三、详细实现步骤

1. 视频流或图片获取

前端(微信小程序)捕捉到视频帧或图片后,通过 API 将图像数据(通常为 Base64 编码或二进制数据)发送至后端。

前端发送图像数据示例(微信小程序)

wx.chooseImage({count: 1,success: function(res) {const tempFilePaths = res.tempFilePaths;wx.getFileSystemManager().readFile({filePath: tempFilePaths[0],encoding: 'base64',success: function(data) {wx.request({url: 'https://localhost/api/task/detect',method: 'POST',data: {user_id: 'unique_user_id',image_data: data.data},success: function(response) {// 处理后端返回的检测结果}});}});}
});
2. 图像预处理

后端接收到图像数据后,进行解码和预处理。

示例代码(Python)

import base64
import cv2
import numpy as npdef decode_image(image_base64):# 解码 Base64 图像数据img_data = base64.b64decode(image_base64)# 转换为 numpy 数组np_arr = np.frombuffer(img_data, np.uint8)# 使用 OpenCV 解码图像img = cv2.imdecode(np_arr, cv2.IMREAD_COLOR)return img
3. 人脸检测与关键点提取

使用 MediaPipe 的 Face Mesh 模型提取面部关键点。

安装 MediaPipe

pip install mediapipe

示例代码(Python)

import mediapipe as mp# 初始化 MediaPipe Face Mesh
mp_face_mesh = mp.solutions.face_mesh
face_mesh = mp_face_mesh.FaceMesh(static_image_mode=False,max_num_faces=1,refine_landmarks=True,min_detection_confidence=0.5,min_tracking_confidence=0.5
)def get_face_landmarks(image):# 将图像从 BGR 转换为 RGBrgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)# 获取关键点results = face_mesh.process(rgb_image)if results.multi_face_landmarks:# 返回第一个人脸的关键点return results.multi_face_landmarks[0]else:return None
4. 动作识别

基于提取的关键点数据,分析用户的具体动作。以下分别介绍左右转头、眨眼和张嘴的检测方法。

4.1 左右转头检测

通过分析左右眼和鼻子的关键点位置,计算头部的旋转角度。

关键点选择

  • 鼻尖(例如 MediaPipe Face Mesh 的 1 号关键点)
  • 左眼外角(例如 33 号关键点)
  • 右眼外角(例如 263 号关键点)

实现步骤

  1. 计算左眼外角与鼻尖的连线向量。
  2. 计算右眼外角与鼻尖的连线向量。
  3. 通过向量之间的角度差,判断头部是否向左或向右转动。

示例代码

import mathdef calculate_angle(p1, p2):# 计算两点连线的角度(相对于水平线)delta_y = p2.y - p1.ydelta_x = p2.x - p1.xangle = math.degrees(math.atan2(delta_y, delta_x))return angledef detect_head_turn(landmarks):# 关键点索引(根据 MediaPipe Face Mesh)nose_tip = landmarks.landmark[1]left_eye_outer = landmarks.landmark[33]right_eye_outer = landmarks.landmark[263]# 计算角度left_angle = calculate_angle(nose_tip, left_eye_outer)right_angle = calculate_angle(nose_tip, right_eye_outer)# 计算平均角度avg_angle = (left_angle + right_angle) / 2# 定义阈值(根据实际测试调整)TURN_LEFT_THRESHOLD = -15  # 向左转头TURN_RIGHT_THRESHOLD = 15  # 向右转头if avg_angle < TURN_LEFT_THRESHOLD:return 'left'elif avg_angle > TURN_RIGHT_THRESHOLD:return 'right'else:return 'straight'
4.2 眨眼检测

通过监测眼睛的纵横比(EAR, Eye Aspect Ratio)来检测眨眼次数。

关键点选择

  • 左眼:关键点 362, 385, 387, 263, 373, 380
  • 右眼:关键点 33, 160, 158, 133, 153, 144

实现步骤

  1. 计算每只眼睛的 EAR。
  2. 当 EAR 低于某个阈值时,判断为闭眼。
  3. 记录眨眼次数。

示例代码

def eye_aspect_ratio(landmarks, eye_indices):# 计算 EAR# eye_indices 包含 6 个关键点的索引p1 = landmarks.landmark[eye_indices[1]]p2 = landmarks.landmark[eye_indices[5]]p3 = landmarks.landmark[eye_indices[2]]p4 = landmarks.landmark[eye_indices[4]]p5 = landmarks.landmark[eye_indices[0]]p6 = landmarks.landmark[eye_indices[3]]# 纵向距离vertical_1 = math.sqrt((p2.x - p4.x)**2 + (p2.y - p4.y)**2)vertical_2 = math.sqrt((p3.x - p5.x)**2 + (p3.y - p5.y)**2)# 横向距离horizontal = math.sqrt((p1.x - p6.x)**2 + (p1.y - p6.y)**2)ear = (vertical_1 + vertical_2) / (2.0 * horizontal)return eardef detect_blink(landmarks, blink_counter, total_blinks):LEFT_EYE = [362, 385, 387, 263, 373, 380]RIGHT_EYE = [33, 160, 158, 133, 153, 144]EAR_THRESHOLD = 0.21  # 根据实际测试调整CONSEC_FRAMES = 3  # 眨眼最少持续的帧数left_ear = eye_aspect_ratio(landmarks, LEFT_EYE)right_ear = eye_aspect_ratio(landmarks, RIGHT_EYE)ear = (left_ear + right_ear) / 2.0if ear < EAR_THRESHOLD:blink_counter += 1else:if blink_counter >= CONSEC_FRAMES:total_blinks += 1blink_counter = 0return blink_counter, total_blinks
4.3 张嘴检测

通过计算嘴部纵横比(MAR, Mouth Aspect Ratio)来检测张嘴动作。

关键点选择

  • 上唇上方(例如 13 号关键点)
  • 下唇下方(例如 14 号关键点)
  • 左嘴角(78 号关键点)
  • 右嘴角(308 号关键点)

实现步骤

  1. 计算嘴部的 MAR。
  2. 当 MAR 超过某个阈值时,判断为张嘴。

示例代码

def mouth_aspect_ratio(landmarks):# 关键点索引(根据 MediaPipe Face Mesh)upper_lip = landmarks.landmark[13]lower_lip = landmarks.landmark[14]left_mouth = landmarks.landmark[78]right_mouth = landmarks.landmark[308]# 纵向距离vertical = math.sqrt((upper_lip.x - lower_lip.x)**2 + (upper_lip.y - lower_lip.y)**2)# 横向距离horizontal = math.sqrt((left_mouth.x - right_mouth.x)**2 + (left_mouth.y - right_mouth.y)**2)mar = vertical / horizontalreturn mardef detect_mouth_open(landmarks, mouth_opened):MAR_THRESHOLD = 0.6  # 根据实际测试调整mar = mouth_aspect_ratio(landmarks)if mar > MAR_THRESHOLD:mouth_opened = Trueelse:mouth_opened = Falsereturn mouth_opened
5. 综合动作识别

将上述各个动作的检测方法整合,形成综合的动作识别流程。

示例代码

def recognize_actions(landmarks, state):# state 包含用于记录眨眼状态的变量,如 blink_counter, total_blinks, mouth_openedaction_results = {}# 检测左右转头head_direction = detect_head_turn(landmarks)action_results['head_turn'] = head_direction# 检测眨眼state['blink_counter'], state['total_blinks'] = detect_blink(landmarks, state['blink_counter'], state['total_blinks'])action_results['blink_count'] = state['total_blinks']# 检测张嘴state['mouth_opened'] = detect_mouth_open(landmarks, state['mouth_opened'])action_results['mouth_opened'] = state['mouth_opened']return action_results
6. 后端 API 实现

使用 Flask 构建后端 API,处理前端请求,执行上述图像处理和动作识别逻辑,并返回结果。

安装 Flask

pip install Flask

示例代码(Flask 应用)

from flask import Flask, request, jsonify
import cv2
import base64
import numpy as npapp = Flask(__name__)# 初始化 MediaPipe Face Mesh
import mediapipe as mp
mp_face_mesh = mp.solutions.face_mesh
face_mesh = mp_face_mesh.FaceMesh(static_image_mode=False,max_num_faces=1,refine_landmarks=True,min_detection_confidence=0.5,min_tracking_confidence=0.5
)# 状态管理(简单示例,实际应用建议使用数据库或缓存)
user_states = {}@app.route('/api/task/detect', methods=['POST'])
def detect_task():data = request.jsonuser_id = data.get('user_id')image_base64 = data.get('image_data')if not user_id or not image_base64:return jsonify({'success': False, 'message': '缺少参数'}), 400# 解码图像try:img = decode_image(image_base64)except Exception as e:return jsonify({'success': False, 'message': '图像解码失败'}), 400# 获取关键点landmarks = get_face_landmarks(img)if not landmarks:return jsonify({'success': False, 'message': '未检测到人脸'}), 200# 初始化用户状态if user_id not in user_states:user_states[user_id] = {'current_step': 1,'blink_counter': 0,'total_blinks': 0,'mouth_opened': False}state = user_states[user_id]current_step = state['current_step']# 识别动作action_results = recognize_actions(landmarks, state)# 判断当前步骤success = Falsenext_task = ''if current_step == 1:if action_results['head_turn'] in ['left', 'right']:success = Truenext_task = '请眨眼'state['current_step'] += 1elif current_step == 2:if action_results['blink_count'] >= 1:success = Truenext_task = '请张嘴'state['current_step'] += 1elif current_step == 3:if action_results['mouth_opened']:success = Truenext_task = '所有任务完成'state['current_step'] += 1else:# 所有任务完成success = Truenext_task = '所有任务已完成'if success:if state['current_step'] > 3:return jsonify({'success': True,'message': '成功完成所有任务','next_task': '完成','current_step': state['current_step']}), 200else:return jsonify({'success': True,'message': '检测成功,进入下一步','next_task': next_task,'current_step': state['current_step']}), 200else:return jsonify({'success': False,'message': '检测失败,请重新开始','current_step': 1}), 200@app.route('/api/task/reset', methods=['POST'])
def reset_task():data = request.jsonuser_id = data.get('user_id')if not user_id:return jsonify({'success': False, 'message': '缺少 user_id'}), 400user_states[user_id] = {'current_step': 1,'blink_counter': 0,'total_blinks': 0,'mouth_opened': False}return jsonify({'success': True,'message': '任务已重置','current_step': 1}), 200def decode_image(image_base64):img_data = base64.b64decode(image_base64)np_arr = np.frombuffer(img_data, np.uint8)img = cv2.imdecode(np_arr, cv2.IMREAD_COLOR)return imgdef get_face_landmarks(image):rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)results = face_mesh.process(rgb_image)if results.multi_face_landmarks:return results.multi_face_landmarks[0]else:return None# 包含上述动作检测函数的 recognize_actions 等if __name__ == '__main__':app.run(host='0.0.0.0', port=5000)

四、示例说明

假设用户正在进行 “检测用户是否向左转头” 的第一步任务:

  1. 前端捕捉图像

    • 用户在小程序中启动任务,摄像头捕捉当前帧,并将图像数据发送至后端的 /api/task/detect 接口。
  2. 后端处理

    • 解码图像数据,并使用 MediaPipe 提取面部关键点。
    • 计算鼻尖与左右眼外角的角度差,判断用户是否向左或向右转头。
    • 假设检测到用户向左转头,更新用户任务状态为第二步。
  3. 后端返回结果

    • 返回 success: true,提示用户进入下一步任务“请眨眼”。
  4. 前端反馈

    • 小程序根据后端返回的结果,更新界面提示用户“请眨眼”,并更新进度条。

具体代码执行过程

  • 用户完成向左转头动作,前端发送图像数据。
  • 后端解码图像,提取关键点,计算角度。
  • 检测到头部向左转动,detect_head_turn 返回 'left'
  • 后端判断当前步骤为 1,检测成功,更新步骤为 2,提示下一步任务。
  • 前端接收到 success: true,显示“请眨眼”。

五、优化与注意事项

  1. 实时性与性能优化

    • 后端:使用异步框架(如 FastAPI)提升并发处理能力;使用 GPU 加速(如 NVIDIA CUDA)提升 MediaPipe 的处理速度。
    • 前端:优化图像上传频率,减少网络传输延迟;使用合适的图像分辨率,平衡识别精度与传输速度。
  2. 准确性提升

    • 调整动作识别的阈值(如 EAR_THRESHOLD、MAR_THRESHOLD),根据实际测试数据进行优化。
    • 使用更多关键点或更复杂的算法(如深度学习模型)提升动作识别的准确性。
  3. 错误处理与用户体验

    • 后端:处理异常情况,如未检测到人脸,返回友好的错误信息。
    • 前端:根据后端返回的错误信息,提供明确的用户指引,如“未检测到人脸,请调整位置并重试”。
  4. 安全性

    • 使用 HTTPS 协议保护数据传输安全。
    • 对上传的图像数据进行限制,防止恶意攻击(如限制图像大小、格式等)。
  5. 扩展性

    • 设计模块化的代码结构,便于后续增加更多动作识别任务。
    • 使用数据库或缓存系统(如 Redis)管理用户状态,支持大规模用户同时使用。

六、扩展示例:添加“微笑”检测

假设需要增加一个新任务,检测用户是否微笑。以下为实现步骤:

  1. 关键点选择

    • 上嘴唇中点(例如 13 号关键点)
    • 下嘴唇中点(例如 14 号关键点)
    • 左嘴角(78 号关键点)
    • 右嘴角(308 号关键点)
  2. 微笑检测逻辑

    def smile_aspect_ratio(landmarks):# 关键点索引upper_lip = landmarks.landmark[13]lower_lip = landmarks.landmark[14]left_mouth = landmarks.landmark[78]right_mouth = landmarks.landmark[308]# 纵向距离vertical = math.sqrt((upper_lip.x - lower_lip.x)**2 + (upper_lip.y - lower_lip.y)**2)# 横向距离horizontal = math.sqrt((left_mouth.x - right_mouth.x)**2 + (left_mouth.y - right_mouth.y)**2)sar = vertical / horizontalreturn sardef detect_smile(landmarks):SAR_THRESHOLD = 0.5  # 根据实际测试调整sar = smile_aspect_ratio(landmarks)return sar > SAR_THRESHOLD
    
  3. 集成到后端 API

    recognize_actions 函数中添加微笑检测逻辑,并在任务流程中增加相应步骤。


后端实现对视频流或图片的人脸检测与动作识别功能,关键在于有效利用 MediaPipe 提供的高效人脸关键点提取功能,并基于这些关键点设计合理的动作识别算法。结合前端的摄像头捕捉和后端的高效处理,可以实现实时、准确的任务检测与反馈,提升用户体验。

附录: 关键点索引参考

MediaPipe Face Mesh 提供 468 个面部关键点,常用的一些关键点索引如下:

  • 鼻尖:1
  • 左眼外角:33
  • 右眼外角:263
  • 左眼上方:159
  • 左眼下方:145
  • 右眼上方:386
  • 右眼下方:374
  • 上唇上方:13
  • 下唇下方:14
  • 左嘴角:78
  • 右嘴角:308

详细的关键点索引可以参考 MediaPipe Face Mesh 。

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

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

相关文章

行业商机信息付费小程序系统开发方案

行业商机信息付费小程序系统&#xff0c;主要是整合优质行业资源&#xff0c;实时更新的商机信息。在当今信息爆炸的时代&#xff0c;精准、高效地获取行业商机信息对于企业和个人创业者而言至关重要。 一、使用场景 日常浏览&#xff1a;用户在工作间隙或闲暇时间&#xff0c…

基于Matlab的变压器仿真模型建模方法(13):单相升压自耦变压器的等效电路和仿真模型

1.单相升压自耦变压器的基本方程和等效电路 单相升压自耦变压器的接线原理图如图1所示。在建立自耦变压器的基本方程时,仍然把它看成是从双绕组变压器演变而来。在图1中,设节点a到节点b部分的绕组的匝数为,对应于双绕组变压器的原边绕组;节点c到节点a部分的绕组的绕组匝数为…

Java最新面试题(全网最全、最细、附答案)

一、Java基础 1、基础概念与常识Java 语言有哪些特点? 简单易学&#xff08;语法简单&#xff0c;上手容易&#xff09;&#xff1b;面向对象&#xff08;封装&#xff0c;继承&#xff0c;多态&#xff09;&#xff1b;平台无关性&#xff08; Java 虚拟机实现平台无关性&a…

【简博士统计学习方法】3. 统计学习方法的三要素

3. 统计学习方法的三要素 3.1 监督学习的三要素 3.1.1 模型 假设空间&#xff08;Hypothesis Space&#xff09;&#xff1a;所有可能的条件概率分布或决策函数&#xff0c;用 F \mathcal{F} F表示。 若定义为决策函数的集合&#xff1a; F { f ∣ Y f ( X ) } \mathcal{F…

C# 修改项目类型 应用程序程序改类库

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github&#xff1a;codetoys&#xff0c;所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的&#xff0c;可以在任何平台上使用。 源码指引&#xff1a;github源…

链上数据分析基础课:Puell倍数(Puell Multiple)

PUELL倍数&#xff08;Puell Multiple&#xff09;就是每日币发行金额除以365天的移动平均 每日币发行总额&#xff0c;简单说就是每天结算的币总量和过去一年的平均每天收成的比。这个指标能让我们大概了解矿工的收益情况&#xff0c;还能从矿工的角度看市场趋势和周期变化。 …

初学STM32 --- USMART

目录 USMART简介 USMART主要特点&#xff1a; USMART原理 USMART组成&#xff1a; USMART 的实现流程简单概括 USMART扫描函数&#xff1a; USMART系统命令 USMART移植 USMART简介 USMART是一个串口调试组件&#xff0c;可以大大提高代码调试效率&#xff01; USMART主…

对话|企业如何构建更完善的容器供应链安全防护体系

对话&#xff5c;企业如何构建更完善的容器供应链安全防护体系 云布道师 随着云计算和 DevOps 的兴起&#xff0c;容器技术和自动化成为软件开发中的必要手段&#xff0c;软件供应链也进入了自动化及 CI/CD 阶段。然而&#xff0c;容器技术和自动化虽然提升了软件的更新速度&…

Backend - EF Core(C# 操作数据库 DB)

目录 一、EF Core 1. 使用的ORM框架&#xff08;对象关系映射&#xff09; 2. EFCore 常见两种模式 3. EFCore 提供程序 二、 EF 操作数据库&#xff08;Code First&#xff09; 1. 下载NuGet插件 2.创建模型类文件 3.创建DBContext文件 4.Programs.cs文件 5.appsettings.Devel…

科研绘图系列:R语言单细胞数据常见的可视化图形

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍加载R包数据下载导入数据数据预处理图1图2图3图4图5图6系统信息参考介绍 单细胞数据常见的可视化图形 因为本教程是单细胞数据,因此运行本画图脚本需要电脑的内存最少32Gb 加载…

jenkins入门7 --发送邮件1

jenkins发送邮件配置&#xff08;全局配置&#xff09;_jenkins 怎么发送邮件-CSDN博客 本文通过163发送邮件 1、首先163设置选择pop3/smtp/imap,开启服务&#xff0c;获取授权码 2、jenkins下载邮件插件 登录Jenkins管理界面&#xff0c;点击“Manage Jenkins”。 选择“Man…

30、论文阅读:基于小波的傅里叶信息交互与频率扩散调整的水下图像恢复

Wavelet-based Fourier Information Interaction with Frequency Diffusion Adjustment for Underwater Image Restoration 摘要介绍相关工作水下图像增强扩散模型 论文方法整体架构离散小波变换与傅里叶变换频率初步增强Wide Transformer BlockSpatial-Frequency Fusion Block…

衡量算法效率的方法:时间复杂度、空间复杂度

衡量算法效率的方法&#xff1a;时间复杂度、空间复杂度 一、好算法的特点二、算法效率分析1. 时间复杂度2. 空间复杂度 一、好算法的特点 算法是用数学解决问题的方法。一个好算法有以下几个特点&#xff1a; ①正确性&#xff1a;能正确处理各种输入&#xff08;合法输入、非…

go如何从入门进阶到高级

针对Go语言的学习&#xff0c;不同阶段应采取不同的学习方式&#xff0c;以达到最佳效果.本文将Go的学习分为入门、实战、进阶三个阶段&#xff0c;下面分别详细介绍 一、社区 Go语言中文网 作为专注于Go语言学习与推广的平台&#xff0c;Go语言中文网为开发者提供了丰富的中…

苹果系统MacOS下ObjectC建立的App程序访问opencv加载图片程序

前言 苹果系统下使用opencv感觉还是有些不太方便&#xff0c;总是感觉有点受到限制。本博客描述的是在MacOS下建立App程序然后调用opencv显示图片时出现的一些问题并最后解决的一个过程。 一、程序的建立 选择程序的类型&#xff1a; 选择界面模式和编程语言&#xff1a; 其余…

Nginx——入门介绍、安装与核心配置文件结构(一/五)

目录 1.Nginx 简介1.1.背景介绍1.2.名词解释1.3.常见服务器对比1.3.1.IIS1.3.2.Tomcat1.3.3.Apache1.3.4.Lighttpd1.3.5.其他的服务器 1.4.Nginx 的优点1.4.1.速度更快、并发更高1.4.2.配置简单&#xff0c;扩展性强1.4.3.高可靠性1.4.4.热部署1.4.5.成本低、BSD 许可证 1.5.Ng…

【HarmonyOS-ArkTS语言】计算器的实现【合集】

目录 &#x1f60b;环境配置&#xff1a;华为HarmonyOS开发者 &#x1f3af;学习小目标&#xff1a; &#x1f4fa;演示效果&#xff1a; &#x1f4d6;实验步骤及方法&#xff1a; 1. 在index.ets文件中通过 Extend(Button) 装饰器扩展Button 组件设置按钮样式函数myButt…

【C语言程序设计——选择结构程序设计】预测你的身高(头歌实践教学平台习题)【合集】

目录&#x1f60b; 任务描述 相关知识 1、输入数值 2、选择结构语句 3、计算结果并输出 编程要求 测试说明 通关代码 测试结果 任务描述 本关任务&#xff1a;编写一个程序&#xff0c;该程序需输入个人数据&#xff0c;进而预测其成年后的身高。 相关知识 为了完成本…

【连续学习之LwM算法】2019年CVPR顶会论文:Learning without memorizing

1 介绍 年份&#xff1a;2019 期刊&#xff1a; 2019CVPR 引用量&#xff1a;611 Dhar P, Singh R V, Peng K C, et al. Learning without memorizing[C]//Proceedings of the IEEE/CVF conference on computer vision and pattern recognition. 2019: 5138-5146. 本文提…

【微服务】3、配置管理

微服务配置管理 已掌握的微服务组件及配置管理问题引出 已掌握注册中心、Openfan、远程调用、负载均衡、网关等组件&#xff0c;具备微服务开发能力&#xff0c;但仍存在其他问题待解决。微服务和网关存在大量配置文件&#xff0c;其中包含很多重复配置&#xff0c;如数据库、日…