Wie man Kanten in einem Bild findet

Kantenerkennung zählt in der Bildverarbeitung zu den geläufigen Aufgaben. Man nutzt das, um Positionen von Strukturen zu erkennen, Bewegungsabläufe zu untersuchen oder ganz Allgemein einfach nur, um überhaupt einmal Vorder- von Hintergrund zu trennen. Hierzu gibt es sehr fortgeschrittene Methoden, wir wollen jedoch den Fokus darauf legen, dass man schon mit einfachen Möglichkeiten bereits gute Ergebnisse erzielen kann.

Erkennen der Kanten mithilfe der filter()-Funktion

Als ersten Schritt benötigen wir zunächst ein geeignetes Bild, in dem Vorder- und Hintergrund gut separierbar sind. Zu diesem Zweck leihen wir uns ein Bild eines Eisvogels von Wikipedia (URL im Code am Ende dieses Artikels). Dieses Bild laden wir uns mit url herunter und laden es als Grauskalen-Bild in eine vorbereitete Tabelle. Nachdem wir die Dimensionen mittels image().size.grid und damit das Seitenverhältnis für die späteren Plots extrahiert haben, erstellen wir uns eine Kopie des Bildes, in dem wir die Kantendetektion durchführen wollen.

Um die Kanten zu detektieren, verwenden wir die filter() Funktion, der wir einen beliebigen Filterkern übergeben können. Die einfachste Art, mit einem Filterkern eine Kantendetektion durchzuführen, ist mittels eines linearen Kerns {-1, 0, 1}, den wir je einmals zeilen- und spaltenweise anwenden und die Absolutbeträge der jeweiligen Ergebnisse aufsummieren. Um wieder auf den üblichen Datenbereiches eines Bildes zu kommen, normieren und reskalieren wir das Ergebnis im Anschluss noch. Wir erhalten das Ergebnis, das im Folgenden zu sehen ist.

Komprimieren der Datenbandbreite zur Verbesserung des Kontrastes

Zwar sind die Kanten bereits zu erkennen, allerdings ist der Kontrast teilweise doch recht schwach. Um diesen zu verbessern, komprimieren wir die Datenbandbreite des Ergebnisbildes, indem wir die Daten logarithmieren. Dabei muss man darauf achten, dass man den Hintergrund nicht mit logarithmiert, da er ansonsten verstärkt wird und das Ergebnis stört. Daher verwenden wir nur Pixel aus dem Bild, die eine höhere Intensität als 16/255 haben. Das Ergebnis wird wieder normalisiert und reskaliert, damit es die gesamten Datenbereich eines Bildes einnimmt. Durch diese Operation wurden die Unterschiede zwischen leichten und harten Kanten deutlich verringert; die Kanten des Eisvogels und der Wassertropfen treten im rechten Bild nun deutlich hervor. Nebenbei bemerkt: wenn man für diesen Plot colorscheme=moy verwendet, erhält man das Titelbild.

Ermitteln der Kanten-Koordinaten

Um jetzt tatsächlich mit diesen detektierten Kanten weiterarbeiten zu können, ist es vorteilhaft, wenn man ihre Koordinaten kennt. Dazu kann man direkt die Funktion logtoidx() verwenden, die die Koordinaten jedes Pixels zurückgibt, der nicht gleich null ist. Dabei muss man darauf achten, dass die Koordinaten zeilenweise bestimmt werden. Eine direkte Verbindung dieser Vertices in ihrer Reihenfolge gibt daher nicht eine durchgängige, umschließende Polygonkontur zurück. Eine Bestimmung der korrekten Reihenfolge ist dabei nicht-trivial und läuft auf das Problem des Handlungsreisenden hinaus.

Plottet man das Ergebnis der Koordinatenbestimmung ohne die Punkte zu verbinden, erhält man das Ergebnis auf der rechten Seite.

Annähern der Position des Eisvogels

Als letzten Schritt möchten wir die extrahierten Polygonvertices nutzen, um mit ihnen näherungsweise die Position des Eisvogels in dem Bild zu bestimmen. Dazu nutzen wir die Tatsache aus, dass der Eisvogel viel mehr Kantenpunkte hat, wie es bei den umgebenden Wassertropfen der Fall ist.

Wir bestimmen den Schwerpunkt der Vertices, indem wir die einzelnen Komponenten der Vertices mittels poly().avg.cols mitteln. Außerdem bestimmen wir auf ähnliche Weise die Standardabweichung der Vertices um ihren Schwerpunkt, um den Bereich zu erhalten, der mehr als 68% aller Vertices zu enthält. Im Plot markieren wir dann mit 2-sigma den Bereich, der mehr als 95% aller Vertices enthält, wodurch wir die Ausdehung des Eisvogels in dem Bild bereits gut annähern können.

Bereit zum Nachmachen? Hier ist der Code: