5.7. Bilder zusammensetzen¶
Die Zeichenprimitive der vorherigen Seite malen geometrische Markierungen auf ein Bild – eine Linie, ein Rechteck, ein Stück Text. Das deckt die meisten Annotationen ab, die ein Algorithmus sichtbar machen muss, aber nicht alle. Manchmal ist die Annotation selbst ein Bild: ein erfasstes Referenz-Einzelbild, das neben dem aktuellen angezeigt werden soll, ein Vorschaubild einer früheren Aufnahme, das in einer Ecke der Vorschau erscheint, oder eine zuvor gespeicherte Vorlage, die zur Kalibrierung über ein Live-Einzelbild gelegt wird. Der Mechanismus, um ein Bild auf ein anderes zu zeichnen, ist eine einzige Methode – draw_image() – mit genügend Parametern, um die Position, die Skalierung, die Farbpalette und die Transparenz zu handhaben, die eine echte Komposition benötigt.
5.7.1. Der grundlegende Aufruf¶
In seiner einfachsten Form nimmt draw_image ein weiteres Image und eine Position, an der es gezeichnet werden soll:
reference = image.Image("/sdcard/reference.bmp")
img.draw_image(reference, x=10, y=10)
Das Ziel ist img; die Quelle ist reference; der oberste linke Pixel der Quelle landet bei (10, 10) von img, und die übrigen Pixel der Quelle folgen von dort aus nach rechts und nach unten. Pixel des Ziels, die von der Quelle überdeckt werden, werden mit den entsprechenden Pixeln der Quelle überschrieben; Pixel außerhalb des Bereichs der Quelle bleiben unverändert.
Wenn die Quelle über den Rand des Ziels hinausreicht, werden die herausfallenden Teile stillschweigend abgeschnitten – dasselbe nachsichtige Verhalten, das set_pixel bei Positionen außerhalb des gültigen Bereichs zeigt. Der Anwendungscode muss die Position nicht im Voraus auf die Bildabmessungen begrenzen; er kann die gewünschte Position übergeben und die Methode das Abschneiden erledigen lassen.
5.7.2. Eine Datei direkt laden¶
draw_image akzeptiert anstelle des Image-Arguments einen Dateipfad und lädt die Datei, bevor es sie zusammensetzt:
img.draw_image("/sdcard/reference.bmp", x=10, y=10)
Das sieht nach einer Bequemlichkeit aus – eine Zeile statt zwei – und das ist es auch, aber der Unterschied ist mehr als nur Syntax. Das Erstellen eines Image aus einer Datei reserviert einen Puffer, der die dekodierten Pixel aufnimmt, und dieser Puffer bleibt bestehen, bis die Garbage Collection ihn freigibt. Wenn man den Pfad direkt an draw_image übergibt, kann das Modul die Datei in einen Scratch-Puffer dekodieren, daraus zusammensetzen und den Puffer freigeben, wenn der Aufruf zurückkehrt, ohne dass der Anwendungscode zwischen den Einzelbildern eine Referenz auf ein separates Image halten muss.
5.7.3. Skalierung¶
Wenn Quelle und Ziel unterschiedliche Größen haben – eine Aufnahme mit niedriger Auflösung, die auf eine Leinwand mit höherer Auflösung zusammengesetzt wird, oder ein Vorschaubild, das auf einen bestimmten Bruchteil des Einzelbildes dimensioniert werden muss – sorgen zwei Skalierungsparameter für die Größenanpassung der Quelle beim Zeichnen:
img.draw_image(reference, x=10, y=10, x_scale=2.0, y_scale=2.0)
x_scale und y_scale sind unabhängige Fließkommawerte; werden beide auf denselben Wert gesetzt, skaliert dies gleichmäßig, und werden unterschiedliche Werte übergeben, wird die Quelle entlang einer Achse gedehnt oder gestaucht. Die Skalierung erfolgt zur Zeichenzeit; die Quelle reference wird nicht verändert.
Eine Bitmaske von Hinweis-Flags entscheidet, wie die Skalierung tatsächlich zwischen Pixeln interpoliert. image.BILINEAR erzeugt glattere Ergebnisse auf Kosten von mehr Rechenaufwand; image.BICUBIC erzeugt noch glattere Ergebnisse und kostet abermals mehr; der Standard verwendet Nearest-Neighbour, was am günstigsten und die richtige Wahl ist, wenn die Quelle bereits in der Pixelauflösung des Ziels vorliegt. Flags zur Behandlung des Seitenverhältnisses – SCALE_ASPECT_KEEP, SCALE_ASPECT_EXPAND, SCALE_ASPECT_IGNORE – entscheiden, was zu tun ist, wenn das Seitenverhältnis der Quelle nicht mit dem Rechteck übereinstimmt, in das sie gezeichnet wird.
5.7.4. Alpha-Blending¶
Standardmäßig ersetzt draw_image Zielpixel durch Quellpixel. Wenn das Ziel ein durchscheinendes Overlay ist – sodass das Ziel durch die Quelle hindurchscheint – steuert der Parameter alpha, wie die beiden gemischt werden. alpha=0 zeigt nur das Ziel (keine Quelle); alpha=255 ist der Standard und zeigt nur die Quelle (vollständige Ersetzung); Zwischenwerte mischen die beiden proportional:
img.draw_image(overlay, x=0, y=0, alpha=128)
Ein separates Argument alpha_palette ist der einzige Per-Pixel-Alpha-Mechanismus des Moduls. Es nimmt ein GRAYSCALE-Bild, dessen Werte an der entsprechenden Position in der Quelle als Alpha verwendet werden – zum Beispiel eine Heatmap, deren Alpha mit ihrer Intensität variiert. Das Alpha muss als dieses separate Graustufenargument übergeben werden; ein Quellbild, das seinen eigenen Alphakanal mitführt (etwa ein PNG mit Transparenz), bringt diesen nicht automatisch ein.
5.7.5. Quell-ROI und Palette¶
Zwei weitere Parameter runden den Kompositionsmechanismus ab:
roi=(x, y, w, h)beschränkt die Quelle auf ein Teilrechteck ihrer selbst, sodass nur dieses Rechteck auf das Ziel zusammengesetzt wird. Nützlich zum Zuschneiden innerhalb desselben Aufrufs, ohne ein zugeschnittenes Zwischenbild vorzubereiten.color_paletteersetzt vor dem Zeichnen den Wert jedes Quellpixels über eine Nachschlagetabelle – derselbe Mechanismus, dento_rainbow()undto_ironbow()verwenden, hier offengelegt, damit ein Overlay auf seinem Weg auf das Ziel ohne einen separaten Konvertierungsdurchgang palettisiert werden kann.
Beide lassen sich mit allem anderen im Aufruf kombinieren: der Skalierung, dem Alpha, dem zielseitigen Argument mask und dem zielseitigen Parameter roi, der den Schreibvorgang auf ein Rechteck des Ziels beschränkt.