Cool Stack

Tutoriel n°3 : PyGame et OpenCL : afficher un image générée en OpenCL

L'objectif de ce tutoriel est de vous montrer comment générer une image en OpenCL pour qu'elle soit correctement gérée par pyGame. Ce tutoriel demande des connaissances minimes en pygame (tutoriel N°2) et en pyOpenCL (Tutoriel N°1) ainsi que quelques connaissances basiques en synthèse d'image.

On utilise ici pyopencl et pygame.

import pygame
import pyopencl as cl

Initialisation

Initialisation pygame et pyopencl en créer une surfarray de la taille de l'écran ainsi que son buffer opencl associé.

screen = pygame.display.set_mode(screensize)
bits = pygame.surfarray.pixels2d(pygame.Surface(screensize))
ctx = cl.Context()
queue = cl.CommandQueue(ctx)
bits_buf = cl.Buffer(ctx, cl.mem_flags.WRITE_ONLY, bits.nbytes)

Le programme

Notre programme prend un seul paramètre qui est le buffer de retour. Une image est un tableau de pixel. Chaque pixel est un entier codé sur quatre octets.Chaque octet s'appelle une composante. Les trois premiers composantes correspondent au Rouge, Vert et Bleu de la couleur du pixel (les trois couleurs primaires) et le quatrième souvent appeler Alpha peut être utilisée pour la transparence ou la profondeur. Nous affecterons 0 pour la composante alpha. Les composantes Rouge, Vert et Bleu sont codées avec un entier entre 0 et 255. 0 pour du noir, 255 pour le maximum. On obtient donc du blanc en mettant à 255 les trois composantes. On utilise le type uchar4 pour cet exemple, ce qui permet de pouvoir accéder à chaque composante séparément, mais nous pourrions aussi utiliser un uint. Ici comme nous générons une image, nous avons donc des coordonnées en 2D. On utilise get_global_id(0) pour récupérer l'id de la première dimension et get_global_id(1) pour la seconde. On peut aussi utiliser get_global_size(0) pour récupérer la taille de la première dimension (la largeur).

__kernel void sum( __global uchar4* c){
  int x = get_global_id(0);
  int y = get_global_id(1);
  int w = get_global_size(0);
  int id = x + y * w;
  uchar4 col;
  col.x = y % 255; //B
  col.y = 0;       //G
  col.z = x % 255; //R
  col.w = 0;       //A
  c[id] = col;
}

Lancer la génération de l'image et l'afficher à l'écran

Il faut donc:

  • Lancer le programme
  • Récupéré le résultat
  • Afficher le résultat
prg.sum(queue, bits.shape, bits_buf)
cl.enqueue_read_buffer(queue, bits_buf, bits).wait()
pygame.surfarray.blit_array(screen,bits)

Conclusion

Les notions vues dans ce tutoriel sont la synthèse du tutoriel sur pyOpenCL et de celui du PyGame et numpy. Les notions ne sont donc pas totalement nouvelle. Voici le code complet de cet exemple :

import pygame
import pyopencl as cl
screen = pygame.display.set_mode((640,480))
running = 1
bits = pygame.surfarray.pixels2d(pygame.Surface((640,480)))
ctx = cl.Context()
queue = cl.CommandQueue(ctx)
bits_buf = cl.Buffer(ctx, cl.mem_flags.WRITE_ONLY, bits.nbytes)
prg = cl.Program(ctx, """
__kernel void sum( __global uchar4* c){
    int x = get_global_id(0);
    int y = get_global_id(1);
    int w = get_global_size(0);
    int id = x + y * w;
    uchar4 col;
    col.x = y % 255;//B
    col.y = 0;      //G
    col.z = x % 255;//R
    col.w = 0;      //A
    c[id] = col;
  }
  """).build()
while running:
    event = pygame.event.poll()
    if event.type == pygame.QUIT:
        running = 0
    prg.sum(queue, bits.shape, bits_buf)
    cl.enqueue_read_buffer(queue, bits_buf, bits).wait()
    pygame.surfarray.blit_array(screen,bits)
    pygame.display.flip()

Posted Jeu 07 janvier 2010 by Stéphane Planquart in programmation graphique