一小时学会用Opencv做贪吃蛇游戏(Python版)

作者 | 源代码•宸

来源丨CSDN博客

使用Mediapipe

水平镜像处理

import cvzone

import cv2

import numpy as np

from cvzone.HandTrackingModule import HandDetector

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

cap.set(3, 1280) # 宽

cap.set(4, 720) # 高

detector = HandDetector(detectionCon=0.8, maxHands=2)

# 处理每一帧图像

whileTrue:

success, img = cap.read()

# 翻转图像,使自身和摄像头中的自己呈镜像关系

img = cv2.flip(img, 1) # 将手水平翻转

hands, img = detector.findHands(img)

cv2.imshow("Image", img)

cv2.waitKey(1)

修改代码

import cvzone

import cv2

import numpy as np

from cvzone.HandTrackingModule import HandDetector

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

cap.set(3, 1280) # 宽

cap.set(4, 720) # 高

detector = HandDetector(detectionCon=0.8, maxHands=1)

# 处理每一帧图像

whileTrue:

success, img = cap.read()

# 翻转图像,使自身和摄像头中的自己呈镜像关系

img = cv2.flip(img, 1) # 将手水平翻转

hands, img = detector.findHands(img, flipType=False) # 左手是左手,右手是右手,映射正确

cv2.imshow("Image", img)

    cv2.waitKey(1)

问题修复完毕

观察手的信息

import cvzone

import cv2

import numpy as np

from cvzone.HandTrackingModule import HandDetector

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

cap.set(3, 1280) # 宽

cap.set(4, 720) # 高

detector = HandDetector(detectionCon=0.8, maxHands=1)

# 处理每一帧图像

whileTrue:

success, img = cap.read()

# 翻转图像,使自身和摄像头中的自己呈镜像关系

img = cv2.flip(img, 1) # 将手水平翻转

hands, img = detector.findHands(img, flipType=False) # 左手是左手,右手是右手,映射正确

print(hands)

cv2.imshow("Image", img)

    cv2.waitKey(1)

输出结果

[{‘lmList’: [[1088, 633, 0], [1012, 655, -24], [940, 629, -32], [894, 596, -35], [875, 562, -36], [949, 504, -17], [891, 441, -16], [862, 419, -16], [838, 403, -16], [995, 480, -3], [943, 418, 8], [924, 426, 17], [920, 440, 22], [1044, 480, 8], [998, 455, 17], [987, 489, 21], [993, 513, 23], [1085, 492, 19], [1048, 477, 27], [1036, 505, 35], [1041, 528, 40]], ‘bbox’: (838, 403, 250, 252), ‘center’: (963, 529), ‘type’: ‘Left’}]

做个小蛇

import math

import cvzone

import cv2

import numpy as np

from cvzone.HandTrackingModule import HandDetector

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

cap.set(3, 1280) # 宽

cap.set(4, 720) # 高

detector = HandDetector(detectionCon=0.8, maxHands=1)

classSnakeGameClass:

def__init__(self): # 构造方法

self.points = [] # 蛇身上所有的点

self.lengths = [] # 每个点之间的长度

self.currentLength = 0# 蛇的总长

self.allowedLength = 150# 蛇允许的总长度

self.previousHead = 0, 0# 第二个头结点

defupdate(self, imgMain, currentHead): # 实例方法

px, py = self.previousHead

cx, cy = currentHead

self.points.append([cx, cy]) # 添加蛇的点列表节点

distance = math.hypot(cx - px, cy - py) # 两点之间的距离

self.lengths.append(distance) # 添加蛇的距离列表内容

self.currentLength += distance

self.previousHead = cx, cy

# Draw Snake

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

if i != 0:

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

# 对列表最后一个点也就是蛇头画为紫色点

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

return imgMain

game = SnakeGameClass()

# 处理每一帧图像

whileTrue:# 不断迭代更新

success, img = cap.read()

# 翻转图像,使自身和摄像头中的自己呈镜像关系

img = cv2.flip(img, 1) # 将手水平翻转

hands, img = detector.findHands(img, flipType=False) # 左手是左手,右手是右手,映射正确

ifhands:

lmList = hands[0][lmList] # hands是由N个字典组成的列表

pointIndex = lmList[8][0:2] # 只要食指指尖的x和y坐标

img = game.update(img, pointIndex)

cv2.imshow("Image", img)

    cv2.waitKey(1)

添加甜甜圈

import math

import random

import cvzone

import cv2

import numpy as np

from cvzone.HandTrackingModule import HandDetector

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

cap.set(3, 1280) # 宽

cap.set(4, 720) # 高

detector = HandDetector(detectionCon=0.8, maxHands=1)

classSnakeGameClass:

def__init__(self, pathFood):# 构造方法

self.points = [] # 蛇身上所有的点

self.lengths = [] # 每个点之间的长度

self.currentLength = 0# 蛇的总长

self.allowedLength = 150# 蛇允许的总长度

self.previousHead = 0, 0# 第二个头结点

self.imgFood = cv2.imread(pathFood, cv2.IMREAD_UNCHANGED)

self.hFood, self.wFood, _ = self.imgFood.shape

self.foodPoint = 0, 0

self.randomFoodLocation()

defrandomFoodLocation(self):

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

defupdate(self, imgMain, currentHead):# 实例方法

px, py = self.previousHead

cx, cy = currentHead

self.points.append([cx, cy]) # 添加蛇的点列表节点

distance = math.hypot(cx - px, cy - py) # 两点之间的距离

self.lengths.append(distance) # 添加蛇的距离列表内容

self.currentLength += distance

self.previousHead = cx, cy

# Length Reduction

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

break

# Draw Snake

if self.points:

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

if i != 0:

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

# 对列表最后一个点也就是蛇头画为紫色点

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

# Draw Food

rx, ry = self.foodPoint

imgMain = cvzone.overlayPNG(imgMain, self.imgFood,

(rx - self.wFood // 2, ry - self.hFood // 2))

return imgMain

game = SnakeGameClass("donut.png")

# 处理每一帧图像

whileTrue: # 不断迭代更新

success, img = cap.read()

# 翻转图像,使自身和摄像头中的自己呈镜像关系

img = cv2.flip(img, 1) # 将手水平翻转

hands, img = detector.findHands(img, flipType=False) # 左手是左手,右手是右手,映射正确

if hands:

lmList = hands[0][lmList] # hands是由N个字典组成的列表

pointIndex = lmList[8][0:2] # 只要食指指尖的x和y坐标

img = game.update(img, pointIndex)

cv2.imshow("Image", img)

    cv2.waitKey(1)

donut.png

部分代码解释说明

imgMain = cvzone.overlayPNG(imgMain, self.imgFood,                                    (rx - self.wFood // 2, ry - self.hFood // 2))

为什么不是

imgMain = cvzone.overlayPNG(imgMain, self.imgFood, (rx , ry))

那是因为,随机生成一个点后,有坐标(x,y)

增加分数机制

import math

import random

import cvzone

import cv2

import numpy as np

from cvzone.HandTrackingModule import HandDetector

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

cap.set(3, 1280) # 宽

cap.set(4, 720) # 高

detector = HandDetector(detectionCon=0.8, maxHands=1)

classSnakeGameClass:

def__init__(self, pathFood):# 构造方法

self.points = [] # 蛇身上所有的点

self.lengths = [] # 每个点之间的长度

self.currentLength = 0# 蛇的总长

self.allowedLength = 150# 蛇允许的总长度

self.previousHead = 0, 0# 第二个头结点

self.imgFood = cv2.imread(pathFood, cv2.IMREAD_UNCHANGED)

self.hFood, self.wFood, _ = self.imgFood.shape

self.foodPoint = 0, 0

self.randomFoodLocation()

self.score = 0

defrandomFoodLocation(self):

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

defupdate(self, imgMain, currentHead):# 实例方法

px, py = self.previousHead

cx, cy = currentHead

self.points.append([cx, cy]) # 添加蛇的点列表节点

distance = math.hypot(cx - px, cy - py) # 两点之间的距离

self.lengths.append(distance) # 添加蛇的距离列表内容

self.currentLength += distance

self.previousHead = cx, cy

# Length Reduction

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

break

# Check if snake ate the food

rx, ry = self.foodPoint

if rx - self.wFood // 2 2and \

ry - self.hFood // 2 2:

self.randomFoodLocation()

self.allowedLength += 50

self.score += 1

print(self.score)

# Draw Snake

if self.points:

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

if i != 0:

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

# 对列表最后一个点也就是蛇头画为紫色点

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

# Draw Food

imgMain = cvzone.overlayPNG(imgMain, self.imgFood,

(rx - self.wFood // 2, ry - self.hFood // 2))

return imgMain

game = SnakeGameClass("donut.png")

# 处理每一帧图像

whileTrue: # 不断迭代更新

success, img = cap.read()

# 翻转图像,使自身和摄像头中的自己呈镜像关系

img = cv2.flip(img, 1) # 将手水平翻转

hands, img = detector.findHands(img, flipType=False) # 左手是左手,右手是右手,映射正确

if hands:

lmList = hands[0][lmList] # hands是由N个字典组成的列表

pointIndex = lmList[8][0:2] # 只要食指指尖的x和y坐标

img = game.update(img, pointIndex)

cv2.imshow("Image", img)

    cv2.waitKey(1)

完整代码

import math

import random

import cvzone

import cv2

import numpy as np

from cvzone.HandTrackingModule import HandDetector

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

cap.set(3, 1280) # 宽

cap.set(4, 720) # 高

detector = HandDetector(detectionCon=0.8, maxHands=1)

classSnakeGameClass:

def__init__(self, pathFood):# 构造方法

self.points = [] # 蛇身上所有的点

self.lengths = [] # 每个点之间的长度

self.currentLength = 0# 蛇的总长

self.allowedLength = 150# 蛇允许的总长度

self.previousHead = 0, 0# 第二个头结点

self.imgFood = cv2.imread(pathFood, cv2.IMREAD_UNCHANGED)

self.hFood, self.wFood, _ = self.imgFood.shape

self.foodPoint = 0, 0

self.randomFoodLocation()

self.score = 0

self.gameOver = False

defrandomFoodLocation(self):

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

defupdate(self, imgMain, currentHead):# 实例方法

if self.gameOver:

cvzone.putTextRect(imgMain, "Game Over", [300, 400],

scale=7, thickness=5, offset=20)

cvzone.putTextRect(imgMain, fYour Score:{self.score}, [300, 550],

scale=7, thickness=5, offset=20)

else:

px, py = self.previousHead

cx, cy = currentHead

self.points.append([cx, cy]) # 添加蛇的点列表节点

distance = math.hypot(cx - px, cy - py) # 两点之间的距离

self.lengths.append(distance) # 添加蛇的距离列表内容

self.currentLength += distance

self.previousHead = cx, cy

# Length Reduction

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

break

# Check if snake ate the food

rx, ry = self.foodPoint

if rx - self.wFood // 2 2and \

ry - self.hFood // 2 2:

self.randomFoodLocation()

self.allowedLength += 50

self.score += 1

print(self.score)

# Draw Snake

if self.points:

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

if i != 0:

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

# 对列表最后一个点也就是蛇头画为紫色点

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

# Draw Food

imgMain = cvzone.overlayPNG(imgMain, self.imgFood,

(rx - self.wFood // 2, ry - self.hFood // 2))

cvzone.putTextRect(imgMain, fYour Score:{self.score}, [50, 80],

scale=3, thickness=5, offset=10)

# Check for Collision

pts = np.array(self.points[:-2], np.int32)

pts = pts.reshape((-1, 1, 2)) # 重塑为一个行数未知但只有一列且每个元素有2个子元素的矩阵

cv2.polylines(imgMain, [pts], False, (0, 200, 0), 3)

# 第三个参数是False,我们得到的是不闭合的线

minDist = cv2.pointPolygonTest(pts, (cx, cy), True)

# 参数True表示输出该像素点到轮廓最近距离

if-1 1:

print("Hit")

self.gameOver = True

self.points = [] # 蛇身上所有的点

self.lengths = [] # 每个点之间的长度

self.currentLength = 0# 蛇的总长

self.allowedLength = 150# 蛇允许的总长度

self.previousHead = 0, 0# 第二个头结点

self.randomFoodLocation()

return imgMain

game = SnakeGameClass("donut.png")

# 处理每一帧图像

whileTrue: # 不断迭代更新

success, img = cap.read()

# 翻转图像,使自身和摄像头中的自己呈镜像关系

img = cv2.flip(img, 1) # 将手水平翻转

hands, img = detector.findHands(img, flipType=False) # 左手是左手,右手是右手,映射正确

if hands:

lmList = hands[0][lmList] # hands是由N个字典组成的列表

pointIndex = lmList[8][0:2] # 只要食指指尖的x和y坐标

img = game.update(img, pointIndex)

cv2.imshow("Image", img)

key = cv2.waitKey(1)

if key == ord(r):

        game.gameOver = False