[PYTHON] Anwendungen mit PyOpenCL (PIL & PyOpenGL)

Ich glaube, ich habe es erst kürzlich geschrieben, aber es ist der 15. Tag des GPGPU-Adventskalenders. Du kannst es schon beenden, oder?

PyOpenCL erleichtert das Programmieren von GPGPU ein wenig. Daher zeige ich Ihnen, wie Sie eine Anwendung mit GPGPU auch mit anderen Python-Bibliotheken erstellen können. Es gibt zwei Beispiele, eines mit dem Image-Objekt von OpenCL und der Bildverarbeitungsbibliothek PIL von Python und das andere mit OpenGL-Interoperabilität und PyOpenGL von OpenCL.

PyOpenCL + PIL

Verwenden Sie pyopencl.Image, um mit Bildobjekten in PyOpenCL zu arbeiten. Korrekt. Das ImageFormat-Objekt gibt das Darstellungsformat des Bildes an. Diesmal sind es 32 Bit mit jeweils 8 Bit RGBA pro Pixel, also sieht es aus wie "cl.ImageFormat (cl.channel_order.RGBA, cl.channel_type.UNSIGNED_INT8)". Wenn Sie ein numpy ndarray erstellen, das den Hostspeicher wie "numpy.empty ((height, width, 4), dtype = numpy.uint8)" verarbeitet, können Sie Pixelinformationen direkt vom Image-Objekt empfangen. Wenn Sie PIL verwenden, können Sie die in ndarray enthaltenen Bildinformationen einfach in einer Datei speichern.

Der gesamte Quellcode ist unten. Verwenden Sie übrigens beim Schreiben einer Kernelfunktion in einem Python-Dokumentstring mit PyOpenCL Syntax Highlighting for Vim. Der OpenCL C-Sprachteil ist ebenfalls farbig, was praktisch ist.

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')

Ich denke, dass der Kernelfunktionsteil auf eine Weise geschrieben ist, die für die OpenCL C-Sprache einzigartig ist. Wenn Sie sich diesen Bereich wie OpenCL API 1.2-Referenzkarte ansehen, können Sie verschiedene neue Dinge entdecken. Hmm.

Bei der Ausführung wird ein solches Bild generiert.

結果画像

Dies ist gut, da die Kernelfunktion in Pixeleinheiten verarbeitet wird und die Sichtbarkeit des Programms verbessert wird.

PyOpenCL + PyOpenGL

Wenn Sie PyOpenCL und PyOpenGL zusammen ernst nehmen, gibt es viele Schritte und es ist ein wenig schwierig zu erklären, daher Beispiel in PyOpenCL enthalten ) Wird erklärt. Dieses Programm gibt nur mit SinGL einen Sinuswellengraphen aus und berechnet die Scheitelpunktposition zum Zeitpunkt der Initialisierung mit OpenCL.

Der Hauptteil des Programms ist wie folgt.

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()

In der OpenCL / OpenGL-Zusammenarbeit sind nur die Inhalte der Initialisierung wichtig. Schauen wir sie uns also der Reihe nach an.

Erstellen Sie zunächst einen OpenCL-Kontext mit aktivierter OpenGL-Integration.

    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]])

Ich schreibe etwas, das verwirrend erscheint, aber in den meisten Fällen denke ich, dass nur der Teil in der try-Klausel ausreicht. Meine Umgebung befindet sich auf dem Mac im ersten if-Zustand, aber im Gegenteil, sie schlägt mit einem Fehler fehl, sodass ich sie auskommentiert habe, aber ich habe das Gefühl, dass sie normal funktioniert.

Initialisieren Sie als Nächstes den OpenGL-Puffer. Der Fokus liegt darauf, vbo mit glGenBuffers zu bekommen. Der hier erhaltene Wert ist erforderlich, um das GLBuffer-Objekt von PyOpenCL zu generieren.

    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)

Die folgende Zeile ist die Erstellung des GLBuffer-Objekts.

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

Zum Schluss den OpenGL-Puffer von OpenCL aus bedienen. Wenn Sie den OpenGL-Puffer auf der OpenCL-Seite betreiben, legen Sie ihn zwischen enqueue_acquire_gl_objects und 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])

Das sind alle Grundlagen. Wenn Sie das gesamte Programm ausführen, wird der folgende Bildschirm angezeigt. 結果画面

Es wird viel einfacher sein als in C-Sprache zu schreiben. Möglicherweise sind jedoch einige OpenGL-Kenntnisse erforderlich.

Demos, die sich etwas mehr bewegen, sind dieser Bereich und [dieser Bereich](http://enja.org/2011/03/22/adventures-in-pyopencl-part- Es befindet sich bei 2-Teilchen-mit-Pyopengl /).

Recommended Posts

Anwendungen mit PyOpenCL (PIL & PyOpenGL)
PIL-Installation mit Pip
Shader-Programmierung mit pyOpenGL
Bildverarbeitung mit PIL
Bildverarbeitung mit PIL (Pillow)
Erste Schritte mit Python-Webanwendungen
Zeichnen Sie mit PyOpenGL. Verwirrung um VBO
Zeichnen Sie Figuren mit OpenCV und PIL