5.14. Gauß-Glättung und Kanten

Zwei Aufgaben dominieren, wofür Nachbarschaftsfenster im klassischen maschinellen Sehen verwendet werden: das saubere Glätten von Pixel-zu-Pixel-Variation und das Auffinden der Kanten, an denen sich das Bild scharf ändert. Der Gauß-Filter ist das Standardwerkzeug für das Erste, die Laplace-basierten Detektoren das Standardwerkzeug für das Zweite – und die beiden lassen sich kombinieren, weil jeder Kantendetektor auf einer leicht geglätteten Eingabe besser funktioniert.

5.14.1. Der Gauß-Filter

gaussian() ist der mittengewichtete Verwandte von mean(). Beide berechnen einen Durchschnitt über die Nachbarschaft jedes Pixels, aber die Gewichte des Gauß-Filters sind nicht gleichförmig: Pixel näher zur Mitte der Nachbarschaft zählen mehr, Pixel am Rand der Nachbarschaft zählen weniger, wobei die Gewichte der vertrauten Glockenkurve folgen, die dem Filter seinen Namen gibt.

Die glockenförmige Gewichtung ist es, was einen Gauß-Filter glatter macht als einen Boxdurchschnitt. Mittelwertfilterung kann an den Rändern von Objekten sichtbare Artefakte erzeugen – eine harte Abschneidung in der Gewichtung führt an scharfen Übergängen kleine Ringmuster ein. Die sanft abfallenden Gewichte des Gauß-Filters vermeiden dieses Ringen und erzeugen ein Ergebnis, das dem näherkommt, wie „verschwommen“ aussehen sollte. Der Preis ist mehr Rechenaufwand pro Pixel als beim Mittelwertfilter, aber nicht dramatisch viel mehr – die Kosten pro Pixel liegen immer noch weit unter denen des bilateralen Filters.

img.gaussian(1)    # 3x3 Gaussian -- a clean light blur
img.gaussian(2)    # 5x5 Gaussian -- stronger smoothing

Die Gauß-Glättung ist die übliche erste Stufe fast jeder Kantenerkennungs-Pipeline. Die unten beschriebenen Kantendetektoren verstärken allesamt hochfrequente Inhalte, einschließlich des Sensorrauschens, das der Algorithmus eigentlich nicht erkennen will. Wenn man zuerst eine leichte Gauß-Glättung laufen lässt, unterdrückt das dieses Rauschen, ohne echte Kanten stark zu verwischen, sodass der Kantendetektor echte Kanten statt Sprenkel findet.

5.14.2. Unscharfmaskierung

Eine Gauß-verwischte Kopie eines Bildes ist das Rohmaterial, das die Technik der Unscharfmaske für die klassische Schärfung verwendet. Wird unsharp=True am Filter gesetzt, schaltet das von „das verwischte Bild erzeugen“ auf „das verwischte Bild vom Original abziehen und die Differenz wieder zum Original hinzufügen“ um – der Effekt ist, dass hochfrequente Kanten relativ zu glatten Innenflächen verstärkt werden.

img.gaussian(1, unsharp=True)

Die optionalen Parameter mul und add skalieren die Stärke des Unscharf-Ergebnisses; die Standardwerte (mul=1.0, add=0.0) ergeben eine moderate Schärfung, die das Sensorrauschen nicht übertreibt.

5.14.3. Der Laplace-Filter

laplacian() führt eine diskrete Näherung der zweiten räumlichen Ableitung des Bildes aus. Die Ausgabe ist groß dort, wo sich Pixelwerte schnell ändern, und nahe null dort, wo sie konstant sind oder sich linear ändern. Die natürliche Lesart des Ergebnisses ist eine Kantenreaktion: Pixel, an denen sich das Bild rasch ändert, leuchten auf, Pixel in glatten Innenflächen bleiben dunkel.

img.laplacian(1)   # 3x3 Laplacian -- edge response

Dieselben Parameter wie bei gaussian sind verfügbar. sharpen=True erzeugt ein geschärftes Bild (der Laplace-Wert wird wieder zum Original addiert, statt eigenständig zurückgegeben zu werden). mul und add skalieren die Reaktion.

Eine praktische Anwendung über die Kantenerkennung hinaus ist die Fokusmessung. Die über einen Bereich gemittelte Laplace-Reaktion liefert ein grobes Maß dafür, wie viel hochfrequenten Inhalt der Bereich trägt; bei einem gut fokussierten Einzelbild ist dieser Durchschnitt hoch, bei einem unscharfen sinkt er. Das Vergleichen der Laplace-Reaktion über Einzelbilder hinweg ist die günstige Art zu fragen „ist das Objektiv fokussiert?“, ohne eine teurere Kontrastmetrik zu benötigen.

5.14.4. Die find_edges-Methode

find_edges() führt eine vollständige Kantenerkennungs-Pipeline aus, statt nur eines Kantenreaktionsfilters. Sie arbeitet mit Graustufenbildern, und das Ergebnis ist ein Binärbild, dessen von null verschiedene Pixel die Positionen markieren, an denen die Eingabe die Art von Helligkeitsänderung aufweist, die als Kante zählen soll.

Die Methode nimmt einen edge_type-Parameter, der zwischen zwei Algorithmen wählt:

EDGE_SIMPLE führt einen Hochpassfilter aus, wendet einen Schwellenwert an und gibt das Ergebnis zurück. Schnell, aber die Ausgabe enthält jede Helligkeitsänderung oberhalb des Schwellenwerts, einschließlich Rauschen und Textur, die die Anwendung wahrscheinlich nicht interessieren. Vernünftig für saubere Bilder und für Fälle, in denen das Rauschen durch einen späteren morphologischen Durchlauf bereinigt wird.

EDGE_CANNY führt den Canny-Kantendetektor aus – den klassischen mehrstufigen Algorithmus. Er berechnet den Helligkeitsgradienten, unterdrückt entlang der Gradientenrichtung jede Reaktion, die kein Maximum ist (sodass jede Kante ein Pixel breit ist), und wendet einen Hysterese-Schwellenwert an (sodass eine Kante, die an einer Stelle stark ist, auch dort weiterverfolgt wird, wo sie dazwischen verblasst). Das Ergebnis ist eine saubere, dünne, zusammenhängende Menge von Kantenpixeln, wie sie jeder klassische kantenbasierte Algorithmus haben möchte.

Der threshold-Parameter ist ein zweielementiges Tupel (low, high). Für EDGE_CANNY ist der high-Wert der Grenzwert, oberhalb dessen ein Pixel definitiv eine Kante ist, und der low-Wert ist der Grenzwert, oberhalb dessen ein Pixel nur dann eine Kante ist, wenn es mit einer definitiven verbunden ist. Für EDGE_SIMPLE zählt nur der high-Wert; er ist der einzelne Grenzwert, oberhalb dessen ein Pixel als Kante zählt. Der Standardwert von (100, 200) ist ein Ausgangspunkt, den es sich lohnt für die jeweilige Szene anzupassen.

img.gaussian(1)                                   # pre-smooth
img.find_edges(image.EDGE_CANNY, threshold=(50, 100))

Der Canny-Detektor ist für nahezu jede Anwendung, bei der Kanten eine Rolle spielen, die bessere Wahl. Das schnellere EDGE_SIMPLE lohnt sich, im Gedächtnis zu behalten für die Fälle, in denen die Kosten von Canny ein Problem darstellen und die Rauschunterdrückung seiner Hysterese eigentlich nicht benötigt wird.

5.14.5. Adaptive Schwellenwertbildung mit dem Gauß-Filter

Wie die statistischen Filter akzeptiert gaussian() das Schlüsselwortpaar threshold=True / offset=N für die adaptive Schwellenwertbildung. Das Verhalten ist dasselbe wie bei mean(): Die Gauß-Statistik an jeder Position wird zum lokalen Grenzwert, und das Quellpixel wird mit der Statistik plus dem Offset verglichen, um ein binäres Ergebnis zu erzeugen.

Die Gauß-Variante ist gewöhnlich die sauberste Wahl für die adaptive Schwellenwertbildung, wenn die Eingabe einigermaßen rauschfrei ist. Der gewichtete Durchschnitt liefert einen glatteren Grenzwert als der Mittelwertfilter, mit weniger Artefakten an scharfen Beleuchtungsübergängen.