Bildbearbeitung


Faltung (Convolution)


Eine Faltung ist lineare Abbildung. Eine Matrix wird mit Hilfe eines "Faltungskernels" in eine andere Matrix abgebildet. Um sie auf Bilder anzuwenden, müssen diese zunächst in Matrizen verwandelt werden. Beachten Sie dabei: Wenn man die Größe des Programmfensters durch z.B. size(800,600) angibt, dann verwendet man häufig für den weiteren Programmcode w = width ; h = height; . Auch wenn man die Größe eines Bildes angeben will, wird als erste Zahl immer die Breite des Bildes und danach die Höhe angeben. Selbst dann, wenn Bild und Programmfester gleich groß sein sollten, tut man gut daran, neue Buchstaben bei den Bilddimensionen zu verwenden. Zum Beispiel W =bildOriginal.width; H = bildOriginal.height; . Weshalb auch bei gleichen Werten? Es liegt daran, dass die Matrizen die Reihenfolge von Breite und Höhe andersherum verwenden. Die Matrix zu obigem Bild wäre dann bearbeitetMatrix = new int[H][W]; Falls Sie das entsprechende Kapitel im Lehrbuch nicht gelesen haben sollten, hier eine Realisierung in Processing:

public int[][] matrixVonBild(PImage bildEingabe){
      PImage bild = bildEingabe;
      bearbeitetMatrix = new int[H][W]; //println(H,W);
      for(int i = 0; i < W ;i++){
        for(int j = 0; j < H; j++){
           bearbeitetMatrix[j][i] = bild.pixels[i + j*W];
         }
      }
      return bearbeitetMatrix;
}


Versuchen Sie nun, den Programmcode für die Umwandlung einer Matrix in ein Bild zu schreiben. Wenn Sie Hilfe brauchen, schauen Sie sich den dazu passenden Sketch an. Dort wird auch gezeigt, wie man den Dialog "Datei öffnen" umsetzt. Außerdem ist mit einer Programmzeile dafür gesorgt, dass die schwarzen Pixel eines Bildes gezählt werden. Dies wird uns später nützlich sein.
Das folgende Bild zeigt an einem Beispiel, wie Faltungen durchgeführt werden:


Der Faltungskernel wird zunächst mit dem violett dargestellten Teil der Ausgangsmatrix multipliziert (Zeile mal Spalte). Die Rechnung ist unter dem Bild dargestellt. Aus 65 wird somit 34. Was wird dann aus der 12 unter 189? (Lösung am Seitenende)
Was genau tut der Faltungskernel im obigen Fall? Vier Nachbarpixel (-1) bewirken, dass der abzubildende Wert (65) sich von eben diesen Nachbarn stärker unterscheidet. Das Bild gewinnt daher an Schärfe.
Die Ränder der Matrix allerdings kann man mit dieser Methode leider nicht abbilden. Es gibt verschiedene Methoden, dieses Manko zu beheben. Die einfachst ist, die zugehörigen Randpixel wegzuschneiden. Bei einem Pixel von vielleicht 2000 Pixeln Bildbreite, ist dies akzeptabel.
Will man bei der Durchführung der Faltung noch weitere Umgebungswerte des abzubildenden Wertes einbeziehen, dann verwendet man eine 5x5, 7x7 oder 9x9 Matrix als Faltungskernel. Dadurch lässt sich letztendlich das Bild sehr viel feiner bearbeiten.

Kanten finden mit Sobel


Hierfür benötigt man zwei Kernel. Einen für die x- und einen für die y-Richtung. Das Ergebnis der Faltung ist eine näherungsweise Ableitung in x- und y-Richtung. Man stelle sich einfach vor, dass als Funktionswerte die Grauwerte der jeweiligen Pixel verwendet werden. Da dies nur diskrete Werte sind, ist die Ableitung für jedes Pixel dx bzw. dy. Dies sind die beiden Sobel-Kernel:
float[][] gMatrixX = {{1, 0, -1},
                                { 2, 0, -2},
                                { 1, 0, -1}}; //x-Richtung

float[][] gMatrixY = {{ 1, 2, 1 },
                                { 0, 0, 0},
                                { -1, -2, -1}}; //y-Richtung


Schauen wir uns an einem einfachen Bild an, wie die beiden Faltungskernel arbeiten:


Das Bild eines massiven Achtecks ist links zu sehen. Rechts daneben zunächst die Anwendung von gMartrixX auf dieses Bild, also derjenigen Matrix, die eine Näherung der Ableitung in x-Richtung bewirkt. Die Ableitung in eine Richtung ist dann groß, wenn sich der Grauwert stark ändert. Sie ist Null, wenn er gleich bleibt. Weshalb also sind hier die waagerechten Kanten des Achtecks nicht zu sehen? Die Ableitung in diese Richtung bleibt gleich, und somit sind alle dortigen Pixel schwarz.
(Der Einfachheit halber behandeln wir hier nur Graustufenbilder. Falls man Farbe verwenden will, muss man jeden Farbkanal so behandeln, wie wir es mit den Graustufen gemacht haben.)
Das dritte Bild in der obigen Reihe zeigt das Ergebnis, wenn man nur gMartrixY auf das Original anwendet. Das Bild ganz links ist durchaus überzeugend, wenn das Ziel war, die Kanten zu finden. Irgend wie müssen hier beide Faltungskernel angewand worden sein. Wie genau man hier vorgeht, zeigt das folgende Bild:
Mit dem Satz des Pythagoras bekommen wir die effektive Ableitung und zusätzlich, mit Hilfe des inversen Tangens, einen Winkel a. Im ersten Augenblick scheint diese Information obsolet. Kurze Überlegung: Wieviele Nachbarn hat ein Pixel, das nicht am Rand liegt? Wenn wir die diagonalen Pixel hinznehmen, sind es acht. Die zugehörigen Winkel sind 0, 45, 90 und 135 Grad. Dazu kommen die jeweils um 180 Grad größeren Winkel 180, 225,... Nun runden wir den Winkel a so, dass er zu einem dieser Winkel wird. Also 30 Grad wird zu 45 Grad, 96 Grad zu 90 Grad usw..
Das besagte Pixel bekommt hierdurch zwei Nachbarn, mit der Eigenschaft, dass sich in dieser Richtung der Grauwert am stärksten ändert. Stellen wir uns vor, dass einer dieser Nachbarn einen höheren Grauwert besitzt, als besagtes Pixel selbst. Wenn wir in diesem Fall genau dieses Pixel schwarz einfärben, dann wird die Kante enger begrenzt und somit präziser.
Schauen Sie sich in folgendem Bild die konkrete Situation für ein bestimmtes Pixel an:


Links ist die Faltung ohne Korrektur zu sehen. Wenn wir die Ecke oben links als (0,0) bezeichnen, dann betrachten wir hier das Pixel (3;2). Unten sind die zugehörigen Winkel eingetragen, hier also 45 Grad. (Die y-Achse zeigt bei Processing nach unten, daher sind 45 Grad hier -45 Grad. ) Beide zugehörigen Nachbarn senkrecht zur Ausbreitungsrichtung haben einen höheren Grauwert als das Pixel selbst. Durch die Korrektur wird das Pixel daher schwarz einfärbt und macht die Kante damit schmaler. Sehen wir uns das Erbnis bei einem vollständigen Bild an:





Oben das Original, danach Anwendung von Sobel ohne Korrektur (mit Invertierung und Schwellenwert). Darunter das entsprechende Ergebnis mit Korrektur. Der Schwellenwert sorgt dafür, dass helles Grau weiß wird. Sehr schön ist dabei zu sehen, dass ohne Korrektur dunklere Bereiche, zum Beispiel für die Wolken, auch nach der Anwendung des Schwellenwertes bleiben. Um zu verstehen, was das bedeutet, sehen wir uns das folgende Bild an, bei dem weder Invertierung noch Schwellenwert angewandt wurde:



Die Korrektur wurde hier durchgeführt. Ohne Korrektur wäre daher der Bereich der hellen Pixel deutlich größer, was dann bei der Invertierung zu weniger präzisen Kanten führt.

Kanten finden mit Scharr


Die Sobel-Methode zur Bestimmung der Ableitungen ist nur eine Näherung. Etwas bessere Ergebnisse bei der Suche nach Kanten bekommt man mit der Scharr-Methode. Dies sind die zugehörigen Faltungs-Kernel:
float[][] gMatrixX = {{ 3, 0,   -3},
                               {10, 0, -10},
                                { 3, 0,   -3}}; //x-Richtung

float[][] gMatrixY = {{  3,   10,   3 },
                                { 0,    0,   0},
                                { -3, -10, -3}}; //y-Richtung


Hier ein Beispiel, das mit den Scharr-Kernel berechnet wurde. Korrektur, Invertierung und Schwellenwert wurden ebenfalls angewendet:






Lösung der obigen Aufgabe: -228. Wird zu 228, falls die Zahlen Grauwerte zwischen 0 und 255 darstellen.



Sketch bildBearbeitungen.

Menu