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

目录
  • 介绍
  • 1.安装工具包
  • 2.检测手部关键点
  • 3.蛇身移动
  • 4.蛇进食增加身体长度
  • 5.自身碰撞及界面的处理

介绍

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

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

游戏进行时:

游戏结束界面:

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
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: 返回绘制了关键点及连线后的图像

代码如下:

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

效果图如下:

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

[{'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,那么就不做任何处理。

在上述代码中补充:

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 加一。并在下一帧随机改变食物的位置。

在上述代码中补充:

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,退出结算界面,使得下一帧能执行蛇身移动操作。

在上述代码中补充:

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

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

以上就是Python+OpenCV自制AI视觉版贪吃蛇游戏的详细内容,更多关于Python OpenCV贪吃蛇的资料请关注我们其它相关文章!

(0)

相关推荐

  • Python实现贪吃蛇小游戏(单人模式)

    简单用py写了一个贪吃蛇游戏,有单人.双人模式,比较简单,适合初学者练手.基本上每行重要的语句都有注释,做了什么事一目了然 这里先介绍单人模式 一.游戏设计要点 1.游戏主体窗口(尺寸).画布(尺寸.位置).按钮(尺寸.位置).文字(大小.颜色.位置).图像.背景音乐及相关响应函数(主要是鼠标移动及点击的响应)的设计与合理排布 2.蛇与食物的类的属性设计 3.蛇位置的更新(根据键盘输入).吃到食物加分的判定.食物的更新 4.蛇死亡的判定条件设计 二.主要模块 1.pygame 2.sys 3.r

  • Python实现自动玩贪吃蛇程序

    目录 实现效果 代码 实现效果 先看看效果 这比我手动的快多了,而且是单机的,自动玩没惹骂我,哈哈 ,多人游戏整个自动玩会被骂死~ 代码 没装软件的先安装一下软件,没装模块的安装一下pygame模块. pip install pygame 导入模块 import pygame,sys,time,random from pygame.locals import * 定义颜色变量 redColour = pygame.Color(255,0,0) blackColour = pygame.Color

  • 68行Python代码实现带难度升级的贪吃蛇

    目录 一.前言 二.实现效果 三.环境要求 四.源码分享 五.总结 一.前言 之前版本很多小伙伴都觉得难度过高,另外也有粉丝问还能不能精简代码. 所以这版降低了难度 (由原来过关增加5km/h改为3.5KM/h)并通过反射代替IF ELSE的写法,删除了一些冗余的代码,将代码由85行压缩到了68行 (不必要的压缩代码是不建议的,这里压缩代码只是为了好玩) 二.实现效果 个人最高纪录 三.环境要求 python 3+ pygame包 安装命令:打开cmd 输入: pip install pygam

  • 用Python简单实现个贪吃蛇小游戏(保姆级教程)

    一.前言 本期介绍 Python 练手级项目--贪吃蛇! 原本想推荐一个贪吃蛇的开源项目:python-console-snake,但由于该项目最近一次更新是 8 年前,而且在运行的时候出现了诸多问题.索性我就动手用 Python 重新写了一个贪吃蛇游戏. 下面我们就一起用 Python 实现一个简单有趣的命令行贪吃蛇小游戏,启动命令: git clone https://github.com/AnthonySun256/easy_games cd easy_games python snake

  • 利用Python如何制作贪吃蛇及AI版贪吃蛇详解

    用python制作普通贪吃蛇 哈喽,大家不知道是上午好还是中午好还是下午好还是晚上好! 贪吃蛇,应该是90后小时候的记忆(连我这个00后也不例外),今天,我们就用python这款编程语言来实现贪吃蛇 系统:所有都可以 需导入模块: random pygame pygame.locals sys 下载以上模块指令: random和sys是Python自带的,我们只需要下载pygame即可 下载pygame: 在开始菜单输入"cmd"回车打开,输入``指令:pip install pyga

  • python实现双人贪吃蛇小游戏

    小编今天要给大家分享的是双人贪吃蛇,大家可以和自己的兄弟,姐妹,爸爸,妈妈等一起玩哟!我先介绍一下游戏: 运行游戏,进入初始界面,按下空格键. 玩家(1):w,a,s,d 玩家(2):↑,←,↓,→ 玩家要争夺7个实物,直到吃完为止 游戏结束. 下面是小编写的代码: import math import random import pygame from pygame.locals import * running = False playing = False screen = None ti

  • Python实现贪吃蛇小游戏(双人模式)

    简单用py写了一个贪吃蛇游戏,有单人.双人模式,比较简单,适合初学者练手.本上每行重要的语句都有注释,做了什么事一目了然 这里介绍双人模式 单人模式戳这里:Python简易贪吃蛇小游戏(单人模式) 一.游戏设计要点 1.游戏主体窗口(尺寸).画布(尺寸.位置).按钮(尺寸.位置).文字(大小.颜色.位置).图像.背景音乐及相关响应函数(主要是鼠标移动及点击的响应)的设计与合理排布 2.蛇与食物的类的属性设计 3.蛇位置的更新(根据键盘输入).吃到食物加分的判定.食物的更新 4.蛇死亡的判定条件设

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

    目录 介绍 1.安装工具包 2.检测手部关键点 3.蛇身移动 4.蛇进食增加身体长度 5.自身碰撞及界面的处理 介绍 各位同学好,今天和大家分享一下如何使用 mediapipe+opencv 自制贪吃蛇小游戏.先放张图看效果. 规则:食指指尖控制蛇头,指尖每接触到黄色方块,计数加一,蛇身变长,方块随机切换位置.如果指尖停止移动,或者移动过程中蛇头撞到蛇身,那么游戏结束.点击键盘上的R键重新开始游戏. 游戏进行时: 游戏结束界面: 1. 安装工具包 pip install opencv_pytho

  • python实战之利用pygame实现贪吃蛇游戏(二)

    一.前言 在上一篇博客中,我们实现了基本的界面搭建,这次实现一下逻辑部分. 二.创建蛇 首先,先分析一下蛇的移动,不然我们一定会吃亏的(别问,问就是自己写了一堆无效代码). 蛇的移动其实并没有想象中那样复杂,每一个模块都需要有一个方向,按照方向进行移动. 其实实际上就是一个出队的感觉,即每一个元素都取代上一个元素的位置,然后再按照贪吃蛇当前的方向,移动一下头节点即可. snake.py: """"

  • python实战之利用pygame实现贪吃蛇游戏(一)

    一.前言 之前尝试了自己用pygame写井字棋,这次玩的是贪吃蛇系列. 个人感觉模块可能会比较大,所以选择将函数和主要逻辑代码分在了两个文件中. fuc为函数模块,存储了事件感应和刷新界面等部分. main模块则是游戏的核心. 二.搭建界面 这里我就不重复了,可以先看一下这篇博客 其中界面的基本要素都有. main.py import pygame from fuc import * # 基本属性 lattice_wh = 20 #长宽 snake_color = (84, 255, 159)

  • C语言实现控制台版贪吃蛇游戏

    用c语言写的期末作业:C语言实现控制台版贪吃蛇游戏的具体代码,供大家参考,具体内容如下 /* { conio.h 阻塞式: getch(): 从无回显的控制台获取字符.无缓冲区,只有当按下一个键才会执行后面的程序. 非阻塞式: kbhit() 检测缓冲区中是否有字符:执行该函数后程序不会停下,而是继续执行下面的代码 由于getch()和kbhit()已弃用, 在编译时会产生警告, 可以用_getch()和_kbhit()替换它们, 或者在包含头文件前加上#pragma warning(disab

  • Java实现简单版贪吃蛇游戏

    本文实例为大家分享了Java实现简单版贪吃蛇游戏的具体代码,供大家参考,具体内容如下 这是一个比较简洁的小游戏,主要有三个类,一个主类,一个食物类,一个贪吃蛇类. 1.首先定义主类,主类中主要用来创建窗口 public class Main { public static final int WIDTH=600; public static final int HEIGHT=600; public static void main(String[] args) { JFrame win =new

  • JavaScript实现网页版贪吃蛇游戏

    本文实例为大家分享了JavaScript实现网页贪吃蛇游戏的具体代码,供大家参考,具体内容如下 <!DOCTYPE html> <html> <head><title>贪吃蛇</title> </head> <body> <canvas id="canvas" width="400" height="400"></canvas> <s

  • js实现网页版贪吃蛇游戏

    使用原生 js 实现贪吃蛇小游戏,首先这个 小游戏的目录结构如下: 有 贪吃蛇 , 食物 ,地图 ,还有 游戏 当我们在浏览器打开 index.html 的时候,会出现 移动的小蛇 ,随机生成的食物(这里只有一个,当前食物被吃掉,才会初始化下一个),用户通过键盘上的方向键控制小蛇移动的方向 当小蛇触碰到了墙,即画布边缘的时候,游戏结束! 接下来就是代码实现啦 ~ 食物模块 //食物的自调用函数 (function(){ //创建一个数组 来存放元素 var elements=[]; //食物就是

  • 利用python实现简易版的贪吃蛇游戏(面向python小白)

    引言 作为python 小白,总是觉得自己要做好百分之二百的准备,才能开始写程序.以至于常常整天在那看各种语法教程,学了几个月还是只会print('hello world'). 这样做效率太低,正确的做法,是到身边找问题,然后编程实现.比如说,我学了高等数学,我是不是应该考虑下如何去用编程实现求导或者积分操作,如果想不出怎么办,是不是应该 baidu 一下,别人是如何实现数值积分或是符号积分的.我们每天买东西都要用到加减甚至乘除,那么我是否能编写个简单的计算器,如果命令行太丑的话,我是否能够快速

  • 教你一步步利用python实现贪吃蛇游戏

    0 引言 前几天,星球有人提到贪吃蛇,一下子就勾起了我的兴趣,毕竟在那个Nokia称霸的年代,这款游戏可是经典中的经典啊!而用Python(蛇)玩Snake(贪吃蛇),那再合适不过了

随机推荐