当前位置: 首页 > article >正文

MediaPipe Hands保姆级教程:从零搭建手势识别系统,无需GPU

MediaPipe Hands保姆级教程从零搭建手势识别系统无需GPU1. 引言为什么选择MediaPipe Hands想象一下你正在开发一个智能健身应用需要识别用户的“点赞”手势来计数或者你在做一个教育软件想通过手势控制PPT翻页。传统方案要么需要昂贵的GPU硬件要么开发门槛极高让很多创意想法止步于原型阶段。今天我要带你从零开始用MediaPipe Hands搭建一个完整的手势识别系统。最棒的是整个过程完全不需要GPU一台普通的笔记本电脑就能流畅运行。我们将基于一个集成了“彩虹骨骼”可视化功能的预置镜像让你在10分钟内看到自己的手势被精准识别和标注出来。这篇文章将手把手教你完成环境部署、代码编写、效果测试的全过程。无论你是刚接触计算机视觉的新手还是想快速验证手势交互创意的开发者这篇保姆级教程都能让你快速上手把想法变成现实。2. 环境准备与镜像部署2.1 理解我们的技术栈在开始动手之前先简单了解一下我们要用到的核心组件MediaPipe HandsGoogle开源的手部关键点检测模型轻量高效专门为CPU优化OpenCV计算机视觉处理库用于图像读取、显示和绘制Flask轻量级Web框架让我们能通过浏览器上传图片并查看结果彩虹骨骼可视化我们镜像的特色功能用不同颜色标注五根手指识别更直观这个组合的最大优势就是“开箱即用”。模型已经内置在镜像里你不需要下载任何额外的文件也不需要配置复杂的深度学习环境。2.2 一键部署镜像我们的镜像已经预置了所有依赖部署过程极其简单获取镜像在CSDN星图镜像广场找到“AI 手势识别与追踪”镜像启动服务点击启动按钮系统会自动分配资源并启动容器访问Web界面启动完成后点击提供的HTTP访问链接整个过程通常只需要1-2分钟。启动成功后你会看到一个简单的Web页面上面有文件上传按钮。这就是我们手势识别系统的前端界面。如果你在其他平台部署也可以使用Docker命令直接运行docker run -p 5000:5000 --name hand-tracking your-image-name这个命令会在本地5000端口启动服务你可以通过浏览器访问http://localhost:5000来使用。3. 核心代码实现详解3.1 手部关键点检测基础代码让我们从最核心的部分开始——如何使用MediaPipe检测手部关键点。创建一个新的Python文件比如hand_detector.pyimport cv2 import mediapipe as mp import numpy as np class HandDetector: def __init__(self, static_modeFalse, max_hands2, detection_confidence0.5): 初始化手部检测器 参数说明 - static_mode: True处理静态图片False处理视频流 - max_hands: 最多检测的手部数量 - detection_confidence: 检测置信度阈值越高越严格 # 初始化MediaPipe Hands模块 self.mp_hands mp.solutions.hands self.hands self.mp_hands.Hands( static_image_modestatic_mode, max_num_handsmax_hands, min_detection_confidencedetection_confidence, min_tracking_confidence0.5 # 跟踪置信度 ) # 用于绘制关键点的工具 self.mp_drawing mp.solutions.drawing_utils self.mp_drawing_styles mp.solutions.drawing_styles def detect(self, image): 检测图像中的手部关键点 参数 - image: 输入的BGR格式图像 返回 - results: 检测结果包含关键点坐标等信息 - annotated_image: 绘制了关键点的图像 # 将BGR图像转换为RGBMediaPipe需要RGB格式 rgb_image cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # 为了提高性能可以标记图像为不可写 rgb_image.flags.writeable False # 执行手部检测 results self.hands.process(rgb_image) # 将图像标记回可写状态 rgb_image.flags.writeable True # 创建一个用于绘制的图像副本 annotated_image image.copy() # 如果检测到手部绘制关键点 if results.multi_hand_landmarks: for hand_landmarks in results.multi_hand_landmarks: # 使用MediaPipe自带的绘制方法默认样式 self.mp_drawing.draw_landmarks( annotated_image, hand_landmarks, self.mp_hands.HAND_CONNECTIONS, self.mp_drawing_styles.get_default_hand_landmarks_style(), self.mp_drawing_styles.get_default_hand_connections_style() ) return results, annotated_image def get_landmark_coordinates(self, results, image_shape): 获取关键点的具体坐标值 参数 - results: 检测结果 - image_shape: 图像的形状 (height, width, channels) 返回 - landmarks_list: 关键点坐标列表每个点包含(x, y, z)坐标 landmarks_list [] if results.multi_hand_landmarks: h, w, _ image_shape for hand_landmarks in results.multi_hand_landmarks: hand_landmarks_data [] # 遍历21个关键点 for idx, landmark in enumerate(hand_landmarks.landmark): # 将归一化坐标转换为像素坐标 x_px int(landmark.x * w) y_px int(landmark.y * h) z_px landmark.z # z坐标是相对深度保持原值 hand_landmarks_data.append({ id: idx, x: x_px, y: y_px, z: z_px, name: self.get_landmark_name(idx) }) landmarks_list.append(hand_landmarks_data) return landmarks_list def get_landmark_name(self, landmark_id): 根据关键点ID获取对应的名称 landmark_names { 0: WRIST, # 手腕 1: THUMB_CMC, 2: THUMB_MCP, 3: THUMB_IP, 4: THUMB_TIP, # 拇指 5: INDEX_FINGER_MCP, 6: INDEX_FINGER_PIP, 7: INDEX_FINGER_DIP, 8: INDEX_FINGER_TIP, # 食指 9: MIDDLE_FINGER_MCP, 10: MIDDLE_FINGER_PIP, 11: MIDDLE_FINGER_DIP, 12: MIDDLE_FINGER_TIP, # 中指 13: RING_FINGER_MCP, 14: RING_FINGER_PIP, 15: RING_FINGER_DIP, 16: RING_FINGER_TIP, # 无名指 17: PINKY_MCP, 18: PINKY_PIP, 19: PINKY_DIP, 20: PINKY_TIP # 小指 } return landmark_names.get(landmark_id, fUNKNOWN_{landmark_id})这段代码创建了一个完整的HandDetector类你可以直接在自己的项目中使用。它封装了MediaPipe Hands的核心功能包括初始化、检测、坐标转换和关键点命名。3.2 实现彩虹骨骼可视化现在让我们实现镜像的特色功能——彩虹骨骼可视化。这个功能用不同颜色标注五根手指让手势状态一目了然class RainbowVisualizer: def __init__(self): 初始化彩虹骨骼可视化器 # 定义五指的颜色BGR格式 self.finger_colors { THUMB: (0, 255, 255), # 黄色 INDEX: (255, 0, 255), # 紫色 MIDDLE: (255, 255, 0), # 青色 RING: (0, 255, 0), # 绿色 PINKY: (0, 0, 255) # 红色 } # 定义每根手指的关键点连接关系 self.finger_connections { THUMB: [0, 1, 2, 3, 4], # 手腕 - CMC - MCP - IP - 指尖 INDEX: [0, 5, 6, 7, 8], # 手腕 - MCP - PIP - DIP - 指尖 MIDDLE: [0, 9, 10, 11, 12], # 手腕 - MCP - PIP - DIP - 指尖 RING: [0, 13, 14, 15, 16], # 手腕 - MCP - PIP - DIP - 指尖 PINKY: [0, 17, 18, 19, 20] # 手腕 - MCP - PIP - DIP - 指尖 } # 关节点的绘制参数 self.joint_radius 6 self.joint_color (255, 255, 255) # 白色 self.joint_thickness -1 # 实心圆 # 骨骼线的绘制参数 self.bone_thickness 3 def draw_rainbow_skeleton(self, image, landmarks_data): 在图像上绘制彩虹骨骼 参数 - image: 原始图像 - landmarks_data: 关键点坐标数据 返回 - annotated_image: 绘制了彩虹骨骼的图像 # 创建图像副本 annotated_image image.copy() h, w image.shape[:2] # 绘制所有关节点白色圆点 for hand in landmarks_data: for point in hand: x, y point[x], point[y] cv2.circle(annotated_image, (x, y), self.joint_radius, self.joint_color, self.joint_thickness) # 为每根手指绘制彩色骨骼线 for finger_name, connections in self.finger_connections.items(): color self.finger_colors[finger_name] for hand in landmarks_data: # 绘制这根手指的所有骨骼连接 for i in range(len(connections) - 1): start_idx connections[i] end_idx connections[i 1] # 获取起点和终点的坐标 start_point hand[start_idx] end_point hand[end_idx] # 绘制骨骼线 cv2.line(annotated_image, (start_point[x], start_point[y]), (end_point[x], end_point[y]), color, self.bone_thickness) return annotated_image def get_finger_status(self, landmarks_data): 分析手指状态伸直或弯曲 参数 - landmarks_data: 关键点坐标数据 返回 - finger_status: 每根手指的状态字典 finger_status {} if not landmarks_data: return finger_status # 这里以第一只手为例 hand landmarks_data[0] # 计算每根手指的指尖与掌根的距离 wrist hand[0] # 手腕点 for finger_name, connections in self.finger_connections.items(): tip_idx connections[-1] # 指尖的索引 mcp_idx connections[1] # 掌指关节的索引 tip_point hand[tip_idx] mcp_point hand[mcp_idx] # 计算指尖到手腕的距离 tip_to_wrist np.sqrt( (tip_point[x] - wrist[x])**2 (tip_point[y] - wrist[y])**2 ) # 计算掌指关节到手腕的距离 mcp_to_wrist np.sqrt( (mcp_point[x] - wrist[x])**2 (mcp_point[y] - wrist[y])**2 ) # 如果指尖距离明显大于掌指关节距离说明手指伸直 is_straight tip_to_wrist mcp_to_wrist * 1.2 finger_status[finger_name] { straight: is_straight, tip_distance: tip_to_wrist, mcp_distance: mcp_to_wrist } return finger_status def recognize_gesture(self, finger_status): 根据手指状态识别常见手势 参数 - finger_status: 手指状态字典 返回 - gesture_name: 识别出的手势名称 if not finger_status: return NO_HAND # 检查每根手指是否伸直 thumb_straight finger_status[THUMB][straight] index_straight finger_status[INDEX][straight] middle_straight finger_status[MIDDLE][straight] ring_straight finger_status[RING][straight] pinky_straight finger_status[PINKY][straight] # 常见手势判断 if (index_straight and middle_straight and not thumb_straight and not ring_straight and not pinky_straight): return VICTORY # 比耶 elif (thumb_straight and index_straight and not middle_straight and not ring_straight and not pinky_straight): return POINTING # 指向 elif (thumb_straight and not index_straight and not middle_straight and not ring_straight and not pinky_straight): return THUMBS_UP # 点赞 elif (index_straight and middle_straight and ring_straight and pinky_straight and not thumb_straight): return FOUR # 数字4 elif all([thumb_straight, index_straight, middle_straight, ring_straight, pinky_straight]): return OPEN_PALM # 张开手掌 elif all([not thumb_straight, not index_straight, not middle_straight, not ring_straight, not pinky_straight]): return FIST # 握拳 else: return UNKNOWN彩虹骨骼可视化不仅让结果更美观还能帮助我们直观地理解手势状态。通过不同颜色的区分你可以一眼看出哪根手指是伸直的哪根是弯曲的。3.3 创建完整的Web应用现在让我们把这些功能整合成一个完整的Web应用。创建一个app.py文件from flask import Flask, request, render_template, send_file, jsonify import cv2 import os from datetime import datetime from hand_detector import HandDetector from rainbow_visualizer import RainbowVisualizer app Flask(__name__) # 初始化检测器和可视化器 detector HandDetector(static_modeTrue, max_hands2, detection_confidence0.7) visualizer RainbowVisualizer() # 创建上传目录 UPLOAD_FOLDER uploads RESULT_FOLDER results os.makedirs(UPLOAD_FOLDER, exist_okTrue) os.makedirs(RESULT_FOLDER, exist_okTrue) app.route(/) def index(): 显示主页面 return !DOCTYPE html html head title手势识别系统/title style body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; background-color: #f5f5f5; } .container { background: white; padding: 30px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); } h1 { color: #333; text-align: center; } .upload-section { text-align: center; margin: 30px 0; } input[typefile] { padding: 10px; border: 2px dashed #ccc; border-radius: 5px; width: 100%; max-width: 400px; } button { background-color: #4CAF50; color: white; padding: 12px 24px; border: none; border-radius: 5px; cursor: pointer; font-size: 16px; margin-top: 10px; } button:hover { background-color: #45a049; } .result-section { margin-top: 30px; text-align: center; } .image-container { display: flex; justify-content: space-around; flex-wrap: wrap; margin-top: 20px; } .image-box { margin: 10px; text-align: center; } .image-box img { max-width: 100%; height: auto; border: 1px solid #ddd; border-radius: 5px; } .info-box { background-color: #e8f5e9; padding: 15px; border-radius: 5px; margin-top: 20px; text-align: left; } .color-legend { display: flex; justify-content: center; gap: 20px; margin: 20px 0; flex-wrap: wrap; } .color-item { display: flex; align-items: center; gap: 5px; } .color-box { width: 20px; height: 20px; border-radius: 3px; } /style /head body div classcontainer h1 AI手势识别系统/h1 p上传包含手部的图片系统将自动检测并绘制彩虹骨骼/p div classcolor-legend div classcolor-item div classcolor-box stylebackground-color: #FFFF00;/div span拇指 (黄色)/span /div div classcolor-item div classcolor-box stylebackground-color: #800080;/div span食指 (紫色)/span /div div classcolor-item div classcolor-box stylebackground-color: #00FFFF;/div span中指 (青色)/span /div div classcolor-item div classcolor-box stylebackground-color: #00FF00;/div span无名指 (绿色)/span /div div classcolor-item div classcolor-box stylebackground-color: #FF0000;/div span小指 (红色)/span /div /div div classupload-section form action/upload methodpost enctypemultipart/form-data input typefile namefile acceptimage/* required brbr button typesubmit识别手势/button /form /div div classinfo-box h3使用提示/h3 ul li支持JPG、PNG格式的图片/li li建议上传清晰的手部图片/li li可以尝试点赞、比耶、握拳等手势/li li系统最多可同时检测两只手/li li处理时间通常在1秒以内/li /ul /div /div /body /html app.route(/upload, methods[POST]) def upload_file(): 处理文件上传和手势识别 if file not in request.files: return 没有选择文件, 400 file request.files[file] if file.filename : return 没有选择文件, 400 # 生成唯一的文件名 timestamp datetime.now().strftime(%Y%m%d_%H%M%S) original_filename foriginal_{timestamp}.jpg result_filename fresult_{timestamp}.jpg original_path os.path.join(UPLOAD_FOLDER, original_filename) result_path os.path.join(RESULT_FOLDER, result_filename) # 保存上传的文件 file.save(original_path) try: # 读取图像 image cv2.imread(original_path) if image is None: return 无法读取图像文件, 400 # 检测手部关键点 results, _ detector.detect(image) # 获取关键点坐标 landmarks_data detector.get_landmark_coordinates(results, image.shape) if not landmarks_data: return div styletext-align: center; padding: 50px; h2未检测到手部/h2 p请确保图片中包含清晰的手部并重新上传/p a href/返回首页/a /div , 400 # 绘制彩虹骨骼 annotated_image visualizer.draw_rainbow_skeleton(image, landmarks_data) # 分析手势 finger_status visualizer.get_finger_status(landmarks_data) gesture visualizer.recognize_gesture(finger_status) # 保存结果 cv2.imwrite(result_path, annotated_image) # 生成结果页面 return f !DOCTYPE html html head title识别结果/title style body {{ font-family: Arial, sans-serif; max-width: 1000px; margin: 0 auto; padding: 20px; background-color: #f5f5f5; }} .container {{ background: white; padding: 30px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }} h1 {{ color: #333; text-align: center; }} .result-info {{ background-color: #e3f2fd; padding: 20px; border-radius: 5px; margin: 20px 0; }} .image-comparison {{ display: flex; justify-content: space-around; flex-wrap: wrap; gap: 20px; margin: 30px 0; }} .image-box {{ text-align: center; flex: 1; min-width: 300px; }} .image-box img {{ max-width: 100%; height: auto; border: 1px solid #ddd; border-radius: 5px; }} .gesture-result {{ background-color: #4CAF50; color: white; padding: 15px; border-radius: 5px; text-align: center; font-size: 24px; margin: 20px 0; }} .action-buttons {{ text-align: center; margin-top: 30px; }} .btn {{ display: inline-block; padding: 12px 24px; margin: 0 10px; background-color: #4CAF50; color: white; text-decoration: none; border-radius: 5px; border: none; cursor: pointer; font-size: 16px; }} .btn:hover {{ background-color: #45a049; }} .btn-secondary {{ background-color: #2196F3; }} .btn-secondary:hover {{ background-color: #1976D2; }} /style /head body div classcontainer h1 手势识别完成/h1 div classgesture-result 识别结果strong{gesture}/strong /div div classresult-info h3检测信息/h3 ul li检测到手部数量{len(landmarks_data)}/li li每只手21个关键点/li li处理时间1秒/li li图像尺寸{image.shape[1]} × {image.shape[0]} 像素/li /ul /div div classimage-comparison div classimage-box h3原始图片/h3 img src/uploads/{original_filename} alt原始图片 /div div classimage-box h3识别结果彩虹骨骼/h3 img src/results/{result_filename} alt识别结果 /div /div div classaction-buttons a href/ classbtn识别新图片/a a href/results/{result_filename} download classbtn btn-secondary下载结果图片/a /div /div /body /html except Exception as e: return f处理过程中出现错误{str(e)}, 500 app.route(/uploads/filename) def serve_upload(filename): 提供上传的图片 return send_file(os.path.join(UPLOAD_FOLDER, filename)) app.route(/results/filename) def serve_result(filename): 提供处理后的图片 return send_file(os.path.join(RESULT_FOLDER, filename)) app.route(/api/detect, methods[POST]) def api_detect(): 提供API接口返回JSON格式的结果 if file not in request.files: return jsonify({error: No file provided}), 400 file request.files[file] # 读取图像 image_data file.read() nparr np.frombuffer(image_data, np.uint8) image cv2.imdecode(nparr, cv2.IMREAD_COLOR) if image is None: return jsonify({error: Invalid image}), 400 # 检测手部关键点 results, _ detector.detect(image) landmarks_data detector.get_landmark_coordinates(results, image.shape) if not landmarks_data: return jsonify({hands_detected: 0, landmarks: []}) # 分析手势 finger_status visualizer.get_finger_status(landmarks_data) gesture visualizer.recognize_gesture(finger_status) response { hands_detected: len(landmarks_data), gesture: gesture, landmarks: landmarks_data, finger_status: finger_status, timestamp: datetime.now().isoformat() } return jsonify(response) if __name__ __main__: # 启动Flask应用 app.run(host0.0.0.0, port5000, debugTrue)这个完整的Web应用提供了美观的用户界面支持文件上传实时的手势识别和彩虹骨骼绘制手势类型识别点赞、比耶、握拳等结果对比展示原始图片 vs 识别结果RESTful API接口方便其他系统调用4. 快速上手与测试4.1 运行你的手势识别系统现在让我们启动系统并进行测试安装依赖如果你是从零开始pip install opencv-python mediapipe flask numpy启动应用python app.py访问应用 打开浏览器访问http://localhost:5000上传测试图片 你可以使用手机拍摄自己的手势照片或者从网上下载一些包含手部的图片进行测试。4.2 测试不同手势为了全面测试系统的识别能力建议尝试以下几种常见手势张开手掌五指完全伸直握拳五指完全弯曲点赞拇指伸直其他四指弯曲比耶食指和中指伸直其他手指弯曲OK手势拇指和食指形成圆圈数字手势尝试1-5的数字手势每种手势的彩虹骨骼显示效果都不同你可以观察不同手指的颜色变化。4.3 使用API接口如果你想要在其他程序中调用手势识别功能可以使用我们提供的API接口import requests # 准备测试图片 with open(test_hand.jpg, rb) as f: files {file: f} # 发送POST请求 response requests.post(http://localhost:5000/api/detect, filesfiles) if response.status_code 200: result response.json() print(f检测到手部数量: {result[hands_detected]}) print(f识别手势: {result[gesture]}) # 获取关键点坐标 for hand_idx, hand_landmarks in enumerate(result[landmarks]): print(f\n第 {hand_idx 1} 只手的关键点:) for point in hand_landmarks: print(f {point[name]}: ({point[x]}, {point[y]})) else: print(f请求失败: {response.status_code})这个API返回JSON格式的数据包含检测到的手部数量、识别的手势类型以及每个关键点的具体坐标。5. 常见问题与解决方案5.1 检测不到手部怎么办如果你上传了图片但系统没有检测到手部可以尝试以下方法调整图片质量确保手部在图片中清晰可见避免过于复杂或杂乱的背景保证光线充足避免阴影遮挡调整检测参数 在HandDetector初始化时可以降低detection_confidence的值# 降低置信度阈值提高检测灵敏度 detector HandDetector(detection_confidence0.3)预处理图片 在检测前对图片进行预处理def preprocess_image(image): # 调整亮度对比度 alpha 1.2 # 对比度系数 beta 30 # 亮度增量 enhanced cv2.convertScaleAbs(image, alphaalpha, betabeta) # 高斯模糊去噪 blurred cv2.GaussianBlur(enhanced, (5, 5), 0) return blurred5.2 识别准确率如何提升虽然MediaPipe Hands已经相当准确但你还可以通过以下方式进一步提升识别效果多角度训练数据 如果你的应用场景固定可以收集一些特定角度的图片进行测试。后处理优化def smooth_landmarks(current_landmarks, previous_landmarks, alpha0.5): 平滑关键点坐标减少抖动 if previous_landmarks is None: return current_landmarks smoothed [] for curr, prev in zip(current_landmarks, previous_landmarks): smoothed_point { x: int(alpha * curr[x] (1-alpha) * prev[x]), y: int(alpha * curr[y] (1-alpha) * prev[y]), z: alpha * curr[z] (1-alpha) * prev[z] } smoothed.append(smoothed_point) return smoothed手势规则优化 根据你的具体需求调整手势识别规则def custom_gesture_recognition(finger_status): 自定义手势识别规则 # 这里可以根据你的需求添加特定的手势判断逻辑 pass5.3 性能优化建议如果你的应用需要处理大量图片或视频流可以考虑以下优化图片尺寸调整def resize_for_detection(image, max_dimension640): 调整图片尺寸以加快处理速度 h, w image.shape[:2] if max(h, w) max_dimension: scale max_dimension / max(h, w) new_w int(w * scale) new_h int(h * scale) return cv2.resize(image, (new_w, new_h)) return image批量处理 如果需要处理多张图片可以使用批量处理def batch_process(image_paths, batch_size4): 批量处理图片 results [] for i in range(0, len(image_paths), batch_size): batch image_paths[i:ibatch_size] # 并行处理批次中的图片 batch_results process_batch(batch) results.extend(batch_results) return results缓存机制 对于重复的图片可以使用缓存避免重复计算import hashlib image_cache {} def get_image_hash(image): 计算图片的哈希值 return hashlib.md5(image.tobytes()).hexdigest() def process_with_cache(image): 带缓存的图片处理 img_hash get_image_hash(image) if img_hash in image_cache: return image_cache[img_hash] # 处理图片 result process_image(image) # 存入缓存 image_cache[img_hash] result return result6. 扩展应用与进阶功能6.1 实时视频流处理将系统扩展到实时视频处理非常简单import cv2 from hand_detector import HandDetector from rainbow_visualizer import RainbowVisualizer def process_video_stream(): 处理摄像头视频流 # 初始化 detector HandDetector(static_modeFalse, max_hands2) visualizer RainbowVisualizer() # 打开摄像头 cap cv2.VideoCapture(0) # 设置摄像头参数 cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) print(按 q 键退出...) while True: # 读取帧 ret, frame cap.read() if not ret: break # 镜像翻转让显示更自然 frame cv2.flip(frame, 1) # 检测手部关键点 results, _ detector.detect(frame) landmarks_data detector.get_landmark_coordinates(results, frame.shape) # 如果检测到手部绘制彩虹骨骼 if landmarks_data: frame visualizer.draw_rainbow_skeleton(frame, landmarks_data) # 识别手势 finger_status visualizer.get_finger_status(landmarks_data) gesture visualizer.recognize_gesture(finger_status) # 在画面上显示手势名称 cv2.putText(frame, fGesture: {gesture}, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) # 显示结果 cv2.imshow(Hand Tracking, frame) # 按q退出 if cv2.waitKey(1) 0xFF ord(q): break # 释放资源 cap.release() cv2.destroyAllWindows() if __name__ __main__: process_video_stream()这个实时版本可以让你通过摄像头实时看到手势识别效果非常适合交互式应用开发。6.2 手势控制应用示例基于手势识别我们可以开发各种有趣的应用。这里是一个简单的幻灯片控制示例import pyautogui import time class GestureController: def __init__(self): self.last_gesture None self.gesture_start_time None self.hold_threshold 1.0 # 手势保持1秒才触发 def control_presentation(self, gesture): 根据手势控制幻灯片 current_time time.time() # 如果是新手势记录开始时间 if gesture ! self.last_gesture: self.last_gesture gesture self.gesture_start_time current_time return # 检查手势是否保持足够时间 if current_time - self.gesture_start_time self.hold_threshold: return # 执行控制操作 if gesture POINTING: # 食指指向 - 下一张幻灯片 pyautogui.press(right) print(下一张幻灯片) self.gesture_start_time current_time # 重置计时 elif gesture THUMBS_UP: # 点赞 - 开始播放 pyautogui.press(f5) print(开始播放) self.gesture_start_time current_time elif gesture THUMBS_DOWN: # 倒赞 - 结束播放 pyautogui.press(esc) print(结束播放) self.gesture_start_time current_time elif gesture OPEN_PALM: # 张开手掌 - 暂停/继续 pyautogui.press(space) print(暂停/继续) self.gesture_start_time current_time # 在主循环中使用 controller GestureController() while True: # 获取当前手势从你的识别系统 current_gesture get_current_gesture() # 这需要你从识别系统获取 # 控制幻灯片 controller.control_presentation(current_gesture) time.sleep(0.1) # 控制频率6.3 集成到其他项目手势识别系统可以轻松集成到各种项目中游戏控制用手势控制游戏角色智能家居用手势控制灯光、音乐等教育应用手势交互的学习软件无障碍应用为行动不便的用户提供新的交互方式艺术创作手势控制的数字绘画工具7. 总结7.1 核心收获回顾通过这篇教程我们完成了一个完整的手势识别系统搭建环境部署简单基于预置镜像无需GPU开箱即用核心代码完整从手部检测到彩虹骨骼可视化代码清晰易懂Web应用完善提供美观的界面和实用的API接口实时处理支持轻松扩展到视频流处理应用场景丰富可以集成到各种交互项目中7.2 关键优势总结零GPU依赖完全在CPU上运行成本低部署简单高精度识别MediaPipe Hands提供21个3D关键点识别准确可视化友好彩虹骨骼让手势状态一目了然易于扩展代码结构清晰方便添加新功能实时性能单张图片处理仅需毫秒级视频流也能流畅运行7.3 下一步学习建议如果你对这个系统感兴趣想要进一步深入学习深入研究MediaPipe探索MediaPipe的其他功能如姿态估计、人脸检测等优化识别算法根据你的具体需求调整手势识别规则开发完整应用基于这个系统开发一个真正可用的手势控制应用学习3D手势利用MediaPipe提供的3D坐标开发空间手势识别多模态融合结合语音、眼动等其他交互方式创造更自然的用户体验手势识别技术正在改变我们与数字世界的交互方式。从简单的图片识别到复杂的实时控制这个领域有着无限的可能性。希望这篇教程能成为你探索这个精彩世界的起点。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。

相关文章:

MediaPipe Hands保姆级教程:从零搭建手势识别系统,无需GPU

MediaPipe Hands保姆级教程:从零搭建手势识别系统,无需GPU 1. 引言:为什么选择MediaPipe Hands? 想象一下,你正在开发一个智能健身应用,需要识别用户的“点赞”手势来计数;或者你在做一个教育…...

【多模态大模型可解释性破局指南】:20年AI专家亲授3大可解释性框架、5类可视化归因工具及工业级落地避坑清单

第一章:多模态大模型可解释性研究的范式跃迁 2026奇点智能技术大会(https://ml-summit.org) 传统可解释性方法长期受限于单模态假设——文本归因依赖梯度或扰动,图像解释聚焦类激活映射(CAM),而语音或视频则常被降维为…...

多模态大模型如何兼顾视觉理解、语音生成与跨模态推理?——揭秘统一表征下的动态任务路由机制

第一章:多模态大模型多任务学习策略 2026奇点智能技术大会(https://ml-summit.org) 多模态大模型的多任务学习并非简单地将图像分类、文本生成、语音理解等任务并行堆叠,而是需在共享表征空间中建立语义对齐与梯度协同机制。关键挑战在于不同模态数据的…...

半固态充电宝是智商税?实测针刺不起火,2026选购避坑指南

清明假期刚过,眼看五一假期又要到了,为了应对假期出行的充电需要,每个人都多多少少都配备了充电宝,各大电商平台上的充电宝又开始打“骨折价”。但不知道你发现没有,这两年“充电宝自燃”“充电宝在包里爆炸”的热搜越…...

Photoshop图层批量导出终极指南:Export-Layers-to-Files-Fast快速上手教程

Photoshop图层批量导出终极指南:Export-Layers-to-Files-Fast快速上手教程 【免费下载链接】Photoshop-Export-Layers-to-Files-Fast This script allows you to export your layers as individual files at a speed much faster than the built-in script from Ado…...

如何快速激活Windows和Office:KMS_VL_ALL_AIO智能脚本完整指南

如何快速激活Windows和Office:KMS_VL_ALL_AIO智能脚本完整指南 【免费下载链接】KMS_VL_ALL_AIO Smart Activation Script 项目地址: https://gitcode.com/gh_mirrors/km/KMS_VL_ALL_AIO KMS_VL_ALL_AIO是一款功能全面的Windows和Office激活脚本工具&#xf…...

Kotaemon应用指南:从安装到配置,打造专属企业知识大脑

Kotaemon应用指南:从安装到配置,打造专属企业知识大脑 1. 认识Kotaemon:企业级RAG解决方案 在信息爆炸的时代,企业面临的最大挑战不是获取知识,而是如何高效利用已有知识。传统文档管理系统往往让员工陷入"知道…...

Ubuntu桌面应用开机自启动终极指南:从.desktop配置到环境变量设置

Ubuntu桌面应用开机自启动终极指南:从.desktop配置到环境变量设置 在Ubuntu桌面环境中,让应用程序随系统启动自动运行是提升工作效率的常见需求。无论是开发工具、监控程序还是日常生产力软件,合理的自启动配置都能让我们省去每次手动打开的麻…...

终极指南:如何免费解锁Cursor Pro的完整AI编程功能

终极指南:如何免费解锁Cursor Pro的完整AI编程功能 【免费下载链接】cursor-free-vip [Support 0.45](Multi Language 多语言)自动注册 Cursor Ai ,自动重置机器ID , 免费升级使用Pro 功能: Youve reached your trial …...

【学习笔记】ROS2 常用工具最全总结:功能、特点与使用场景

ROS2(Robot Operating System 2)作为新一代机器人操作系统,凭借模块化、跨平台、实时性强的优势,广泛应用于科研、工业、教育等领域。其生态体系中包含大量实用工具,覆盖机器人建模、仿真、可视化、调试、导航、建图等…...

Hermes Agent爆火:是OpenClaw终结者,还是反OpenClaw情绪的烟花?

【Hermes Agent冲上热门榜首】这两天,Hermes Agent冲上了OpenRouter“最热门的编程Agent”榜首,分类榜显示用量达到149B tokens。在OpenRouter平台上,Hermes Agent进入本周增长最快的应用和Agent榜单,一周内实现了367%的增长&…...

终极指南:使用CefFlashBrowser轻松重温经典Flash游戏和课件

终极指南:使用CefFlashBrowser轻松重温经典Flash游戏和课件 【免费下载链接】CefFlashBrowser Flash浏览器 / Flash Browser 项目地址: https://gitcode.com/gh_mirrors/ce/CefFlashBrowser CefFlashBrowser是一款专为解决现代浏览器Flash兼容性问题而设计的…...

从汽车到工厂:深入浅出解析PTP在TSN和AUTOSAR中的实现差异

从汽车到工厂:深入浅出解析PTP在TSN和AUTOSAR中的实现差异 在工业自动化和汽车电子领域,时间同步技术正成为支撑下一代智能系统的关键基础设施。想象一下,当一辆自动驾驶汽车以120公里时速行驶时,其传感器、控制器和执行器之间的时…...

深入解析devm_regulator_get:Linux电源管理的自动化资源获取机制

1. 揭开devm_regulator_get的神秘面纱 第一次在Linux驱动代码里看到devm_regulator_get这个函数时,我盯着屏幕发了五分钟呆。这名字长得像俄罗斯套娃,拆开看每个单词都认识,合在一起却让人摸不着头脑。后来在调试一块开发板的电源问题时&…...

BEYOND REALITY Z-Image免配置环境:Docker镜像开箱即用写实人像生成

BEYOND REALITY Z-Image免配置环境:Docker镜像开箱即用写实人像生成 提示:本文介绍的镜像基于开源项目封装,旨在降低使用门槛。所有内容仅供技术交流,请遵守相关法律法规,合理使用AI技术。 1. 项目简介 BEYOND REALIT…...

企业微信小程序接入腾讯TRTC多人会议,从类目审核到上线的完整避坑指南

企业微信小程序接入腾讯TRTC多人会议全流程实战指南 当企业需要为内部培训或跨地域协作搭建稳定的音视频会议系统时,微信小程序结合腾讯TRTC服务无疑是个高效的选择。但不同于个人开发者的Demo验证,企业级应用从账号注册到最终上线,每个环节…...

CloudFlare内网穿透实战:从零搭建到稳定运行

1. 为什么选择CloudFlare做内网穿透? 最近几年内网穿透需求爆发式增长,很多开发者都需要远程访问家里的NAS、调试树莓派或者展示本地开发环境。传统方案要么需要公网IP(现在越来越难申请),要么需要自建服务器&#xf…...

VBA年终损益一键结转宏,打破手动做结转分录传统,财务表格嵌入宏代码,一键自动结转全年收支算净利润,不用死编分录,AI操作碾压手工做账逻辑。

一套“VBA 年终损益一键结转宏”完整实战方案,定位非常锋利: 把“手工编结转分录”变成“一键自动结账” 让年终损益结转从会计苦力活变成系统自动动作 ✅ 智能会计课程 Excel 总账实训 ✅ 中小企业 / 代理记账年终结账 ✅ 技术博客 VBA 实战案例 一、…...

XUnity AutoTranslator完整指南:一键实现Unity游戏多语言实时翻译

XUnity AutoTranslator完整指南:一键实现Unity游戏多语言实时翻译 【免费下载链接】XUnity.AutoTranslator 项目地址: https://gitcode.com/gh_mirrors/xu/XUnity.AutoTranslator 想要畅玩外语游戏却苦于语言障碍?XUnity AutoTranslator作为一款…...

SQL电商真假订单分拣代码,颠覆全流水统算营收老误区,SQL语句智能剥离刷单虚假订单,独立归集,真实成交数据,机器精准筛别,完胜人工肉眼分辨对账。

一套“SQL 电商真假订单智能分拣系统”完整实战方案,定位非常锋利:用 SQL 把“刷单水分”从营收里彻底挤干让财务报表从“看起来很好”变成“真的很好”非常适合你作为✅ 智能会计课程 电商会计实战✅ 电商企业 / 代运营公司财务对账✅ 技术博客 SQL …...

一位老程序员的生涯回顾

一位老程序员的生涯回顾 在技术飞速迭代的浪潮中,老程序员张工的三十年职业生涯像一本厚重的代码日志,记录着从穿孔纸带到云计算的跨越。他的故事不仅是技术的演进史,更是一代人对初心的坚守。 **从BASIC到人工智能** 上世纪90年代&#x…...

Newtonsoft.Json反序列化空值报错?5分钟搞定System.Int32类型转换问题

Newtonsoft.Json反序列化空值处理实战指南 当你在C#项目中处理JSON数据时,是否遇到过这样的报错信息?"Error converting value {null} to type System.Int32"——这几乎是每个C#开发者在使用Newtonsoft.Json进行反序列化时都会碰到的经典问题。…...

踩坑记录:SpringBoot 2.7集成Knife4j OpenAPI3时,@Parameter(required=false)为啥不生效?附解决方案

SpringBoot 2.7与Knife4j深度整合:解决Parameter(requiredfalse)失效的底层逻辑 最近在重构公司内部的一个微服务项目时,遇到了一个看似简单却让人抓狂的问题:明明在接口参数上标注了Parameter(requiredfalse),前端调用时却依然报…...

旅游推荐系统 Python+Django+Vue.js

博主说明:本文项目编号 25009 ,文末自助获取源码 \color{red}{25009,文末自助获取源码} 25009,文末自助获取源码 目录 一、系统介绍1.1 需求分析1.1.1 用户功能描述1.1.2 管理员功能描述 1.2 技术栈 二、演示录屏三、启动教程四、…...

基于CODESYS平台与汇川AM系列PLC的手轮精准对位与ECAT轴协同控制实战解析

1. 手轮与ECAT轴协同控制的核心价值 在精密装配、半导体设备或高精度加工场景中,操作人员经常需要微调设备位置到微米级精度。传统按钮点动方式就像用铁锤雕刻核桃——力度难以把控。而5V差分式手轮配合ECAT总线伺服,相当于给设备装上了"微调旋钮&q…...

从防御者视角复盘:一次完整的钓鱼攻击模拟(Kali+setoolkit)与痕迹分析

从防御者视角复盘:一次完整的钓鱼攻击模拟与痕迹分析 钓鱼攻击一直是网络安全领域中最常见且最具威胁的攻击手段之一。攻击者通过伪装成可信来源,诱导受害者泄露敏感信息。本文将从一个独特的防御者视角出发,通过模拟一次完整的钓鱼攻击过程&…...

CSS如何优化CSS选择器引入_避免过度嵌套保持高性能

CSS选择器匹配从右往左,.header .nav .item:hover需回溯验证父级,而.nav-item:hover直接哈希查找;BEM通过命名契约替代结构依赖;import阻塞并串行加载,应禁用;重排由布局相关API或伪类触发,非选…...

思科模拟器实战:构建高可用校园网络

1. 为什么校园网需要高可用设计 校园网络就像学校的"神经系统",一旦瘫痪整个教学秩序就会乱套。去年我参与过一所重点中学的网络改造项目,当时他们的老网络在期末考试期间突然宕机,导致在线考试系统崩溃,最后不得不启用…...

高精度运算工具类

高精度运算工具类(ArithTool)📖 背景:为什么要用高精度运算?在Java中使用 double 或 float 进行浮点数运算时,经常会遇到精度丢失的问题。这是因为计算机使用二进制存储浮点数,而某些十进制小数…...

快速上手StructBERT语义分析工具:中文句子匹配实战指南

快速上手StructBERT语义分析工具:中文句子匹配实战指南 1. 工具概览与核心价值 StructBERT语义分析工具是一款专为中文文本设计的本地化语义匹配解决方案。它基于阿里达摩院开源的StructBERT-Large模型,通过深度学习技术实现句子级别的语义相似度计算。…...