基于微信小程序的面部动作检测系统
引言
本技术文档旨在详细阐述一个基于微信小程序的面部动作检测系统的技术路线、实现方法及关键技术框架。系统的核心功能包括检测用户的左右转头、眨眼和张嘴动作,并根据检测结果逐步引导用户完成任务。为确保系统的安全性和准确性,特别是防止用户通过图片或视频欺骗系统,本文进一步深入分析并提出了相应的优化和防护措施。
系统架构概述
系统采用前后端分离的架构,前端为微信小程序,负责用户交互和界面展示;后端为基于Python的API服务,负责图像处理、动作识别和任务状态管理。系统通过HTTPS协议进行数据交互,前后端通信采用JSON格式。
系统架构图
前端实现细节
技术框架与组件
- 微信小程序框架
- 使用WXML、WXSS、JavaScript或TypeScript进行开发。
- UI 组件库
- 使用WeUI或第三方UI库快速搭建界面。
- 数据交互
- 利用
wx.request
接口与后端API进行通信。
- 利用
- 防欺骗措施
- 实时性要求高,结合前端技术实现活体检测提示。
核心功能模块
- 任务显示模块
- 动态显示当前任务提示(如“请向左转头”)。
- 实时反馈模块
- 实时显示检测结果(成功/失败)。
- 进度条与状态提示
- 使用进度条展示任务完成进度。
- 重新开始选项
- 提供“重新开始”按钮,允许用户重新进行任务检测。
- 活体检测提示
- 在采集图像时提示用户进行自然动作(如眨眼、张嘴)以确保活体性。
数据采集与传输
数据采集
- 静态图片采集
- 使用
wx.chooseImage
捕捉用户当前图像。
- 使用
- 视频帧采集
- 使用摄像头实时捕捉视频流,并定时截取帧进行检测。
数据传输流程
- 捕捉图像/视频帧
- 用户点击“开始检测”后,前端启动摄像头并捕捉图像或视频帧。
- 编码图像数据
- 使用Base64对图像数据进行编码。
- 构建JSON请求
- 包含
user_id
和image_data
字段。
- 包含
- 发送HTTP POST请求
- 通过
wx.request
将JSON数据发送至后端API。
- 通过
示例代码:捕捉静态图片并发送至后端
// pages/capture/capture.js
Page({ captureImage: function() {wx.chooseImage({count: 1,sourceType: ['camera'],success: function(res) {const tempFilePath = res.tempFilePaths[0];wx.getFileSystemManager().readFile({filePath: tempFilePath,encoding: 'base64',success: function(data) {const base64Data = data.data;wx.request({url: 'https://192.8.56.100/api/task/detect',method: 'POST',header: {'Content-Type': 'application/json'},data: {user_id: 'unique_user_id',image_data: base64Data},success: function(response) {// 处理后端返回的检测结果console.log(response.data);// 更新界面提示},fail: function(error) {console.error('请求失败', error);// 提示用户网络错误}});},fail: function(error) {console.error('读取文件失败', error);// 提示用户读取文件失败}});},fail: function(error) {console.error('选择图片失败', error);// 提示用户选择图片失败}});}
});
示例代码:实时视频帧采集并发送至后端
// pages/capture/capture.js
Page({data: {cameraContext: null,intervalId: null},onLoad: function() {this.setData({cameraContext: wx.createCameraContext()});},startCapture: function() {const intervalId = setInterval(() => {this.data.cameraContext.takePhoto({quality: 'low',success: (res) => {const base64Path = res.tempImagePath;wx.getFileSystemManager().readFile({filePath: base64Path,encoding: 'base64',success: (data) => {wx.request({url: 'https://192.8.56.100/api/task/detect',method: 'POST',header: {'Content-Type': 'application/json'},data: {user_id: 'unique_user_id',image_data: data.data},success: (response) => {// 处理后端返回的检测结果console.log(response.data);// 更新界面提示},fail: (error) => {console.error('请求失败', error);// 提示用户网络错误}});},fail: (error) => {console.error('读取文件失败', error);// 提示用户读取文件失败}});},fail: (error) => {console.error('拍照失败', error);// 提示用户拍照失败}});}, 1000); // 每秒截取一帧this.setData({ intervalId });},stopCapture: function() {clearInterval(this.data.intervalId);}
});
用户界面设计
- 任务提示
- 显示当前任务描述,如“请向左转头”、“请眨眼”、“请张嘴”。
- 实时反馈
- 使用颜色变化或图标显示检测结果(成功/失败)。
- 进度条
- 展示任务完成的进度,例如三步任务进度。
- 重新开始按钮
- 提供用户在检测失败时重新开始任务的选项。
- 活体检测提示
- 在活体检测过程中,提示用户进行自然动作(如“请自然眨眼”),防止用户使用照片或视频欺骗系统。
后端实现细节
技术选型
- 编程语言:Python
- Web框架:FastAPI(高性能,支持异步处理)
- 图像处理库:OpenCV
- 人脸检测与关键点提取:MediaPipe
- 状态管理:Redis(高效管理用户任务状态)
- 容器化:Docker(可选,用于部署)
- 活体检测模型:基于动作识别的简单活体检测,结合动作提示确保用户实时互动
核心功能模块
- API 接口设计
POST /api/task/detect
:接收用户图像数据,进行动作检测,返回检测结果。GET /api/task/status
:获取当前任务状态。POST /api/task/reset
:重置任务状态。
- 图像预处理
- 解码Base64图像数据,转换为OpenCV图像数组。
- 人脸检测与关键点提取
- 使用MediaPipe Face Mesh提取面部关键点。
- 动作识别
- 分别检测左右转头、眨眼、张嘴。
- 增加活体检测逻辑,确保用户进行实时互动。
- 状态管理
- 使用Redis记录每个用户的当前任务进度和状态。
- 防欺骗措施
- 结合活体检测,确保用户进行实时的动作交互,防止使用图片或视频欺骗系统。
数据处理流程
- 接收图像数据
- 接收前端通过
POST /api/task/detect
发送的Base64编码图像数据和user_id
。
- 接收前端通过
- 解码与预处理
- 将Base64编码的图像数据解码为OpenCV图像数组。
- 人脸检测与关键点提取
- 使用MediaPipe提取面部关键点,获取468个关键点。
- 动作识别与活体检测
- 根据当前任务步骤,识别相应的动作。
- 增加活体检测逻辑,通过多次动作交互确保用户为真人。
- 结果封装与返回
- 根据动作识别结果和任务进度,构建JSON响应返回前端。
- 状态更新
- 更新用户的任务进度和状态,存储至Redis。
示例代码
# main.py
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import base64
import cv2
import numpy as np
import mediapipe as mp
import math
import redis
import json
import time
import randomapp = FastAPI()# 初始化 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
)# 初始化 Redis 客户端
redis_client = redis.Redis(host='localhost', port=6379, db=0)# 定义请求数据模型
class DetectRequest(BaseModel):user_id: strimage_data: str# 定义响应数据模型
class DetectResponse(BaseModel):success: boolmessage: strnext_task: str = Nonecurrent_step: int = None@app.post("/api/task/detect", response_model=DetectResponse)
async def detect_task(request: DetectRequest):user_id = request.user_idimage_base64 = request.image_dataif not user_id or not image_base64:raise HTTPException(status_code=400, detail="缺少 user_id 或 image_data")# 解码 Base64 图像数据try:image = decode_image(image_base64)except Exception as e:raise HTTPException(status_code=400, detail="图像解码失败")# 获取人脸关键点landmarks = get_face_landmarks(image)if not landmarks:return DetectResponse(success=False, message="未检测到人脸")# 获取或初始化用户状态state = get_user_state(user_id)# 识别动作action_results = recognize_actions(landmarks, state)# 评估当前步骤success, next_task, updated_step = evaluate_current_step(state, action_results)# 更新状态if success:if updated_step > 4:# 所有任务完成,重置状态reset_user_state(user_id)return DetectResponse(success=True, message="成功完成所有任务", next_task="完成", current_step=updated_step)else:update_user_state(user_id, 'current_step', updated_step)return DetectResponse(success=True, message="检测成功,进入下一步", next_task=next_task, current_step=updated_step)else:reset_user_state(user_id)return DetectResponse(success=False, message="检测失败,请重新开始", current_step=1)def decode_image(image_base64: str) -> np.ndarray:img_data = base64.b64decode(image_base64)np_arr = np.frombuffer(img_data, np.uint8)img = cv2.imdecode(np_arr, cv2.IMREAD_COLOR)if img is None:raise ValueError("无法解码图像")return imgdef get_face_landmarks(image: np.ndarray):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 Nonedef get_user_state(user_id: str) -> dict:state_json = redis_client.get(f"user:{user_id}:state")if state_json:return json.loads(state_json)else:# 初始化状态initial_state = {'current_step': 1,'blink_counter': 0,'total_blinks': 0,'mouth_opened': False,'head_direction_history': [],'blink_history': [],'mouth_history': [],'last_action_time': time.time()}redis_client.set(f"user:{user_id}:state", json.dumps(initial_state))return initial_statedef update_user_state(user_id: str, key: str, value):state = get_user_state(user_id)state[key] = valueredis_client.set(f"user:{user_id}:state", json.dumps(state))def reset_user_state(user_id: str):initial_state = {'current_step': 1,'blink_counter': 0,'total_blinks': 0,'mouth_opened': False,'head_direction_history': [],'blink_history': [],'mouth_history': [],'last_action_time': time.time()}redis_client.set(f"user:{user_id}:state", json.dumps(initial_state))def recognize_actions(landmarks, state: dict) -> dict:action_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']# 记录历史数据用于活体检测state['head_direction_history'].append(head_direction)state['blink_history'].append(state['total_blinks'])state['mouth_history'].append(state['mouth_opened'])# 限制历史记录长度state['head_direction_history'] = state['head_direction_history'][-10:]state['blink_history'] = state['blink_history'][-10:]state['mouth_history'] = state['mouth_history'][-10:]return action_resultsdef evaluate_current_step(state: dict, action_results: dict):current_step = state['current_step']success = Falsenext_task = ''updated_step = current_stepcurrent_time = time.time()if current_step == 1:if action_results['head_turn'] in ['left', 'right']:if current_time - state.get('last_action_time', current_time) <= 10:success = Truenext_task = '请眨眼'updated_step += 1elif current_step == 2:if action_results['blink_count'] >= 1:if current_time - state.get('last_action_time', current_time) <= 10:success = Truenext_task = '请张嘴'updated_step += 1elif current_step == 3:if action_results['mouth_opened']:if current_time - state.get('last_action_time', current_time) <= 10:success = Truenext_task = '完成所有任务'updated_step += 1elif current_step == 4:# 可根据需求增加更多步骤success = Truenext_task = '所有任务已完成'updated_step += 1else:# 所有任务完成success = Truenext_task = '所有任务已完成'if success:state['last_action_time'] = current_time# 活体检测逻辑if not perform_liveness_detection(state):success = Falsenext_task = '未通过活体检测,请重新开始'updated_step = 1return success, next_task, updated_stepdef perform_liveness_detection(state: dict) -> bool:"""活体检测逻辑,通过检测用户是否进行了多次不同动作。"""head_turns = state.get('head_direction_history', [])blinks = state.get('blink_history', [])mouth_ops = state.get('mouth_history', [])# 检测是否有多次头部转动head_turn_count = len([dir for dir in head_turns if dir in ['left', 'right']])# 检测是否有眨眼blink_total = sum(blinks)# 检测是否有张嘴mouth_opened_count = len([m for m in mouth_ops if m])# 简单阈值判断,具体阈值需根据实际测试调整if head_turn_count >= 2 and blink_total >= 2 and mouth_opened_count >= 1:return Trueelse:return False# 动作检测相关函数
def 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):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) / 2TURN_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'def eye_aspect_ratio(landmarks, eye_indices):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_blinksdef mouth_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)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# 增强的活体检测方法
def get_next_task(current_step: int) -> str:tasks = {1: ['请向左转头', '请向右转头'],2: ['请眨眼', '请微笑'],3: ['请张嘴', '请眨眼'],4: ['请向左转头', '请向右转头']}return random.choice(tasks.get(current_step, ['完成所有任务']))
防止欺骗与误导的措施
为了防止用户通过图片、视频或其他手段欺骗系统,确保检测过程中的用户为真人,系统在设计中引入了以下防护措施:
-
活体检测
- 逻辑:通过多次不同动作的检测,确保用户进行实时互动。
- 实现:
- 多动作验证:要求用户在不同任务步骤中完成多种不同类型的面部动作,如转头、眨眼、张嘴等。
- 动作多样性:系统随机提示用户进行不同的动作,增加用户实时交互的随机性,防止用户预先录制视频。
- 动作时间验证:记录用户完成每个动作的时间,确保动作在合理的时间内完成,防止使用延迟播放的预录制视频。
- 面部动态特征分析:利用MediaPipe提取的高精度关键点,分析面部的微动态特征,如眼睛的快速眨动、嘴部的微小动作等,进一步验证活体性。
-
图像质量与实时性要求
- 图像质量控制
- 分辨率限制:前端采集的图像分辨率需满足一定要求,确保后端能够准确提取面部关键点。
- 图像清晰度:要求用户在良好光线下进行检测,避免因光线不足或过强导致的图像模糊。
- 动态模糊处理:对采集的图像进行动态模糊检测,避免用户在运动中欺骗系统。
- 实时性要求
- 动作完成时间:系统要求用户在合理时间内完成指定动作,确保动作的实时性。
- 数据传输效率:优化前端与后端的数据传输,减少网络延迟,提升实时性。
- 图像质量控制
-
安全性措施
- 数据传输安全
- HTTPS协议:所有前后端通信均通过HTTPS协议,确保数据传输的安全性和完整性。
- 数据加密:对敏感数据进行加密存储,防止数据泄露。
- 输入验证与防护
- 数据验证:后端严格验证接收到的数据格式和内容,防止注入攻击和其他安全威胁。
- 图像大小限制:限制上传图像的大小和格式,防止恶意攻击。
- 用户隐私保护
- 数据最小化:仅收集必要的用户数据,避免过度收集。
- 隐私政策:明确告知用户数据的使用和存储规则,遵守相关隐私法规。
- 数据删除选项:提供用户数据删除选项,增强用户信任。
- 数据传输安全
-
用户体验优化
- 实时反馈与提示
- 检测结果展示:实时展示检测结果,提升用户参与感。
- 加载动画:在数据处理过程中,提供加载动画或进度指示,减少用户等待时的焦虑。
- 错误提示:提供明确的错误提示和解决方案,如“未检测到人脸,请调整位置并重试”。
- 界面设计优化
- 友好的UI组件:使用清晰、简洁的UI组件,提升整体用户体验。
- 响应式设计:确保界面在不同设备和屏幕尺寸下的良好展示效果。
- 实时反馈与提示
前后端数据交互详细细节
API 接口定义
1. 获取任务状态
GET /api/task/status
请求参数:
user_id
(可选):标识用户的唯一ID。
响应示例:
{"current_step": 1,"total_steps": 4,"task_description": "请向左转头"
}
2. 检测任务
POST /api/task/detect
请求参数:
user_id
:用户唯一ID。image_data
:Base64编码的图像数据。
请求数据格式:
{"user_id": "unique_user_id","image_data": "Base64编码的图像数据"
}
响应数据格式:
-
成功响应:
{"success": true,"message": "检测成功,进入下一步","next_task": "请眨眼","current_step": 2 }
-
失败响应:
{"success": false,"message": "检测失败,请重新开始","current_step": 1 }
3. 重置任务
POST /api/task/reset
请求参数:
user_id
:用户唯一ID。
请求数据格式:
{"user_id": "unique_user_id"
}
响应数据格式:
{"success": true,"message": "任务已重置","current_step": 1
}
数据格式与标准
1. 前后端通信数据格式
-
请求数据:
{"user_id": "unique_user_id","image_data": "Base64编码的图像数据" }
-
响应数据(成功):
{"success": true,"message": "检测成功,进入下一步","next_task": "请眨眼","current_step": 2 }
-
响应数据(失败):
{"success": false,"message": "检测失败,请重新开始","current_step": 1 }
2. 图像数据格式
- 编码方式:Base64编码。
- 图像格式:JPEG或PNG,根据前端配置。
- 传输方式:嵌入在JSON请求的
image_data
字段中。
3. 关键点数据格式
- MediaPipe关键点:每个关键点包含
x
,y
,z
坐标,归一化至[0,1]范围。 - 数据结构:列表形式,每个元素为关键点对象。
示例:
{"landmark": [{"x": 0.5, "y": 0.5, "z": 0.0},{"x": 0.6, "y": 0.5, "z": 0.0},...]
}
数据流转过程示意图
数据处理阶段详解
1. 数据采集阶段
- 输入:摄像头捕捉到的图像或视频帧。
- 处理:
- 捕捉图像。
- 读取文件并编码为Base64。
- 输出:Base64编码的图像数据。
技术框架:微信小程序API (wx.chooseImage
, wx.getFileSystemManager
)
2. 数据传输阶段
- 输入:Base64编码的图像数据。
- 处理:
- 构建JSON请求体。
- 通过HTTPS POST请求发送至后端。
- 输出:通过网络传输的JSON数据。
技术框架:微信小程序API (wx.request
)
3. 数据接收与解码阶段
- 输入:后端接收到的JSON请求。
- 处理:
- 解析JSON获取
user_id
和image_data
。 - 解码Base64图像数据为图像数组(NumPy)。
- 解析JSON获取
- 输出:解码后的图像数组。
技术框架:FastAPI, OpenCV
4. 人脸检测与关键点提取阶段
- 输入:图像数组。
- 处理:
- 使用MediaPipe Face Mesh提取人脸关键点。
- 输出:人脸关键点数据(MediaPipe Landmark对象)。
技术框架:MediaPipe, OpenCV
5. 动作识别与活体检测阶段
- 输入:人脸关键点数据。
- 处理:
- 计算头部转动角度,判断左右转头。
- 计算EAR,检测眨眼次数。
- 计算MAR,检测是否张嘴。
- 记录动作历史,用于活体检测。
- 评估活体性,决定是否通过检测。
- 输出:动作识别结果及活体检测结果。
技术框架:Python数学计算
6. 任务状态管理阶段
- 输入:当前动作识别结果。
- 处理:
- 根据当前任务步骤和检测结果更新用户状态。
- 使用Redis存储更新后的状态。
- 输出:更新后的任务状态。
技术框架:Redis
7. 结果封装与返回阶段
- 输入:动作识别结果及更新后的任务状态。
- 处理:
- 根据检测结果和任务进度构建响应消息。
- 封装为JSON格式。
- 输出:JSON响应数据。
技术框架:FastAPI, Pydantic
8. 前端接收与反馈阶段
- 输入:后端返回的JSON响应。
- 处理:
- 解析JSON数据。
- 根据
success
字段更新界面提示和进度条。
- 输出:更新后的用户界面,提示用户进行下一步任务或重新开始。
技术框架:微信小程序API (wx.request
回调函数)
关键技术选型
前端
- 微信小程序:用于跨平台用户界面开发,支持广泛的微信用户基础。
- WeUI:提供微信风格的UI组件,提升开发效率和用户体验。
- 关键API:
wx.chooseImage
:捕捉图像。wx.getFileSystemManager
:读取文件内容。wx.request
:发送HTTP请求。
后端
- FastAPI:高性能、易用的Python Web框架,支持异步处理。
- Uvicorn:ASGI服务器,用于运行FastAPI应用,提升性能。
- MediaPipe Face Mesh:高效的人脸检测与关键点提取,提供468个面部关键点。
- OpenCV:用于图像预处理,如颜色空间转换、图像解码等。
- Redis:高效的内存数据存储,适用于管理用户任务状态,支持高并发访问。
- Pydantic:用于数据验证和模型定义,确保数据的完整性和准确性。
安全与防护
- HTTPS:确保所有前后端通信的安全性,防止数据被窃取或篡改。
- Redis:高效管理用户状态,支持快速读写操作,适合高并发场景。
- 数据验证:后端使用Pydantic进行严格的数据验证,防止恶意数据输入。
部署与运维
- Docker:容器化部署后端服务,提升部署效率和环境一致性,简化运维管理。
- Nginx:作为反向代理服务器,处理前端请求转发,提供负载均衡和安全防护。
- 云服务:如AWS、阿里云,用于部署后端服务和Redis,确保系统的高可用性和可扩展性。
关键技术框架与算法详细说明
1. FastAPI
特点:
- 高性能,基于ASGI,支持异步处理。
- 自动生成OpenAPI文档,便于API的调试和测试。
- 内置数据验证与类型注解支持(Pydantic),确保数据的准确性。
优势:
- 支持异步编程,提升并发处理能力。
- 简洁的代码结构,易于维护和扩展。
- 兼容性强,易于与其他Python库集成。
2. MediaPipe Face Mesh
特点:
- 实时高精度人脸关键点检测,提供468个面部关键点。
- 支持多种平台和设备,适用于移动端应用。
- 高效,适用于实时应用场景。
优势:
- 精度高,覆盖面部各个区域,适合复杂的动作检测。
- 易于集成,与OpenCV配合使用,提升图像处理效率。
- 开源且持续更新,社区支持良好。
3. Redis
特点:
- 高性能的内存数据存储,支持快速读写操作。
- 支持多种数据结构(字符串、哈希、列表、集合等)。
- 提供持久化选项,确保数据的可靠性。
优势:
- 低延迟,适用于高并发场景,确保系统响应速度。
- 支持分布式部署,易于扩展,提升系统的可用性和可扩展性。
- 丰富的功能,如发布/订阅、事务处理等,满足多样化需求。
4. 动作识别算法
4.1 头部旋转检测
- 原理:通过计算鼻尖与左右眼外角的向量角度,判断头部旋转方向。
- 关键步骤:
- 提取鼻尖、左眼外角和右眼外角的关键点坐标。
- 计算鼻尖到左右眼外角的向量角度。
- 根据角度差异,判断头部是否向左或向右转动。
实现代码:
def 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):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) / 2TURN_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),检测眨眼次数。EAR是眼睛纵向距离与横向距离的比值,眨眼时EAR会显著减小。
- 关键步骤:
- 提取左眼和右眼的关键点。
- 计算EAR值。
- 当EAR低于阈值时,视为闭眼,计数一次眨眼。
实现代码:
def eye_aspect_ratio(landmarks, eye_indices):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),检测是否张嘴。MAR是嘴部纵向距离与横向距离的比值,张嘴时MAR会显著增大。
- 关键步骤:
- 提取上唇中点、下唇中点、左右嘴角的关键点。
- 计算MAR值。
- 当MAR超过阈值时,视为张嘴。
实现代码:
def mouth_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)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
优化与注意事项
1. 性能优化
1.1 后端优化
- 异步处理:利用FastAPI的异步特性,提升并发处理能力。
- GPU加速:在服务器端使用GPU加速MediaPipe和OpenCV的处理速度,提升实时性。
- 批处理请求:在高并发场景下,考虑批量处理图像请求,减少处理开销。
1.2 前端优化
- 图像压缩:在前端对图像进行适当压缩,减少传输数据量,提升传输效率。
- 采集频率控制:合理控制视频帧采集频率,平衡实时性与性能。
2. 安全性
2.1 数据传输安全
- 强制使用HTTPS:确保所有数据传输通过HTTPS,防止中间人攻击。
- 身份验证:引入用户身份验证机制,如Token验证,确保请求的合法性。
2.2 输入验证
- 严格数据验证:后端使用Pydantic进行严格的数据验证,确保数据格式和内容的正确性。
- 图像大小与格式限制:限制上传图像的大小和格式,防止恶意文件上传。
3. 用户体验
3.1 实时反馈
- 检测结果展示:前端实时展示检测结果,提升用户参与感和体验。
- 加载动画:在后端处理图像时,前端显示加载动画,减少用户等待时的焦虑。
3.2 错误处理
- 明确错误提示:在检测失败或网络异常时,提供明确的错误提示和解决方案。
- 重试机制:在网络请求失败时,提供重试选项,提升系统的鲁棒性。
4. 扩展性
4.1 模块化设计
- 代码结构清晰:前后端代码结构清晰,易于维护和扩展。
- 插件化组件:前端使用插件化的UI组件,后端使用模块化的函数库,便于添加新功能。
4.2 可配置性
- 参数化阈值:将动作检测的阈值(如EAR_THRESHOLD、MAR_THRESHOLD)配置化,方便后期调整优化。
- 任务步骤配置:将任务步骤和动作提示配置化,便于添加或修改检测任务。
4.3 服务扩展
- 微服务架构:考虑将不同功能模块(如人脸检测、动作识别、状态管理)拆分为独立的微服务,提升系统的可维护性和扩展性。
- 负载均衡:使用Nginx等负载均衡工具,提升系统的并发处理能力和稳定性。
相关文章:

基于微信小程序的面部动作检测系统
引言 本技术文档旨在详细阐述一个基于微信小程序的面部动作检测系统的技术路线、实现方法及关键技术框架。系统的核心功能包括检测用户的左右转头、眨眼和张嘴动作,并根据检测结果逐步引导用户完成任务。为确保系统的安全性和准确性,特别是防止用户通过…...

Activation Functions
Chapter4:Activation Functions 声明:本篇博客笔记来源于《Neural Networks from scratch in Python》,作者的youtube 其实关于神经网络的入门博主已经写过几篇了,这里就不再赘述,附上链接。 1.一文窥见神经网络 2.神经…...

《Vue3实战教程》37:Vue3生产部署
如果您有疑问,请观看视频教程《Vue3实战教程》 生产部署 开发环境 vs. 生产环境 在开发过程中,Vue 提供了许多功能来提升开发体验: 对常见错误和隐患的警告对组件 props / 自定义事件的校验响应性调试钩子开发工具集成 然而ÿ…...

Linux:各发行版及其包管理工具
相关阅读 Linuxhttps://blog.csdn.net/weixin_45791458/category_12234591.html?spm1001.2014.3001.5482 Debian 包管理工具:dpkg(低级包管理器)、apt(高级包管理器,建立在dpkg基础上)包格式:…...

【计算机网络】课程 作业一 搭建连续覆盖的办公网络
作业一 搭建连续覆盖的办公网络 题目:论述题(共1题,100分) 充分利用所学习的数据链路层局域网知识,加上物理层的基础知识,请给一个办公场所(三层,每层约100平方)…...

C++ 设计模式:单例模式(Singleton Pattern)
链接:C 设计模式 链接:C 设计模式 - 享元模式 单例模式(Singleton Pattern)是创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来访问这个实例。单例模式在需要全局共享资源或控制实例数量的…...

OpenCV调整图像亮度和对比度
【欢迎关注编码小哥,学习更多实用的编程方法和技巧】 1、基本方法---线性变换 // 亮度和对比度调整 cv::Mat adjustBrightnessContrast(const cv::Mat& src, double alpha, int beta) {cv::Mat dst;src.convertTo(dst, -1, alpha, beta);return dst; }// 使用…...

Kafka Offset explorer使用
Kafka集群配置好以后以后运维这边先用工具测试一下,便于rd展开后续的工作,本地调试时一般使用Offset explorer工具进行连接 使用SASL(Simple Authentication and Security Layer)验证方式 使用SCRAM-SHA-256(Salted Challenge Response Authentication…...

二维码文件在线管理系统-收费版
需求背景 如果大家想要在网上管理自己的文件,而且需要生成二维码,下面推荐【草料二维码】,这个系统很好。特别适合那些制造业,实体业的使用手册,你可以生成一个二维码,贴在设备上,然后这个二维码…...

UE4.27 Android环境下获取手机电量
获取电量方法 使用的方法时FAndroidMisc::GetBatteryLevel(); 出现的问题 但是在电脑上编译时发现,会发现编译无法通过。 因为安卓环境下编译时,包含 #include "Android/AndroidPlatformMisc.h" 头文件是可以正常链接的,但在电…...

vue-i18n报错
1. 开发环境报错Uncaught (in promise) TypeError: ‘set’ on proxy: trap returned falsish for property ‘$t’ legacy需要设置为false const i18n createI18n({legacy: false,// 默认语言locale: lang,// 设置语言环境messages, })2. 打包配置tsc --noEmit时报错&#…...

Docker新手:在tencent云上实现Python服务打包到容器
1 使用docker的原因 一致性和可移植性:Docker 容器可以在任何支持 Docker 的环境中运行,无论是开发者的笔记本电脑、测试服务器还是生产环境。这确保了应用在不同环境中的行为一致,减少了“在我的机器上可以运行”的问题。 隔离性ÿ…...

React基础知识学习
学习React前端框架是一个系统而深入的过程,以下是一份详细的学习指南: 一、React基础知识 React简介 React是一个用于构建用户界面的JavaScript库,由Facebook开发和维护。它强调组件化和声明式编程,使得构建复杂的用户界面变得更…...

ES IK分词器插件
前言 ES中默认了许多分词器,但是对中文的支持并不友好,IK分词器是一个专门为中文文本设计的分词工具,它不是ES的内置组件,而是一个需要单独安装和配置的插件。 Ik分词器的下载安装(Winows 版本) 下载地址:…...

二十三种设计模式-抽象工厂模式
抽象工厂模式(Abstract Factory Pattern)是一种创建型设计模式,它提供了一种方式,用于创建一系列相关或相互依赖的对象,而不需要指定它们具体的类。这种模式主要用于系统需要独立于其产品的创建逻辑时,并且…...

python opencv的orb特征检测(Oriented FAST and Rotated BRIEF)
官方文档:https://docs.opencv.org/4.10.0/d1/d89/tutorial_py_orb.html SIFT/SURF/ORB对比 https://www.bilibili.com/video/BV1Yw411S7hH?spm_id_from333.788.player.switch&vd_source26bb43d70f463acac2b0cce092be2eaa&p80 ORB代码 import numpy a…...

高阶数据结构----布隆过滤器和位图
(一)位图 位图是用来存放某种状态的,因为一个bit上只能存0和1所以一般只有两种状态的情况下适合用位图,所以非常适合判断数据在或者不在,而且位图十分节省空间,很适合于海量数据,且容易存储&…...

VScode使用密钥进行ssh连接服务器方法
如何正常连接ssh的方式可以看我原来那篇文章:Windows上使用VSCode连接远程服务器ssh 1.连接 点击ssh加号,然后关键点在第2步的书写上 2.命令 2的位置写命令: ssh -i "C:\Users\userName\.ssh\id_rsa" usernameIP -p 端口号 端…...

艾体宝产品丨加速开发:Redis 首款 VS Code 扩展上线!
Redis 宣布推出其首款专为 VS Code 设计的 Redis 扩展。这一扩展将 Redis 功能直接整合进您的集成开发环境(IDE),旨在简化您的工作流程,提升工作效率。 我们一直致力于构建强大的开发者生态系统,并在您工作的每一步提…...

应用架构模式
设计模式 设计模式是指根据通用需求来设计解决方案的模板或蓝图,使用设计模式能够更加有效地解决设计过程中的常见问题。设计模式针对不同的问题域有不同的内涵,主要涉及业务、架构、程序设计等问题域,本文主要讨论架构设计模式。 业务设计模…...

注入少量可学习的向量参数: 注入适配器IA3
注入少量可学习的向量参数: 注入适配器IA3 简介:IA3通过学习向量来对激活层加权进行缩放,从而获得更强的性能,同时仅引入相对少量的新参数。它的诞生背景是为了改进LoRA,与LoRA不同的是,IA3直接处理学习向量,而不是学习低秩权重矩阵,这使得可训练参数的数量更少,并且原…...

【C++】B2076 球弹跳高度的计算
博客主页: [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 💯前言💯题目描述输入格式输出格式输入输出示例 💯两种代码实现及其对比我的代码实现代码分析优点与不足 老师的代码实现代码分析优点与不足 💯两种实现的对…...

【Python】selenium结合js模拟鼠标点击、拦截弹窗、鼠标悬停方法汇总(使用 execute_script 执行点击的方法)
我们在写selenium获取网络信息的时候,有时候我们会受到对方浏览器的监控,对方通过分析用户行为模式,如点击、滚动、停留时间等,网站可以识别出异常行为,进而对Selenium爬虫进行限制。 这里我们可以加入JavaScript的使…...

CatBoost算法详解与PyTorch实现
CatBoost算法详解与PyTorch实现 目录 CatBoost算法详解与PyTorch实现@[TOC](目录)1. CatBoost算法概述1.1 梯度提升树(GBDT)1.2 CatBoost的优势2. CatBoost的核心技术2.1 类别特征处理2.2 对称树结构2.3 有序提升技术2.4 正则化技术3. PyTorch实现CatBoost3.1 环境准备3.2 Py…...

“TypeScript版:数据结构与算法-初识算法“
引言 在算法与编程的广阔世界里,总有一些作品以其独特的魅力和卓越的设计脱颖而出,成为我们学习和研究的典范。今天,我非常荣幸地向大家分享一个令人印象深刻的算法——Hello算法。 Hello算法不仅展现了作者深厚的编程功底,更以…...

mysql中递归的使用 WITH RECURSIVE
MySQL递归查询的基本语法和用法 MySQL 8.0及以上版本支持使用WITH RECURSIVE来进行递归查询。WITH RECURSIVE定义了一个递归的公用表表达式(CTE),它包含两个部分:递归的基础部分(非递归部分)和递归部分。 …...

点击取消按钮,console出来数据更改了,页面视图没有更新
点击取消按钮,console出来数据更改了,页面视图没有更新 前言 实现效果:点击取消按钮,页面视图全部为空, 遇到的问题: 点击取消按钮,console出来数据更改了,SchemaJson 都是默认值啦…...

web框架在什么程度上受限 ?
Web框架提供了开发网站和Web应用的基础结构和工具,但它们也有一些限制。了解这些限制有助于选择合适的框架或决定何时可能需要寻找或开发替代方案。 1、问题背景 提问者计划构建一个 RESTful web 服务,该服务将只使用 JSON/XML 接口,不包含 …...

实践:事件循环
实践:事件循环 代码示例 console.log(1); setTimeout(() > console.log(2), 0); Promise.resolve(3).then(res > console.log(res)); console.log(4);上述的代码的输出结果是什么 1和4肯定优先输出,因为他们会立即方式堆栈的执行上下文中执行&am…...

C++ 设计模式:建造者模式(Builder Pattern)
链接:C 设计模式 链接:C 设计模式 - 工厂方法 链接:C 设计模式 - 抽象工厂 链接:C 设计模式 - 原型模式 建造者模式(Builder Pattern)是一种创建型设计模式,它允许你分步骤创建复杂对象。与其他…...