[PYTHON] Applications avec PyOpenCL (PIL et PyOpenGL)

Je pense que je l'ai écrit récemment, mais c'est le 15e jour du Calendrier de l'Avent GPGPU. Vous pouvez déjà le terminer, non?

PyOpenCL facilite la programmation de GPGPU, je vais donc vous montrer comment créer facilement une application à l'aide de GPGPU en utilisant également d'autres bibliothèques Python. Il y a deux exemples, l'un utilisant l'objet Image d'OpenCL et la bibliothèque de traitement d'image Python PIL, et l'autre utilisant l'interopérabilité OpenGL d'OpenCL et PyOpenGL.

PyOpenCL + PIL

Utilisez pyopencl.Image pour travailler avec des objets Image dans PyOpenCL. C'est vrai. L'objet ImageFormat donne le format de représentation de l'image. Cette fois, c'est 32 bits de 8 bits chacun de RGBA par pixel, donc ça ressemble à cl.ImageFormat (cl.channel_order.RGBA, cl.channel_type.UNSIGNED_INT8). Si vous créez un ndarray numpy qui gère la mémoire hôte comme numpy.empty ((height, width, 4), dtype = numpy.uint8), vous pouvez recevoir des informations sur les pixels directement de l'objet Image. Si vous utilisez PIL, vous pouvez facilement enregistrer les informations d'image que vous avez dans ndarray dans un fichier.

Le code source complet est ci-dessous. D'ailleurs, lors de l'écriture d'une fonction noyau dans une docstring Python avec PyOpenCL, utilisez Syntax Highlighting for Vim. , La partie du langage OpenCL C est également colorée, ce qui est pratique.

polarchecker.py


# vim: filetype=pyopencl.python
import numpy as np
from PIL import Image
import pyopencl as cl
 
SRC = '''//CL//
__constant const int CHECKER_SIZE = 10;
 
float2 polar(const int2);
int disc(const int2);
 
 
float2 polar(const int2 pos)
{
    const float x = pos.x;
    const float y = pos.y;
    return (float2)(sqrt(hypot(x, y)), atan2(x, y));
}
 
 
int disc(const int2 pos)
{
    const float2 ppos = polar(pos);
    return ((int)(ppos.x) + (int)(ppos.y * CHECKER_SIZE / M_PI_F)) % 2;
}
 
 
__kernel void gen(write_only image2d_t dest)
{
    const int2 pos = (int2)(get_global_id(0), get_global_id(1));
    uint4 pix;
    if (disc(pos)) {
        pix = (uint4)(0, 0, 255, 255);
    } else {
        pix = (uint4)(255, 255, 255, 255);
    }
    write_imageui(dest, pos, pix);
}
'''
 
ctx = cl.create_some_context()
queue = cl.CommandQueue(ctx)
prg = cl.Program(ctx, SRC).build()
 
width = 640
height = 480
fmt = cl.ImageFormat(cl.channel_order.RGBA, cl.channel_type.UNSIGNED_INT8)
buf = cl.Image(ctx, cl.mem_flags.WRITE_ONLY, fmt, shape=(width, height))
 
prg.gen(queue, (width, height), None, buf)
 
result = np.empty((height, width, 4), dtype=np.uint8)
cl.enqueue_copy(queue, result, buf, origin=(0, 0), region=(width, height))
 
image = Image.fromarray(result)
image.save('checker.png')

Je pense que la partie fonction du noyau est écrite d'une manière unique au langage OpenCL C. Si vous regardez ce domaine tel que Carte de référence OpenCL API 1.2, vous découvrirez peut-être diverses nouveautés. Hmm.

Une fois exécutée, une image comme celle-ci sera générée.

結果画像

C'est bien car la fonction du noyau est traitée en unités de pixels et la visibilité du programme est améliorée.

PyOpenCL + PyOpenGL

Si vous prenez PyOpenCL et PyOpenGL ensemble au sérieux, il y a de nombreuses étapes et c'est un peu difficile à expliquer, donc Exemple inclus avec PyOpenCL Sera expliqué. Ce programme est un programme qui produit uniquement un graphe sinusoïdal avec OpenGL et calcule la position du sommet en utilisant OpenCL au moment de l'initialisation.

La partie principale du programme est la suivante.

if __name__ == '__main__':
    import sys
    glutInit(sys.argv)
    if len(sys.argv) > 1:
        n_vertices = int(sys.argv[1])
    glutInitWindowSize(800, 160)
    glutInitWindowPosition(0, 0)
    glutCreateWindow('OpenCL/OpenGL Interop Tutorial: Sin Generator')
    glutDisplayFunc(display)
    glutReshapeFunc(reshape)
    initialize()
    glutMainLoop()

Seul le contenu de initialize est important dans la coopération OpenCL / OpenGL, alors regardons-les dans l'ordre.

Tout d'abord, créez un contexte OpenCL avec l'intégration OpenGL activée.

    platform = cl.get_platforms()[0]

    from pyopencl.tools import get_gl_sharing_context_properties
    import sys
    if sys.platform == "darwin":
        ctx = cl.Context(properties=get_gl_sharing_context_properties(),
                devices=[])
    else:
        # Some OSs prefer clCreateContextFromType, some prefer
        # clCreateContext. Try both.
        try:
            ctx = cl.Context(properties=[
                (cl.context_properties.PLATFORM, platform)]
                + get_gl_sharing_context_properties())
        except:
            ctx = cl.Context(properties=[
                (cl.context_properties.PLATFORM, platform)]
                + get_gl_sharing_context_properties(),
                devices = [platform.get_devices()[0]])

J'écris quelque chose qui semble déroutant, mais dans la plupart des cas, je pense que seule la partie à l'intérieur de la clause try est suffisante. Mon environnement est dans la première condition sur Mac, mais au contraire, il échoue avec une erreur, alors je l'ai commenté, mais je pense que cela semble fonctionner normalement.

Ensuite, initialisez le tampon OpenGL. L'accent est mis sur l'obtention de vbo avec glGenBuffers. La valeur obtenue ici est nécessaire pour générer l'objet GLBuffer de PyOpenCL.

    glClearColor(1, 1, 1, 1)
    glColor(0, 0, 1)
    vbo = glGenBuffers(1)
    glBindBuffer(GL_ARRAY_BUFFER, vbo)
    rawGlBufferData(GL_ARRAY_BUFFER, n_vertices * 2 * 4, None, GL_STATIC_DRAW)
    glEnableClientState(GL_VERTEX_ARRAY)
    glVertexPointer(2, GL_FLOAT, 0, None)

La ligne suivante est la création de l'objet GLBuffer.

    coords_dev = cl.GLBuffer(ctx, cl.mem_flags.READ_WRITE, int(vbo))

Enfin, utilisez le tampon OpenGL à partir d'OpenCL. Lorsque vous utilisez le tampon OpenGL côté OpenCL, mettez-le en sandwich entre enqueue_acquire_gl_objects et enqueue_release_gl_objects.

    cl.enqueue_acquire_gl_objects(queue, [coords_dev])
    prog.generate_sin(queue, (n_vertices,), None, coords_dev)
    cl.enqueue_release_gl_objects(queue, [coords_dev])

Voilà toutes les bases. Lorsque vous exécutez l'ensemble du programme, l'écran suivant s'affiche. 結果画面

Ce sera beaucoup plus facile que d'écrire en langage C. Cependant, une certaine connaissance d'OpenGL peut être requise.

Les démos qui bougent un peu plus sont cette zone et [cette zone](http://enja.org/2011/03/22/adventures-in-pyopencl-part- Il est situé à 2-particules-avec-pyopengl /).

Recommended Posts

Applications avec PyOpenCL (PIL et PyOpenGL)
Installation PIL avec pip
Programmation Shader avec pyOpenGL
Traitement d'image avec PIL
Traitement d'image avec PIL (Pillow)
Premiers pas avec les applications Web Python
Dessinez avec PyOpenGL. Confusion autour de VBO
Dessinez des figures avec OpenCV et PIL