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

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

王林
リリース: 2023-05-08 17:46:18
転載
1830 人が閲覧しました

はじめに

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

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

1. ファイル構成

1.1 インポート ツール キット

1

2

3

4

5

6

7

8

9

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

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 代码展示

在上述代码中补充

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

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 < ballpos[0] < 100+w1 代表球到了球拍横坐标区域范围内部了,y1 < ballpos[1] < y1+h2 代表球的y坐标在球拍y坐标内部,这时表明击球成功,speedx = -speedx 只改变沿x轴的速度方向,不改变沿y轴的速度方向。

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

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

(3)计数器

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

4.2 代码展示

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

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

関連ラベル:
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート