广告位联系
返回顶部
分享到

Python+OpenCV自制AI视觉版贪吃蛇游戏

python 来源:互联网 作者:秩名 发布时间:2022-03-11 14:05:29 人浏览
摘要

介绍 各位同学好,今天和大家分享一下如何使用 mediapipe+opencv 自制贪吃蛇小游戏。先放张图看效果。 规则:食指指尖控制蛇头,指尖每接触到黄色方块,计数加一,蛇身变长,方块随

介绍

各位同学好,今天和大家分享一下如何使用 mediapipe+opencv 自制贪吃蛇小游戏。先放张图看效果。

规则:食指指尖控制蛇头,指尖每接触到黄色方块,计数加一,蛇身变长,方块随机切换位置。如果指尖停止移动,或者移动过程中蛇头撞到蛇身,那么游戏结束。点击键盘上的R键重新开始游戏。

游戏进行时:

游戏结束界面:

1. 安装工具包

1

2

3

4

5

6

7

8

9

10

11

12

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

import numpy as np

from cvzone.HandTrackingModule import HandDetector  # 导入手部检测模块

import math

import random

21个手部关键点信息如下,本节我们主要研究食指指尖'8'的坐标(x,y)信息。

2. 检测手部关键点

(1)cvzone.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组成。x和y分别由图像的宽度和高度归一化为[0,1]。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: 返回绘制了关键点及连线后的图像

代码如下:

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

import cv2

import cvzone

import numpy as np

from cvzone.HandTrackingModule import HandDetector  # 导入手部检测模块

  

#(1)获取摄像头

cap = cv2.VideoCapture(0) # 0代表电脑自带的摄像头

# 设置显示窗口的size

cap.set(3, 1280)  # 窗口宽1280

cap.set(4, 720)   # 窗口高720

  

#(2)模型配置

detector = HandDetector(maxHands=1,  # 最多检测1只手

                        detectionCon=0.8)  # 最小检测置信度0.8

  

#(3)图像处理

while True:

  

    # 每次读取一帧相机图像,返回是否读取成功success,读取的帧图像img

    success, img = cap.read()

  

    # 图像翻转,使图像和自己呈镜像关系

    img = cv2.flip(img, 1)  # 0代表上下翻转,1代表左右翻转

  

    # 检测手部关键点。返回手部信息hands,绘制关键点后的图像img

    hands, img = detector.findHands(img, flipType=False)  # 由于上一行翻转过图像了,这里就不用翻转了

  

    # 查看关键点信息

    print(hands)

  

    #(4)关键点处理

    if hands:  # 如果检测到手了,那就处理关键点

  

        # 获得食指指尖坐标(x,y)

        hand = hands[0]  # 获取一只手的全部信息

        lmList = hand['lmList']  # 获得这只手的21个关键点的坐标(x,y,z)

        pointIndex = lmList[8][0:2]  # 只获取食指指尖关键点的(x,y)坐标

  

        # 以食指指尖为圆心画圈(圆心坐标是元组类型),半径为15,青色填充

        cv2.circle(img, tuple(pointIndex), 15, (255,0,0), cv2.FILLED)

  

    #(5)显示图像

    cv2.imshow('img', img)  # 输入图像显示窗口的名称及图像

     # 每帧滞留1毫秒后消失,并且按下ESC键退出

    if cv2.waitKey(1) & 0xFF == 27:

        break

  

# 释放视频资源

cap.release()

cv2.destroyAllWindows()

效果图如下:

打印手部关键点信息如下:

1

2

3

4

[{'lmList': [[1152, 675, 0], [1085, 693, -37], [1030, 698, -68], [1003, 698, -97], [1003, 679, -122], [1001, 511, -48], [1041, 546, -81], [1093, 608, -102], [1134, 652, -110], [1075, 484, -46], [1119, 534, -84], [1171, 605, -101], [1217, 659, -103], [1141, 481, -45], [1177, 529, -83], [1219, 590, -84], [1253, 642, -73], [1195, 494, -47], [1221, 521, -73], [1245, 566, -65], [1267, 602, -49]],

'bbox': (1001, 481, 266, 217),

'center': (1134, 589),

'type': 'Right'}]

3. 蛇身移动

构造一个处理蛇身移动的类,要求在没吃食物时,蛇身保持固定的长度跟随食指指尖移动。

举个例子,如果当前的蛇身节点列表 self.points 包含 [a, b, c, d] 这四个节点,a节点代表蛇尾,d节点代表蛇头。在下一帧,食指指尖移动到 e 点,将 e 节点追加到蛇身节点列表中,那么现在的列表包含 [a, b, c, d, e] 节点,其中 e 节点为新的蛇头。

此时判断当前蛇身总长度 self.currentLength(列表中所有节点之间的长度之和)是否大于蛇身固定长度 self.allowedLength,保证在移动过程中蛇身长度不变。

如果当前蛇身总长度 self.currentLength 大于固定长度 self.allowedLength,那么在节点列表中从尾到头依次删除节点,列表 [a, b, c, d, e] 中 a 表示蛇尾节点,先删除,判断列表 [b, c, d, e] 的节点之间的总长度是否满足要求。若仍大于固定长度,那么就再删除 b 节点,再判断。

如果当前蛇身总长度 self.currentLength 小于固定长度 self.allowedLength,那么就不做任何处理。

在上述代码中补充:

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

import cv2

import cvzone

import numpy as np

from cvzone.HandTrackingModule import HandDetector  # 导入手部检测模块

import math

  

# 构造一个贪吃蛇移动的类

class SnakeGameClass:

  

    #(一)初始化

    def __init__(self):

  

        self.points = []  # 蛇的身体的节点坐标

        self.lengths = []  # 蛇身各个节点之间的坐标

        self.currentLength = 0  # 当前蛇身长度

        self.allowedLength = 150  # 没吃东西时,蛇的总长度

        self.previousHead = (0,0)  # 前一个蛇头节点的坐标

  

    #(二)更新增加蛇身长度

    def update(self, imgMain, currentHead): # 输入图像,当前蛇头的坐标

  

        px, py = self.previousHead  # 获得前一个蛇头的x和y坐标

        cx, cy = currentHead  # 当前蛇头节点的x和y坐标

         

        # 添加当前蛇头的坐标到蛇身节点坐标列表中

        self.points.append([cx,cy])

  

        # 计算两个节点之间的距离

        distance = math.hypot(cx-px, cy-py)  # 计算平方和开根

        # 将节点之间的距离添加到蛇身节点距离列表中

        self.lengths.append(distance)

        # 增加当前蛇身长度

        self.currentLength += distance

  

        # 更新蛇头坐标

        self.previousHead = (cx,cy)

  

        #(三)减少蛇尾长度,即移动过程中蛇头到蛇尾的长度不大于150

        if self.currentLength > self.allowedLength:

  

            # 遍历所有的节点线段长度。新更新的蛇头索引在列表后面,蛇尾的索引在列表前面

            for i, length in enumerate(self.lengths):

  

                # 从蛇尾到蛇头依次减线段长度,得到的长度是否满足要求

                self.currentLength -= length

  

                # 从列表中删除蛇尾端的线段长度,以及蛇尾节点

                self.lengths.pop(i)

                self.points.pop(i)

  

                # 如果当前蛇身长度小于规定长度,满足要求,退出循环

                if self.currentLength < self.allowedLength:

                    break

  

        #(四)绘制蛇

        # 当节点列表中有值了,才能绘制

        if self.points:

  

            # 遍历蛇身节点坐标

            for i, point in enumerate(self.points): 

                # 绘制前后两个节点之间的连线

                if i != 0:

                    cv2.line(imgMain, tuple(self.points[i-1]), tuple(self.points[i]), (0,255,0), 20)

  

            # 在蛇头的位置画个圆

            cv2.circle(imgMain, tuple(self.points[-1]), 20, (255,0,0), cv2.FILLED)

  

        # 返回更新后的图像

        return imgMain

  

  

#(1)获取摄像头

cap = cv2.VideoCapture(0) # 0代表电脑自带的摄像头

# 设置显示窗口的size

cap.set(3, 1280)  # 窗口宽1280

cap.set(4, 720)   # 窗口高720

  

#(2)模型配置

detector = HandDetector(maxHands=1,  # 最多检测1只手

                        detectionCon=0.8)  # 最小检测置信度0.8

  

# 接收创建贪吃蛇的类

game = SnakeGameClass()

  

#(3)图像处理

while True:

  

    # 每次读取一帧相机图像,返回是否读取成功success,读取的帧图像img

    success, img = cap.read()

  

    # 图像翻转,使图像和自己呈镜像关系

    img = cv2.flip(img, 1)  # 0代表上下翻转,1代表左右翻转

  

    # 检测手部关键点。返回手部信息hands,绘制关键点后的图像img

    hands, img = detector.findHands(img, flipType=False)  # 由于上一行翻转过图像了,这里就不用翻转了

  

    # 查看关键点信息

    print(hands)

  

    #(4)关键点处理

    if hands:  # 如果检测到手了,那就处理关键点

  

        # 获得食指指尖坐标(x,y)

        hand = hands[0]  # 获取一只手的全部信息

        lmList = hand['lmList']  # 获得这只手的21个关键点的坐标(x,y,z)

        pointIndex = lmList[8][0:2]  # 只获取食指指尖关键点的(x,y)坐标

  

        # 更新贪吃蛇的节点,给出蛇头节点坐标。返回更新后的图像

        img = game.update(img, pointIndex)

  

    #(5)显示图像

    cv2.imshow('img', img)  # 输入图像显示窗口的名称及图像

     # 每帧滞留1毫秒后消失,并且按下ESC键退出

    if cv2.waitKey(1) & 0xFF == 27:

        break

  

# 释放视频资源

cap.release()

cv2.destroyAllWindows()

效果图如下,蛇身保持默认固定长度随着指尖而移动。

4. 蛇进食增加身体长度

先看下面代码 SnakeGameClass 类中的第(五)步。给食物(即绘制的矩形)随机给出一个中心点坐标,自定义的类方法 randomFoodLocation(),执行该方法则食物的中心点坐标的x在[100,1000]中随机取一个数,y在[100,600]中随机取一个数。

下面代码定义的类中的第(七)步。判断食指指尖(即蛇头节点坐标)是否在矩形内部,如果在内部,那么蛇身移动过程中的固定长度 self.allowedLength 增加50个像素值。得分 self.score 加一。并在下一帧随机改变食物的位置。

在上述代码中补充:

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

144

145

146

147

148

149

150

151

import cv2

import cvzone

import numpy as np

from cvzone.HandTrackingModule import HandDetector  # 导入手部检测模块

import math

import random

  

# 构造一个贪吃蛇移动的类

class SnakeGameClass:

  

    #(一)初始化

    def __init__(self):

  

        self.score = 0  # 积分器

        self.points = []  # 蛇的身体的节点坐标

        self.lengths = []  # 蛇身各个节点之间的坐标

        self.currentLength = 0  # 当前蛇身长度

        self.allowedLength = 150  # 没吃东西时,蛇的总长度

        self.previousHead = (0,0)  # 前一个蛇头节点的坐标

  

        self.foodPoint = (0,0)  # 食物的起始位置

        self.randomFoodLocation()  # 随机改变食物的位置

  

    #(五)食物随机出现的位置

    def randomFoodLocation(self):

        # x在100至1000之间,y在100至600之间,随机取一个整数

        self.foodPoint = random.randint(100, 1000),  random.randint(100, 600)

  

    #(二)更新增加蛇身长度

    def update(self, imgMain, currentHead): # 输入图像,当前蛇头的坐标

  

        px, py = self.previousHead  # 获得前一个蛇头的x和y坐标

        cx, cy = currentHead  # 当前蛇头节点的x和y坐标

         

        # 添加当前蛇头的坐标到蛇身节点坐标列表中

        self.points.append([cx,cy])

  

        # 计算两个节点之间的距离

        distance = math.hypot(cx-px, cy-py)  # 计算平方和开根

        # 将节点之间的距离添加到蛇身节点距离列表中

        self.lengths.append(distance)

        # 增加当前蛇身长度

        self.currentLength += distance

  

        # 更新蛇头坐标

        self.previousHead = (cx,cy)

  

        #(三)减少蛇尾长度,即移动过程中蛇头到蛇尾的长度不大于150

        if self.currentLength > self.allowedLength:

  

            # 遍历所有的节点线段长度。新更新的蛇头索引在列表后面,蛇尾的索引在列表前面

            for i, length in enumerate(self.lengths):

  

                # 从蛇尾到蛇头依次减线段长度,得到的长度是否满足要求

                self.currentLength -= length

  

                # 从列表中删除蛇尾端的线段长度,以及蛇尾节点

                self.lengths.pop(i)

                self.points.pop(i)

  

                # 如果当前蛇身长度小于规定长度,满足要求,退出循环

                if self.currentLength < self.allowedLength:

                    break

         

        #(七)检查蛇是否吃了食物

        rx, ry = self.foodPoint  # 得到食物的中心点坐标位置

         

        # 绘制矩形作为蛇的食物

        cv2.rectangle(imgMain, (rx-20, ry-20), (rx+20, ry+20), (255,255,0), cv2.FILLED)

        cv2.rectangle(imgMain, (rx-20, ry-20), (rx+20, ry+20), (0,255,255), 5)       

        cv2.rectangle(imgMain, (rx-5, ry-5), (rx+5, ry+5), (0,0,255), cv2.FILLED) 

  

        # 检查指尖(即蛇头cx,cy)是否在矩形内部

        if rx-20 < cx < rx+20 and ry-20< cy < ry+20:

  

            # 随机更换食物的位置

            self.randomFoodLocation()

  

            # 增加蛇身的限制长度,每吃1个食物就能变长50

            self.allowedLength += 50

  

            # 吃食物的计数加一

            self.score += 1

  

            print('eat!', f'score:{self.score}')

  

        #(四)绘制蛇

        # 当节点列表中有值了,才能绘制

        if self.points:

  

            # 遍历蛇身节点坐标

            for i, point in enumerate(self.points): 

                # 绘制前后两个节点之间的连线

                if i != 0:

                    cv2.line(imgMain, tuple(self.points[i-1]), tuple(self.points[i]), (0,255,0), 20)

                    cv2.line(imgMain, tuple(self.points[i-1]), tuple(self.points[i]), (0,0,255), 15)

  

            # 在蛇头的位置画个圆

            cv2.circle(imgMain, tuple(self.points[-1]), 20, (255,255,0), cv2.FILLED)

            cv2.circle(imgMain, tuple(self.points[-1]), 18, (255,0,0), 3)           

            cv2.circle(imgMain, tuple(self.points[-1]), 5, (0,0,0), cv2.FILLED)

  

        # 返回更新后的图像

        return imgMain

  

  

#(1)获取摄像头

cap = cv2.VideoCapture(0) # 0代表电脑自带的摄像头

# 设置显示窗口的size

cap.set(3, 1280)  # 窗口宽1280

cap.set(4, 720)   # 窗口高720

  

#(2)模型配置

detector = HandDetector(maxHands=1,  # 最多检测1只手

                        detectionCon=0.8)  # 最小检测置信度0.8

  

# 接收创建贪吃蛇的类

game = SnakeGameClass()

  

#(3)图像处理

while True:

  

    # 每次读取一帧相机图像,返回是否读取成功success,读取的帧图像img

    success, img = cap.read()

  

    # 图像翻转,使图像和自己呈镜像关系

    img = cv2.flip(img, 1)  # 0代表上下翻转,1代表左右翻转

  

    # 检测手部关键点。返回手部信息hands,绘制关键点后的图像img

    hands, img = detector.findHands(img, flipType=False)  # 由于上一行翻转过图像了,这里就不用翻转了

  

    #(4)关键点处理

    if hands:  # 如果检测到手了,那就处理关键点

  

        # 获得食指指尖坐标(x,y)

        hand = hands[0]  # 获取一只手的全部信息

        lmList = hand['lmList']  # 获得这只手的21个关键点的坐标(x,y,z)

        pointIndex = lmList[8][0:2]  # 只获取食指指尖关键点的(x,y)坐标

  

        # 更新贪吃蛇的节点,给出蛇头节点坐标。返回更新后的图像

        img = game.update(img, pointIndex)

  

    #(5)显示图像

    cv2.imshow('img', img)  # 输入图像显示窗口的名称及图像

     # 每帧滞留1毫秒后消失,并且按下ESC键退出

    if cv2.waitKey(1) & 0xFF == 27:

        break

  

# 释放视频资源

cap.release()

cv2.destroyAllWindows()

效果图如下:

5. 自身碰撞及界面的处理

先看到自定义类 SnakeGameClass 中的第(八)步,蛇身节点列表 self.points 中包含从蛇头到蛇尾的所有节点。如 [a, b, c, d, e] 节点,a 节点代表蛇尾,e 节点代表蛇头。这里我就粗糙地判断一下是否碰撞,如果大家有更好的判断方法可以改动这第(八)步。计算蛇头 e 节点到所有节点之间的距离,如果小于某个值就代表碰撞了,游戏结束 self.gameover = True

再看到自定义类中的第(三)步。如果游戏结束 self.gameover = True,那就在下一帧中绘制结算界面,不再执行蛇身移动程序。

再看到主程序中的第(5)步。其中 k == ord('r'),按下键盘上的 r 键来重新游戏。将所有蛇身变量初始化,并将 self.gameover = False,退出结算界面,使得下一帧能执行蛇身移动操作。

在上述代码中补充:

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

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

import cv2

import cvzone

from matplotlib.cbook import pts_to_midstep

import numpy as np

from cvzone.HandTrackingModule import HandDetector  # 导入手部检测模块

import math

import random

  

# 构造一个贪吃蛇移动的类

class SnakeGameClass:

  

    #(一)初始化

    def __init__(self):

  

        self.score = 0  # 积分器

        self.points = []  # 蛇的身体的节点坐标

        self.lengths = []  # 蛇身各个节点之间的坐标

        self.currentLength = 0  # 当前蛇身长度

        self.allowedLength = 150  # 没吃东西时,蛇的总长度

        self.previousHead = (0,0)  # 前一个蛇头节点的坐标

  

        self.foodPoint = (0,0)  # 食物的起始位置

        self.randomFoodLocation()  # 随机改变食物的位置

  

        self.gameover = False  # 蛇头撞到蛇身,变成True,游戏结束

  

    #(二)食物随机出现的位置

    def randomFoodLocation(self):

        # x在100至1000之间,y在100至600之间,随机取一个整数

        self.foodPoint = random.randint(100, 1000),  random.randint(100, 600)

  

    #(三)更新增加蛇身长度

    def update(self, imgMain, currentHead): # 输入图像,当前蛇头的坐标

  

        # 游戏结束,显示文本

        if self.gameover:

            cvzone.putTextRect(imgMain, 'GameOver', [400,300], 5, 3, colorR=(0,255,255), colorT=(0,0,255))

            cvzone.putTextRect(imgMain, f'Score:{self.score}', [450,400], 5, 3, colorR=(255,255,0))

            cvzone.putTextRect(imgMain, f"Press Key 'R' to Restart", [230,500], 4, 3, colorR=(0,255,0), colorT=(255,0,0))

  

        else:

            px, py = self.previousHead  # 获得前一个蛇头的x和y坐标

            cx, cy = currentHead  # 当前蛇头节点的x和y坐标

             

            # 添加当前蛇头的坐标到蛇身节点坐标列表中

            self.points.append([cx,cy])

  

            # 计算两个节点之间的距离

            distance = math.hypot(cx-px, cy-py)  # 计算平方和开根

            # 将节点之间的距离添加到蛇身节点距离列表中

            self.lengths.append(distance)

            # 增加当前蛇身长度

            self.currentLength += distance

  

            # 更新蛇头坐标

            self.previousHead = (cx,cy)

  

            #(四)减少蛇尾长度,即移动过程中蛇头到蛇尾的长度不大于150

            if self.currentLength > self.allowedLength:

  

                # 遍历所有的节点线段长度。新更新的蛇头索引在列表后面,蛇尾的索引在列表前面

                for i, length in enumerate(self.lengths):

  

                    # 从蛇尾到蛇头依次减线段长度,得到的长度是否满足要求

                    self.currentLength -= length

  

                    # 从列表中删除蛇尾端的线段长度,以及蛇尾节点

                    self.lengths.pop(i)

                    self.points.pop(i)

  

                    # 如果当前蛇身长度小于规定长度,满足要求,退出循环

                    if self.currentLength < self.allowedLength:

                        break

             

            #(五)绘制得分板

            cvzone.putTextRect(imgMain, f'Score:{self.score}', [50,80], 4, 3, colorR=(255,255,0))

  

            #(六)检查蛇是否吃了食物

            rx, ry = self.foodPoint  # 得到食物的中心点坐标位置

             

            # 绘制矩形作为蛇的食物

            cv2.rectangle(imgMain, (rx-20, ry-20), (rx+20, ry+20), (255,255,0), cv2.FILLED)

            cv2.rectangle(imgMain, (rx-20, ry-20), (rx+20, ry+20), (0,255,255), 5)       

            cv2.rectangle(imgMain, (rx-5, ry-5), (rx+5, ry+5), (0,0,255), cv2.FILLED) 

  

            # 检查指尖(即蛇头cx,cy)是否在矩形内部

            if rx-20 < cx < rx+20 and ry-20< cy < ry+20:

  

                # 随机更换食物的位置

                self.randomFoodLocation()

  

                # 增加蛇身的限制长度,每吃1个食物就能变长50

                self.allowedLength += 50

  

                # 吃食物的计数加一

                self.score += 1

  

                print('eat!', f'score:{self.score}')

  

            #(七)绘制蛇

            # 当节点列表中有值了,才能绘制

            if self.points:

  

                # 遍历蛇身节点坐标

                for i, point in enumerate(self.points): 

                    # 绘制前后两个节点之间的连线

                    if i != 0:

                        cv2.line(imgMain, tuple(self.points[i-1]), tuple(self.points[i]), (0,255,0), 20)

                        cv2.line(imgMain, tuple(self.points[i-1]), tuple(self.points[i]), (0,0,255), 15)

  

                # 在蛇头的位置画个圆

                cv2.circle(imgMain, tuple(self.points[-1]), 20, (255,255,0), cv2.FILLED)

                cv2.circle(imgMain, tuple(self.points[-1]), 18, (255,0,0), 3)           

                cv2.circle(imgMain, tuple(self.points[-1]), 5, (0,0,0), cv2.FILLED)

  

            #(八)检查蛇头碰撞到自身       

            for point in self.points[:-2]:  # 不算蛇头到自身的距离

  

                # 计算蛇头和每个节点之间的距离

                dist = math.hypot(cx-point[0], cy-point[1])

  

                # 如果距离小于1.8,那么就证明碰撞了

                if dist < 1.8:

  

                    # 游戏结束

                    self.gameover = True

  

        # 返回更新后的图像

        return imgMain

  

  

#(1)获取摄像头

cap = cv2.VideoCapture(0) # 0代表电脑自带的摄像头

# 设置显示窗口的size

cap.set(3, 1280)  # 窗口宽1280

cap.set(4, 720)   # 窗口高720

  

#(2)模型配置

detector = HandDetector(maxHands=1,  # 最多检测1只手

                        detectionCon=0.8)  # 最小检测置信度0.8

  

# 接收创建贪吃蛇的类

game = SnakeGameClass()

  

#(3)图像处理

while True:

  

    # 每次读取一帧相机图像,返回是否读取成功success,读取的帧图像img

    success, img = cap.read()

  

    # 图像翻转,使图像和自己呈镜像关系

    img = cv2.flip(img, 1)  # 0代表上下翻转,1代表左右翻转

  

    # 检测手部关键点。返回手部信息hands,绘制关键点后的图像img

    hands, img = detector.findHands(img, flipType=False)  # 由于上一行翻转过图像了,这里就不用翻转了

  

    #(4)关键点处理

    if hands:  # 如果检测到手了,那就处理关键点

  

        # 获得食指指尖坐标(x,y)

        hand = hands[0]  # 获取一只手的全部信息

        lmList = hand['lmList']  # 获得这只手的21个关键点的坐标(x,y,z)

        pointIndex = lmList[8][0:2]  # 只获取食指指尖关键点的(x,y)坐标

  

        # 更新贪吃蛇的节点,给出蛇头节点坐标。返回更新后的图像

        img = game.update(img, pointIndex)

  

    #(5)显示图像

    cv2.imshow('img', img)  # 输入图像显示窗口的名称及图像

  

    # 重新开始游戏

    k = cv2.waitKey(1)  # 每帧滞留1毫秒后消失

    if k == ord('r'):  # 键盘'r'键代表重新开始游戏

        game.gameover = False

        game.score = 0  # 积分器

        game.points = []  # 蛇的身体的节点坐标

        game.lengths = []  # 蛇身各个节点之间的坐标

        game.currentLength = 0  # 当前蛇身长度

        game.allowedLength = 150  # 没吃东西时,蛇的总长度

        game.previousHead = (0,0)  # 前一个蛇头节点的坐标               

        game.randomFoodLocation()  # 随机改变食物的位置

     

    if k & 0xFF == 27:  # 键盘ESC键退出程序

        break

  

# 释放视频资源

cap.release()

cv2.destroyAllWindows()

效果图如下,在移动过程中,蛇头每碰到一个食物,蛇身就会变长,如果 停止移动 或 蛇头节点距离蛇身节点过近 就会结束游戏。


版权声明 : 本文内容来源于互联网或用户自行发布贡献,该文观点仅代表原作者本人。本站仅提供信息存储空间服务和不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权, 违法违规的内容, 请发送邮件至2530232025#qq.cn(#换@)举报,一经查实,本站将立刻删除。
原文链接 : https://blog.csdn.net/dgvv4/article/details/123386493
相关文章
  • Python Django教程之实现新闻应用程序

    Python Django教程之实现新闻应用程序
    Django是一个用Python编写的高级框架,它允许我们创建服务器端Web应用程序。在本文中,我们将了解如何使用Django创建新闻应用程序。 我们将
  • 书写Python代码的一种更优雅方式(推荐!)

    书写Python代码的一种更优雅方式(推荐!)
    一些比较熟悉pandas的读者朋友应该经常会使用query()、eval()、pipe()、assign()等pandas的常用方法,书写可读性很高的「链式」数据分析处理代码
  • Python灰度变换中伽马变换分析实现

    Python灰度变换中伽马变换分析实现
    1. 介绍 伽马变换主要目的是对比度拉伸,将图像灰度较低的部分进行修正 伽马变换针对的是对单个像素点的变换,也就是点对点的映射 形
  • 使用OpenCV实现迷宫解密的全过程

    使用OpenCV实现迷宫解密的全过程
    一、你能自己走出迷宫吗? 如下图所示,可以看到是一张较为复杂的迷宫图,相信也有人尝试过自己一点一点的找出口,但我们肉眼来解谜
  • Python中的数据精度问题的介绍

    Python中的数据精度问题的介绍
    一、python运算时精度问题 1.运行时精度问题 在Python中(其他语言中也存在这个问题,这是计算机采用二进制导致的),有时候由于二进制和
  • Python随机值生成的常用方法

    Python随机值生成的常用方法
    一、随机整数 1.包含上下限:[a, b] 1 2 3 4 import random #1、随机整数:包含上下限:[a, b] for i in range(10): print(random.randint(0,5),end= | ) 查看运行结
  • Python字典高级用法深入分析讲解
    一、 collections 中 defaultdict 的使用 1.字典的键映射多个值 将下面的列表转成字典 l = [(a,2),(b,3),(a,1),(b,4),(a,3),(a,1),(b,3)] 一个字典就是一个键对
  • Python浅析多态与鸭子类型使用实例
    什么多态:同一事物有多种形态 为何要有多态=》多态会带来什么样的特性,多态性 多态性指的是可以在不考虑对象具体类型的情况下而直
  • Python字典高级用法深入分析介绍
    一、 collections 中 defaultdict 的使用 1.字典的键映射多个值 将下面的列表转成字典 l = [(a,2),(b,3),(a,1),(b,4),(a,3),(a,1),(b,3)] 一个字典就是一个键对
  • Python淘宝或京东等秒杀抢购脚本实现(秒杀脚本

    Python淘宝或京东等秒杀抢购脚本实现(秒杀脚本
    我们的目标是秒杀淘宝或京东等的订单,这里面有几个关键点,首先需要登录淘宝或京东,其次你需要准备好订单,最后要在指定时间快速
  • 本站所有内容来源于互联网或用户自行发布,本站仅提供信息存储空间服务,不拥有版权,不承担法律责任。如有侵犯您的权益,请您联系站长处理!
  • Copyright © 2017-2022 F11.CN All Rights Reserved. F11站长开发者网 版权所有 | 苏ICP备2022031554号-1 | 51LA统计