>  기사  >  백엔드 개발  >  Python을 사용하여 테이블 하키 시각적 게임을 만드는 방법

Python을 사용하여 테이블 하키 시각적 게임을 만드는 방법

王林
王林앞으로
2023-05-08 17:46:181424검색

소개

규칙은 다음과 같습니다. 왼손은 흰색 라켓을 제어하고, 라켓은 위아래로만 움직일 수 있습니다. 빨간색 원은 공이 파란색 테두리와 충돌합니다. 공이 노란색 영역에 들어가고 게임이 종료되면 아래의 분홍색 계산판에 각 공이 왼쪽과 오른쪽에 몇 번 맞았는지 기록됩니다.

Python을 사용하여 테이블 하키 시각적 게임을 만드는 방법

1. 파일 구성

1.1 가져오기 툴킷

pip install opencv_python==4.2.0.34  # 安装opencv
pip install mediapipe  # 安装mediapipe
# pip install mediapipe --user  #有user报错的话试试这个
pip install cvzone  # 安装cvzone
 
# 导入工具包
import cv2
import cvzone
from cvzone.HandTrackingModule import HandDetector  # 导入手部检测模块

손 키 포인트 21개 좌표는 다음과 같습니다.

Python을 사용하여 테이블 하키 시각적 게임을 만드는 방법

1.2 재료 사진 준비

시작하기 전에 테이블 사진을 준비하고 공 사진, 라켓 사진. 그림은 PPT를 이용해서 그렸는데, 공과 라켓의 그림은 .png 형식으로 저장해야 합니다. 읽기 쉽도록 같은 폴더에 넣으세요.

Python을 사용하여 테이블 하키 시각적 게임을 만드는 방법

2. 손 키포인트 검출 및 소재 가져오기

2.1 방법 소개

(1) cvzone.HandTrackingModule.HandDetector()손 키포인트 검출 방법vzone.HandTrackingModule.HandDetector()手部关键点检测方法

参数:

mode: 默认为 False,将输入图像视为视频流。它将尝试在第一个输入图像中检测手,并在成功检测后进一步定位手的坐标。在随后的图像中,一旦检测到所有 maxHands 手并定位了相应的手的坐标,它就会跟踪这些坐标,而不会调用另一个检测,直到它失去对任何一只手的跟踪。这减少了延迟,非常适合处理视频帧。如果设置为 True,则在每个输入图像上运行手部检测,用于处理一批静态的、可能不相关的图像。

maxHands: 最多检测几只手,默认为 2

detectionCon: 手部检测模型的最小置信值(0-1之间),超过阈值则检测成功。默认为 0.5

minTrackingCon: 坐标跟踪模型的最小置信值 (0-1之间),用于将手部坐标视为成功跟踪,不成功则在下一个输入图像上自动调用手部检测。将其设置为更高的值可以提高解决方案的稳健性,但代价是更高的延迟。如果 mode 为 True,则忽略这个参数,手部检测将在每个图像上运行。默认为 0.5

它的参数和返回值类似于官方函数 mediapipe.solutions.hands.Hands()

MULTI_HAND_LANDMARKS: 被检测/跟踪的手的集合,其中每只手被表示为21个手部地标的列表,每个地标由x, y, z组成。

MULTI_HANDEDNESS: 被检测/追踪的手是左手还是右手的集合。每只手由label(标签)和score(分数)组成。 label 是 'Left' 或 'Right' 值的字符串。 score 是预测左右手的估计概率。

(2)cvzone.HandTrackingModule.HandDetector.findHands() 找到手部关键点并绘图

参数:

img: 需要检测关键点的帧图像,格式为BGR

draw: 是否需要在原图像上绘制关键点及识别框

flipType: 图像是否需要翻转,当视频图像和我们自己不是镜像关系时,设为True就可以了

返回值:

hands: 检测到的手部信息,由0或1或2个字典组成的列表。如果检测到两只手就是由两个字典组成的列表。字典中包含:21个关键点坐标(x,y,z),检测框左上坐标及其宽高,检测框中心点坐标,检测出是哪一只手。

img: 返回绘制了关键点及连线后的图像

(3)cv2.addWeighted()

매개변수:

mode: 기본값은 False이며 입력 이미지를 비디오 스트림으로 처리합니다. 첫 번째 입력 이미지에서 손을 감지하고 감지에 성공한 후 손의 좌표를 추가로 찾습니다. 후속 이미지에서는 모든 maxHands 손이 감지되고 해당 손의 좌표를 찾으면 한 손의 추적이 손실될 때까지 다른 감지를 호출하지 않고 해당 좌표를 추적합니다. 이는 대기 시간을 줄이고 비디오 프레임 처리에 이상적입니다. True로 설정하면 잠재적으로 관련이 없는 정적 이미지를 일괄 처리하기 위해 각 입력 이미지에서 손 감지가 실행됩니다.

maxHands: 감지할 최대 손 수, 기본값은 2입니다.

DetectionCon: 손 감지 모델의 최소 신뢰도 값(0~1 사이), 임계값을 초과하면 감지가 성공한 것입니다. 기본값은 0.5

minTrackingCon: 손 좌표를 성공적인 추적으로 간주하는 데 사용되는 좌표 추적 모델의 최소 신뢰도 값(0-1 사이)입니다. 실패할 경우 다음 입력 이미지에서 손 감지가 자동으로 호출됩니다. . 더 높은 값으로 설정하면 솔루션의 견고성이 향상되지만 대기 시간이 길어집니다. 모드가 True이면 이 매개변수가 무시되고 모든 이미지에서 손 감지가 실행됩니다. 기본값은 0.5

매개변수 및 반환 값은 공식 함수 mediapipe.solutions.hands.Hands()

Python을 사용하여 테이블 하키 시각적 게임을 만드는 방법MULTI_HAND_LANDMARKS와 유사합니다. 각 손은 21개의 손 랜드마크로 표시됩니다. 목록, 각 랜드마크 x, y, z로 구성됩니다.

MULTI_HANDEDNESS: 감지/추적되는 손이 왼쪽인지 오른쪽인지에 대한 컬렉션입니다. 각 손은 라벨과 점수로 구성됩니다. label은 '왼쪽' 또는 '오른쪽' 값을 갖는 문자열입니다. 점수는 왼손 또는 오른손을 예측할 추정 확률입니다.

(2) cvzone.HandTrackingModule.HandDetector.findHands() 손의 핵심 포인트를 찾아 그립니다.

매개변수: Python을 사용하여 테이블 하키 시각적 게임을 만드는 방법

img: 핵심 포인트를 감지해야 하는 프레임 이미지, 형식은 BGR

draw : 원본 이미지에 키 포인트 및 인식 프레임을 그려야 하는지 여부

flipType: 이미지를 반전해야 하는지 여부 True

반환 값:

hands: 감지된 손 정보, 0개 또는 1개 또는 2개의 사전 목록. 두 손이 감지되면 두 개의 사전으로 구성된 목록입니다. 사전에는 21개의 키 포인트 좌표(x, y, z), 감지 프레임의 왼쪽 상단 좌표와 너비 및 높이, 감지 프레임의 중심점 좌표 및 감지된 손이 포함됩니다.

img: 핵심 포인트와 연결을 그린 후 이미지를 반환합니다🎜🎜(3) cv2.addWeighted()이미지 융합🎜🎜두 이미지를 특정 비율에 따라 융합합니다. 두 이미지가 필요합니다. 이미지의 크기와 채널 수가 동일합니다🎜🎜두 이미지가 특정 비율로 융합됩니다: cv2.addWeighted(image 1, Weight 1, image 2, Weight 2, Brightness offset)🎜🎜y =에 해당 a x1 + b x2 + c , 여기서 a와 b는 무게를 나타내고 c는 밝기가 얼마나 밝아졌는지 나타냅니다🎜🎜2.2 코드 표시🎜🎜먼저 cv2.imread()의 매개 변수 cv2.IMREAD_UNCHANGED는 열기를 나타냅니다. 알파 채널을 포함하여 이미지를 원래 형식으로 유지합니다. 즉, 이미지를 변경하지 않고 엽니다. 이미지가 컬러이면 컬러로 읽혀지고, 이미지가 회색조이면 회색조로 읽혀집니다. 🎜🎜🎜🎜🎜이 부분은 주로 필기 부분 검출, 배경 이미지와 영상 프레임 이미지 융합🎜
import cv2
import cvzone
from cvzone.HandTrackingModule import HandDetector  # 导入手部检测模块
 
#(1)捕获摄像头
cap = cv2.VideoCapture(0)  # 0代表电脑自带的摄像头
cap.set(3, 1280)  # 读入的图像的宽
cap.set(4, 720)   # 读入的图像的高
 
 
#(2)文件配置
# 导入所有需要对图片文件
imgDesk = cv2.imread('games/desk.jpg')  # 球桌的图片
imgBall = cv2.imread('games/ball.png', cv2.IMREAD_UNCHANGED)  # 球的图片
imgBlock1 = cv2.imread('games/block1', cv2.IMREAD_UNCHANGED)  # 球拍的图片
imgBlock2 = cv2.imread('games/block2', cv2.IMREAD_UNCHANGED)  # 球拍的图片
# 调整球桌图片的size
imgDesk = cv2.resize(imgDesk, dsize=(1280,720))
 
 
#(3)参数设置
# 接收手部关键点识别的方法,最小手部检测模块置信度0.8,最多检测2只手
detector = HandDetector(detectionCon=0.8, maxHands=2)
 
 
#(4)处理帧图像
while True:
 
    # 返回是否读取成功,以及读取后的帧图像
    success, img = cap.read()  # 每次执行读取一帧
    
    # 图片翻转呈镜像关系,1代表左右翻转,0代表上下翻转
    img = cv2.flip(img, flipCode=1)
    
    # 手部关键点检测,返回每个只手的信息和绘制后的图像
    hands, img = detector.findHands(img, flipType=False)  # 上面翻转过了这里就不用翻转了
 
    # 将球桌图片和视频帧图像融合在一起, 两张图的shape要相同
    # 给出每张图片的融合权重, 亮度偏置为0,这样就变成了半透明的显示形式
    img = cv2.addWeighted(img, 0.3, imgDesk, 0.7, 0)
 
    
    #(5)添加桌球的图片,将imgBall放在球桌img的指定坐标位置
    img = cvzone.overlayPNG(img, imgBall, (100,100))
    
    # 图像展示
    cv2.imshow('img', img)
    # 每帧滞留1ms后消失
    k = cv2.waitKey(1)
    # ESC键退出程序
    if k & 0XFF==27:
        break
 
# 释放视频资源
cap.release()
cv2.destroyAllWindows()
🎜렌더링은 다음과 같습니다.🎜🎜🎜🎜🎜3. movement🎜🎜3.1 방법 소개🎜🎜이 부분은 주로 두 가지 작업을 완료합니다. 첫 번째는 왼손과 오른손이 각각 왼쪽과 오른쪽의 라켓을 제어하고, 두 번째는 공이 특정 속도로 움직이는 것입니다. 🎜🎜🎜(1) 라켓을 제어하세요🎜🎜

hand['bbox'] 中包含了手部检测框的左上角坐标和检测框的宽高,使用手掌中心点的 y 坐标来控制球拍的上下移动。由于两个球拍的shape是相同的,因此只要获取一个球拍的高度 h2 即可。使用掌心中点 y 坐标控制球拍中点的 y1 坐标,公式为:y1 = (y + h) // 2 - h2 // 2

接着使用 cvzone.overlayPNG() 就可以将球拍图片覆盖在原图片的指定区域,其中坐标参数是指覆盖区域的左上角坐标。固定横坐标,只上下移动。

(2)球移动

首先要规定球的移动速度 speedx, speedy = 10, 10 代表球每一帧沿x轴正方向移动10个像素,沿y轴正方向移动10个像素,那么球的初始合速度方向是沿图片的正右下角移动

如果球碰撞到了球桌的上下边框,就反弹。speedy = -speedy。代表x方向每帧移动的步长不变,y方向每帧移动的方向反转,即入射角等于出射角。

3.2 代码展示

在上述代码中补充

import cv2
import cvzone
import numpy as np
from cvzone.HandTrackingModule import HandDetector  # 导入手部检测模块
 
#(1)捕获摄像头
cap = cv2.VideoCapture(0)  # 0代表电脑自带的摄像头
cap.set(3, 1280)  # 读入的图像的宽
cap.set(4, 720)   # 读入的图像的高
 
 
#(2)文件配置
# 导入所有需要对图片文件
imgDesk = cv2.imread('games/desk.jpg')  # 球桌的图片
imgBall = cv2.imread('games/ball.png', cv2.IMREAD_UNCHANGED)  # 球的图片
imgBlock1 = cv2.imread('games/block1.png', cv2.IMREAD_UNCHANGED)  # 球拍的图片
imgBlock2 = cv2.imread('games/block2.png', cv2.IMREAD_UNCHANGED)  # 球拍的图片
# 调整球桌图片的size
imgDesk = cv2.resize(imgDesk, dsize=(1280,720))
# 调整球拍的size
imgBlock1 = cv2.resize(imgBlock1, dsize=(50,200))
imgBlock2 = cv2.resize(imgBlock2, dsize=(50,200))
 
 
#(3)参数设置
# 接收手部关键点识别的方法,最小手部检测模块置信度0.8,最多检测2只手
detector = HandDetector(detectionCon=0.8, maxHands=2)
 
# 球的默认位置
ballpos = [100, 100]
 
# 球的移动速度,每帧15个像素
speedx, speedy = 10, 10
 
 
#(4)处理帧图像
while True:
 
    # 返回是否读取成功,以及读取后的帧图像
    success, img = cap.read()  # 每次执行读取一帧
    
    # 图片翻转呈镜像关系,1代表左右翻转,0代表上下翻转
    img = cv2.flip(img, flipCode=1)
    
    # 手部关键点检测,返回每个只手的信息和绘制后的图像
    hands, img = detector.findHands(img, flipType=False)  # 上面翻转过了这里就不用翻转了
 
    # 将球桌图片和视频帧图像融合在一起, 两张图的shape要相同
    # 给出每张图片的融合权重, 亮度偏置为0,这样就变成了半透明的显示形式
    img = cv2.addWeighted(img, 0.4, imgDesk, 0.6, 0)
    
    
    #(5)处理手部关键点,如果检测到手了就进行下一步
    if hands:
        
        # 遍历每检测的2只手,获取每一只手的坐标
        for hand in hands:
            
            # 获取手部检测框的左上坐标xy,宽高wh
            x, y, w, h = hand['bbox']
            
            # 获取球拍的宽高
            h2, w1 = imgBlock1.shape[0:2]
            
            # 球拍的中心y坐标,随着掌心移动
            y1 = (y + h) // 2 - h2 // 2
 
            # 如果检测到了左手
            if hand['type'] == 'Left':
                
                # 左侧的球拍x轴固定,y坐标随左手掌间中点移动
                img = cvzone.overlayPNG(img, imgBlock1, (55,y1))
                
            # 如果检测到了右手
            if hand['type'] == 'Right':
                
                # 右侧的球拍x轴固定,y坐标随右手掌间中点移动
                img = cvzone.overlayPNG(img, imgBlock2, (1280-55,y1))
                      
    #(6)改变球的位置
    # 如果球的y坐标在超出了桌面的上或下边框范围,调整移动方向
    if ballpos[1] >= 600 or ballpos[1] <= 50:
        
        # y方向的速度调整为反方向,那么x方向和y方向的合速度方向调整了
        speedy = -speedy
 
    ballpos[0] = ballpos[0] + speedx  # 调整球的x坐标
    ballpos[1] = ballpos[1] + speedy  # 调整球的y坐标
 
    
    #(5)添加桌球的图片,将imgBall放在球桌img的指定坐标位置
    img = cvzone.overlayPNG(img, imgBall, ballpos)
    
    # 图像展示
    cv2.imshow(&#39;img&#39;, img)
    # 每帧滞留1ms后消失
    k = cv2.waitKey(1)
    # ESC键退出程序
    if k & 0XFF==27:
        break
 
# 释放视频资源
cap.release()
cv2.destroyAllWindows()

效果图如下:

Python을 사용하여 테이블 하키 시각적 게임을 만드는 방법

4. 球拍击球、游戏完善

4.1 方法介绍

这一部分主要完成三项工作,第一是球拍击打到球,球需要反弹;第二是如果球进入黄色区域,游戏结束;第三是左右侧击球得分计数器。

(1)球拍击球

看到代码中的第(5)步,ballpos 代表球的左上角坐标(x,y),100

(2)球进黄区,游戏结束

if ballpos[0] 1150,如果球图片的左上坐标的 x 坐标,在黄区边缘,整个程序退出。当然也可以做一个游戏结束界面,我之前的博文里也有介绍,我偷个懒不写了。

(3)计数器

首先定义个变量初始化记录左右侧的击球次数 score = [0, 0],如果有一侧的球拍击中球,那么对应该侧计数加一。

4.2 代码展示

上面代码是掌心控制球拍,这里改成食指指尖控制球拍中点移动。

import cv2
import cvzone
from cvzone.HandTrackingModule import HandDetector  # 导入手部检测模块
 
#(1)捕获摄像头
cap = cv2.VideoCapture(0)  # 0代表电脑自带的摄像头
cap.set(3, 1280)  # 读入的图像的宽
cap.set(4, 720)   # 读入的图像的高
 
 
#(2)文件配置
# 导入所有需要对图片文件
imgDesk = cv2.imread(&#39;games/desk.jpg&#39;)  # 球桌的图片
imgBall = cv2.imread(&#39;games/ball.png&#39;, cv2.IMREAD_UNCHANGED)  # 球的图片
imgBlock1 = cv2.imread(&#39;games/block1.png&#39;, cv2.IMREAD_UNCHANGED)  # 球拍的图片
imgBlock2 = cv2.imread(&#39;games/block2.png&#39;, cv2.IMREAD_UNCHANGED)  # 球拍的图片
# 调整球桌图片的size
imgDesk = cv2.resize(imgDesk, dsize=(1280,720))
# 调整球拍的size
imgBlock1 = cv2.resize(imgBlock1, dsize=(50,200))
imgBlock2 = cv2.resize(imgBlock2, dsize=(50,200))
 
 
#(3)参数设置
# 接收手部关键点识别的方法,最小手部检测模块置信度0.8,最多检测2只手
detector = HandDetector(detectionCon=0.8, maxHands=2)
 
# 球的默认位置
ballpos = [100, 100]
 
# 球的移动速度,每帧15个像素
speedx, speedy = 10, 10
 
# 记录是否游戏结束
gameover = False
 
# 记录左右的击球数
score = [0, 0]
 
 
#(4)处理帧图像
while True:
 
    # 返回是否读取成功,以及读取后的帧图像
    success, img = cap.read()  # 每次执行读取一帧
    
    # 图片翻转呈镜像关系,1代表左右翻转,0代表上下翻转
    img = cv2.flip(img, flipCode=1)
    
    # 手部关键点检测,返回每个只手的信息和绘制后的图像
    hands, img = detector.findHands(img, flipType=False)  # 上面翻转过了这里就不用翻转了
 
    # 将球桌图片和视频帧图像融合在一起, 两张图的shape要相同
    # 给出每张图片的融合权重, 亮度偏置为0,这样就变成了半透明的显示形式
    img = cv2.addWeighted(img, 0.4, imgDesk, 0.6, 0)
    
    
    #(5)处理手部关键点,如果检测到手了就进行下一步
    if hands:
        
        # 遍历每检测的2只手,获取每一只手的坐标
        for hand in hands:
            
            # 获取食指坐标(x,y,z)
            x, y, z = hand[&#39;lmList&#39;][8]
            
            # 获取球拍的宽高
            h2, w1 = imgBlock1.shape[0:2]
            
            # 球拍的中心y坐标,随着掌心移动
            y1 = y - h2 // 2
 
            # 如果检测到了左手
            if hand[&#39;type&#39;] == &#39;Left&#39;:
                
                # 左侧的球拍x轴固定,y坐标随左手掌间中点移动
                img = cvzone.overlayPNG(img, imgBlock1, (100,y1))
                
                # 检查球是否被左球拍击中, 球的xy坐标是否在球拍xy坐标附近
                if 100 < ballpos[0] < 100+w1 and y1 < ballpos[1] < y1+h2:
                    
                    # 满足条件代表球拍击中了,改变球的移动方向
                    speedx = -speedx  # x方向设为反方向
                    
                    # 得分加一
                    score[0] += 1
                
                
            # 如果检测到了右手
            if hand[&#39;type&#39;] == &#39;Right&#39;:
                
                # 右侧的球拍x轴固定,y坐标随右手掌间中点移动
                img = cvzone.overlayPNG(img, imgBlock2, (1150,y1))
                
                # 检查球是否被右球拍击中
                if 1050 < ballpos[0] < 1050+w1 and y1 < ballpos[1] < y1+h2:
                    
                    # 满足条件代表球拍击中了,改变球的移动方向
                    speedx = -speedx  # x方向设为反方向
                    
                    # 得分加一
                    score[1] += 1
 
 
    #(6)检查球是否没接到,那么游戏结束
    if ballpos[0] < 50 or ballpos[0] > 1150:
        gameover = True
    
    # 游戏结束,画面就不动了
    if gameover is True:
        break
     
    # 游戏没结束就接下去执行
    else:
         #(7)调整球的坐标
         # 如果球的y坐标在超出了桌面的上或下边框范围,调整移动方向
         if ballpos[1] >= 600 or ballpos[1] <= 50:
             
             # y方向的速度调整为反方向,那么x方向和y方向的合速度方向调整了
             speedy = -speedy
         
         # 每一整都调整xy坐标
         ballpos[0] = ballpos[0] + speedx  # 调整球的x坐标
         ballpos[1] = ballpos[1] + speedy  # 调整球的y坐标
    
         #(8)添加桌球的图片,将imgBall放在球桌img的指定坐标位置
         img = cvzone.overlayPNG(img, imgBall, ballpos)
    
    
    #(9)显示记分板
    cvzone.putTextRect(img, f&#39;Left:{score[0]} and Right:{score[1]}&#39;, (400,710))
 
    #(10)图像展示
    cv2.imshow(&#39;img&#39;, img)
    # 每帧滞留1ms后消失
    k = cv2.waitKey(1)
    # ESC键退出程序
    if k & 0XFF==27:
        break
 
# 释放视频资源
cap.release()
cv2.destroyAllWindows()

效果图如下:

Python을 사용하여 테이블 하키 시각적 게임을 만드는 방법

위 내용은 Python을 사용하여 테이블 하키 시각적 게임을 만드는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 yisu.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제