Stagione 93 / Episodio 5
Il vetro
La trasparenza in WebP: il quarto canale in VP8L, il chunk ALPH accanto alla compressione con perdita, filtri e qualità dell'alfa, e come evitare aloni e frange sui bordi.
Note dell'episodio
Bentornati in bottega. Nei mosaici più preziosi non ci sono solo pietre: ci sono tessere di vetro. Alcune opache, altre traslucide, che lasciano intravedere ciò che sta dietro. Il vetro è la tessera più delicata da lavorare, e anche quella che cambia di più l'effetto finale.
Nelle immagini digitali il vetro si chiama canale alfa. È il quarto valore di ogni pixel, accanto a rosso, verde e blu, e dice quanto quel pixel copre lo sfondo: zero vuol dire completamente trasparente, duecentocinquantacinque completamente opaco, e tutti i valori intermedi sono vetro più o meno colorato.
Oggi vediamo come WebP gestisce il vetro, perché è proprio qui che il formato offre qualcosa che, nel 2010, nessun altro formato del web poteva offrire.
Ricordate il problema del primo episodio. Una fotografia di prodotto con lo sfondo trasparente. Una scarpa, una bottiglia, un mobile, da posare sopra uno sfondo colorato che cambia a seconda della pagina.
Con JPEG la trasparenza non c'è proprio. Con PNG c'è, ma il PNG comprime senza perdita anche tutte le sfumature della foto, e una scarpa fotografata bene in PNG può pesare un megabyte o più. Per anni la soluzione è stata una di queste due: un PNG enorme, oppure un JPEG con lo sfondo bianco "finto", che diventava un rettangolo bianco appena lo mettevate su uno sfondo diverso.
WebP ha risolto con un'idea semplice: comprimere il colore con perdita e la trasparenza senza perdita, separatamente, nello stesso file. La scarpa si comprime come una foto, e il suo contorno resta preciso come in un PNG.
Quando l'immagine è compressa senza perdita, nel chunk VP8L, la trasparenza non ha bisogno di niente di speciale. Ogni pixel ha già i suoi quattro canali, alfa compreso, e tutto quello che abbiamo visto nell'episodio scorso vale anche per il vetro: predizione, copie, cache, codici di Huffman.
In un'icona o in un logo, l'alfa è fatto di grandi zone a zero, grandi zone a duecentocinquantacinque e un sottile bordo di valori intermedi. È molto comprimibile e costa poco.
Quando l'immagine è compressa con perdita, invece, c'è un problema. Il bitstream VP8 non sa cos'è l'alfa: è nato per il video, e i video non hanno trasparenza.
La soluzione di WebP è un chunk separato, chiamato ALPH, che nel formato esteso precede immediatamente il chunk VP8. E naturalmente il flag della trasparenza in VP8X deve essere acceso. Il decoder legge prima il vetro, poi le pietre, e li ricompone.
Il chunk ALPH comincia con un byte di intestazione che contiene alcune informazioni. La prima è il metodo di compressione: zero significa che l'alfa è salvato così com'è, un byte per pixel, oppure uno, che significa che è compresso con l'algoritmo senza perdita di VP8L. In pratica, il canale alfa viene trattato come un'immagine in scala di grigi e codificato come si codificherebbe il canale verde di un VP8L.
La seconda informazione è il metodo di filtro, e qui ritroviamo il nostro vecchio amico, la predizione. Prima di comprimere, ogni valore dell'alfa può essere sostituito dalla sua differenza rispetto al vicino a sinistra, a quello sopra, oppure a una stima a gradiente che combina entrambi. Oppure nessun filtro. In un canale alfa con contorni lisci, la differenza dal vicino è quasi sempre zero.
La terza è un'indicazione di pre-elaborazione, che dice se i livelli dell'alfa sono stati ridotti prima della compressione. E questo ci porta al prossimo punto.
Di default il vetro resta perfetto: l'alfa viene compresso senza perdita anche quando i colori sono compressi con perdita. Ma l'encoder di riferimento permette di scambiare un po' di precisione con qualche byte in meno, con un parametro chiamato qualità dell'alfa, da zero a cento.
Abbassandolo, l'encoder riduce il numero di livelli di trasparenza diversi, come se il vetraio avesse meno sfumature di vetro a disposizione. Un'ombra morbida sotto un prodotto, che va dal nero semitrasparente al trasparente, passa da duecento sfumature a qualche decina. In genere non si nota, e i byte risparmiati possono essere molti. Il decoder, se l'intestazione lo segnala, può anche applicare un leggero smussamento per nascondere i gradini.
Quando conviene? Quando l'alfa ha tante sfumature, come ombre, fumo, riflessi. Quando l'alfa è fatto solo di pieno e vuoto con un bordo sottile, il guadagno è minimo e non vale la pena toccarlo.
Ora una cosa che chi lavora con immagini trasparenti impara sempre a sue spese. L'alonatura.
Prendete un logo bianco su sfondo trasparente. I pixel del bordo sono semitrasparenti. Ma che colore hanno, sotto il vetro? Dipende da come è stata prodotta l'immagine. Se il programma di grafica l'ha esportata sopra uno sfondo nero, quei pixel di bordo potrebbero essere grigi scuri. Messo su una pagina chiara, il logo mostra un sottile alone scuro.
WebP salva i colori in forma non premoltiplicata: il colore e la trasparenza sono indipendenti. Questo è un bene, perché è il formato più fedele. Ma significa anche che quel colore "nascosto" sotto il vetro conta. E c'è l'aggravante dell'episodio scorso: l'encoder si sente libero di cambiare il colore dei pixel completamente trasparenti. Di solito li porta verso valori che comprimono meglio, il che è innocuo. Ma se poi qualcuno scala l'immagine, i pixel trasparenti finiscono mescolati nei bordi e il loro colore riemerge.
Le contromisure sono due. Partire da una sorgente pulita, esportata senza sfondo. E se l'immagine verrà rielaborata, usare l'opzione exact per conservare i colori dei pixel invisibili.
C'è un'interazione meno ovvia. Nella compressione con perdita i colori sono sottocampionati: un valore di colore ogni due per due pixel. L'alfa invece è a piena risoluzione.
Sui contorni netti tra un oggetto colorato e la trasparenza, quindi, il bordo dell'alfa è preciso ma il colore sotto quel bordo è "spalmato" su quattro pixel. Su oggetti fotografici non si vede. Su grafica con bordi saturi, per esempio un'icona rossa, si può vedere una frangia. Anche qui sharp YUV aiuta, ma per la grafica la risposta giusta resta quella dell'episodio scorso: senza perdita, o quasi senza perdita.
Mettiamo in fila una regola che potete usare domani in un progetto. Foto con sfondo trasparente: compressione con perdita più alfa senza perdita, cioè il comportamento di default. Foto con ombre morbide: come prima, provando ad abbassare la qualità dell'alfa e confrontando i pesi. Loghi, icone, interfacce trasparenti: compressione senza perdita, o quasi senza perdita se ci sono sfumature. Immagini che verranno ridimensionate o rielaborate: opzione exact.
E un'ultima cosa: se un'immagine non ha davvero bisogno della trasparenza, perché tutti i pixel sono opachi, non portatevela dietro. Molti strumenti lo capiscono da soli e non scrivono il chunk ALPH, ma vale la pena controllarlo. Il programma riffwalk del secondo episodio ve lo dice in un attimo.
Ricapitolando: in VP8L la trasparenza è semplicemente il quarto canale. Con la compressione con perdita vive in un chunk separato, ALPH, compresso di default senza perdita con l'algoritmo di VP8L, eventualmente filtrato e, se lo chiedete, quantizzato. Occhio al colore sotto il vetro e ai bordi colorati.
Nel prossimo episodio i mosaici cominciano a muoversi. Parliamo di animazione: come WebP ha provato a mandare in pensione il GIF, con fotogrammi, sfondi, fusioni e tempi. Ci sentiamo lì.