5.9. Arithmetische Operationen¶
Die Zeichenfamilie im vorherigen Abschnitt malt in ein Bild hinein. Die arithmetische Familie kombiniert zwei Bilder zu einem dritten – indem sie ihre Pixelwerte addiert, eines vom anderen subtrahiert oder an jeder Position das Minimum oder Maximum bildet. Auf dieser kleinen Menge pixelweiser arithmetischer Operationen bauen Frame Differencing, Hintergrundsubtraktion, Belichtungs-Stacking und eine Handvoll weiterer klassischer Muster auf.
Die arithmetische Familie der Image-Klasse ist klein genug, um sie auf einmal aufzuzählen:
add()– pixelweiseself + other, auf das Maximum des Formats begrenzt.sub()– pixelweiseself - other, am unteren Ende auf0begrenzt.rsub()– pixelweiseother - self, auf0begrenzt (dieselbe Arithmetik wiesubmit vertauschten Operanden).min()– pixelweises Minimum der beiden Werte.max()– pixelweises Maximum.difference()– pixelweise|self - other|, die absolute Differenz.
Hinzu kommen zwei verwandte Einzelbild-Operationen:
invert()– ersetzt jedes Pixel durch255 - pixel(oder das entsprechende Maximum für das Format).
Zwei Quellverläufe A und B sowie das Ergebnis jeder paarweisen Operation, die auf sie angewendet wird. Jede Operation läuft Position für Position ab – was im Ergebnis an einer bestimmten Stelle erscheint, hängt nur von den beiden Quellpixeln an dieser Stelle ab.¶
5.9.1. Zwei Operandenformen¶
Jede der Zwei-Bilder-Methoden akzeptiert für ihren zweiten Operanden eine der beiden Formen:
Ein weiteres
Imagederselben Dimensionen. Die Arithmetik läuft Position für Position ab – das Ergebnis bei(x, y)ist die Operation, die auf die Quellpixel bei(x, y)beider Bilder angewendet wird.Ein Skalarwert – ein Integer für Graustufen, ein
(r, g, b)-Tupel für RGB565. Derselbe Skalar wird an jeder Position angewendet.
Die Skalarform ist nützlich, wenn die Anwendung jedes Pixel um einen konstanten Betrag verschieben möchte. img.add(40) hellt das gesamte Bild um 40 auf; img.sub((20, 20, 20)) verdunkelt jedes Pixel um 20 pro Kanal; img.max(50) hebt jedes Pixel unter 50 auf 50 an und lässt die übrigen unverändert – die Art von Operation, die einen nahezu schwarzen Sensorboden in ein flaches Dunkelgrau verwandelt, gegen das nachfolgende Stufen arbeiten können.
5.9.2. Begrenzung¶
Pixelwerte bleiben bei jeder Operation innerhalb des Wertebereichs des Formats. Für einen 8-Bit-Kanal bedeutet das 0 – 255: Alles, was über 255 hinaus überlaufen würde, wird auf 255 zurückbegrenzt, und alles, was unter 0 gehen würde, wird auf 0 hochbegrenzt. Es gibt keinen Wraparound.
Diese Wahl ist in der Praxis von Bedeutung. add, das Pixel aufhellt, erzeugt nie ein plötzliches Verdunkelungsartefakt am hellen Ende, wo die Mathematik andernfalls überlaufen würde; sub, das Pixel verdunkelt, erzeugt nie ein plötzliches Aufhellungsartefakt am dunklen Ende, wo es andernfalls unterlaufen würde. Die Ergebnisse bleiben visuell aussagekräftig, auf Kosten eines gewissen Informationsverlusts an den gesättigten Extremen.
Die Begrenzung ist auch der Grund, warum sub und rsub unterschiedliche Ergebnisse liefern. img_a.sub(img_b) ergibt den Teil von a, der heller als b ist, und überall sonst null; img_a.rsub(img_b) ergibt den Teil von b, der heller als a ist. Beide sind nützlich für die einseitige Änderungserkennung – wenn sich die Anwendung nur für Pixel interessiert, die heller wurden, oder nur für Pixel, die dunkler wurden – aber keines erfasst alle Änderungen zwischen zwei Einzelbildern.
5.9.3. Die Differenzoperation¶
Für die zweiseitige Änderungserkennung ist die Operation der Wahl difference(), die an jeder Position |self - other| berechnet – die absolute, vorzeichenfreie Differenz. Jedes Pixel, das sich in irgendeine Richtung geändert hat, erscheint im Ergebnis als Nicht-Null-Wert, wobei die Größe proportional dazu ist, wie stark es sich an dieser Position geändert hat.
Diese Eigenschaft – nicht null genau dort, wo die beiden Bilder voneinander abweichen – macht difference zum Arbeitspferd der bildweisen Änderungserkennung. Ein beim Start gespeichertes Referenz-Einzelbild und eine frische Aufnahme erzeugen, durch difference geführt, ein Bild, dessen Nicht-Null-Pixel jede Position markieren, an der sich etwas in der Szene bewegt oder die Helligkeit geändert hat.
5.9.4. Eingrenzung mit mask¶
Alle arithmetischen Methoden akzeptieren das Schlüsselwortargument mask, das auf der Seite zu Regionen und Masken eingeführt wurde. Wenn eine Maske übergeben wird, läuft die Operation nur an Positionen, an denen die Maske ungleich null ist; überall sonst bleibt das Zielbild unverändert.
Diese Komposition taucht in zwei Mustern auf. Das erste ist das Einschränken einer Operation auf einen bekannten Bereich: zum Beispiel das Addieren zweier Einzelbilder nur innerhalb des Begrenzungsrahmens einer erkannten Markierung. Das zweite ist das stückweise Aufbauen eines zusammengesetzten Einzelbildes – min über eine Folge von Einzelbildern innerhalb einer Vordergrundmaske, max über dieselbe Folge innerhalb der komplementären Maske – diese Art von Muster.
5.9.5. An Ort und Stelle, unter Erhalt der Eingaben¶
Die arithmetischen Methoden folgen alle der zuvor etablierten Arbeitskonvention: Jede modifiziert das Quellbild an Ort und Stelle und gibt dasselbe Bild zur Verkettung zurück. Die Pixel der Quelle sind nach dem Aufruf verschwunden – ersetzt durch das Ergebnis der Operation gegen das, was als zweiter Operand übergeben wurde.
Wenn die Anwendung beide Eingaben erhalten muss, besteht das sichere Muster darin, zunächst eine von ihnen zu kopieren:
diff = current.copy() # leaves current intact
diff.difference(reference) # diff now holds the absolute difference
Dieses Muster – kopieren, dann operieren – ist das Rückgrat jeder Frame-Differencing-Pipeline, bei der das Referenz-Einzelbild den Vergleich überstehen muss, damit es beim nächsten aufgenommenen Einzelbild wiederverwendet werden kann.
Mit sechs kombinierenden Operationen, zwei Einzelbild-Operationen, einem Arbeitspferd für absolute Differenzen und dem mask-Schlüsselwort zur Eingrenzung deckt das Pixel-Arithmetik-Toolkit die Helligkeits- und Kanalkombinationen ab, die das klassische maschinelle Sehen benötigt. Die verbleibenden arithmetikähnlichen Werkzeuge an der Oberfläche arbeiten Bit für Bit statt Wert für Wert.