5.21. Scalare, oglindire și decupare

Subsecțiunile anterioare au lucrat toate pe pixeli aflați în aceleași poziții în care au început. Familia de transformări schimbă acest lucru. Scalarea trimite fiecare pixel de intrare la o poziție de ieșire diferită, posibil la mai multe poziții de ieșire deodată (la mărire) sau la o poziție partajată cu mai mulți alți pixeli de intrare (la micșorare). Oglindirea și rotația fac același lucru printr-o mapare diferită. Decuparea păstrează un subset dreptunghiular de pixeli de intrare și îl elimină pe restul.

Modulul image expune această familie prin trei metode care împart majoritatea argumentelor și a comportamentului:

  • copy() – produce o copie a imaginii, eventual scalată, decupată sau reorientată.

  • crop() – aceeași operație ca copy, dar cu așteptarea că aplicația va selecta un sub-dreptunghi din sursă.

  • scale() – același lucru, cu așteptarea că aplicația va redimensiona rezultatul.

Cele trei împart aceleași argumente și aceeași mecanică de transformare; diferența este unde ajunge rezultatul în mod implicit. copy() produce o imagine nouă, în timp ce crop() și scale() modifică sursa pe loc.

5.21.1. Argumentele comune

Un singur apel combină orice combinație de scalare, decupare, orientare și extragere de canal pe care o cere aplicația:

x_scale și y_scale scalează intrarea de-a lungul axelor orizontală și verticală independent. Ambele au valoarea implicită 1.0 (fără scalare). Valori diferite pentru fiecare produc o scalare neuniformă – un cadru întins de două ori mai lat decât este de înalt, de exemplu.

roi restrânge intrarea la un dreptunghi al imaginii sursă, preluând doar acei pixeli prin restul transformării. Aceasta este partea de „decupare” a operației: transmiteți un roi pentru a extrage o sub-regiune.

hint este un câmp de biți de indicatori care selectează metoda de interpolare și orice oglindiri de orientare. Mai mulți indicatori se combină prin SAU pe biți (hint=image.BILINEAR | image.HMIRROR). Indicatorii se împart în două grupuri – familia de interpolare și familia de orientare – care nu au nicio legătură unul cu celălalt, dar împart același câmp de biți.

rgb_channel selectează un singur canal al unei surse RGB565. 0 înseamnă roșu, 1 înseamnă verde, 2 înseamnă albastru; rezultatul iese ca o imagine în tonuri de gri care conține doar acel canal. Util pentru aplicarea pragului doar pe canalul roșu, de exemplu.

color_palette și alpha_palette remapează valorile pixelilor printr-o tabelă de căutare la ieșire, la fel cum fac metodele de conversie to_rainbow() și to_ironbow().

copy=True și copy_to_fb=True urmează aceeași convenție pe care o folosește orice altă metodă producătoare de rezultate – pe loc în mod implicit, copy=True alocă un rezultat separat, copy_to_fb=True plasează rezultatul în tamponul de cadre (frame buffer) pentru previzualizarea din IDE.

5.21.2. Interpolare: AREA, BILINEAR, BICUBIC

Atunci când scalarea trimite fiecare pixel de ieșire la o poziție care nu se aliniază cu niciun pixel de intrare anume, metoda trebuie să aleagă ce valoare să scrie. Trei indicatori controlează modul:

image.BILINEAR interpolează între cei patru pixeli de intrare cei mai apropiați ponderați după distanța lor față de poziția de ieșire. Rezultatul este mai neted decât cel cu vecinul cel mai apropiat, fără zimți vizibili pe liniile diagonale, dar aritmetica suplimentară costă de aproximativ patru ori cât trecerea cu vecinul cel mai apropiat. Alegerea potrivită pentru majoritatea lucrărilor de mărire și pentru orice factor de scară neîntreg.

image.BICUBIC interpolează între cei șaisprezece pixeli de intrare cei mai apropiați folosind o curbă cubică, ceea ce produce rezultate și mai netede cu prețul unei aritmetici suplimentare. Cea mai bună calitate pentru aplicațiile sensibile la cost care au nevoie de ea; rareori merită calculul suplimentar pentru cadre live pe care IDE-ul le va doar afișa.

image.AREA mediază fiecare pixel de intrare care cade în interiorul amprentei pixelului de ieșire – algoritmul potrivit pentru micșorare. Interpolarea biliniară și cea bicubică sunt interpolatoare: ele estimează o valoare între pixelii sursă, ceea ce are nevoie mărirea, dar la micșorare fiecare pixel de ieșire acoperă mulți pixeli sursă, iar un interpolator citește doar pe cei câțiva mai apropiați – detaliul pe care îl omite revine ca aliere. image.AREA integrează în schimb fiecare pixel acoperit în medie.

Algoritmul de scalare implicit, fără niciun indiciu, este vecinul cel mai apropiat, care este cel mai ieftin și răspunsul corect atunci când sursa este deja la rezoluția de pixeli a destinației.

5.21.3. Orientare: oglindiri și rotații

Indicatorii de orientare sunt un set mic de transformări booleene care se compun liber între ele și cu indicatorii de interpolare:

  • image.VFLIP oglindește imaginea vertical (partea de sus devine partea de jos).

  • image.HMIRROR o oglindește orizontal (stânga devine dreapta).

  • image.TRANSPOSE interschimbă axele x și y (rândurile devin coloane).

Majoritatea rotațiilor provin din compunerea acestor trei. Modulul expune de asemenea scurtături denumite:

  • image.ROTATE_90 (= VFLIP | TRANSPOSE)

  • image.ROTATE_180 (= HMIRROR | VFLIP)

  • image.ROTATE_270 (= HMIRROR | TRANSPOSE)

În cod:

img.copy(hint=image.ROTATE_90, copy_to_fb=True)

5.21.4. Tratarea raportului de aspect

Atunci când raportul de aspect al sursei nu se potrivește cu dreptunghiul în care este desenată, trei indicatori decid ce să facă cu nepotrivirea:

image.SCALE_ASPECT_KEEP păstrează raportul de aspect al sursei și adaugă benzi negre rezultatului – sursa este scalată până când încape în interiorul destinației, cu pixeli goi (zero) umplând restul destinației. Alegerea potrivită atunci când păstrarea sursei nedistorsionate contează mai mult decât umplerea întregii ieșiri.

image.SCALE_ASPECT_EXPAND păstrează raportul de aspect al sursei și o decupează – sursa este scalată până când umple destinația, cu părțile care depășesc destinația tăiate. Alegerea potrivită atunci când umplerea întregii ieșiri contează mai mult decât vizualizarea fiecărei părți a sursei.

image.SCALE_ASPECT_IGNORE ignoră raportul de aspect și întinde sursa pentru a umple destinația, acceptând orice distorsiune introduce acest lucru. Alegerea potrivită atunci când aplicația a ținut deja cont de distorsiune – de exemplu, când dimensiunile destinației nu sunt de fapt un dreptunghi al aceleiași scene.

Valoarea implicită (fără niciun indicator de aspect setat) este aceeași cu SCALE_ASPECT_IGNORE: întindere pentru umplere. Aplicațiile cărora le pasă de raportul de aspect specifică explicit unul dintre cele trei.

5.21.5. Când să recurgeți la fiecare

Majoritatea redimensionărilor folosesc scale() cu o pereche x_scale / y_scale și un indiciu de interpolare:

img.scale(x_scale=0.5, y_scale=0.5, hint=image.AREA)

Majoritatea rotațiilor folosesc același apel cu hint=image.ROTATE_90 sau similar.

Decuparea folosește crop() cu un roi neimplicit:

img.crop(roi=(40, 30, 200, 150))

Atunci când sursa trebuie să supraviețuiască operației – la capturarea unui cadru de referință, la realizarea unei miniaturi a unui cadru care urmează să fie procesat distructiv – copy() produce rezultatul ca o imagine nouă și lasă sursa neatinsă:

thumbnail = img.copy(x_scale=0.25, y_scale=0.25, hint=image.AREA)

Acea valoare implicită este diferența reală din spatele celor trei nume: scale și crop transformă pe loc, copy alocă. Cuvintele-cheie de plasare a rezultatului fac legătura: copy=True pe scale sau crop alocă rezultatul ca un tampon separat din heap în loc să suprascrie sursa, iar copy_to_fb=True pe oricare dintre cele trei îl plasează în tamponul de cadre (frame buffer) pentru previzualizarea din IDE.