AI手势识别与追踪Flask后端:API服务搭建教程
1. 引言
1.1 学习目标
本文将带你从零开始,构建一个基于MediaPipe Hands模型的 AI 手势识别与追踪 Flask 后端服务。你将学会如何:
- 部署 MediaPipe 实现高精度手部关键点检测
- 构建 RESTful API 接口供前端调用
- 实现“彩虹骨骼”可视化逻辑并返回图像数据
- 封装为可独立运行的本地服务,支持图片上传与处理
最终成果是一个可通过 HTTP 请求访问的手势识别系统,适用于人机交互、智能控制、虚拟现实等场景。
1.2 前置知识
建议读者具备以下基础: - Python 编程能力(熟悉 Flask 框架更佳) - 了解基本的 HTTP 协议和 REST API 概念 - 熟悉图像处理基础(如 OpenCV 使用)
本教程完全在 CPU 上运行,无需 GPU 支持,适合边缘设备或轻量级部署。
1.3 教程价值
不同于简单的模型演示,本文提供的是可工程化落地的完整后端方案,包含错误处理、接口设计、性能优化和生产级代码结构,帮助开发者快速集成手势识别功能到实际项目中。
2. 核心技术解析
2.1 MediaPipe Hands 模型原理
MediaPipe 是 Google 开发的一套跨平台机器学习管道框架,其中Hands 模型专用于手部姿态估计。
该模型采用两阶段检测机制:
手部区域定位(Palm Detection)
使用 SSD(Single Shot Detector)结构在整张图像中快速定位手掌区域,即使手部较小或部分遮挡也能有效捕捉。关键点回归(Hand Landmark Estimation)
在裁剪出的手部区域内,通过回归网络预测21 个 3D 关键点坐标(x, y, z),覆盖指尖、指节、掌心和手腕等位置。
📌为何选择 MediaPipe?
- 官方预训练模型,开箱即用
- 支持单手/双手同时检测
- 提供深度信息(z 坐标),可用于距离感知
- 轻量化设计,CPU 推理速度可达 30+ FPS
2.2 彩虹骨骼可视化算法
传统骨骼绘制使用单一颜色线条连接关键点,视觉辨识度低。我们引入“彩虹骨骼”机制,为每根手指分配独特颜色,提升可读性与科技感。
手指关键点索引映射(MediaPipe 定义)
| 手指 | 起始点 | 终止点 | 颜色 |
|---|---|---|---|
| 拇指 | 1 → 4 | 黄色#FFFF00 | |
| 食指 | 5 → 8 | 紫色#800080 | |
| 中指 | 9 → 12 | 青色#00FFFF | |
| 无名指 | 13 → 16 | 绿色#00FF00 | |
| 小指 | 17 → 20 | 红色#FF0000 |
⚠️ 注意:关键点 0 为手腕,其余按五指分组递增编号。
通过 OpenCV 的cv2.line()和cv2.circle()函数逐段绘制彩色线段,并叠加白色圆点表示关节点。
3. Flask 后端实现
3.1 项目目录结构
hand_tracking_api/ ├── app.py # 主应用入口 ├── utils/ │ └── hand_tracker.py # 手势识别核心类 ├── static/ │ └── uploads/ # 用户上传图片存储 ├── templates/ │ └── index.html # WebUI 页面(可选) ├── requirements.txt # 依赖包声明 └── README.md3.2 依赖安装
创建requirements.txt文件:
flask==2.3.3 opencv-python==4.8.0.74 mediapipe==0.10.9 numpy==1.24.3安装命令:
pip install -r requirements.txt3.3 核心代码实现
utils/hand_tracker.py—— 手势识别模块
import cv2 import mediapipe as mp import numpy as np class HandTracker: def __init__(self): self.mp_drawing = mp.solutions.drawing_utils self.mp_hands = mp.solutions.hands self.hands = self.mp_hands.Hands( static_image_mode=True, max_num_hands=2, min_detection_confidence=0.6, min_tracking_confidence=0.5 ) # 彩虹颜色定义(BGR格式) self.finger_colors = [ (0, 255, 255), # 黄:拇指 (128, 0, 128), # 紫:食指 (255, 255, 0), # 青:中指 (0, 255, 0), # 绿:无名指 (0, 0, 255) # 红:小指 ] # 每根手指的关键点范围 [(start, end), ...] self.finger_segments = [(1,4), (5,8), (9,12), (13,16), (17,20)] def draw_rainbow_skeleton(self, image, landmarks): h, w, _ = image.shape # 转换为NumPy数组便于操作 points = [(int(landmarks[i].x * w), int(landmarks[i].y * h)) for i in range(21)] # 绘制白点(所有关节) for x, y in points: cv2.circle(image, (x, y), 5, (255, 255, 255), -1) # 分别绘制五根手指的彩线 for idx, (start, end) in enumerate(self.finger_segments): color = self.finger_colors[idx] for i in range(start, end + 1): if i < 20: # 防止越界 pt1 = points[i] pt2 = points[i + 1] cv2.line(image, pt1, pt2, color, 2) return image def process_image(self, image_path): image = cv2.imread(image_path) if image is None: return {"error": "无法读取图像文件"} rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) results = self.hands.process(rgb_image) if not results.multi_hand_landmarks: return {"landmarks": [], "message": "未检测到手部"} output_image = image.copy() all_landmarks = [] for hand_landmarks in results.multi_hand_landmarks: # 提取3D坐标 landmarks_3d = [ { "x": lm.x, "y": lm.y, "z": lm.z } for lm in hand_landmarks.landmark ] all_landmarks.append(landmarks_3d) # 绘制彩虹骨骼 output_image = self.draw_rainbow_skeleton(output_image, hand_landmarks.landmark) # 保存结果图 output_path = image_path.replace("uploads/", "uploads/result_") cv2.imwrite(output_path, output_image) return { "landmarks": all_landmarks, "result_image": output_path, "hand_count": len(all_landmarks) }app.py—— Flask 主程序
from flask import Flask, request, jsonify, send_file, render_template import os from utils.hand_tracker import HandTracker app = Flask(__name__) tracker = HandTracker() UPLOAD_FOLDER = 'static/uploads' os.makedirs(UPLOAD_FOLDER, exist_ok=True) app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER @app.route('/') def index(): return render_template('index.html') @app.route('/api/track', methods=['POST']) def track_hand(): if 'file' not in request.files: return jsonify({"error": "缺少文件字段"}), 400 file = request.files['file'] if file.filename == '': return jsonify({"error": "未选择文件"}), 400 if file: filename = file.filename filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename) file.save(filepath) try: result = tracker.process_image(filepath) if "error" in result: return jsonify(result), 400 return jsonify(result) except Exception as e: return jsonify({"error": str(e)}), 500 @app.route('/result/<filename>') def result_image(filename): return send_file(f"static/uploads/{filename}") if __name__ == '__main__': app.run(host='0.0.0.0', port=5000, debug=False)3.4 前端测试页面(可选)
创建templates/index.html:
<!DOCTYPE html> <html> <head><title>AI手势识别API测试</title></head> <body> <h2>上传手部照片进行彩虹骨骼分析</h2> <form action="/api/track" method="post" enctype="multipart/form-data"> <input type="file" name="file" accept="image/*" required /> <button type="submit">上传并分析</button> </form> <p>支持“比耶”、“点赞”、“张开手掌”等手势</p> </body> </html>4. 运行与测试
4.1 启动服务
python app.py服务将在http://0.0.0.0:5000启动。
点击平台提供的 HTTP 访问按钮即可进入 WebUI。
4.2 API 接口说明
POST/api/track
请求参数: -file: 图像文件(JPEG/PNG)
成功响应示例:
{ "hand_count": 1, "landmarks": [ [ {"x": 0.5, "y": 0.6, "z": -0.02}, ... ] ], "result_image": "static/uploads/result_test.jpg" }失败响应:
{ "error": "无法读取图像文件" }4.3 测试建议
推荐上传以下手势测试效果: - ✌️ “比耶”:清晰展示食指与中指分离 - 👍 “点赞”:观察拇指独立运动 - 🤚 “张开手掌”:验证五指是否全部被正确追踪
5. 性能优化与最佳实践
5.1 CPU 推理加速技巧
尽管 MediaPipe 已针对 CPU 优化,仍可通过以下方式进一步提升性能:
- 降低输入图像分辨率:建议缩放至 480p 或 720p
- 启用静态模式:对单张图像设置
static_image_mode=True - 限制最大手数:若仅需单手识别,设
max_num_hands=1
5.2 错误处理增强
增加对图像格式、大小、损坏文件的校验:
from PIL import Image def validate_image(filepath): try: img = Image.open(filepath) img.verify() return True except: return False5.3 安全性建议
- 限制上传文件类型(只允许
.jpg,.png) - 设置文件大小上限(如 5MB)
- 使用随机文件名防止路径冲突
6. 总结
6.1 核心收获
本文实现了基于 MediaPipe 的 AI 手势识别 Flask 后端服务,涵盖:
- 高精度 21 点 3D 手部关键点检测
- 创新的“彩虹骨骼”可视化算法,提升交互体验
- 可扩展的 REST API 设计,支持前后端分离架构
- 完全本地运行,不依赖外部下载,稳定性强
6.2 下一步建议
- 扩展为视频流处理(WebSocket 或 RTSP)
- 添加手势分类器(如识别“握拳”、“OK”等)
- 部署为 Docker 容器,便于多环境迁移
- 结合语音或动作指令实现完整人机交互系统
💡获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。