PyOpenNI + PyGame

Luego de haber instalado todas las bibliotecas correspondientes para nuestra Kinect .. muchos tutoriales ya hay dando vueltas [1] [2][3], Nos encontramos con PyOpenNI y PyGame para demostrar lo sencillo que es interactuar con sensores 3D en menos de 30′

Veremos rápidamente el código por bloques …
Primero que nada tenemos las clases básicas de nuestro juego, la clase Ball, que será el elemento de nuestro simple juego, BallManager que será la encargada de actualizar e instanciar todas las pelotitas deseadas, una clase para la Kinect y otra para el manejo del juego en general (Clase Game).

La clase Ball, se encarga de darle imagen, posición, velocidad y ángulo a nuestra pelota, dandole funciones como move y bounce para calcular hacia donde y cómo debe moverse.

class Ball(pygame.sprite.Sprite):
    """Ball Class for set image, speed and angle """
    def __init__(self, xy, speed, angle):
        pygame.sprite.Sprite.__init__(self)
        self.img_load('images/sphere.png')
        self.rect.centerx, self.rect.centery = xy
        self.speed =  speed
        self.angle = angle
        
    def move(self):
        self.rect.centerx += np.sin(self.angle) * self.speed
        self.rect.centery -= np.cos(self.angle) * self.speed
        
    def bounce(self):
        if self.rect.centerx > SCREEN_WIDTH - self.rect.width:
            self.rect.centerx = 2*(SCREEN_WIDTH - self.rect.width) - self.rect.centerx
            self.angle = -self.angle*2
        elif self.rect.centerx < self.rect.width:
            self.rect.centerx = 2*self.rect.width - self.rect.centerx
            self.angle = -self.angle*2
        if self.rect.centery > SCREEN_HEIGHT - self.rect.height:
            self.rect.centery = 2*(SCREEN_HEIGHT - self.rect.height) - self.rect.centery
            self.angle = np.pi - self.angle*2    
        elif self.rect.centery < self.rect.height:
            self.rect.centery = 2*self.rect.width - self.rect.centery
            self.angle = np.pi - self.angle*2
        
    def img_load(self, filename):
        self.image = pygame.image.load(filename)
        self.image = pygame.transform.scale(self.image, (60,60))  
        self.rect = self.image.get_rect()
        

La clase ballManager se encarga de instanciar y actualizar la posición de todas las pelotas que tenemos en el juego ..

class BallManager(object):
    """ Create and update state of balls"""
    def __init__(self, numballs = 30, balls = []):      
        self.blist = balls
        self.multiple_balls(numballs)

    def update(self):
        for ball in self.blist:
            ball.move()
            ball.bounce()

    def add_ball(self, xy, speed, angle):
        self.blist.append(Ball(xy, speed, angle))

    def multiple_balls(self, numballs):
        for i in range(numballs):
            self.add_ball((np.random.randint(0, SCREEN_WIDTH),
                          np.random.randint(0, SCREEN_HEIGHT)),
                          np.random.randint(4, 20),
                          np.random.uniform(0, np.pi*2))

En la clase Kinect tenemos la creación de contextos y generadores para indicar cuales son los datos que queremos tomar de nuestro sensor, gracias a openni ya contamos con generadores de gestos, por lo que podemos avisar de antemano cual es el gesto que esperamos reconocer (en nuestro caso un saludo agitando la mano).

class Kinect(object):
    """Manage context and generator of the kinect"""
    def __init__(self, game):

        self.context = openni.Context()
        self.context.init()
        self.depth_generator = openni.DepthGenerator()
        self.depth_generator.create(self.context)
        self.depth_generator.set_resolution_preset(openni.RES_VGA)
        self.depth_generator.fps = 30

        self.image_generator = openni.ImageGenerator()
        self.image_generator.create(self.context)
        self.image_generator.set_resolution_preset(openni.RES_VGA)

        self.gesture_generator = openni.GestureGenerator()
        self.gesture_generator.create(self.context)
        self.gesture_generator.add_gesture('Wave')

        self.hands_generator = openni.HandsGenerator()
        self.hands_generator.create(self.context)

        self.gesture_generator.register_gesture_cb(self.gesture_detected, self.gesture_progress)
        self.hands_generator.register_hand_cb(self.create, self.update, self.destroy)

        self.game = game

    def gesture_detected(self, src, gesture, id, end_point):
        print "Detected gesture:", gesture
        self.hands_generator.start_tracking(end_point)

    def gesture_progress(self, src, gesture, point, progress):
        pass

    def destroy(self, src, id, time):
        pass

    def create(self, src, id, pos, time):
        pass

    def update(self, src, id, pos, time):
        if pos != None:
            for ball in self.game.ball_manager.blist:
                new_rect = pygame.Rect(int(ball.rect.x), int(ball.rect.y), 60, 60)
                kinect_rect = pygame.Rect(self.game.size[0]/2 - int(pos[0]), self.game.size[1]/2 - int(pos[1]), 40, 40)
                print "kinect %s and ball %s" %(kinect_rect.center, new_rect.center)
                if new_rect.colliderect(kinect_rect):
                    print "wiii %d" %(len(self.game.ball_manager.blist))
                    ball.kill()
            pygame.draw.circle(self.game.display_surf, (0, 128, 255), (self.game.size[0]/2 - int(pos[0]), self.game.size[1]/2 - int(pos[1])), 10)

    def capture_rgb(self):
        rgb_frame = np.fromstring(self.image_generator.get_raw_image_map_bgr(), dtype=np.uint8).reshape(SCREEN_HEIGHT, SCREEN_WIDTH, 3)
        image = cv.fromarray(rgb_frame)
        cv.Flip(image, None, 1)
        cv.CvtColor(cv.fromarray(rgb_frame), image, cv.CV_BGR2RGB)
        self.game.frame = pygame.image.frombuffer(image.tostring(), cv.GetSize(image), 'RGB')

Vale decir que no es nada limpio en cuestiones objetosas que el update de colisiones se haga dentro de la clase Kinect, pero recuerden que la idea es mostrar que en 20 min de codeo sale andando un jueguito básico.

Y como último nuestra clase Game, que se encarga de las acciones básicas de un main de juego, el inicio, renderizado, el loop general .. etc.

class Game(object):
    """Define screen, sprites and states of the game"""
    def __init__(self):
        self.timer = pygame.time.Clock()
        self.sprites = pygame.sprite.RenderUpdates()
        self._running = True
        self.display_surf = None
        self.background = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT))
        self.background.fill((0,0,0))
        self.size = (SCREEN_WIDTH, SCREEN_HEIGHT)
        self.frame = None
        self.my_kinect = Kinect(self)
        self.ball_manager = BallManager(30)
        for ball in self.ball_manager.blist:
            self.sprites.add(ball)

    def on_init(self):
        pygame.init()
        self.display_surf = pygame.display.set_mode(self.size, pygame.HWSURFACE | pygame.DOUBLEBUF)
        self.display_surf.blit(self.background, (0,0))
        self._running = True
        self.my_kinect.context.start_generating_all()

    def on_event(self, event):
        if event.type == pygame.QUIT:
            self._running = False

    def on_loop(self):        
        self.my_kinect.context.wait_any_update_all()
        self.my_kinect.capture_rgb()
        self.ball_manager.update()

    def on_render(self):
        self.sprites.clear(self.display_surf, self.background)
        self.display_surf.blit(self.frame, (0,0))
        for sprite in self.sprites:
                sprite.update()
        dirty = self.sprites.draw(self.display_surf)
        pygame.display.update(dirty)
        pygame.display.flip()

    def on_cleanup(self):
        pygame.quit()
 
    def on_execute(self):
        self.on_init()
        while( self._running ):
            for event in pygame.event.get():
                self.on_event(event)
            self.on_loop()
            self.on_render()
        self.on_cleanup()

El código completo se puede encontrar en https://github.com/celiacintas/KinectStuff/blob/master/game_colegios.py

Y un video super acelerado … porque digamos que como juego no es el más entretenido

Anuncios

Mapa Logístico con PyLab – NumPy – PyOpenCL

En este caso vamos a meternos en PyOpenCL con la excusa de los mapas logísticos .

ACA tienen el código fuente de un excelente tutorial, en el cual me base.

El mapa logístico puede expresarse matematicamente como:

X_{n+1} = \alpha X_n (1-X_n)

Habiendo dicho esto veremos el código para nuestro kernel (.cl):

/*kernel for implementing the Verhulst model.
Fractal and Chaos - Chapter 5 - Iterative Feedback Processes and Chaos. */

__kernel void verhulst(global float* alpha, global float* xd, global float* output)
{
    float temp;
    int idx = get_local_id(0)+get_local_size(0)*get_group_id(0);
    temp = xd[0];
    int i;
    for(i=0;i<=idx;i++)
    {
        temp = alpha[0]*temp*(1-temp);
    }
    output[idx] = temp;
}

Y nuestro programa encargado de crear el contexto, crear buffers, etc.

# http://documen.tician.de/pyopencl/
# Ian Johnson.
#
#
# Celita changes buffers, data and parameters
# for implementation of the logistic map
#

import pyopencl as cl
import numpy
import pylab
import argparse

class CL:
    def __init__(self):
        self.ctx = cl.create_some_context()
        self.queue = cl.CommandQueue(self.ctx)

    def loadProgram(self, filename):
        #read in the OpenCL source file as a string
        f = open(filename, 'r')
        fstr = "".join(f.readlines())
        print fstr
        #create the program
        self.program = cl.Program(self.ctx, fstr).build()

    def logisticMap(self, alpha=0.9, xd=0.2):
        mf = cl.mem_flags

        #initialize client side (CPU) arrays
        self.alpha = numpy.array([alpha], dtype=numpy.float32)
        self.xd = numpy.array([xd], dtype=numpy.float32)
        self.output = numpy.zeros(50, numpy.float32)

        #create OpenCL buffers
        self.alpha_buf = cl.Buffer(self.ctx, mf.READ_ONLY | mf.COPY_HOST_PTR,
                                   hostbuf=self.alpha)
        self.xd_buf = cl.Buffer(self.ctx, mf.READ_ONLY | mf.COPY_HOST_PTR,
                                hostbuf=self.xd)
        self.dest_buf = cl.Buffer(self.ctx, mf.WRITE_ONLY, self.output.nbytes)

    def execute(self):
        self.program.verhulst(self.queue, self.alpha.shape, None,
                              self.alpha_buf, self.xd_buf, self.dest_buf)
        cl.enqueue_copy(self.queue, self.output, self.dest_buf)
        print "alpha: ", self.alpha
        print "xn: ", self.xd
        print "results: ", self.output

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description=
                                     'Logistic Map with control parameter')
    parser.add_argument("--alpha", dest="alpha", default=0.9,
                        help='This option is used to set the alpha')
    args = parser.parse_args()
    alphaIn = args.alpha

    myLogisticMap = CL()
    myLogisticMap.loadProgram("verhulst.cl")
    myLogisticMap.logisticMap(alpha=alphaIn)
    myLogisticMap.execute()

    pylab.plot(myLogisticMap.output, 'ro-')
    pylab.ylim(0, 1)
    pylab.text(40, 0.2, r'$\alpha = %f$' % (myLogisticMap.alpha))
    pylab.title("Successive Iterations of the logistic Map")
    pylab.show()

Como se ve en el codigo se puede pasar el \alpha por parametros o dejar que se tome el por defecto …
Y como resultado veríamos algo asi …

Para mayor seguridad de código actualizado verlo ACA

🙂

The Hitchhiker Pythonits’s Guide to the Galaxy

Esta vez haremos el descabellado intento de plotear una galaxia espiral morfológicamente mas o menos correcta, para esto haremos uso/abuso de Python seguido de sus secuaces Scipy y Mayavi. Utilizando hasta el cansancio funciones lambda, mapeos y filtros entre otros yuyos.

Para seguir adelante tenemos que refrescar que es un Automata Celular y para que servían las Coordenadas Polares .

El código se puede ver ACA al ser un poco mas extenso que los anteriores dejo el link a github.

Y se vería algo asi …

El pase de diapositivas requiere JavaScript.

Conjunto de Cantor con Python/Mayavi

Reutilizando el código anterior con mínimas modificaciones tenemos como resultado el Conjunto de Cantor (http://es.wikipedia.org/wiki/Conjunto_de_Cantor).

Solo colocaré la parte modifcada del código anterior, no se olviden de importar mayavi!


def generar(self, nivel, cursor, distancia):
    if nivel == 0:
       a = (cursor.x, cursor.x, cursor.x)
       b = (cursor.x + distancia*np.cos(cursor.ang),
            cursor.y + distancia*np.sin(cursor.ang))
       mlab.points3d(a, a, a, colormap="copper", scale_factor=.25)
       
       cursor.x = b[0]
       cursor.y = b[1]
    else:
       print "nivel = ", nivel
       for i in self.gramatica[self.inicial]:
           if i == 'A':
              self.generar(nivel-1, cursor, distancia/3.0)
           elif i == 'B':
              cursor.x += 3*distancia

if __name__ == "__main__":
     miSistema = SistemaL(gramatica = {'A':"ABA", 'B':"BBB"},
                          inicial = 'A',distancia = 1, nivel = 5)
     miSistema.iniciar()
     miSistema.generar(miSistema.nivel, miSistema.cursor, miSistema.distancia)
     print "termine con todos los niveles"
     mlab.show()

Y así quedaría …

El pase de diapositivas requiere JavaScript.

btw: puede haber quedado algo residual del anterior problema 🙂

Curva de Koch con Numpy y Pylab

Esta vez venimos con código para generar la famosa Curva de Koch 
http://es.wikipedia.org/wiki/Copo_de_nieve_de_Koch , este es solo la curva..no el copo ...
pero el sistema es el mismo. 
Como verán el código es genérico porq tiene toda la intención de usarse 
con otras gramáticas de Lin. .. pero para eso falta un poco de pulido.
Los puntos clave pasan por la recursión, el cursor y los niveles :).
Espero que les sea útil.
#! /usr/bin/python2
# -*- coding: utf-8 -*-

import numpy as np
import pylab as pl

class Cursor:
    def __init__(self,x,y, angulo):
        self.x = x
        self.y = y
        self.ang = angulo

class SistemaL:
     def __init__(self,gramatica, inicial, distancia, nivel):
        self.gramatica = gramatica
        self.inicial = inicial
        self.distancia = distancia
        self.nivel = nivel

     def iniciar(self):
        self.cursor = Cursor(0,0,0)
        pl.plot([self.cursor.x, self.cursor.x+ self.distancia],
                [self.cursor.y, self.cursor.y])

     def generar(self, nivel, cursor, distancia):
        if nivel == 0:
           a = (cursor.x, cursor.y)
           b = (cursor.x + distancia*np.cos(cursor.ang),
                cursor.y + distancia*np.sin(cursor.ang))
           print a, "-->", b
           pl.plot([a[0],b[0]],[a[1],b[1]])
           cursor.x = b[0]
           cursor.y = b[1]
        else:
           print "nivel = ", nivel
           for i in self.gramatica[self.inicial]:
               if i == 'F':
                    self.generar(nivel-1, cursor, distancia/3)
               elif i == '+':
                    cursor.ang = cursor.ang - np.pi/3
               elif i == '-':
                    cursor.ang = cursor.ang + np.pi/3

if __name__ == "__main__":
     miSistema = SistemaL(gramatica = {'F':"F-F++F-F"},inicial = 'F',
                          distancia = 10000, nivel = 5)
     miSistema.iniciar()
     miSistema.generar(miSistema.nivel, miSistema.cursor,
                       miSistema.distancia)
     print "termine con todos los niveles"
     pl.show()

Yyyy unas imágenes de como sería nuestro resultado ...

El pase de diapositivas requiere JavaScript.