import cv2 import mediapipe as mp # 在Pygame中使用归一化坐标 import pygame import time import random # 初始化游戏窗口 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 you_failed(elapsed_time): global obstacles obstacles.clear() """处理失败的函数,弹出消息或重置游戏等""" global start_time start_time=time.time() print(f"You failed! Elapsed time: {elapsed_time:.2f} seconds") # 在此处添加其他失败处理逻辑,例如重置游戏、显示消息等 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 = int(elapsed_time) # 整数秒数 # 计算小球位置 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_seconds} 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()