python单人壁球游戏,python画立体球
本文主要和大家分享如何使用mediapipe opencv制作桌上曲棍球的互动游戏。文章中的样例代码解释的很详细,感兴趣的朋友可以看看。
00-1010简介1。文件配置1.1导入工具包1.2素材图片准备2。手关键点检测,材料导入2.1方法介绍2.2代码显示3。关键点处理,球拍移动3.1方法介绍3.2代码显示4。球拍击球和比赛完美性4.1方法介绍4.2代码显示
目录
大家好,今天我想和大家分享一下如何使用mediapipe opencv制作桌上曲棍球的互动游戏。先放个图看看效果。
规则如下:左手控制白色球拍;右手控制紫色球拍;球拍只能上下移动;红色是冰球;球碰到上下两边的蓝色边框,两边的球拍会反弹;如果球进入黄色区域,游戏结束;下面的粉色计数板记录了左右两边打了多少枪。
介绍
1. 文件配置
PIP opencv _ Python==4.2.0.34 #安装OpenCV
Pip媒体管道#安装媒体管道
# pipi install media pipe-user #如果用户报告错误,请尝试此操作。
Pip cvzone #安装cv区域
#导入工具包
导入cv2
导入cvzone
fromzone。手跟踪模式导入手检测器#被导入到手检测模块
21个手部关键点的坐标如下:
1.1 导入工具包
开始之前,准备好桌子、球和球拍的图片。我用PPT画的图。球和球拍的图片必须保存在。png格式。放在同一个文件夹里阅读。
1.2 素材图片准备
2. 手部关键点检测、素材导入
cvzone。手动跟踪模式。手检测器(handdetector)手关键点检测方法
参数:
模式:默认值为False,将输入图像视为视频流。它将尝试在第一个输入图像中检测手,并在成功检测后进一步定位手的坐标。在随后的图像中,一旦检测到所有maxHands手并且定位到相应手的坐标,它将跟踪这些坐标,而不调用另一个检测,直到它失去对任何手的跟踪。这减少了延迟,非常适合处理视频帧。如果设定为True,则对每个输入图像运行手部检测,以处理一批静态且可能不相关的图像。
MaxHands:最多检查几手,默认值为2。
DetectionCon:手部检测模型的最小置信度值(0到1之间)。如果超过阈值,则检测成功。默认值为0.5。
MinTrackingCon:坐标跟踪模型的最小置信度值(0到1之间),用于将手部坐标视为跟踪成功,如果不成功,则在下一幅输入图像上自动调用手部检测。将其设置为更高的值可以提高解决方案的健壮性,但代价是更高的延迟。如果mode为True,此参数将被忽略,手检测将在每个图像上运行。默认值为0.5。
其参数和返回值类似于官方函数media pipe . solutions . hands . hands()
MULTI_HAND_LANDMARKS:检测/跟踪的手的集合,其中每个手被表示为21个手标志的列表,每个手标志由X、Y和z组成
多手性:检测/跟踪的手是左手还是右手的集合。每手牌由一个标签和一个分数组成。标签是“左”吗
9; 或 'Right' 值的字符串。 score 是预测左右手的估计概率。
(2)cvzone.HandTrackingModule.HandDetector.findHands()
找到手部关键点并绘图
参数:
img: 需要检测关键点的帧图像,格式为BGR
draw: 是否需要在原图像上绘制关键点及识别框
flipType: 图像是否需要翻转,当视频图像和我们自己不是镜像关系时,设为True就可以了
返回值:
hands: 检测到的手部信息,由0或1或2个字典组成的列表。如果检测到两只手就是由两个字典组成的列表。字典中包含:21个关键点坐标(x,y,z),检测框左上坐标及其宽高,检测框中心点坐标,检测出是哪一只手。
img: 返回绘制了关键点及连线后的图像
(3)cv2.addWeighted()
图像融合
将两张图像按一定比例融合在一起,需要两张图像的size和通道数相同
两张图像按一定比例融合: cv2.addWeighted(图像1, 权重1, 图像2, 权重2, 亮度偏置)
相当于 y = a x1 + b x2 + c,其中 a、b 代表权重,c 代表亮度上提亮多少
2.2 代码展示
首先 cv2.imread() 中的参数 cv2.IMREAD_UNCHANGED 是指用图片的原来格式打开,包含Alpha通道。即以不改变图片的方式打开,图片是彩色那么读进来就是彩色,图片是灰度图那么读进来就是灰度图,读进来的图片的shape如下:
该部分代码主要负责手部关键点检测,融合背景图像和视频帧图像
import cv2import 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. 关键点处理、球拍移动
3.1 方法介绍
这部分主要完成两项工作,第一是左右手分别控制左侧和右侧的球拍,第二个是球以一定的速度移动。
(1)控制球拍
hand['bbox'] 中包含了手部检测框的左上角坐标和检测框的宽高,使用手掌中心点的 y 坐标来控制球拍的上下移动。由于两个球拍的shape是相同的,因此只要获取一个球拍的高度 h1 即可。使用掌心中点 y 坐标控制球拍中点的 y1 坐标,公式为:y1 = (y + h) // 2 - h1 // 2
接着使用 cvzone.overlayPNG() 就可以将球拍图片覆盖在原图片的指定区域,其中坐标参数是指覆盖区域的左上角坐标。固定横坐标,只上下移动。
(2)球移动
首先要规定球的移动速度 speedx, speedy = 10, 10 代表球每一帧沿x轴正方向移动10个像素,沿y轴正方向移动10个像素,那么球的初始合速度方向是沿图片的正右下角移动
如果球碰撞到了球桌的上下边框,就反弹。speedy = -speedy。代表x方向每帧移动的步长不变,y方向每帧移动的方向反转,即入射角等于出射角。
3.2 代码展示
在上述代码中补充
import cv2import 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]
# 获取球拍的宽高
h1, w1 = imgBlock1.shape[0:2]
# 球拍的中心y坐标,随着掌心移动
y1 = (y + h) // 2 - h1 // 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(img, img)
# 每帧滞留1ms后消失
k = cv2.waitKey(1)
# ESC键退出程序
if k & 0XFF==27:
break
# 释放视频资源
cap.release()
cv2.destroyAllWindows()
效果图如下:
4. 球拍击球、游戏完善
4.1 方法介绍
这一部分主要完成三项工作,第一是球拍击打到球,球需要反弹;第二是如果球进入黄色区域,游戏结束;第三是左右侧击球得分计数器。
(1)球拍击球
看到代码中的第(5)步,ballpos 代表球的左上角坐标(x,y),100 < ballpos[0] < 100+w1 代表球到了球拍横坐标区域范围内部了,y1 < ballpos[1] < y1+h1 代表球的y坐标在球拍y坐标内部,这时表明击球成功,speedx = -speedx 只改变沿x轴的速度方向,不改变沿y轴的速度方向。
(2)球进黄区,游戏结束
if ballpos[0] < 50 or ballpos[0] > 1150,如果球图片的左上坐标的 x 坐标,在黄区边缘,整个程序退出。当然也可以做一个游戏结束界面,我之前的博文里也有介绍,我偷个懒不写了。
(3)计数器
首先定义个变量初始化记录左右侧的击球次数 score = [0, 0],如果有一侧的球拍击中球,那么对应该侧计数加一。
4.2 代码展示
上面代码是掌心控制球拍,这里改成食指指尖控制球拍中点移动。
import cv2import 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.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
# 记录是否游戏结束
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[lmList][8]
# 获取球拍的宽高
h1, w1 = imgBlock1.shape[0:2]
# 球拍的中心y坐标,随着掌心移动
y1 = y - h1 // 2
# 如果检测到了左手
if hand[type] == Left:
# 左侧的球拍x轴固定,y坐标随左手掌间中点移动
img = cvzone.overlayPNG(img, imgBlock1, (100,y1))
# 检查球是否被左球拍击中, 球的xy坐标是否在球拍xy坐标附近
if 100 < ballpos[0] < 100+w1 and y1 < ballpos[1] < y1+h1:
# 满足条件代表球拍击中了,改变球的移动方向
speedx = -speedx # x方向设为反方向
# 得分加一
score[0] += 1
# 如果检测到了右手
if hand[type] == Right:
# 右侧的球拍x轴固定,y坐标随右手掌间中点移动
img = cvzone.overlayPNG(img, imgBlock2, (1150,y1))
# 检查球是否被右球拍击中
if 1050 < ballpos[0] < 1050+w1 and y1 < ballpos[1] < y1+h1:
# 满足条件代表球拍击中了,改变球的移动方向
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, fLeft:{score[0]} and Right:{score[1]}, (400,710))
#(10)图像展示
cv2.imshow(img, img)
# 每帧滞留1ms后消失
k = cv2.waitKey(1)
# ESC键退出程序
if k & 0XFF==27:
break
# 释放视频资源
cap.release()
cv2.destroyAllWindows()
效果图如下:
到此这篇关于基于Python自制视觉桌上冰球小游戏的文章就介绍到这了,更多相关Python桌上冰球游戏内容请搜索盛行IT软件开发工作室以前的文章或继续浏览下面的相关文章希望大家以后多多支持盛行IT软件开发工作室!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。