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

SciPyCon Argentina 2013

SciPyCon Argentina 2013

La conferencia está orientada a la divulgación de herramientas y/o proyectos realizados con Python en el ámbito científico, académico e industrial, y sus principales objetivos incluyen:

Dar oportunidad a la divulgación del lenguaje Python en la comunidad científica argentina.
Dar a conocer y/o revisar herramientas disponibles, orientadas a problemas científicos así como a otros de naturaleza más puntual.
Mostrar bibliotecas científicas desarrolladas por la comunidad mundial.
Combinar la educación, la ingeniería y la ciencia a través de Python.
Generar un marco apropiado para concretar encuentros, foros de discusión y proyectos educativos y de investigación y desarrollo relacionados con Python.

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

🙂

PyEEG & Análisis de Series Temporales

En este caso haremos una breve presentación de las funcionalidades que nos otorga PyEEG para el trabajo con electroencefalogramas, fusionando con matplotlib para mostrar los resultados.

PyEGG tiene muchas funcionalidades:

  • bin_power(), Power Spectral Density (PSD), spectrum power in a set of frequency bins, and, Relative Intensity Ratio (RIR), PSD normalized by total power in all frequency bins
  • pfd(), Petrosian Fractal Dimension (PFD)
  • hfd(), Higuchi Fractal Dimension (HFD)
  • hjorth(), Hjorth mobility and complexity
  • spectral_entropy(), spectral entropy, the entropy of RIRs
  • svd_entropy(), Singular Value Decomposition (SVD) entropy
  • fisher_info(), Fisher Information, the entropy of SVD values, i.e., SVD spectrum
  • ap_entropy(), Approximate Entropy (ApEn)
  • samp_entropy(), Sample Entropy (SampEn)
  • dfa(), Detrended Fluctuation Analysis (DFA)
  • hurst(), Hurst Exponent (Hurst)

En nuestro caso hicimos uso de DFA , en pocas palabras trata de cuantificar la autosemejanza de series temporales (En el link DFA esta genialmente explicado).

Los datos con los que trabajamos fueron tomados de  Universidad de Bonn.

Ahora vamos al código:

**Aclaración, solo por cuestiones de ploteo se le agrego a la función dfa que devuelva otros valores mas …

#!/usr/bin/env python2
# -*- coding: utf-8 -*-

#The data analyzed in our study is available on this page. 
# http://epileptologie-bonn.de/cms/front_content.php?idcat=193&lang=3
#The sampling rate of the data was 173.61 Hz. For a more 
#detailed description of the data please refer to the manuscript
#Please note, however, that the time series have the spectral 
#bandwith of the aquisition system, which is 0.5 Hz to 85 Hz.

from pyeeg import dfa
import pylab 
from scipy import log10
from numpy import arange, loadtxt, random
from random import randrange

def randomize(data):
	randomData = []
	for i in range(len(data)-1):
		randomData.append(data[randrange(0, len(data)-1)])
	
	return randomData

if __name__ == "__main__":
	
	originalData = loadtxt('F/F003.txt').T
	randomData = randomize(originalData)
	#get the dfa for both set of values
	alphaO, forplotO, forplotNO = dfa(originalData)
	alphaR, forplotR, forplotNR = dfa(randomData)

	#plot the results
	fig = pylab.figure()
	ax = fig.add_subplot(3, 1, 1)
	ori = ax.plot(log10(forplotNO), forplotO, 'b-', label = "Original Data")
	ran = ax.plot(log10(forplotNR), forplotR, 'g-', label = "Random Data")

	pylab.title("DFA")
	pylab.legend([ori[0], ran[0]], ['Original Data','Random Data'])
	pylab.text(2,3, r'$\alpha Original = %f $'%(alphaO), multialignment = 'center')
	pylab.text(2,2, r'$\alpha Random  = %f $'%(alphaR), multialignment = 'center')

	#plot the original and randomize data
	bx = fig.add_subplot(3, 1, 2)
	bx.plot(randomData, 'g-')
	pylab.title("Random Data")
	cx = fig.add_subplot(3, 1, 3)
	cx.plot(originalData, 'b-')
	pylab.title("Original Data")

	pylab.show()

Para observar la variación del \alpha lo que se hizo fue tomar la muestra original y transformarla en ruido blanco (el cual debe dar simil a 0.5)

Por si alguien le interesa ACA van a encontrar el pyeeg que retorna las funciones que se encuentran ploteadas, un setup.py para los herejes que lo necesiten corriendo en W$.

🙂

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🙂