5.7. Composizione di immagini¶
Le primitive di disegno della pagina precedente dipingono segni geometrici su un’immagine: una linea, un rettangolo, una porzione di testo. Questo copre la maggior parte delle annotazioni che un algoritmo deve rendere visibili, ma non tutte. A volte l’annotazione è essa stessa un’immagine: un frame di riferimento acquisito da mostrare affiancato a quello corrente, una miniatura di un’acquisizione precedente mostrata in un angolo dell’anteprima, un template memorizzato in precedenza visualizzato sopra un frame dal vivo per la calibrazione. Il meccanismo per disegnare un’immagine su un’altra è un unico metodo, draw_image(), con un numero di parametri sufficiente a gestire la posizione, la scalatura, la palette di colori e la trasparenza richieste da una composizione reale.
5.7.1. La chiamata di base¶
Nella sua forma più semplice, draw_image accetta un’altra Image e una posizione in cui disegnarla:
reference = image.Image("/sdcard/reference.bmp")
img.draw_image(reference, x=10, y=10)
La destinazione è img; la sorgente è reference; il pixel in alto a sinistra della sorgente si colloca a (10, 10) di img, e i restanti pixel della sorgente seguono verso destra e verso il basso a partire da lì. I pixel della destinazione coperti dalla sorgente vengono sovrascritti con i pixel corrispondenti della sorgente; i pixel al di fuori dell’ingombro della sorgente restano invariati.
Se la sorgente si estende oltre il bordo della destinazione, le parti che fuoriescono vengono ritagliate silenziosamente, lo stesso comportamento indulgente che set_pixel mostra per le posizioni fuori intervallo. Il codice applicativo non deve limitare in anticipo la posizione alle dimensioni dell’immagine; può passare la posizione desiderata e lasciare che il metodo si occupi del ritaglio.
5.7.2. Caricare un file inline¶
draw_image accetta un percorso di file al posto dell’argomento Image e carica il file prima di comporlo:
img.draw_image("/sdcard/reference.bmp", x=10, y=10)
Sembra una comodità - una riga invece di due - e lo è, ma la differenza va oltre la sintassi. Costruire un’immagine Image da un file alloca un buffer per contenere i pixel decodificati, e quel buffer sopravvive finché il garbage collection non lo rilascia. Passare il percorso direttamente a draw_image consente al modulo di decodificare il file in un buffer temporaneo, comporre a partire da esso e rilasciare il buffer al termine della chiamata, senza che il codice applicativo debba mantenere un riferimento a un’immagine Image separata tra un frame e l’altro.
5.7.3. Scalatura¶
Quando la sorgente e la destinazione hanno dimensioni diverse - un’acquisizione a bassa risoluzione composta su un canvas a risoluzione più alta, o una miniatura che deve essere dimensionata a una particolare frazione del frame - due parametri di scala si occupano di ridimensionare la sorgente mentre viene disegnata:
img.draw_image(reference, x=10, y=10, x_scale=2.0, y_scale=2.0)
x_scale e y_scale sono valori float indipendenti; passarli entrambi allo stesso valore scala in modo uniforme, mentre passare valori diversi allunga o restringe la sorgente lungo un asse. La scalatura avviene al momento del disegno; la sorgente reference non viene modificata.
Una bitmask di flag di suggerimento (hint) decide come la scalatura interpola effettivamente tra i pixel. image.BILINEAR produce risultati più morbidi al costo di un calcolo maggiore; image.BICUBIC produce risultati ancora più morbidi a un costo ulteriormente superiore; il valore predefinito usa l’interpolazione nearest-neighbour, che è la più economica e la scelta giusta quando la sorgente è già alla risoluzione in pixel della destinazione. I flag di gestione dell’aspetto - SCALE_ASPECT_KEEP, SCALE_ASPECT_EXPAND, SCALE_ASPECT_IGNORE - decidono cosa fare quando il rapporto d’aspetto della sorgente non corrisponde al rettangolo in cui viene disegnata.
5.7.4. Fusione alfa (alpha blending)¶
Per impostazione predefinita, draw_image sostituisce i pixel della destinazione con i pixel della sorgente. Quando l’obiettivo è un overlay traslucido - in modo che la destinazione traspaia attraverso la sorgente - il parametro alpha controlla come i due vengono mescolati. alpha=0 mostra solo la destinazione (nessuna sorgente); alpha=255 è il valore predefinito e mostra solo la sorgente (sostituzione completa); i valori intermedi mescolano i due in modo proporzionale:
img.draw_image(overlay, x=0, y=0, alpha=128)
Un argomento alpha_palette separato è l’unico meccanismo del modulo per gestire l’alfa per singolo pixel. Accetta un’immagine GRAYSCALE i cui valori vengono usati come alfa nella posizione corrispondente della sorgente - una heatmap il cui alfa varia con la sua intensità, per esempio. L’alfa deve essere fornito come quell’argomento separato in scala di grigi; un’immagine sorgente che porta con sé il proprio canale alfa (un PNG con trasparenza, ad esempio) non lo trasporta automaticamente.
5.7.5. ROI e palette della sorgente¶
Altri due parametri completano il meccanismo di composizione:
roi=(x, y, w, h)limita la sorgente a un sotto-rettangolo di se stessa, in modo che solo quel rettangolo venga composto sulla destinazione. Utile per ritagliare all’interno della stessa chiamata, senza preparare un intermedio già ritagliato.color_palettesostituisce il valore di ciascun pixel della sorgente tramite una tabella di lookup prima del disegno - lo stesso meccanismo usato dato_rainbow()eto_ironbow(), esposto qui in modo che un overlay possa essere convertito in palette durante il suo trasferimento sulla destinazione senza un passaggio di conversione separato.
Entrambi si compongono con tutto il resto della chiamata: la scalatura, l’alfa, l’argomento mask lato destinazione e il parametro roi lato destinazione che limita la scrittura a un rettangolo della destinazione.