from concurrent.futures import thread from threading import Thread import threading import cv2 import mediapipe as mp import pygame import time import random import os import tkinter as tk from PIL import Image, ImageTk # 初始化游戏窗口 pygame.init() # 字体初始化 font = pygame.font.Font(None, 36) # None使用默认字体,大小为36 screen = pygame.display.set_mode((600, 800)) ball_radius = 20 ball_color = (255, 105, 180) # 粉色 # 初始化摄像头 cap = cv2.VideoCapture(0) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) # 设置宽度 cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720) # 设置高度 # 验证设置 actual_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) actual_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) print(f"Working Resolution: {actual_width}x{actual_height}") # 创建自适应窗口 # cv2.namedWindow('Face Detection', cv2.WINDOW_NORMAL) # cv2.resizeWindow('Face Detection', actual_width, actual_height) # 初始化MediaPipe mp_face = mp.solutions.face_detection # type: ignore face_detection = mp_face.FaceDetection(min_detection_confidence=0.5) # 获取实际分辨率 width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) print(f"实际分辨率: {width}x{height}") # 定义目标裁剪尺寸 CROP_SIZE = 720 # 计算裁剪区域坐标 x_start = (width - CROP_SIZE) // 2 y_start = (height - CROP_SIZE) // 2 x_end = x_start + CROP_SIZE y_end = y_start + CROP_SIZE clock = pygame.time.Clock() # 用于控制帧率 # 全局变量初始化 (请确保在主程序中进行初始化) OBSTACLE_INTERVAL = 1400 # 毫秒,障碍物生成间隔 OBSTACLE_SPEED = 5 # 障碍物下移速度 start_time = time.time() # 游戏开始时间# 全局变量初始化 obstacles = [] # 存储当前障碍物的列表 obstacle_timer = 0 # 通用障碍物生成计时器 # 新增全局变量用于跟踪左右两侧障碍物的生成时间 last_obstacle_time_left = 0 last_obstacle_time_right = 0 MIN_OBSTACLE_INTERVAL = 2000 # 毫秒,同一侧障碍物最小生成间隔 start_time = time.time() # 游戏开始时间 def get_player_avatar(): global cap """获取当前游玩者的头像(人脸图像)。""" mp_face = mp.solutions.face_detection # type: ignore face_detection = mp_face.FaceDetection(min_detection_confidence=0.5) ret, frame = cap.read() if not ret: return None # 如果未读取到帧,返回 None frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) results = face_detection.process(frame_rgb) if results.detections: max_area = 0 best_detection = None # 找到最大的面框 for detection in results.detections: bbox = detection.location_data.relative_bounding_box area = bbox.width * bbox.height if area > max_area: max_area = area best_detection = bbox if best_detection: # 计算面框在原始帧中的坐标 h, w, _ = frame.shape x_min = int(best_detection.xmin * w) y_min = int(best_detection.ymin * h) x_max = int((best_detection.xmin + best_detection.width) * w) y_max = int((best_detection.ymin + best_detection.height) * h) # 增加边界,确保完整性 border_x = int(0.1 * (x_max - x_min)) # 边界宽度为框宽度的10% border_y = int(0.1 * (y_max - y_min)) # 边界高度为框高度的10% # 更新坐标以增加边界,同时确保坐标在有效范围内 x_min = max(x_min - border_x, 0) # 确保不小于0 y_min = max(y_min - border_y, 0) # 确保不小于0 x_max = min(x_max + border_x, w) # 确保不超出图像宽度 y_max = min(y_max + border_y, h) # 确保不超出图像高度 # 裁剪出面框区域 avatar = frame[y_min:y_max, x_min:x_max] return avatar return None # 如果未检测到人脸,返回 None def you_failed(elapsed_time): global obstacles obstacles.clear() """处理失败的函数,弹出消息或重置游戏等""" global start_time print(f"You failed! Elapsed dis: {elapsed_time:.2f} meters") if not os.path.exists("faces"): os.makedirs("faces") file_name = os.path.join("faces",f"{elapsed_time:.2f}.png") avatar = get_player_avatar() if avatar is not None: cv2.imwrite(file_name,avatar) start_time=time.time() # 在此处添加其他失败处理逻辑,例如重置游戏、显示消息等 def draw_ball(normalized_x, normalized_y): global obstacles, obstacle_timer, last_obstacle_time_left, last_obstacle_time_right, start_time screen.fill((0, 0, 0)) """在Pygame窗口中绘制小球并处理障碍物""" # 计算经过的时间 elapsed_time = time.time() - start_time # elapsed_seconds = # 整数秒数 # 计算小球位置 ball_x = int(normalized_x * (screen.get_width() - ball_radius * 2)) + ball_radius ball_y = int(0.6 * (screen.get_height() - ball_radius * 2)) + ball_radius # # 检查小球是否碰到屏幕边界 # if ball_x - ball_radius <= 0 or ball_x + ball_radius >= screen.get_width(): # elapsed_time = time.time() - start_time # you_failed(elapsed_time) # return # 绘制小球 pygame.draw.circle(screen, ball_color, (ball_x, ball_y), ball_radius) # 生成障碍物 current_time = pygame.time.get_ticks() if current_time - obstacle_timer > OBSTACLE_INTERVAL: # 尝试随机选择左右两侧生成障碍物 sides = ['left', 'right'] random.shuffle(sides) # 随机打乱侧别顺序以增加随机性 # 障碍物宽度和高度 obstacle_width = random.randint(200, 500) obstacle_height = random.randint(20, 100) for side in sides: if side == 'left': # 检查左侧的障碍物生成间隔 if current_time - last_obstacle_time_left >= MIN_OBSTACLE_INTERVAL: x_pos = 0 last_obstacle_time_left = current_time else: continue # 左侧间隔不够,尝试右侧 else: # 检查右侧的障碍物生成间隔 if current_time - last_obstacle_time_right >= MIN_OBSTACLE_INTERVAL: x_pos = screen.get_width() - obstacle_width last_obstacle_time_right = current_time else: continue # 右侧间隔不够,尝试左侧S # 确定x_pos根据侧别 if side == 'left': x_pos = 0 else: x_pos = screen.get_width() - obstacle_width y_pos = -obstacle_height # 从屏幕上方外部生成 obstacles.append(pygame.Rect(x_pos, y_pos, obstacle_width, obstacle_height)) obstacle_timer = current_time # 重置生成计时器 break # 成功生成一个障碍物后退出循环 # 移动并绘制障碍物 for obstacle in obstacles[:]: obstacle.y += OBSTACLE_SPEED # 绘制障碍物 pygame.draw.rect(screen, (0, 255, 0), obstacle) # 绿色障碍物 # 检查是否与小球碰撞 if obstacle.colliderect(pygame.Rect(ball_x - ball_radius, ball_y - ball_radius, ball_radius * 2, ball_radius * 2)): elapsed_time = time.time() - start_time you_failed(elapsed_time) return # 移除已离开屏幕的障碍物 if obstacle.y > screen.get_height(): obstacles.remove(obstacle) # 绘制计时器文本 timer_text = font.render(f"{elapsed_time:.2f} M", True, (255, 255, 255)) # 白色文本 screen.blit(timer_text, (10, 10)) # 在左上角绘制文本(10, 10)是坐标 pygame.display.flip() clock.tick(60) # 验证裁剪可行性 if x_start < 0 or y_start < 0: raise ValueError("摄像头分辨率不足以裁剪720x720区域") mp_drawing = mp.solutions.drawing_utils # type: ignore while cap.isOpened(): ret, frame = cap.read() if not ret: break frame = cv2.flip(frame,1) # 人脸检测 rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) results = face_detection.process(rgb_frame) if results.detections: for detection in results.detections: # 使用MediaPipe自带绘制方法 mp_drawing.draw_detection(frame, detection) if results.detections: detection = results.detections[0] max_area = 0 for idetection in results.detections: bbox = idetection.location_data.relative_bounding_box area = bbox.width * bbox.height # 计算面框的面积 if area > max_area: # 找到最大的 max_area = area detection = idetection bbox = detection.location_data.relative_bounding_box # 计算原始坐标(相对于完整帧) raw_x = int(bbox.xmin * width) raw_y = int(bbox.ymin * height) raw_w = int(bbox.width * width) raw_h = int(bbox.height * height) center_x = raw_x + raw_w // 2 center_y = raw_y + raw_h // 2 # 转换为裁剪区域坐标 crop_x = center_x - x_start crop_y = center_y - y_start # 归一化到0-1范围(带边界保护) normalized_x = max(0.0, min(1.0, crop_x / CROP_SIZE)) normalized_y = max(0.0, min(1.0, crop_y / CROP_SIZE)) draw_ball(normalized_x,normalized_y) # print(f"归一化坐标: ({normalized_x:.2f}, {normalized_y:.2f})") # 可视化裁剪区域 cv2.rectangle(frame, (x_start, y_start), (x_end, y_end), (0,255,0), 2) cv2.imshow('Preview', frame) if cv2.waitKey(1) == ord('q'): break cap.release() cv2.destroyAllWindows()