步骤:

  1. 编写简易pygame精灵游戏(只实现键盘上下左右控制)
  2. 解决opencv手势识别核心问题
  3. 上述2部分对接上

pygame部分我们只加载个背景,然后里面放1只乌龟精灵,用键盘的上下左右键来控制,直接给出代码:

乌龟精灵代码(DemoSpirit.py):

import pygame

class DemoSpirit(pygame.sprite.Sprite):
def __init__(self, target, screen_size, position):
pygame.sprite.Sprite.__init__(self)
self.target_surface = target
self.screen_size = screen_size
self.position = position
self.image = pygame.image.load("resources\\wugui.png").convert_alpha()
self.image = pygame.transform.smoothscale(self.image, (50, 50)) def draw(self):
# random_text = font_200.render('***', True, white_color)
self.target_surface.blit(self.image, self.position) def move_left(self):
if self.position[0]-10 > 0:
self.position=(self.position[0]-10, self.position[1]) def move_right(self):
if self.position[0]+10 < self.screen_size[0]:
self.position=(self.position[0]+10, self.position[1]) def move_up(self):
if self.position[1] - 10 > 0:
self.position=(self.position[0], self.position[1]-10) def move_down(self):
if self.position[1] + 10 < self.screen_size[1]:
self.position=(self.position[0], self.position[1]+10)

  

游戏主循环代码(game-main.py):

import pygame
from pygame.locals import * background_image_filename = 'resources/back.jpg' pygame.init() # 2、初始化init() 及设置
screen_list = pygame.display.list_modes()
screen_size = screen_list[16]
screen = pygame.display.set_mode(screen_size)
background = pygame.image.load(background_image_filename).convert()
background = pygame.transform.scale(background, screen_size) clock = pygame.time.Clock() pos = (screen_size[0] * 0.6, screen_size[1] * 0.3) from cvgame.DemoSpirit import DemoSpirit s1 = DemoSpirit(screen, screen_size, pos) # 开始游戏循环
while True:
for event in pygame.event.get():
if event.type == KEYDOWN:
if event.key == K_UP:
s1.move_up()
if event.key == K_DOWN:
s1.move_down()
if event.key == K_LEFT:
s1.move_left()
if event.key == K_RIGHT:
s1.move_right()
elif event.key == K_q:
exit()
screen.blit(background, (0, 0))
s1.draw() pygame.display.update() # 6、update 更新屏幕显示
clock.tick(100)  

效果图:

接下来,进入手势识别领域

我们是做了个小技巧来侧面绕过手跟踪问题,如下图(直接指定了左手右手监控区域,这样就不需要动态跟踪手的rect了):

    while success:
success, img = cap.read()
frame = imutils.resize(img, width=700) cv2.rectangle(frame, (50, 0), (264, 250), (170, 170, 0)) #左手区域
cv2.rectangle(frame, (426, 0), (640, 250), (170, 170, 0)) #右手区域 cv2.imshow("Frame_Original", frame) #显示 rightHand = frame[0:250, 50:264]
leftHand = frame[0:210, 426:640] left_hand_event = grdetect(leftHand, fgbg_left, verbose=True) #检测左手手势识别事件
right_hand_event = grdetect(rightHand, fgbg_right, verbose=True) #检测右手手势识别事件
print('left hand: ', left_hand_event, 'right hand: ', right_hand_event) #打印出来检测结果

  

主要看看grdetect和fgbg_left/fgbg_right:

看上图,除了手之外,还有个大背景,首先得把背景去掉,才能识别出前景色-手,fgbg_left/fgbg_right其实就是用来干着活的,分别为左手、右手的背景减噪用的

fgbg_left = cv2.createBackgroundSubtractorMOG2()
fgbg_right = cv2.createBackgroundSubtractorMOG2() def train_bg(fgbg, roi):
fgbg.apply(roi) def start():
global fgbg_left
global fgbg_right
trainingBackgroundCount = 200 #200次来训练背景减噪训练
while trainingBackgroundCount>0:
success, img = cap.read()
frame = imutils.resize(img, width=700)
cv2.imshow("Frame_Original", frame)
rightHand = frame[0:250, 50:264]
leftHand = frame[0:210, 426:640] train_bg(fgbg_left, leftHand) #训练左手区域
train_bg(fgbg_right, rightHand) #训练右手区域 key = cv2.waitKey(1) & 0xFF
trainingBackgroundCount -= 1  

再来看看核心函数

def grdetect(array, fgbg, verbose=False):
event = {'type': 'none'}
copy = array.copy()
array = _remove_background(array, fgbg) #移除背景,会用到背景减噪(上述提到)
thresh = _bodyskin_detetc(array) #高斯+二值化
contours = _get_contours(thresh.copy()) #计算图像的轮廓点,可能会返回多个轮廓
largecont = max(contours, key=lambda contour: cv2.contourArea(contour)) #选择面积最大的轮廓
hull = cv2.convexHull(largecont, returnPoints=False) #根据轮廓点计算凸点
defects = cv2.convexityDefects(largecont, hull) #计算轮廓的凹点(凸缺陷)
if defects is not None:
# 利用凹陷点坐标, 根据余弦定理计算图像中锐角个数
copy, ndefects = _get_defects_count(copy, largecont, defects, verbose=verbose)
# 根据锐角个数判断手势, 会有一定的误差
if ndefects == 0:
event['type'] = '0'
elif ndefects == 1:
event['type'] = '2'
elif ndefects == 2:
event['type'] = '3'
elif ndefects == 3:
event['type'] = '4'
elif ndefects == 4:
event['type'] = '5'
return event

  

剩下的就是上述的支持函数了

def _remove_background(frame, fgbg):
fgmask = fgbg.apply(frame, learningRate=0) #learningRate=0代表不更新背景噪声,也就是不学习
kernel = np.ones((3, 3), np.uint8)
fgmask = cv2.erode(fgmask, kernel, iterations=1)
res = cv2.bitwise_and(frame, frame, mask=fgmask)
return res def _bodyskin_detetc(frame):
# 肤色检测: YCrCb之Cr分量 + OTSU二值化
ycrcb = cv2.cvtColor(frame, cv2.COLOR_BGR2YCrCb) # 分解为YUV图像,得到CR分量
(_, cr, _) = cv2.split(ycrcb)
cr1 = cv2.GaussianBlur(cr, (5, 5), 0) # 高斯滤波
_, skin = cv2.threshold(cr1, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) # OTSU图像二值化
return skin # 检测图像中的凸点(手指)个数
def _get_contours(array):
# 利用findContours检测图像中的轮廓, 其中返回值contours包含了图像中所有轮廓的坐标点
contours, _ = cv2.findContours(array, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
return contours _COLOR_RED = (0, 0, 255) def _get_eucledian_distance(beg, end): # 计算两点之间的坐标
i = str(beg).split(',')
j = i[0].split('(')
x1 = int(j[1])
k = i[1].split(')')
y1 = int(k[0])
i = str(end).split(',')
j = i[0].split('(')
x2 = int(j[1])
k = i[1].split(')')
y2 = int(k[0])
d = math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2))
return d # 根据图像中凹凸点中的 (开始点, 结束点, 远点)的坐标, 利用余弦定理计算两根手指之间的夹角, 其必为锐角, 根据锐角的个数判别手势.
def _get_defects_count(array, contour, defects, verbose=False):
ndefects = 0
for i in range(defects.shape[0]):
s, e, f, _ = defects[i, 0]
beg = tuple(contour[s][0])
end = tuple(contour[e][0])
far = tuple(contour[f][0])
a = _get_eucledian_distance(beg, end)
b = _get_eucledian_distance(beg, far)
c = _get_eucledian_distance(end, far)
angle = math.acos((b ** 2 + c ** 2 - a ** 2) / (2 * b * c)) # * 57
if angle <= math.pi / 2: # 90:
ndefects = ndefects + 1
if verbose:
cv2.circle(array, far, 3, _COLOR_RED, -1)
if verbose:
cv2.line(array, beg, end, _COLOR_RED, 1)
return array, ndefects

  

手势识别控制pygame精灵的更多相关文章

  1. pygame 精灵的行走及二段跳实现方法

    不得不承认<Python游戏编程入门>这本书翻译.排版非常之烂,但是里面的demo还是很好的,之前做了些改编放到这里. 先是素材: 背景 精灵 所有素材均取自此书 接下来就是精灵类的创建了 ...

  2. 18 11 05 继续补齐对python中的class不熟悉的地方 和 pygame 精灵

    ---恢复内容开始--- class game : #历史最高分----- 是定义类的属性 top_score =0 def __init__(self, player_name) : #是定义的实例 ...

  3. pygame精灵类实现房子爆炸效果

    # coding=utf8 import random import pygame from pygame.locals import * from cStringIO import StringIO ...

  4. Pygame 入门基本指南

    最近正在利用 Python 制作一个小游戏,但对于 Pygame 不熟悉,故在学习的过程记录相关知识点 Pygame 中文文档下载:Here Pygame第1-1课:入门 什么是Pygame? Pyg ...

  5. [unity3d插件]2dtoolkit系列一 创建精灵

    从今天开始要做一个2d游戏,由于之前都是做cocos2dx的,然后接触了一段时间的unity3d,都是做3D方面的东西,得知要做2d游戏还是有点开心的,或许因为不想丢失之前的2d游戏的一些思想,然后接 ...

  6. (二 -3-1) 天猫精灵接入Home Assistant-自动发现Mqtt设备--灯系列 esp8266程序

    设备1 上电自动注册自己是个1个开关 HASS网页和手机APP控制 外部开关上升沿中断控制 天猫精灵语音控制 一键配网 记录以往WIFI信息 设备2 上电后,自动注册自己有三个开关控制 HASS网页和 ...

  7. pygame中模块说明

    参考博客:https://blog.csdn.net/qq_27717921/article/details/53231762 pygame模块概览 1.display模块 功能:生成windows窗 ...

  8. pygame(class类)调用视图的方法

    以下将介绍pygame精灵动画的基础知识,希望对大家有帮助:1.在此,精灵类必须继承pygame.sprite.Sprite并初始化pygame.sprite.Sprite.__init__(self ...

  9. 初学pygame

    #Author:cljimport pygamepygame.display.set_mode((640,480),0,32)#设置窗口大小 返回的也是一个surface对象,resolution可以 ...

随机推荐

  1. Jquery Validate表单验证,动态添加和删除验证规则

    最近一直在忙着维护Jquery的商城,用到了Validate的表单验证,觉得很有意思,就纪录一下. // 动态添加验证规则 $("#invoice_send_region_id") ...

  2. Weblogic/WAS之Full GC监控与计算

    在网上看到关于内存回收机制,同大家一起分析探讨.堆内存划分为Eden.Survivor 和 Tenured/Old 空间,如下图所示: Minor GC 会清理年轻代的内存,Major GC 是清理老 ...

  3. LuoguP1402 酒店之王

    LuoguP1402 酒店之王 最大流题目.带有一定的思维技(tao)巧(lu) 依旧分析题目.如果只有房间或者菜一种限制.那么就是一道裸的最大流了 可是两种条件都应当满足, 这貌似也可以做. 因为每 ...

  4. koa2入门--01.ES6简单复习、koa2安装以及例子

    1.ES6简单复习 /*let 和 const: let用于定义一个块作用域的变量,const 定义一个常量 */ let a = 'test'; const b = 2; /*对象的属性和方法的简写 ...

  5. 牛客多校第四场sequence C (线段树+单调栈)

    牛客多校第四场sequence C (线段树+单调栈) 传送门:https://ac.nowcoder.com/acm/contest/884/C 题意: 求一个$\max {1 \leq l \le ...

  6. 解决netcore在docker容器中连接oracle报错(timezone region not found)

    错误提示: timezone region not found错误原因:docker 容器内时区不是 CST 导致解决办法:1.在dockerfile 中增加一下命令ENV TZ=Asia/Shang ...

  7. .net core 读取Excal文件数据及注意事项

    添加ExcelDataReader.DataSet引用. 调用下列方法: public class XlsHelper { public static System.Data.DataSet GetX ...

  8. TCP/IP||UDP广播和多播

    1.概述 广播和多播应用于UDP,TCP是一个面向连接协议,意味着分别运行与两个主机内的两进程间存在一个连接,在考虑多个主机内的共享通信网络,每个以太网帧包含源主机和目的主机以太网地址(48bit), ...

  9. 【题解】BZOJ4883: [Lydsy1705月赛]棋盘上的守卫(最小生成基环森林)

    [题解]BZOJ4883: [Lydsy1705月赛]棋盘上的守卫(最小生成基环森林) 神题 我的想法是,每行每列都要有匹配且一个点只能匹配一个,于是就把格点和每行每列建点出来做一个最小生成树,但是不 ...

  10. $bzoj2560$ 串珠子 容斥+$dp$

    正解:容斥+$dp$ 解题报告: 传送门$QwQ$ $umm$虽然题目蛮简练的了但还是有点难理解,,,我再抽象一点儿,就说有$n$个点,点$i$和点$j$之间有$a_{i,j}$条无向边可以连,问有多 ...