2010-09-15 9 views
8

Ich habe eine Anwendung, wo ich brauche die durchschnittliche Intensität eines Bildes für etwa 1 Million Bilder. Es fühlt sich an wie ein Job für einen GPU-Fragment-Shader, aber Fragment-Shader sind für lokale Berechnungen pro Pixel, während die Bildmittelung eine globale Operation ist.Summen Bild Intensitäten in GPU

Ein Ansatz, den ich in Betracht gezogen habe, ist das Laden des Bildes in eine Textur, Anwenden einer 2x2 Box-Unschärfe, Laden des Ergebnisses zurück in eine N/2 x N/2 Textur und Wiederholen, bis die Ausgabe 1x1 ist. Dies würde jedoch Log-Anwendungen des Shaders erfordern.

Gibt es eine Möglichkeit, dies in einem Durchgang zu tun? Oder sollte ich einfach CUDA/OpenCL verwenden?

+0

Meine Anwendung führt Fasenabgleich eines projizierten 3D-Modells mit einem Eingabebild durch. Ich rendere ein Bild, das die Silhouettenkanten meines Modells enthält, und für jedes Kantenpixel verwende ich eine Nachschlagetabelle, um das nächste Kantenpixel im Eingabebild zu finden. Dann brauche ich das Durchschnittsergebnis, das mir gut sagt, dass das Modell zu den Daten passt. Ich habe versucht, die gerenderten Kantenpixel von opengl zu lesen und Fasenabgleich auf der CPU durchzuführen, aber die Leseoperation war ein großer Flaschenhals.Ich hatte gehofft, dass ich eine große Beschleunigung bekommen würde, wenn ich die ganze Sache auf der GPU machen und nur einen Wert einlesen würde. –

+0

(ctd) Da ich die Lookup-Tabelle als Textur übergeben kann, kann ich die Lookups in einem Vertex-Shader tun, aber ich habe immer noch den Engpass beim Lesen der Daten zurück in den Hauptspeicher. –

+0

Nichts zwingt Sie zu Unschärfe 2x2, - Sie könnten zum Beispiel mit 16x16 Box verwischen und danach in N/16 x N/16 Textur laden. Auf diese Weise können Sie große Beschleunigungs- und weniger Kopiervorgänge erreichen ... –

Antwort

4

Die Summierungsoperation ist ein spezieller Fall der "Reduktion", eine Standardoperation in CUDA- und OpenCL-Bibliotheken. Eine nette Beschreibung steht auf der cuda demos page zur Verfügung. In CUDA sind Thrust und CUDPP nur zwei Beispiele für Bibliotheken, die eine Reduktion ermöglichen. Ich bin weniger vertraut mit OpenCL, aber CLPP scheint eine gute Bibliothek zu sein, die Reduktion bietet. Kopieren Sie einfach Ihren Farbpuffer in ein OpenGL-Pixelpufferobjekt, und verwenden Sie den entsprechenden OpenGL-Interoperabilitätsaufruf, um den Speicher dieses Pixelpuffers in CUDA/OpenCL zugänglich zu machen.

Wenn es mit der opengl-API (wie die ursprüngliche Frage erforderlich) getan werden muss, besteht die Lösung darin, eine Textur zu rendern, eine Mipmap der Textur zu erstellen und die 1x1-Textur einzulesen. Sie müssen die Filterung richtig einstellen (bilinear ist geeignet, denke ich), aber es sollte nahe an die richtige Antwort kommen, Modulo-Präzisionsfehler.

1

Mein Bauch sagt mir, Sie sollten versuchen, Ihre Implementierung in OpenCL. Sie können Ihre Bildgröße und Grafikhardware optimieren, indem Sie die Bilder in maßgeschneiderte Datenblöcke aufteilen, die dann parallel summiert werden. Könnte sehr schnell sein.

Fragment Shader sind ideal für Faltungen, aber dieses Ergebnis wird normalerweise in die gl_FragColor geschrieben, so dass es sinnvoll ist. Letztendlich müssen Sie jedes Pixel in der Textur durchlaufen und das Ergebnis summieren, das dann im Hauptprogramm zurückgelesen wird. Generieren von Bildstatistiken vielleicht nicht, wofür der Fragment-Shader entworfen wurde, und es ist nicht klar, dass ein großer Leistungsgewinn zu haben ist, da ein bestimmter Puffer nicht garantiert ist, der sich in dem GPU-Speicher befindet.

Es klingt, als ob Sie diesen Algorithmus auf ein Echtzeit-Bewegungserkennungsszenario oder eine andere automatisierte Feature-Erkennungsanwendung anwenden. Es kann schneller sein, einige Statistiken aus einer Pixelprobe als das gesamte Bild zu berechnen und dann einen maschinellen Lernklassifikator zu erstellen.

Viel Glück für Sie auf jeden Fall!

+0

Vielen Dank für Ihre Antwort. Ich werde in OpenCL nachsehen. –

1

Es braucht keine CUDA, wenn Sie sich an GLSL halten möchten. Wie in der hier erwähnten CUDA-Lösung kann es in einem Fragment-Shader direkt nach vorne erfolgen. Sie benötigen jedoch etwa Protokollaufrufe (Auflösung). Richten Sie einfach einen Shader ein, der 2x2 Pixel-Samples vom Originalbild aufnimmt und die durchschnittliche Summe dieser Samples ausgibt. Das Ergebnis ist ein Bild mit halber Auflösung in beiden Achsen. Wiederholen Sie dies, bis das Bild 1x1 px ist. Einige Überlegungen: Verwenden Sie GL_FLOAT Luminanz Texturen wenn verfügbar, um eine genauere Summe zu erhalten. Verwenden Sie glViewport, um den Renderbereich in jeder Phase zu viertel zu machen. Das Ergebnis endet dann im oberen linken Pixel Ihres Framebuffers.