手势识别控制pygame精灵

步骤:

  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

  

原文地址:https://www.cnblogs.com/aarond/p/gesture.html

时间: 2024-10-10 23:34:19

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

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

不得不承认<Python游戏编程入门>这本书翻译.排版非常之烂,但是里面的demo还是很好的,之前做了些改编放到这里. 先是素材: 背景 精灵 所有素材均取自此书 接下来就是精灵类的创建了: class MySprite(pygame.sprite.Sprite): def __init__(self, target): pygame.sprite.Sprite.__init__(self) self.master_image = None self.frame = 0 self.old_fr

Pygame - Python游戏编程入门(2)

前言 前几天我们做出了一个可控制的飞机,今天我们来做一些小改进,这是代码的一些小改进,却是我们小游戏的一大改进啊~(╯°口°)╯(┴—┴ 然后再引进另外一个主题,pygame.sprite,精灵模块,那它究竟又有什么用呢? 正片开始~ 1. 对主循环的优化 记得我们的上一个版本吗?我们在主循环中不断地绘制背景和飞机,这样的做法其实很消耗cpu资源的,但在这种现象在我们的demo中并不明显,这是为什么呢?我想主要原因应该是我们使用了update()函数(部分刷新,surface与surface之间

NGUI研究之Sprite精灵与精灵动画的使用

 学习了几天Unity3D强大的NGUI插件,觉得NGUI中最大的亮点之一就是Sprite精灵.我们先说说精灵是什么东西?它可以在一张大图中去截取一部分(大图就是整体图像集合,而截取的小图就是一个精灵),然后起一个精灵的名称,使用时通过精灵的名称就能直接绘制,并且精灵还可以播放动画.总之真的非常强大.本节我们学习如何创建自己的精灵文件.基本使用不了解的看NGUI研究之开始学习制作第一个例子 首先我们在Project中创建一个Prefab对象,默认给它Transform变换属性,暂时我们给Pr

pygame中模块说明

参考博客:https://blog.csdn.net/qq_27717921/article/details/53231762 pygame模块概览 1.display模块 功能:生成windows窗口 pygame.display.set_mode(resolution=(0,0),flags=0,depth=0) 返回一个特定大小和属性的surface对象,resolution可以控制生成windows窗口的大小,flags代表的是扩展选项,depath不推荐设置 flags标志位控制你想要

Python基础班每日整理(七)

04_项目实战 pygame的初始化和退出pygame.init() pygame.quit() exit() pygame.Rect(x,y,width,height) 描述矩形的类包含以下属性(x.y.size.width.height.left.right.top.bottom.center.centerx.centery) screen = pygame.display.set_mode(resolution=(0,0), flags=0, depth=0) 初始化游戏显示窗口返回的是一

10分钟教你用Python做个打飞机小游戏超详细教程

更多精彩尽在微信公众号[程序猿声] 我知道你们一定想先看效果如何 00 目录 整体框架 开始之前-精灵类Sprite 子弹类class Bullet 玩家飞机类class Player 敌机类class Enemy 游戏主体循环以及帧率设置 让子弹飞 刷出敌机 打怪 把飞机敌机子弹都画出来 处理键盘事件 分数显示 和 GameOver 最终代码 01 前言 这次还是用python的pygame库来做的游戏.关于这个库的内容,读者可以上网了解一下.本文只讲解用到的知识.代码参考自网上,自己也做了一

Cocos2D-x特点

Cocos2D-x继承了Cocos2D的全部特点,包括如下内容: ?流程控制:非常容易地管理不同场景之间的流程控制. ?精灵:快速而方便的精灵. ?动作:可组合精灵的动作(如移动.旋转和缩放等),使精灵动起来. ?特效:包括波浪.旋转和透镜等特性. ?平面地图:支持平面地图和45度角地图. ?转换:从一个场景移动到另外一个不同的场景. ?菜单:创建内部菜单,包括主菜单和游戏菜单. ?文本渲染:支持文本渲染标签. ?文档:包括编程指南.API参考.视频教学和很多简单的测试例子. ?BSD许可:BS

2、Cocos2dx 3.0游戏开发找小三之引擎简介

尊重开发者的劳动成果,转载的时候请务必注明出处:http://blog.csdn.net/haomengzhu/article/details/27094663 引擎简介 Cocos2d-x 的原型是 Cocos2d,一个最早来源于几位 Python 开发者在 PyWeek 竞赛中的作品, 目的是封装底层绘图代码,简 化 2D 游戏的开发过程,避免每次都"重新发明轮子". 有了 Cocos2d,开发者就可以把全部精力集中在游戏开发上,而不必关心绘图的细节. 这个 Python 版本的引

【cocos2dx 3.2】瓦片地图制作

使用Tiled编辑地图 每一个图层只能放一种瓦片 瓦片的大小最好是32*32的倍数 对象层里面设置路径的坐标 主程序中获取对象层中的坐标,做相应的操作 设置口袋精灵类: Monster.h #include "cocos2d.h" USING_NS_CC; class Monster : public Sprite { public: virtual bool init(Vector<Node*> points); static Monster* create(Vector