ホームページ  >  記事  >  バックエンド開発  >  Python を使用してテーブル ホッケーのビジュアル ゲームを作成する方法

Python を使用してテーブル ホッケーのビジュアル ゲームを作成する方法

王林
王林転載
2023-05-08 17:46:181426ブラウズ

はじめに

ルールは次のとおりです: 左手は白いラケットをコントロールし、右手は紫のラケットをコントロールします。ラケットは上下にのみ移動できます。赤い丸はアイスボールです。ボールが上下の青い枠に衝突するとラケットが跳ねます; ボールが黄色のエリアに入るとゲームオーバー; 下のピンクのカウントボードには左右がボールを打った回数が記録されます。

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 ()手のキーポイント検出方法

パラメータ:

mode: デフォルトは False で、入力画像はビデオ ストリームとして扱われます。最初の入力画像で手を検出し、検出に成功した後はさらに手の座標を特定しようとします。後続のイメージでは、すべての maxHands 手が検出され、対応する手の座標が特定されると、いずれかの手の追跡が失われるまで、別の検出を呼び出すことなく、それらの座標を追跡します。これにより遅延が軽減され、ビデオ フレームの処理に最適です。 True に設定すると、無関係である可能性のある静的な画像のバッチを処理するために、各入力画像に対して手の検出が実行されます。

maxHands: 検出する手の最大数、デフォルトは 2

detectionCon: 手検出モデルの最小信頼値 (0 ~ 1)、しきい値を超えた場合、検出は成功しました。デフォルトは 0.5

minTrackingCon: 座標追跡モデルの最小信頼値 (0 ~ 1) で、手の座標を追跡が成功したとみなすために使用されます。失敗した場合は、手の検出が自動的に呼び出されます。次の入力画像です。より高い値に設定すると、ソリューションの堅牢性が向上しますが、待ち時間が長くなります。モードが True の場合、このパラメータは無視され、すべての画像に対して手の検出が実行されます。デフォルトは 0.5

そのパラメータと戻り値は、公式関数 mediapipe.solutions.hands.Hands()

MULTI_HAND_LANDMARKS: 検出/追跡された手のコレクション。それぞれの手がそれぞれ x、y、z で構成される 21 個の手のランドマークのリストとして表されます。

MULTI_HANDEDNESS: 検出/追跡されている手が左か右かのコレクション。各ハンドはラベルとスコアで構成されます。 label は、「Left」または「Right」値を持つ文字列です。スコアは、左手または右手を予測する推定確率です。

(2)cvzone.HandTrackingModule.HandDetector.findHands() 手のキーポイントを見つけて描画

パラメータ:

img: 検出する必要がありますkey Points ポイントのフレーム画像、形式は BGR

draw: 元画像上にキーポイントや認識枠を描画する必要があるかどうか

flipType: 画像を描画する必要があるかどうか反転、ビデオ画像が自分自身の鏡像でない場合は、True に設定してください

戻り値:

hands: 検出された手の情報、0 または 1 または 2 つの辞書で構成されるリスト。 2 つの手が検出された場合、それは 2 つの辞書で構成されるリストになります。辞書には、21 個のキーポイントの座標 (x、y、z)、検出枠の左上の座標とその幅と高さ、検出枠の中心点の座標、およびどの手が検出されたかが含まれます。

img: キーポイントと接続を描画した後に画像を返します

(3)cv2.addWeighted()画像融合

2 つの画像を混合します画像は特定の比率に従って結合されます。2 つの画像のサイズとチャネル数は同じである必要があります。

2 つの画像は特定の比率に従って結合されます。 cv2.addWeighted(image 1、重み 1、画像 2、重み 2、明るさのバイアス 設定)

は y = a x1 b x2 c に相当します。ここで、a と b は重みを表し、c は明るさをどれだけ明るくするかを表します

2.2 コード表示

##最初の cv2. imread() のパラメータ cv2.IMREAD_UNCHANGED は、アルファ チャネルを含む元の形式で画像を開くことを参照します。つまり、画像をそのまま開きます 画像がカラーの場合はカラーで読み込まれます 画像がグレースケールの場合はグレースケールで読み込まれます 読み込まれた画像の形状は次のようになります。

Python を使用してテーブル ホッケーのビジュアル ゲームを作成する方法#コードのこの部分は、主に手のキー ポイントの検出と、背景画像とビデオ フレーム画像の融合を担当します

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. キーポイントの処理、ラケットの動きPython を使用してテーブル ホッケーのビジュアル ゲームを作成する方法

3.1 メソッドの紹介

このパートでは主に 2 つのタスクを完了します。左右のラケットをそれぞれ左手と右手で持ち、2つ目はボールを特定の方向に動かすことです。

(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 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はyisu.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。