This commit is contained in:
Zengtudor 2025-03-01 10:42:23 +08:00
parent a2774513cb
commit 94146dacbb
3 changed files with 143 additions and 5 deletions

70
main.py
View File

@ -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)

View File

@ -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"}

76
show_avatar.py Normal file
View File

@ -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)