diff --git a/main.py b/main.py index ab46feb..cc54e76 100644 --- a/main.py +++ b/main.py @@ -1,9 +1,15 @@ +from concurrent.futures import thread +from threading import Thread +import threading import cv2 import mediapipe as mp -# 在Pygame中使用归一化坐标 import pygame import time import random +import os +import tkinter as tk +from PIL import Image, ImageTk + # 初始化游戏窗口 pygame.init() # 字体初始化 @@ -57,13 +63,69 @@ 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() - print(f"You failed! Elapsed time: {elapsed_time:.2f} seconds") # 在此处添加其他失败处理逻辑,例如重置游戏、显示消息等 def draw_ball(normalized_x, normalized_y): @@ -72,7 +134,7 @@ def draw_ball(normalized_x, normalized_y): """在Pygame窗口中绘制小球并处理障碍物""" # 计算经过的时间 elapsed_time = time.time() - start_time - elapsed_seconds = int(elapsed_time) # 整数秒数 + # elapsed_seconds = # 整数秒数 # 计算小球位置 ball_x = int(normalized_x * (screen.get_width() - ball_radius * 2)) + ball_radius @@ -139,7 +201,7 @@ def draw_ball(normalized_x, normalized_y): obstacles.remove(obstacle) # 绘制计时器文本 - timer_text = font.render(f"{elapsed_seconds} M", True, (255, 255, 255)) # 白色文本 + 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) diff --git a/pyproject.toml b/pyproject.toml index 153d89c..ee2f362 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ description = "Default template for PDM package" authors = [ {name = "Zengtudor", email = "Zengtudor@outlook.com"}, ] -dependencies = ["opencv-python>=4.11.0.86", "mediapipe>=0.10.21", "numpy>=1.26.4", "pygame>=2.6.1"] +dependencies = ["opencv-python>=4.11.0.86", "mediapipe>=0.10.21", "numpy>=1.26.4", "pygame>=2.6.1", "Pillow>=11.1.0"] requires-python = "==3.12.*" readme = "README.md" license = {text = "MIT"} diff --git a/show_avatar.py b/show_avatar.py new file mode 100644 index 0000000..3980721 --- /dev/null +++ b/show_avatar.py @@ -0,0 +1,76 @@ +import os +import tkinter as tk +from PIL import Image, ImageTk, ImageDraw, ImageFont +import threading +import time + +def load_images_from_folder(folder): + """从指定文件夹加载图像文件,并按文件前的数字排序.""" + images = [] + for filename in sorted(os.listdir(folder), key=lambda x: float(x[:-4]), reverse=True): + if filename.endswith(('.png', '.jpg', '.jpeg')): # 只处理图像文件 + img_path = os.path.join(folder, filename) + images.append((img_path, filename[:-4])) # 返回图像路径及文件名(去掉扩展名) + return images + +def create_image_viewer(folder_path): + """创建图像查看器并展示指定文件夹中的图像, 返回刷新函数.""" + + def show_images(): + """显示前五张图像于标签中.""" + for i, (image_path, filename) in enumerate(images[:8]): + img = Image.open(image_path).convert("RGBA") + img = img.resize((200, 200), Image.Resampling.LANCZOS) # 调整图像大小,使用 LANCZOS + + # 在图像上添加文本 + draw = ImageDraw.Draw(img) + font = ImageFont.load_default() # 使用默认字体 + text_position = (10, 10) # 文本位置 + try: + font = ImageFont.truetype("arial.ttf", 30) # 加载自定义字体并设置大小 + except IOError: + font = ImageFont.load_default() # 如果无法加载自定义字体则使用默认字体 + draw.text(text_position, f"{filename} M", fill="red",font=font) # 添加文本 + + img = ImageTk.PhotoImage(img) + + # 计算位置并创建标签显示图像 + image_label = tk.Label(root, image=img) + image_label.image = img # type: ignore # 保持对图像的引用 + image_label.grid(row=i//2, column=i%2) # 放置在一列中,垂直排列 + + def refresh_images(): + """刷新图像列表.""" + nonlocal images + images = load_images_from_folder(folder_path) # 重新加载图像 + for widget in root.grid_slaves(): # 清除之前的图像标签 + widget.destroy() + show_images() # 显示新图像 + + def auto_refresh(): + """每五秒自动刷新图像.""" + while True: + refresh_images() + time.sleep(10) + + # 创建Tkinter窗口 + root = tk.Tk() + root.title("Display Faces Images") + + # 加载图像 + images = load_images_from_folder(folder_path) + + # 显示图像 + show_images() + + # 启动自动刷新线程 + refresh_thread = threading.Thread(target=auto_refresh, daemon=True) + refresh_thread.start() + + # 启动程序 + root.after(0, lambda: root.focus_force()) # 确保窗口获得焦点 + root.mainloop() + +# 使用示例: +folder_path = "faces" # 图像文件夹路径 +create_image_viewer(folder_path) \ No newline at end of file