news 2026/8/16 12:22:19

Mac 真人手势识别切水果游戏

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Mac 真人手势识别切水果游戏

1. 环境

mac python10

2. 代码

import cv2 import mediapipe as mp import time import numpy as np import random import math # 初始化MediaPipe解决方案 mp_hands = mp.solutions.hands mp_face_mesh = mp.solutions.face_mesh mp_draw = mp.solutions.drawing_utils # 自定义绘制样式 face_drawing_spec = mp_draw.DrawingSpec(thickness=1, circle_radius=1, color=(0, 255, 0)) hand_drawing_spec = mp_draw.DrawingSpec(thickness=2, circle_radius=3, color=(0, 0, 255)) # 初始化模型 hands = mp_hands.Hands( static_image_mode=False, max_num_hands=2, min_detection_confidence=0.5, # 降低检测阈值,提高灵敏度 min_tracking_confidence=0.3 # 降低跟踪阈值 ) face_mesh = mp_face_mesh.FaceMesh( static_image_mode=False, max_num_faces=1, min_detection_confidence=0.5, min_tracking_confidence=0.5 ) # 打开摄像头 cap = cv2.VideoCapture(0) if not cap.isOpened(): print("无法打开摄像头,请检查连接。") exit() # 设置摄像头分辨率(适中分辨率以保证性能) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 720) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720) print("=== 切水果游戏 ===") print("游戏规则:") print("1. 伸出食指作为'刀'来切水果") print("2. 切中水果得分,错过扣分") print("3. 保持笑脸可以获得分数加成!") print("游戏时间:2分钟") print("控制:") print("Q - 退出游戏") print("R - 重新开始游戏") print("H - 显示/隐藏手部检测") print("F - 显示/隐藏面部检测") print("==================") # 游戏状态 show_hands = True show_face = False game_active = True score = 0 combo = 0 max_combo = 0 game_start_time = time.time() game_duration = 60 # 游戏时长120秒(2分钟) show_fireworks = False fireworks_start_time = 0 fireworks_duration = 5 # 烟花效果持续时间(秒) # FPS计算 pTime = 0 # 烟花粒子类 class FireworkParticle: def __init__(self, x, y): self.x = x self.y = y self.color = ( random.randint(150, 255), random.randint(150, 255), random.randint(150, 255) ) self.size = random.randint(2, 6) self.speed_x = random.uniform(-5, 5) self.speed_y = random.uniform(-8, -3) self.life = 100 self.gravity = 0.2 self.trail = [] def update(self): self.x += self.speed_x self.y += self.speed_y self.speed_y += self.gravity self.life -= 2 # 添加轨迹点 self.trail.append((int(self.x), int(self.y))) if len(self.trail) > 5: self.trail.pop(0) return self.life > 0 def draw(self, frame): # 绘制轨迹 for i in range(1, len(self.trail)): alpha = i / len(self.trail) color = ( int(self.color[0] * alpha), int(self.color[1] * alpha), int(self.color[2] * alpha) ) cv2.line(frame, self.trail[i-1], self.trail[i], color, 1) # 绘制粒子 cv2.circle(frame, (int(self.x), int(self.y)), self.size, self.color, -1) cv2.circle(frame, (int(self.x), int(self.y)), self.size, (255, 255, 255), 1) # 烟花效果 fireworks = [] def create_fireworks_effect(x, y, count=30): """在指定位置创建烟花效果""" for _ in range(count): fireworks.append(FireworkParticle(x, y)) # 水果类 class Fruit: def __init__(self, width, height): self.types = ['apple', 'banana', 'orange', 'watermelon', 'bomb'] self.colors = { 'apple': (0, 0, 255), # 红色 'banana': (0, 200, 255), # 黄色 'orange': (0, 165, 255), # 橙色 'watermelon': (0, 255, 0), # 绿色 'bomb': (100, 100, 100) # 灰色 } self.type = random.choice(self.types) self.color = self.colors[self.type] self.radius = random.randint(20, 45) # 减小最小半径,让小水果更容易识别 # 初始位置(从屏幕顶部随机位置出现) self.x = random.randint(self.radius, width - self.radius) self.y = -self.radius # 速度 self.speed_x = random.uniform(-2.0, 2.0) self.speed_y = random.uniform(3.0, 6.0) # 旋转(用于动画效果) self.angle = 0 self.rotation_speed = random.uniform(-3.0, 3.0) # 状态 self.sliced = False self.slice_time = 0 self.slice_animation = 0 # 得分值 self.points = { 'apple': 10, 'banana': 15, 'orange': 20, 'watermelon': 30, 'bomb': -50 # 炸弹扣分 } self.point_value = self.points[self.type] # 切割特效颜色 self.slice_color = ( min(255, self.color[0] + 50), min(255, self.color[1] + 50), min(255, self.color[2] + 50) ) def update(self, height, width): if not self.sliced: # 更新位置 self.x += self.speed_x self.y += self.speed_y # 边界反弹 if self.x <= self.radius or self.x >= width - self.radius: self.speed_x *= -0.8 # 添加摩擦力 self.x = max(self.radius, min(width - self.radius, self.x)) # 旋转 self.angle += self.rotation_speed # 检查是否掉出屏幕 if self.y > height + self.radius: return False # 水果应该被移除 else: # 切割动画 self.slice_animation += 1 if time.time() - self.slice_time > 1.5: # 1.5秒后消失 return False return True def draw(self, frame): if not self.sliced: # 绘制水果(带旋转效果) angle_rad = math.radians(self.angle) # 绘制水果主体 cv2.circle(frame, (int(self.x), int(self.y)), self.radius, self.color, -1) # 绘制高光(只对大水果) if self.radius > 25: highlight_radius = self.radius // 3 highlight_x = int(self.x - highlight_radius * math.cos(angle_rad)) highlight_y = int(self.y - highlight_radius * math.sin(angle_rad)) cv2.circle(frame, (highlight_x, highlight_y), highlight_radius // 2, (255, 255, 255), -1) # 绘制水果细节(只对大水果) if self.radius > 30: if self.type == 'apple': # 苹果梗 cv2.line(frame, (int(self.x), int(self.y - self.radius)), (int(self.x), int(self.y - self.radius - 15)), (80, 40, 0), 2) cv2.circle(frame, (int(self.x), int(self.y - self.radius - 15)), 2, (100, 50, 0), -1) elif self.type == 'banana': # 香蕉曲线 cv2.ellipse(frame, (int(self.x), int(self.y)), (self.radius-8, self.radius), self.angle, 0, 180, (30, 120, 180), 1) elif self.type == 'bomb': # 炸弹引线 cv2.line(frame, (int(self.x), int(self.y - self.radius)), (int(self.x + 10), int(self.y - self.radius - 20)), (50, 50, 50), 2) cv2.circle(frame, (int(self.x + 10), int(self.y - self.radius - 20)), 2, (200, 0, 0), -1) else: # 绘制被切割的水果(两部分) slice_offset = min(self.slice_animation * 5, 30) rotation = self.angle + self.slice_animation * 10 # 左半部分(旋转下落) left_x = int(self.x - slice_offset) left_y = int(self.y + self.slice_animation * 2) cv2.ellipse(frame, (left_x, left_y), (self.radius, self.radius//2), rotation, 0, 180, self.slice_color, -1) # 右半部分(旋转下落) right_x = int(self.x + slice_offset) right_y = int(self.y + self.slice_animation * 2) cv2.ellipse(frame, (right_x, right_y), (self.radius, self.radius//2), -rotation, 180, 360, self.slice_color, -1) # 绘制果汁溅射效果 for _ in range(max(1, self.radius // 10)): # 根据水果大小决定溅射点数 juice_x = int(self.x) + random.randint(-self.radius, self.radius) juice_y = int(self.y) + random.randint(-self.radius//2, self.radius//2) juice_size = random.randint(2, max(3, self.radius//8)) cv2.circle(frame, (juice_x, juice_y), juice_size, self.slice_color, -1) def check_slice(self, finger_x, finger_y, prev_finger_x, prev_finger_y): """检查手指是否切中了水果 - 优化小水果识别""" if self.sliced: return False # 方法1:检查当前手指位置是否在水果内(主要方法) distance = math.sqrt((finger_x - self.x)**2 + (finger_y - self.y)**2) # 动态阈值:小水果使用更宽松的阈值 slice_threshold = self.radius * 1.2 # 增加20%的检测范围 # 方法2:检查手指移动路径是否穿过水果(备用方法) path_sliced = False if prev_finger_x > 0 and prev_finger_y > 0 and distance > slice_threshold: # 计算手指移动线段与水果圆心的最短距离 line_length = math.sqrt((finger_x - prev_finger_x)**2 + (finger_y - prev_finger_y)**2) if line_length > 0: # 计算点到线段的距离 t = max(0, min(1, ((self.x - prev_finger_x)*(finger_x - prev_finger_x) + (self.y - prev_finger_y)*(finger_y - prev_finger_y)) / (line_length**2))) closest_x = prev_finger_x + t * (finger_x - prev_finger_x) closest_y = prev_finger_y + t * (finger_y - prev_finger_y) distance_to_path = math.sqrt((self.x - closest_x)**2 + (self.y - closest_y)**2) # 如果路径穿过水果 if distance_to_path <= slice_threshold: path_sliced = True # 如果当前位置或路径穿过水果 if distance <= slice_threshold or path_sliced: self.sliced = True self.slice_time = time.time() return True return False # 游戏水果列表 fruits = [] fruit_spawn_rate = 1.2 # 每秒生成水果的概率 # 手指追踪 finger_trail = [] prev_finger_x, prev_finger_y = -1, -1 max_trail_length = 15 def detect_finger_position(hand_landmarks, frame_shape): """检测食指指尖位置 - 简化版本""" h, w, _ = frame_shape # 食指指尖的索引是8 index_finger_tip = hand_landmarks.landmark[8] # 转换为像素坐标 x = int(index_finger_tip.x * w) y = int(index_finger_tip.y * h) # 由于后面会做镜像翻转,这里需要计算镜像后的x坐标 # 镜像公式:x_mirrored = width - x x_mirrored = w - x return x_mirrored, y def count_extended_fingers_simple(hand_landmarks, frame_shape): """计算伸直的手指数量 - 简化但更可靠的版本""" h, w, _ = frame_shape # 关键点索引 tip_ids = [4, 8, 12, 16, 20] # 指尖:拇指、食指、中指、无名指、小指 pip_ids = [3, 6, 10, 14, 18] # 第二关节 extended_count = 0 index_finger_extended = False # 检查拇指(简化逻辑) thumb_tip = hand_landmarks.landmark[tip_ids[0]] thumb_pip = hand_landmarks.landmark[pip_ids[0]] # 如果拇指指尖在PIP关节左侧(对于右手) if thumb_tip.x < thumb_pip.x: extended_count += 1 # 检查其他四指 for i in range(1, 5): tip = hand_landmarks.landmark[tip_ids[i]] pip = hand_landmarks.landmark[pip_ids[i]] # 简单判断:如果指尖在PIP关节上方(y坐标更小) # 添加一些容错空间 if tip.y < pip.y - 0.02: # 减少阈值,提高灵敏度 extended_count += 1 if i == 1: # 食指 index_finger_extended = True return extended_count, index_finger_extended def is_index_finger_extended(hand_landmarks): """专门检测食指是否伸直 - 更精确的方法""" # 食指关键点 index_tip = hand_landmarks.landmark[8] # 食指指尖 index_pip = hand_landmarks.landmark[6] # 食指PIP关节 index_mcp = hand_landmarks.landmark[5] # 食指MCP关节 # 计算角度来判断食指是否伸直 # 方法1:检查指尖是否在PIP关节上方 if index_tip.y < index_pip.y: # 方法2:检查指尖到MCP的距离是否大于PIP到MCP的距离 tip_to_mcp = math.sqrt((index_tip.x - index_mcp.x)**2 + (index_tip.y - index_mcp.y)**2) pip_to_mcp = math.sqrt((index_pip.x - index_mcp.x)**2 + (index_pip.y - index_mcp.y)**2) # 如果指尖离MCP比PIP离MCP更远,说明手指伸直 if tip_to_mcp > pip_to_mcp * 1.1: return True return False def detect_expression(face_landmarks, frame_shape): """基于面部关键点检测简单表情""" h, w, _ = frame_shape landmarks = face_landmarks.landmark # 嘴部关键点 mouth_top = landmarks[13] mouth_bottom = landmarks[14] mouth_top_y = int(mouth_top.y * h) mouth_bottom_y = int(mouth_bottom.y * h) mouth_height = abs(mouth_bottom_y - mouth_top_y) # 嘴角 left_corner = landmarks[61] right_corner = landmarks[291] # 表情判断 expression = "Neutral" color = (200, 200, 200) is_smiling = False # 检测微笑 if mouth_height > 15: if left_corner.y < mouth_top.y and right_corner.y < mouth_top.y: expression = "Smiling" color = (0, 255, 255) is_smiling = True else: expression = "Surprised" color = (255, 0, 0) return expression, color, is_smiling def draw_game_info(frame, fps, score, combo, max_combo, time_left, is_smiling, finger_x, finger_y): """绘制游戏信息面板""" h, w = frame.shape[:2] # 创建半透明信息面板 overlay = frame.copy() cv2.rectangle(overlay, (10, 10), (450, 220), (0, 0, 0), -1) frame = cv2.addWeighted(overlay, 0.7, frame, 0.3, 0) # 显示游戏信息 cv2.putText(frame, f'Fruit Slash!', (20, 45), cv2.FONT_HERSHEY_SIMPLEX, 1.3, (0, 255, 255), 3) cv2.putText(frame, f'Score: {score}', (20, 85), cv2.FONT_HERSHEY_SIMPLEX, 1.1, (255, 255, 255), 2) combo_color = (0, 255, 0) if combo > 1 else (200, 200, 200) cv2.putText(frame, f'Combo: x{combo}', (20, 125), cv2.FONT_HERSHEY_SIMPLEX, 1.1, combo_color, 2) # 时间显示颜色变化 time_color = (255, 255, 0) if time_left < 30: # 最后30秒变红色 time_color = (0, 0, 255) if int(time_left) % 2 == 0 else (255, 255, 0) minutes = int(time_left) // 60 seconds = int(time_left) % 60 cv2.putText(frame, f'Time: {minutes:02d}:{seconds:02d}', (20, 165), cv2.FONT_HERSHEY_SIMPLEX, 1.1, time_color, 2) # 表情加成提示 if is_smiling: cv2.putText(frame, f'Smile Bonus: +{combo*2}', (20, 205), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 255), 2) # 游戏提示 cv2.putText(frame, "Use INDEX finger to slice!", (w-400, 40), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (150, 150, 255), 2) # 在手指位置绘制"刀"的图标 if finger_x > 0 and finger_y > 0 and 0 <= finger_x < w and 0 <= finger_y < h: # 绘制刀光效果 for i in range(1, len(finger_trail)): if i < len(finger_trail) and len(finger_trail[i-1]) == 2 and len(finger_trail[i]) == 2: x1, y1 = finger_trail[i-1] x2, y2 = finger_trail[i] if (0 <= x1 < w and 0 <= y1 < h and 0 <= x2 < w and 0 <= y2 < h): thickness = max(1, 4 - i // 4) alpha = 1.0 - (i / len(finger_trail)) color = ( int(255 * alpha), int(255 * alpha), int(200 * alpha) ) try: cv2.line(frame, (int(x1), int(y1)), (int(x2), int(y2)), color, thickness) except: pass # 当前手指位置(刀尖) try: # 绘制更大的手指指示器,便于瞄准小水果 cv2.circle(frame, (int(finger_x), int(finger_y)), 15, (255, 255, 255), 2) cv2.circle(frame, (int(finger_x), int(finger_y)), 8, (0, 200, 255), -1) # 绘制瞄准十字 cv2.line(frame, (int(finger_x - 10), int(finger_y)), (int(finger_x + 10), int(finger_y)), (255, 255, 255), 1) cv2.line(frame, (int(finger_x), int(finger_y - 10)), (int(finger_x), int(finger_y + 10)), (255, 255, 255), 1) except: pass return frame def draw_game_over(frame, score, max_combo): """绘制游戏结束画面""" h, w = frame.shape[:2] # 半透明黑色背景 overlay = frame.copy() cv2.rectangle(overlay, (0, 0), (w, h), (0, 0, 0), -1) frame = cv2.addWeighted(overlay, 0.7, frame, 0.3, 0) # 游戏结束标题 cv2.putText(frame, "GAME OVER!", (w//2 - 180, h//2 - 100), cv2.FONT_HERSHEY_SIMPLEX, 2.5, (0, 0, 255), 4) # 得分显示 cv2.putText(frame, f"Final Score: {score}", (w//2 - 150, h//2 - 20), cv2.FONT_HERSHEY_SIMPLEX, 1.8, (255, 255, 255), 3) # 最大连击 cv2.putText(frame, f"Max Combo: x{max_combo}", (w//2 - 140, h//2 + 30), cv2.FONT_HERSHEY_SIMPLEX, 1.5, (0, 255, 255), 3) # 评价 if score >= 1000: evaluation = "EXCELLENT! You're a Fruit Ninja Master!" color = (0, 255, 0) elif score >= 500: evaluation = "GREAT! Keep practicing!" color = (0, 200, 255) elif score >= 200: evaluation = "GOOD JOB!" color = (255, 255, 0) else: evaluation = "Nice try! Play again!" color = (255, 150, 0) cv2.putText(frame, evaluation, (w//2 - 200, h//2 + 80), cv2.FONT_HERSHEY_SIMPLEX, 1.2, color, 3) # 重新开始提示 cv2.putText(frame, "Press 'R' to restart or 'Q' to quit", (w//2 - 200, h//2 + 140), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (200, 200, 0), 2) return frame # 主游戏循环 print("开始游戏!请伸出食指...") print("提示:确保手部在摄像头范围内,光照良好") while True: success, frame = cap.read() if not success: print("无法读取视频流。") break # 获取帧尺寸 h, w, _ = frame.shape # 转换颜色空间(在镜像前处理) rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # 游戏逻辑 current_time = time.time() time_left = max(0, game_duration - (current_time - game_start_time)) # 烟花效果逻辑 if show_fireworks: if current_time - fireworks_start_time < fireworks_duration: # 更新和绘制烟花 fireworks_to_remove = [] for i, firework in enumerate(fireworks): if not firework.update(): fireworks_to_remove.append(i) else: firework.draw(frame) # 移除已完成的烟花 for i in sorted(fireworks_to_remove, reverse=True): fireworks.pop(i) # 随机添加新烟花 if random.random() < 0.3: x = random.randint(100, w-100) y = random.randint(100, h-100) create_fireworks_effect(x, y, random.randint(20, 40)) else: show_fireworks = False fireworks = [] # 生成新水果 if game_active and time_left > 0 and random.random() < fruit_spawn_rate * 0.033: fruits.append(Fruit(w, h)) # 处理手部检测 - 优化版本 finger_x, finger_y = -1, -1 extended_fingers = 0 is_slicing = False if show_hands: hand_results = hands.process(rgb_frame) if hand_results.multi_hand_landmarks: for hand_landmarks in hand_results.multi_hand_landmarks: if show_hands: # 只绘制关键点和连接线,减少视觉干扰 mp_draw.draw_landmarks( frame, hand_landmarks, mp_hands.HAND_CONNECTIONS, mp_draw.DrawingSpec(color=(0, 255, 0), thickness=2), mp_draw.DrawingSpec(color=(255, 0, 255), thickness=2) ) # 使用专门的方法检测食指 if is_index_finger_extended(hand_landmarks): # 检测手指位置 fx, fy = detect_finger_position(hand_landmarks, (h, w, 3)) # 确保手指位置有效 if 0 <= fx < w and 0 <= fy < h: finger_x, finger_y = fx, fy is_slicing = True extended_fingers = 1 # 备用方法:使用简化检测 if not is_slicing: ext_count, index_extended = count_extended_fingers_simple(hand_landmarks, (h, w, 3)) if index_extended: fx, fy = detect_finger_position(hand_landmarks, (h, w, 3)) if 0 <= fx < w and 0 <= fy < h: finger_x, finger_y = fx, fy is_slicing = True extended_fingers = ext_count # 镜像翻转(在手指检测后) frame = cv2.flip(frame, 1) # 更新手指轨迹 if is_slicing and finger_x > 0 and finger_y > 0 and 0 <= finger_x < w and 0 <= finger_y < h: # 在镜像后的坐标系中处理手指位置 finger_trail.append((finger_x, finger_y)) if len(finger_trail) > max_trail_length: finger_trail.pop(0) else: if len(finger_trail) > 0: # 逐渐清空轨迹 finger_trail = finger_trail[1:] if len(finger_trail) > 1 else [] # 处理面部检测 is_smiling = False if show_face: face_results = face_mesh.process(rgb_frame) if face_results.multi_face_landmarks: for face_landmarks in face_results.multi_face_landmarks: if show_face: # 只绘制面部关键轮廓 mp_draw.draw_landmarks( frame, face_landmarks, mp_face_mesh.FACEMESH_CONTOURS, landmark_drawing_spec=None, connection_drawing_spec=face_drawing_spec ) # 检测表情 expression, expr_color, smiling = detect_expression(face_landmarks, (h, w, 3)) is_smiling = smiling # 显示表情标签 cv2.putText(frame, expression, (w//2 - 100, 60), cv2.FONT_HERSHEY_SIMPLEX, 1.2, expr_color, 2) # 更新和绘制水果 fruits_to_remove = [] for i, fruit in enumerate(fruits): # 更新水果状态 if not fruit.update(h, w): fruits_to_remove.append(i) continue # 检查是否被切割 if is_slicing and finger_x > 0 and finger_y > 0: if fruit.check_slice(finger_x, finger_y, prev_finger_x, prev_finger_y): # 计算得分 points_earned = fruit.point_value # 连击加成 if points_earned > 0: combo += 1 points_earned *= combo # 微笑加成 if is_smiling: points_earned += combo * 2 max_combo = max(max_combo, combo) # 在水果位置创建烟花效果 create_fireworks_effect(int(fruit.x), int(fruit.y), max(10, fruit.radius//2)) else: # 切到炸弹,连击中断 combo = 0 # 炸弹爆炸效果 for _ in range(max(10, fruit.radius//2)): fireworks.append(FireworkParticle(int(fruit.x), int(fruit.y))) score += points_earned # 显示得分 display_x = max(50, min(w-150, int(fruit.x))) display_y = max(50, min(h-50, int(fruit.y - 40))) score_text = f"+{points_earned}" if points_earned > 0 else f"{points_earned}" text_color = (0, 255, 0) if points_earned > 0 else (0, 0, 255) # 得分文本阴影效果 cv2.putText(frame, score_text, (display_x+2, display_y+2), cv2.FONT_HERSHEY_SIMPLEX, 1.2, (0, 0, 0), 3) cv2.putText(frame, score_text, (display_x, display_y), cv2.FONT_HERSHEY_SIMPLEX, 1.2, text_color, 2) # 绘制水果 fruit.draw(frame) # 保存当前手指位置作为下一帧的"上一帧位置" prev_finger_x, prev_finger_y = finger_x, finger_y # 移除需要删除的水果 for i in sorted(fruits_to_remove, reverse=True): fruits.pop(i) # 检查游戏结束 if game_active and time_left <= 0: game_active = False show_fireworks = True fireworks_start_time = current_time # 创建大量烟花庆祝 for _ in range(5): x = random.randint(100, w-100) y = random.randint(100, h-100) create_fireworks_effect(x, y, random.randint(30, 50)) # 计算FPS cTime = time.time() fps = 1 / (cTime - pTime) if (cTime - pTime) > 0 else 0 pTime = cTime # 绘制游戏界面 if game_active: frame = draw_game_info(frame, fps, score, combo, max_combo, time_left, is_smiling, finger_x, finger_y) # 显示手指状态提示 if is_slicing: cv2.putText(frame, "READY TO SLICE!", (w-300, h-30), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2) else: cv2.putText(frame, "EXTEND INDEX FINGER", (w-350, h-30), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (200, 200, 0), 2) else: frame = draw_game_over(frame, score, max_combo) # 显示图像 cv2.imshow('Fruit Slash Game - 2 Minute Challenge!', frame) # 键盘控制 key = cv2.waitKey(1) & 0xFF if key == ord('q'): # 退出 break elif key == ord('r'): # 重新开始游戏 fruits = [] finger_trail = [] fireworks = [] score = 0 combo = 0 max_combo = 0 game_start_time = time.time() game_active = True show_fireworks = False print("游戏重新开始!") elif key == ord('h'): # 切换手部检测显示 show_hands = not show_hands print(f"手部检测: {'显示' if show_hands else '隐藏'}") elif key == ord('f'): # 切换面部检测显示 show_face = not show_face print(f"面部检测: {'显示' if show_face else '隐藏'}") elif key == ord('d'): # 调试模式:显示检测信息 print(f"手指状态: 切割={is_slicing}, 坐标=({finger_x}, {finger_y})") print(f"水果数量: {len(fruits)}") # 释放资源 cap.release() cv2.destroyAllWindows()

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/16 11:32:23

MySQL进阶篇——InnoDB存储引擎和管理

InnoDB存储引擎逻辑存储结构表空间(.ibd文件)-段-区-页-行一个mysql实例对应多个表空间&#xff0c;用于存储记录&#xff0c;索引等数据&#xff1b;段&#xff1a;分为数据段&#xff08;B树叶子节点&#xff09;、索引段&#xff08;B树非叶子节点&#xff09;、回滚段区&am…

作者头像 李华
网站建设 2026/8/16 9:21:27

单例设计模式

饿汉式单例&#xff1a;在用类前创建好对象&#xff0c;用的时候直接用。懒汉式单例&#xff1a;在用类的时候才创建对象&#xff0c;不提前创建。共同点&#xff1a;两种方式均通过私有化构造函数防止外部直接实例化&#xff0c;并通过静态方法或静态变量提供唯一实例的访问入…

作者头像 李华
网站建设 2026/8/16 10:18:45

百度网盘智能提取码解决方案:技术驱动的自动化访问新体验

百度网盘智能提取码解决方案&#xff1a;技术驱动的自动化访问新体验 【免费下载链接】baidupankey 项目地址: https://gitcode.com/gh_mirrors/ba/baidupankey 在数字化资源获取的日常场景中&#xff0c;百度网盘提取码的查找过程往往成为用户体验的瓶颈环节。传统的手…

作者头像 李华
网站建设 2026/8/14 22:16:31

Windows右键菜单终极清理指南:5步打造高效桌面体验

Windows右键菜单终极清理指南&#xff1a;5步打造高效桌面体验 【免费下载链接】ContextMenuManager &#x1f5b1;️ 纯粹的Windows右键菜单管理程序 项目地址: https://gitcode.com/gh_mirrors/co/ContextMenuManager Windows右键菜单是日常操作中使用频率最高的功能之…

作者头像 李华
网站建设 2026/8/14 11:18:16

Spring Boot 权限控制三件套:JWT 登录校验 + 拦截器 + AOP 角色注解实战

文章目录接口校验&#xff0c;权限拦截通过自定义注解&#xff0c;基于面向切面编程来实现1. 自定义异常2. 自定义注解3. AOP面向切面类4. Controller层使用统一异常处理和信息返回1. 创建统一信息返回类2. 创建全局统一异常处理类3. 创建一个枚举类型4. 创建自定义的异常类拦截…

作者头像 李华
网站建设 2026/8/15 5:35:23

Python验证码处理实战:从12306项目看验证码识别的技术演进

一、引言&#xff1a;验证码&#xff0c;网络安全的第一道防线 在网络应用中&#xff0c;验证码是防止自动化攻击的重要手段。12306作为中国铁路售票系统&#xff0c;其验证码设计尤为复杂&#xff0c;从早期的数字字母组合&#xff0c;到后来的图片点击&#xff0c;再到如今的…

作者头像 李华