5.21. Skalieren, spiegeln und zuschneiden¶
Die vorherigen Unterabschnitte arbeiteten alle an Pixeln an denselben Positionen, an denen sie begonnen haben. Die Familie der Transformationen ändert das. Das Skalieren verschiebt jeden Eingabepixel an eine andere Ausgabeposition, möglicherweise gleichzeitig an mehrere Ausgabepositionen (beim Hochskalieren) oder an eine Position, die er mit mehreren anderen Eingabepixeln teilt (beim Herunterskalieren). Spiegeln und Drehen tun dasselbe über eine andere Abbildung. Das Zuschneiden behält eine rechteckige Teilmenge von Eingabepixeln und verwirft den Rest.
Das image-Modul stellt diese Familie über drei Methoden bereit, die die meisten ihrer Argumente und ihres Verhaltens teilen:
copy()– erzeugt eine Kopie des Bildes, möglicherweise skaliert, zugeschnitten oder neu ausgerichtet.crop()– dieselbe Operation wiecopy, aber mit der Erwartung, dass die Anwendung ein Teilrechteck aus der Quelle herausgreift.scale()– dasselbe noch einmal, mit der Erwartung, dass die Anwendung das Ergebnis in der Größe ändert.
Die drei teilen sich dieselben Argumente und dieselbe Transformationsmechanik; der Unterschied liegt darin, wo das Ergebnis standardmäßig landet. copy() erzeugt ein neues Bild, während crop() und scale() die Quelle direkt ändern.
5.21.2. Interpolation: AREA, BILINEAR, BICUBIC¶
Wenn die Skalierung jeden Ausgabepixel an eine Position verschiebt, die nicht mit einem einzelnen Eingabepixel zusammenfällt, muss die Methode entscheiden, welchen Wert sie schreibt. Drei Flags steuern, wie:
image.BILINEAR interpoliert zwischen den vier nächstgelegenen Eingabepixeln, gewichtet nach ihrem Abstand von der Ausgabeposition. Das Ergebnis ist glatter als bei Nächster-Nachbar, ohne sichtbare Treppenstufen an diagonalen Linien, aber die zusätzliche Arithmetik kostet etwa das Vierfache des Nächster-Nachbar-Durchgangs. Die richtige Wahl für die meisten Hochskalierungsarbeiten und für jeden nicht ganzzahligen Skalierungsfaktor.
image.BICUBIC interpoliert zwischen den sechzehn nächstgelegenen Eingabepixeln mithilfe einer kubischen Kurve, was noch glattere Ergebnisse zum Preis nochmals erhöhter Arithmetik erzeugt. Beste Qualität für die kostensensiblen Anwendungen, die sie benötigen; selten den zusätzlichen Rechenaufwand wert für Live-Einzelbilder, die die IDE nur anzeigt.
image.AREA mittelt jeden Eingabepixel, der in die Grundfläche des Ausgabepixels fällt – der richtige Algorithmus zum Herunterskalieren. Bilinear und bikubisch sind Interpolatoren: Sie schätzen einen Wert zwischen Quellpixeln, was beim Hochskalieren benötigt wird, aber beim Herunterskalieren überdeckt jeder Ausgabepixel viele Quellpixel, und ein Interpolator liest nur die wenigen nächstgelegenen aus – die übersprungenen Details kommen als Aliasing zurück. image.AREA faltet stattdessen jeden überdeckten Pixel in den Mittelwert.
Der Standard-Skalierungsalgorithmus ohne jeden Hinweis ist Nächster-Nachbar, der der günstigste und die richtige Antwort ist, wenn die Quelle bereits in der Pixelauflösung des Ziels vorliegt.
5.21.3. Ausrichtung: Spiegelungen und Drehungen¶
Die Ausrichtungs-Flags sind eine kleine Menge boolescher Transformationen, die sich frei miteinander und mit den Interpolations-Flags kombinieren lassen:
image.VFLIPspiegelt das Bild vertikal (oben wird zu unten).image.HMIRRORspiegelt es horizontal (links wird zu rechts).image.TRANSPOSEvertauscht die x- und y-Achse (Zeilen werden zu Spalten).
Die meisten Drehungen entstehen durch das Zusammensetzen dieser drei. Das Modul stellt außerdem benannte Abkürzungen bereit:
image.ROTATE_90(=VFLIP | TRANSPOSE)image.ROTATE_180(=HMIRROR | VFLIP)image.ROTATE_270(=HMIRROR | TRANSPOSE)
Im Code:
img.copy(hint=image.ROTATE_90, copy_to_fb=True)
5.21.4. Behandlung des Seitenverhältnisses¶
Wenn das Seitenverhältnis der Quelle nicht mit dem Rechteck übereinstimmt, in das sie gezeichnet wird, entscheiden drei Flags, was mit der Diskrepanz geschehen soll:
image.SCALE_ASPECT_KEEP erhält das Seitenverhältnis der Quelle und versieht das Ergebnis mit Letterbox-Rändern – die Quelle wird skaliert, bis sie in das Ziel passt, wobei leere (Null-)Pixel den Rest des Ziels füllen. Die richtige Wahl, wenn es wichtiger ist, die Quelle unverzerrt zu halten, als die gesamte Ausgabe zu füllen.
image.SCALE_ASPECT_EXPAND erhält das Seitenverhältnis der Quelle und schneidet sie zu – die Quelle wird skaliert, bis sie das Ziel füllt, wobei die Teile, die über das Ziel hinausragen, abgeschnitten werden. Die richtige Wahl, wenn es wichtiger ist, die gesamte Ausgabe zu füllen, als jeden Teil der Quelle zu sehen.
image.SCALE_ASPECT_IGNORE ignoriert das Seitenverhältnis und streckt die Quelle, um das Ziel zu füllen, und nimmt dabei jede dadurch entstehende Verzerrung in Kauf. Die richtige Wahl, wenn die Anwendung die Verzerrung bereits berücksichtigt hat – zum Beispiel, wenn die Abmessungen des Ziels nicht tatsächlich ein Rechteck derselben Szene sind.
Der Standard (kein Seitenverhältnis-Flag gesetzt) ist dasselbe wie SCALE_ASPECT_IGNORE: strecken, um zu füllen. Anwendungen, denen das Seitenverhältnis wichtig ist, geben eines der drei ausdrücklich an.
5.21.5. Wann man welche wählt¶
Die meisten Größenänderungen verwenden scale() mit einem x_scale / y_scale-Paar und einem Interpolationshinweis:
img.scale(x_scale=0.5, y_scale=0.5, hint=image.AREA)
Die meisten Drehungen verwenden denselben Aufruf mit hint=image.ROTATE_90 oder ähnlich.
Das Zuschneiden verwendet crop() mit einem nicht standardmäßigen roi:
img.crop(roi=(40, 30, 200, 150))
Wenn die Quelle die Operation überstehen muss – beim Erfassen eines Referenz-Einzelbilds, beim Erstellen eines Miniaturbilds eines Einzelbilds, das gleich destruktiv verarbeitet werden soll –, erzeugt copy() das Ergebnis als neues Bild und lässt die Quelle unberührt:
thumbnail = img.copy(x_scale=0.25, y_scale=0.25, hint=image.AREA)
Dieser Standard ist der eigentliche Unterschied hinter den drei Namen: scale und crop transformieren direkt, copy reserviert. Die Schlüsselwörter zur Ergebnisplatzierung überbrücken die Lücke: copy=True bei scale oder crop reserviert das Ergebnis als separaten Heap-Puffer, anstatt die Quelle zu überschreiben, und copy_to_fb=True bei jeder der drei legt es im Framebuffer für die IDE-Vorschau ab.