5.21. Ridimensionamento, ribaltamento e ritaglio¶
Le sottosezioni precedenti lavoravano tutte sui pixel nelle stesse posizioni da cui partivano. La famiglia delle trasformazioni cambia questo. Il ridimensionamento invia ogni pixel di input a una posizione di output diversa, possibilmente a più posizioni di output contemporaneamente (in caso di ingrandimento) o a una posizione condivisa con diversi altri pixel di input (in caso di riduzione). Il ribaltamento e la rotazione fanno la stessa cosa attraverso una mappatura diversa. Il ritaglio conserva un sottoinsieme rettangolare di pixel di input e scarta il resto.
Il modulo image espone questa famiglia attraverso tre metodi che condividono la maggior parte dei loro argomenti e la maggior parte del loro comportamento:
copy()– produce una copia dell’immagine, eventualmente ridimensionata, ritagliata o ri-orientata.crop()– la stessa operazione dicopy, ma con l’aspettativa che l’applicazione estragga un sotto-rettangolo dalla sorgente.scale()– di nuovo la stessa, con l’aspettativa che l’applicazione ridimensioni il risultato.
I tre condividono gli stessi argomenti e lo stesso meccanismo di trasformazione; la differenza è dove finisce il risultato per impostazione predefinita. copy() produce una nuova immagine, mentre crop() e scale() modificano la sorgente sul posto.
5.21.2. Interpolazione: AREA, BILINEAR, BICUBIC¶
Quando il ridimensionamento invia ogni pixel di output a una posizione che non si allinea con alcun singolo pixel di input, il metodo deve scegliere quale valore scrivere. Tre flag controllano come:
image.BILINEAR interpola tra i quattro pixel di input più vicini, pesati in base alla loro distanza dalla posizione di output. Il risultato è più morbido del nearest-neighbour, senza scalettature visibili sulle linee diagonali, ma l’aritmetica aggiuntiva costa circa quattro volte il passaggio nearest-neighbour. La scelta giusta per la maggior parte dei lavori di ingrandimento e per qualsiasi fattore di scala non intero.
image.BICUBIC interpola tra i sedici pixel di input più vicini usando una curva cubica, il che produce risultati ancora più morbidi a costo di ulteriore aritmetica. La migliore qualità per le applicazioni sensibili al costo che ne hanno bisogno; raramente vale il calcolo aggiuntivo per i frame in tempo reale che l’IDE si limiterà a visualizzare.
image.AREA media ogni pixel di input che ricade all’interno dell’impronta del pixel di output – l’algoritmo giusto per la riduzione. Bilinear e bicubic sono interpolatori: stimano un valore tra i pixel sorgente, che è ciò di cui ha bisogno l’ingrandimento, ma in caso di riduzione ogni pixel di output copre molti pixel sorgente e un interpolatore ne legge solo i pochi più vicini – il dettaglio che salta riemerge come aliasing. image.AREA ripiega invece ogni pixel coperto nella media.
L’algoritmo di ridimensionamento predefinito senza alcun hint è il nearest-neighbour, che è il più economico e la risposta giusta quando la sorgente è già alla risoluzione in pixel della destinazione.
5.21.3. Orientamento: ribaltamenti e rotazioni¶
I flag di orientamento sono un piccolo insieme di trasformazioni booleane che si compongono liberamente tra loro e con i flag di interpolazione:
image.VFLIPribalta l’immagine verticalmente (l’alto diventa il basso).image.HMIRRORla specchia orizzontalmente (la sinistra diventa la destra).image.TRANSPOSEscambia gli assi x e y (le righe diventano colonne).
La maggior parte delle rotazioni deriva dalla composizione di questi tre. Il modulo espone anche delle scorciatoie con nome:
image.ROTATE_90(=VFLIP | TRANSPOSE)image.ROTATE_180(=HMIRROR | VFLIP)image.ROTATE_270(=HMIRROR | TRANSPOSE)
In codice:
img.copy(hint=image.ROTATE_90, copy_to_fb=True)
5.21.4. Gestione del rapporto d’aspetto¶
Quando il rapporto d’aspetto della sorgente non corrisponde al rettangolo in cui viene disegnata, tre flag decidono cosa fare con la discrepanza:
image.SCALE_ASPECT_KEEP preserva il rapporto d’aspetto della sorgente e applica il letterboxing al risultato – la sorgente viene ridimensionata finché non rientra nella destinazione, con pixel vuoti (zero) che riempiono il resto della destinazione. La scelta giusta quando mantenere la sorgente non distorta conta più che riempire tutto l’output.
image.SCALE_ASPECT_EXPAND preserva il rapporto d’aspetto della sorgente e la ritaglia – la sorgente viene ridimensionata finché non riempie la destinazione, con le parti che si estendono oltre la destinazione tagliate via. La scelta giusta quando riempire tutto l’output conta più che vedere ogni parte della sorgente.
image.SCALE_ASPECT_IGNORE ignora il rapporto d’aspetto e allunga la sorgente per riempire la destinazione, accettando qualunque distorsione ne derivi. La scelta giusta quando l’applicazione ha già tenuto conto della distorsione – quando le dimensioni della destinazione non sono in realtà un rettangolo della stessa scena, per esempio.
L’impostazione predefinita (nessun flag di aspetto impostato) è la stessa di SCALE_ASPECT_IGNORE: allungare per riempire. Le applicazioni a cui interessa il rapporto d’aspetto ne specificano esplicitamente uno tra i tre.
5.21.5. Quando usare quale¶
La maggior parte dei ridimensionamenti usa scale() con una coppia x_scale / y_scale e un hint di interpolazione:
img.scale(x_scale=0.5, y_scale=0.5, hint=image.AREA)
La maggior parte delle rotazioni usa la stessa chiamata con hint=image.ROTATE_90 o simile.
Il ritaglio usa crop() con un roi non predefinito:
img.crop(roi=(40, 30, 200, 150))
Quando la sorgente deve sopravvivere all’operazione – acquisendo un frame di riferimento, prendendo una miniatura di un frame che sta per essere elaborato in modo distruttivo – copy() produce il risultato come nuova immagine e lascia intatta la sorgente:
thumbnail = img.copy(x_scale=0.25, y_scale=0.25, hint=image.AREA)
Questa impostazione predefinita è la vera differenza dietro i tre nomi: scale e crop trasformano sul posto, copy alloca. Le parole chiave di posizionamento del risultato colmano il divario: copy=True su scale o crop alloca il risultato come buffer separato nell’heap invece di sovrascrivere la sorgente, e copy_to_fb=True su uno qualsiasi dei tre lo colloca nel frame buffer per l’anteprima nell’IDE.