5.20. Regression und Ähnlichkeit¶
Zwei weitere Messungen auf der Klasse Image fassen das Bild als etwas anderes als eine Verteilung von Pixelwerten zusammen. Lineare Regression von schwellenwertgefilterten Pixeln liefert einer Anwendung eine Linie, auf die sie reagieren kann – die klassische Eingabe für einen linienfolgenden Roboter. Die Ähnlichkeitsmessung liefert einer Anwendung eine einzelne Zahl, die beschreibt, wie ähnlich zwei Bilder sind – die natürliche Eingabe für einen Golden-Image-Regressionstest oder einen Detektor für grobe Veränderungen.
5.20.1. Lineare Regression¶
Wenn die Vordergrundpixel eines Bildes zufällig eine Linie über das Einzelbild bilden – das Klebeband auf einer Strecke, der ein Roboter folgt, die Linie eines Horizonts, der Rand einer Straße oder eines Korridors –, möchte die Anwendung in der Regel nicht jeden einzelnen Vordergrundpixel. Sie möchte die Ausgleichsgerade durch sie alle, parametrisiert, sodass sie entscheiden kann, wie die Linie ausgerichtet ist und wo sie das Einzelbild kreuzt.
get_regression() führt diese Anpassung durch. Sie nimmt dieselbe Schwellenwert-Tupelform entgegen, die binary() und find_blobs() verwenden, identifiziert jedes Pixel, das dem Schwellenwert entspricht, und gibt ein einzelnes line-Ergebnis zurück, das die Ausgleichsgerade durch diese Pixel beschreibt:
line = img.get_regression([(0, 60)])
if line:
img.draw_line((line.x1(), line.y1(),
line.x2(), line.y2()),
color=(255, 0, 0))
Die Anpassung ist eine Theil-Sen-Lineare-Regression – eine robuste Methode, die Ausreißer besser toleriert als die bekanntere Methode der kleinsten Quadrate. Eine kleine Handvoll Pixel weit von der wahren Linie entfernt verzerrt das Ergebnis nicht so, wie sie es bei den kleinsten Quadraten tun würden, was der Realität eines verrauschten Vordergrunds einer echten Schwellenwertausgabe entspricht.
Das line-Ergebnis trägt die auf das Bildrechteck zugeschnittenen Endpunkte (x1, y1, x2, y2), die Linienlänge und -magnitude (length, magnitude) sowie die geometrische Beschreibung der Linie in Polarform (theta, rho) – den Winkel der Linie zur Horizontalen und ihren senkrechten Abstand vom Ursprung. Die Polarform ist das, was eine Regelschleife in der Regel benötigt: theta sagt dem Roboter, in welche Richtung sich die Linie neigt, rho sagt ihm, wo die Linie das Bild kreuzt, und eine Rückkopplungsschleife über die beiden hält den Roboter mittig auf der Linie.
Eine Handvoll Schlüsselwortargumente stimmen die Robustheit und die Kosten ab. x_stride und y_stride überspringen Pixel während der Anpassung – größere Schrittweiten machen die Regression günstiger auf Kosten der Anpassung von weniger Pixeln. area_threshold und pixels_threshold verwerfen Linien, hinter denen nicht genügend übereinstimmende Pixel stehen. target_size skaliert die Eingabe vor der Anpassung auf eine kleinere Größe um – die Regression läuft schneller auf einem 80-mal-60-Surrogat des Bildes, ohne viel an Genauigkeit der Linienrichtung einzubüßen.
Wenn keine akzeptable Linie angepasst werden konnte – wenn der Schwellenwert keine Pixel traf oder ein Muster traf, das nicht wie eine Linie aussieht –, gibt die Methode None zurück. Echter linienfolgender Code sichert jeden get_regression()-Aufruf mit einer None-Prüfung ab, bevor er auf die Attribute der Linie zugreift.
5.20.2. Bildähnlichkeit¶
Eine andere Art von Messung: Anstatt zu fragen „was enthält das Bild?“, fragt man „wie ähnlich sind diese beiden Bilder?“. Die Operation, zu der man greift, ist get_similarity(), die den Structural Similarity Index (SSIM) zwischen dem Quellbild und einem Referenzbild berechnet.
s = img.get_similarity(reference)
print(s.mean, s.stdev)
SSIM ist die Standardmetrik für Bildähnlichkeit, die in der gesamten Bildverarbeitung verwendet wird, weil sie sich so verhält, wie sich die menschliche Intuition über Ähnlichkeit verhält – eine kleine Verschiebung oder eine kleine Helligkeitsänderung verringert die Bewertung leicht, während eine große strukturelle Veränderung (fehlendes Objekt, andere Szene) sie drastisch verringert. Die Bewertung reicht von -1 bis +1: +1 bedeutet, dass die beiden Bilder identisch sind, 0 bedeutet, dass sie unabhängig sind, und -1 bedeutet, dass sie strukturell entgegengesetzt sind. Ein zurückgegebenes similarity-Objekt stellt das mittlere SSIM über das Bild sowie die Standardabweichung, das Minimum und das Maximum der kachelweisen Bewertungen bereit.
Für die Art von Vergleich, bei der eine kleine Zahl besser ist als eine große – ein Regressionstest, der bei „nichts hat sich geändert“ null melden und ansteigen sollte, je mehr Veränderungen sich anhäufen –, gibt das dssim=True-Flag die strukturelle Unähnlichkeit zurück: das mittlere SSIM von 1 subtrahiert, sodass der Rückgabewert für identische Bilder 0.0 beträgt und ansteigt, je mehr sie sich unterscheiden.
5.20.3. Anwendungsfälle für SSIM¶
Die beiden häufigen Anwendungen:
Golden-Image-Regressionstests. Ein Test-Framework erfasst ein Referenz-Einzelbild unter als gut bekannten Bedingungen und speichert es als Golden Image. Nachfolgende Testläufe erfassen unter denselben Bedingungen und vergleichen mit dem Golden Image mittels SSIM. Eine Bewertung über einem bestimmten Schwellenwert (0.95 oder 0.98 je nach Toleranz) ist ein Bestehen; darunter ist ein Durchfallen. Das Test-Framework muss nicht wissen, was sich geändert hat – die SSIM-Bewertung ist das Signal.
Erkennung grober Veränderungen. Eine Anwendung, die eine gröbere Version der Einzelbild-Differenzbildung möchte – eine, die kleine Helligkeitsänderungen ignoriert, aber auf große strukturelle Veränderungen reagiert –, kann SSIM gegen ein Referenz-Einzelbild verwenden, anstatt die pixelweise difference() gefolgt von einem Schwellenwert. SSIM ist weniger empfindlich gegenüber Lichtdrift als die pixelweise Differenzbildung, was es zur besseren Wahl macht, wenn das Ziel darin besteht, zu erkennen, dass „die Szene wesentlich anders aussieht“, statt „irgendein einzelnes Pixel hat sich geändert“.
Beide Anwendungen verwenden denselben Aufruf – img.get_similarity(reference) – und lösen bei einem Schwellenwert der zurückgegebenen Bewertung aus. Der Unterschied besteht lediglich darin, ob der Schwellenwert hoch ist (Regressionstest, der nach einer nahezu identischen Übereinstimmung sucht) oder niedrig (Veränderungserkennung, die nach einer beliebigen großen strukturellen Veränderung sucht).
5.20.4. Die Transformieren-und-Vergleichen-Form¶
Eine nützliche Feinheit: get_similarity() akzeptiert dieselben Parameter x, y, x_scale, y_scale, roi, rgb_channel, alpha, color_palette, alpha_palette, hint und transform wie draw_image(). Das Referenzbild wird durch diese Parameter positioniert, skaliert und transformiert, bevor der SSIM-Vergleich läuft.
Das bedeutet, eine Anwendung kann fragen „wie ähnlich ist diese Szene einem Referenz-Einzelbild nach einer bekannten Verschiebung / Drehung / Skalierung“, ohne ein vortransformiertes Referenzbild vorzubereiten. Es ist der günstige Weg, einen Tracker zu bauen, der einen Parameterraum durchsucht und meldet, welche Transformation der Referenz am besten zum aktuellen Einzelbild passt.