Kanten finden
Wo endet die LED und wo beginnt der Hintergrund? Ein Mensch sieht das sofort. Ein Programm sieht nur Zahlen – und muss die Frage übersetzen in: Wo ändert sich die Helligkeit auf kurzer Strecke stark?
Genau das ist eine Kante. Und weil dieselbe Frage auch dahintersteckt, wenn ein Auto eine Fahrbahnmarkierung sucht oder eine Kamera ein Gesicht findet, ist die Kantensuche einer der meistbenutzten Filter überhaupt.
Der Unterschied zum Nachbarn
Fang klein an: Ziehe von jedem Bildpunkt seinen linken Nachbarn ab. Wo beide gleich sind, kommt 0 heraus – schwarz. Wo ein Sprung ist, kommt eine große Zahl heraus – hell.
Vergleiche die beiden rechten Bilder.
a) Im mittleren Bild sind die senkrechten Umrisse der Kabel deutlich zu sehen, die waagerechte Oberkante der Steckplatine dagegen kaum. Im rechten Bild ist es umgekehrt. Erkläre, warum.
b) Warum steht dort Math.abs? Sage vorher, wie das Bild ohne diesen Aufruf
aussähe, und probiere es dann.
Auflösung
a) unterschiedWaagerecht vergleicht nach links. Damit misst es, wie stark
sich die Helligkeit von links nach rechts ändert – und das tut sie an einer
senkrechten Kante. Eine waagerechte Kante sieht dieser Filter nicht, weil sich
entlang einer Zeile dort nichts ändert.
Ein Filter allein findet also immer nur Kanten einer Richtung. Deshalb braucht man beide.
b) Ohne Math.abs wird die eine Hälfte jeder Kante negativ. Die Leinwand
schneidet negative Werte auf 0 ab, das Bild zeigt also nur noch die Übergänge
von dunkel nach hell und nicht mehr die von hell nach dunkel. Man sieht: Jede
Kante hat nur noch eine Seite.
Für die Kantenstärke ist die Richtung des Sprungs egal – deshalb der Betrag.
Aufgabe 1: Beide Richtungen zusammenführen
Schreibe kanten(int[][] pGrau). Der Filter soll beide Unterschiede berechnen
und sie zu einer Kantenstärke zusammenfassen.
Sauber wäre Math.sqrt(w * w + s * s). Es geht aber auch mit
w + s, und für ein Bild sieht man den Unterschied kaum. Nimm die einfache
Variante, wenn dir die Wurzel zu umständlich ist – aber begründe im Kommentar,
warum du sie nimmst.
Setze anschließend einen Schwellenwert dahinter (den Filter hast du auf der Seite Punktfilter geschrieben). Probiere verschiedene Grenzen und suche die, bei der die Umrisse zu sehen sind, das Rauschen im dunklen Hintergrund aber nicht.
Tipp 1: Zwei Gitter, ein Ergebnis
Du musst nichts neu programmieren. Lass dir beide Unterschiedsgitter von den
vorhandenen Methoden geben und verrechne sie anschließend – wie bei
grauEinfach auf der Seite Farbe und Kanäle.
Tipp 2: Wurzel in der Online-IDE
Math.sqrt erwartet eine Kommazahl und liefert eine. Für den Rückweg ins
Gitter brauchst du eine Umwandlung:
neu[zeile][spalte] = (int) Math.sqrt(w * w + s * s);
Aufgabe 2: Erst weichzeichnen, dann Kanten suchen
Das körnige Rauschen aus der letzten Aufgabe lässt sich loswerden, bevor es entsteht.
a) Sage vorher: Was passiert, wenn du das Bild erst weichzeichnest und dann die Kanten suchst? Werden die Kanten schwächer oder stärker? Und was wird aus dem Rauschen?
b) Probiere es: schwelle(kanten(weich(grau)), 40).
c) Vergleiche mit der umgekehrten Reihenfolge: weich(schwelle(kanten(grau), 40)).
Warum ist das etwas völlig anderes?
Zum Weiterdenken
Der Unterschied zum linken Nachbarn ist der einfachste Kantenfilter. In der Praxis nimmt man den Sobel-Operator, der die ganze 3 × 3-Umgebung gewichtet:
waagerecht senkrecht
-1 0 +1 -1 -2 -1
-2 0 +2 0 0 0
-1 0 +1 +1 +2 +1
Der neue Wert ist die Summe aus „Nachbarwert mal Zahl im Kästchen".
a) Warum steht in der Mitte jeder Zeile die 0? Was sagt das darüber, welcher Bildpunkt für seine eigene Kantenstärke zählt?
b) Warum steht in der mittleren Zeile ±2 statt ±1?
c) Setze den Operator um und vergleiche das Ergebnis mit deinem kanten. Die
Schleifenstruktur kennst du schon vom Weichzeichnen –
nur wird jetzt nicht durch 9 geteilt, sondern gewichtet summiert.