Seminar über Algorithmen - SoSe 2009 Parallele Algorithmen in der Bildverarbeitung von Christopher Keiner 1 1.1 Allgemeines Einleitung Parallele Algorithmen gewinnen immer stärker an Bedeutung. Es existieren immer mehr parallele HardwareArchitekturen, die mittlerweile auch den Heimbereich erreicht haben. Durch die parallele Ausführung von Algorithmen kann ein Prozess stark beschleunigt werden. Im Folgenden werden verschiedene Algorithmen der Bildverarbeitung betrachtet. In der Computergrafik sowie in der Bildverarbeitung ist die Geschwindigkeit ein zentrales Thema. Die Vertex- /und Fragmentshader einer Grafikkarte stellen für die parallelen Algorithmen ein mögliche Plattform zur Verfügung. 1.2 Grundlagen und Definitionen Die natürlichste Darstellung eines Bilds ist für den Menschen ein 2-dimensionales Array aus Pixeln. Es wird diese Repräsentation im Folgenden verwendet,√auch √ wenn für spezielle Probleme andere Darstellungen besser geeignet sind. Das Bild hat die Größe von n × n Pixeln und wird als binär angenommen. Die Pixel können die Werte 0 oder 1 enthalten. √ √ Die folgenden Algorithmen arbeiten auf einem 2-dimensionalen Prozessor-Array der Größe n × n. Die Prozessoren sind untereinander horizontal, vertikal und diagonal verbunden. Jeder Prozessor kann durch die Spalten- und Zeilenzahl (i, j) identifiziert werden. Der (i, j)-Prozessor speichert den Pixel (i, j) des Bildes. Die Nachbarschaft zweier Pixel definiert sich durch die Nachbarschaft der Prozessoren. Zwei Pixel sind also benachbart, wenn sie horizontal, vertikal oder diagonal benachbart sind. Zwei 1-Pixel sind verbunden, wenn es einen Weg von benachbarten 1-Pixel zwischen diesen Pixeln existiert. Die Abarbeitung eines Algorithmus erfolgt synchron. In jedem Schritt t führt jeder einzelne Prozessor die spezifizierte Tätigkeit aus, bevor der nächste Schritt t + 1 beginnt. Die Laufzeit eines Algorithmus ist über die benötigte Anzahl der Schritt definiert. 2 2.1 Component Labeling Component Labeling Problem Eine Komponente besteht aus allen 1-Pixel, sodass jeder Pixel der Komponente mit den anderen Pixeln der Komponente verbunden ist. Das Component Labeling Problem besteht nun darin, dass jedem 1-Pixel einer Komponente das gleiche Label zugewiesen wird. 1 1 1 0 0 1 0 1 1 A A 0 1 0 1 0 1 0 1 A 1 0 0 0 0 0 0 0 A 1 0 0 0 0 1 1 1 A 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 1 0 1 0 0 1 0 1 1 E 1 1 0 0 0 0 0 0 E E B B B B B B C C C C C D C D D Figure 1: Erwartetes Ergebnis eines Component Label Algorithmus 2.2 Simpler Algorithmus Zu Beginn wird eine einfacher Algorithmus vorgestellt. Es existiert eine Initialisierungs- und eine UpdatePhase. Phase 1 Jeder Pixel generiert sich ein eigenes Label aus der Adresse im Array Phase 2 Jeder Pixel aktualisiert das eigene Label mit dem kleinsten Label der Nachbarn, falls dieses Label kleiner als das eigene Label ist. Wiederhole solange bis alle Nachbar-Pixel den gleichen Wert haben. Es wird der innere Durchmesser einer Komponente als der kleinste Wert d definiert, sodass jedes Paar u, v aus 1-Pixeln einer Komponente mit einem Weg der Länge ≤ d verbunden werden kann. In jedem Schritt der Update-Phase wird das kleinste Label der Komponente an die benachbarten Pixel weitergegeben. Die Laufzeit des Algorithmus sich mit dem größten Durchmesser aller Komponenten abschätzen. Im Worst case beträgt die Laufzeit Θ(n). 2.3 Levialdi Algorithmus Der Levialdi Algorithmus besteht aus einer Schrumpfungsphase, in der jede Komponente auf eine Größe von einem Pixel verringert wird und anschließend eliminiert wird. Dieser Pixel erhält ein Label. In der anschließenden Expansionsphase wird die Schrumpfoperation rückgängig gemacht. Die verteilten Label werden dabei zurückpropagiert. Dazu muss jeder Prozessor die Werte während der Schrumpfungsphase speichern. Wechselt bei der Expansionsphase der Wert von 0 auf 1, dann wird das Label des rechten, unteren oder rechten unteren Pixels zugewiesen. Ausgenommen der erste Pixel der Expansionsphase, dem während der Schrumpfungsphase das Label zugewiesen wurde. Die Schrumpfoperation ist als Levialdi Transformation bekannt. Die Transformation folgt den zwei folgenden Regeln. Ein 1-Pixel wird zu einen 0-Pixel, falls der obere, linke und obere linke Pixel den Wert 0 hat. Ein 0-Pixel wird zu einem 1-Pixel, falls der obere und der linke Pixel den Wert 1 besitzt. Im Abbildung 2.3 ist die Levialdi Transformation anschaulich dargestellt. Die Korrektheit des Algorithmus basiert auf den Eigenschaften der Transformation, dass Komponenten nicht vermischt werden und nicht zerfallen. √ Die Laufzeit für die Schrumpfungsphase beträgt höchstens 2 n−1 Schritte. Dies kann über die Invariante gezeigt werden, die besagt, dass √ nach Schritt t alle Pixel (i, j) den Wert 0 haben, fall i+j < t+1 gilt. Insgesamt √ entsteht eine Laufzeit von 4 n − 2. Der Nachteil dieses Algorithmus stellt der Speicherverbrauch von 2 n pro Prozessor dar. 2 0 0 0 0 0 1 1 0 1 0 1 1 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 1 0 0 1 0 1 1 0 1 1 1 1 1 1 0 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 0 1 0 * * * 0 * * * 1 Figure 2: Levialdi Transformation: Der grau markierte Pixel ist aktiv. Auf Grund der Belegung der Nachbarpixel wird entschieden, ob der aktive Pixel geändert wird. 3 3.1 Hough Transformation Erläuterung Es wird in diesem Abschnitt die Hough Transformation eines Bildes vorgestellt. Die Transformation ist analog zum Anfertigen einer Computer-Tomografie Aufnahme eines 3D-Objekts, bei der jeweils das Objeket schichtweise und mit verschiedenen Winkeln durchleuchtet wird. √ √ Gegegeben sei ein n × n großes binäres Bild, sowie ein Projektionswinkel Θ(− π2 < Θ ≤ π2 ). Das Bild wird in parallele Streifen der Höhe 1 mit einem Winkel Θ bezüglich der x-Achse partitioniert. Die Transformation summiert alle 1-Pixel eines Streifens. Anhand des Pixelzentrum wird entschieden, in welchem Band ein Pixel gezählt wird. Mit Hilfe der resultierenden Ergebnisse für jeden Streifen, kann die Hough Transformation dazu eingesetzt werden, bestimmte Formen in einem Bild zu erkennen. Ist die Transformation z.B. unter verschiedenen Winkeln invariant, kann geschloßen werden, dass das Bild eine Kreisscheibe enthält. 3.2 Algorithmus Der Projektionswinkel Θ wird auf den Wert 0 ≤ Θ ≤ π4 beschränkt. Es werden Variablen bi eingeführt, die entlang eines Streifens durch das Array geschickt werden und alle vorkommenden 1-Pixel zählen. Initial sind diese Variablen mit dem Wert 0 belegt und werden an der linken und unteren Grenze des Array eingegeben. Die Variablen bi tragen weiterhin die Information, in welchem Winkel Θ sie durch das Array geschickt werden. Durch die Beschränkung des Winkel Θ ist der nächste Prozessor entweder rechts, oben oder rechts oben zu suchen. Anhand der Winkelinformation kann ein Prozessor dies lokal entscheiden. Als Erweiterung soll ein Prozessor zusätzlich zum eigenen Pixel ebenfalls den Pixel des oberen Nachbarn speichern. Dies kann durch einen zusätzlichen Berechnungsschritt in der Initialisierung realisiert werden. Durch die Erweiterung muss eine Variable nur einen Prozessor in jeder Spalte besuchen. In Abbildung 3.2 ist der Weg der Variablen für einen Winkel von Θ = π/6 gezeigt. √ Die Laufzeit eines Durchlaufs benötigt 2 n Zeit, wenn die linken und die unteren Variablen nacheinander durch das Array geschickt werden, um Kollisionen an Prozessoren zu vermeiden. Für M verschiedene Winkel √ benötigt der Algorithmus mit Pipelining O( n + M ) Zeit. 3 b1 . . . . . . . . b2 . . . . . . . . b3 . . . . . . . . b4 . . . . . . . . b5 . . . . . . . . . . . . . . . . b6 . . . . . . . . b7 . . . . . . . . b8 b9 b10 b11 Figure 3: Algorithmus zur Berechnung der Hough Transformation. Die Variablen werden an der linken und unteren Seite des Arrays eingegeben und wandern den eingezeichneten Weg durch das Array. 4 Konvexe Hülle Gegeben sei zunächst eine Menge P aus Punkten p1 , ..., pn in der Ebene. Die konvexe Hülle ist definiert als kleinstes konvexes Polygon, dass alle Punkte pi umschließt. Es sollen die Eckpunkte, sowie die Punkte, die auf dem Rand der konvexen Hülle liegen, berechnet werden. 4.1 Hüllpunkte Lemma: Gegeben eine Menge von Punkten p1 , ..., pn sortiert nach x-Koordinate. Es wird Θi,j als Winkel zwischen der Strecke pi pj und der negativen y-Achse definiert. Weiterhin sei r(i0 ) das kleinste j > i0 , für das Θi0 ,j maximal ist. Punkt pi ist Punkt der oberen konvexen Hülle gdw. x1 < xi < xn und für alle i0 < i r(i0 ) ≤ i gilt. Bew.: Es wird eine Menge aller Strecken konstruiert, die zwei verschiedene Punkte aus P verbinden. Nach Definition liegen alle Strecken innerhalb der oberen konvexen Hülle. Ein Punkt pi liegt auf dem Rand der konvexen Hülle, wenn er nicht unterhalb einer Strecke liegt und x1 < xi < xn gilt. Ein Punkt liegt unterhalb einer Strecke pi0 pj (j > i0 ), wenn ein Schnittpunkt zwischen der Strecke und dem Strahl von (xi , yi ) nach (xi , ∞) existiert. Dies ist der Fall, wenn Θi0 ,j > Θi0 ,i und i0 ≤ i ≤ j gilt. Dies ist die Bedingung, dass der Anfangspunkt des Strahls pi unterhalb der Strecke liegt. Nach Definition ist r(i0 ) das kleinste j > i0 , für das Θi0 ,j maximal ist. Für jede Strecke zwischen i0 tem und j = r(i0 )ten Punkt ist bekannt, dass alle Punkte im Intervall von xi bis xj unterhalb dieser Strecke liegen müssen. Falls für alle i0 < i r(i0 ) ≤ i erfüllt ist, existiert keine Strecke von i0 nach k > i. Damit liegt pi nicht unter einer Strecke und ist damit Punkt der oberen konvexen Hülle. 4 4.2 Algorithmus Der Algorithmus arbeitet auf einem linearen Array von Prozessoren. Jeder Prozessor speichert einen der Punkte pi . 1. Sortiere Punkte nach x-Koordinate 2. Weise sortierten Punkten Label pi zu. 3. Berechne r(i) für alle i, indem Punkte von rechts nach links gesendet werden. Dabei kann Θi,j für alle j > i berechnet werden. Der Prozessor speichert das Maximum. Damit ist dem iten Prozessor j = r(i) mit maximalem Θi,j bekannt. 4. Führe ein Prefix von links nach rechts mit Maximums-Operation aus. Es ist danach mai0 ≤i r(i0 ) bekannt. Der ite Prozessor kann nun durch Vergleich mit seiner Identität prüfen, ob pi ein Hüllpunkt ist. 5