La mia regola di compressione sembrava semplice: codificare AVIF alla qualità più bassa che riuscisse ancora a superare un target SSIMULACRA2 di 60. Nei set di immagini più grandi consentivo a un solo sample rappresentativo di scendere fino a 58, mentre tutti gli altri dovevano rimanere a 60 o più.
Con PNG e normali immagini sorgente ero soddisfatto di questa regola. Poi ho iniziato a lavorare con file WebP che erano già stati compressi con perdita a partire da originali di qualità superiore prima di arrivare a me.
originale di qualità superiore: ~2 MB
↓
WebP lossy: ~100 KB
↓
AVIF
Quella seconda conversione doveva ancora poter superare 60 rispetto al WebP? All'inizio pensavo di sì. Un 60 resta un 60. Il problema era che nel frattempo era cambiata la reference.
La metrica non era sbagliata. Era cambiata la reference.
SSIMULACRA2 confronta un'immagine di riferimento con un'immagine distorta e assegna un punteggio alla differenza percettiva tra quei due input specifici. È progettata per reagire a danni di compressione come blur, ringing e bordi introdotti artificialmente; il materiale di valutazione pubblicato include inoltre distorsioni JPEG, WebP, AVIF e di altri codec. La documentazione](https://github.com/cloudinary/ssimulacra2/blob/main/README.md%22>documentazione) di SSIMULACRA2 descrive la metrica e i suoi riferimenti qualitativi approssimativi.
Quando codifico AVIF direttamente da una buona sorgente, il confronto è sostanzialmente source → AVIF. Un punteggio di 60 descrive quindi il danno introdotto da quella conversione.
Con un WebP già lossy, la storia reale è diversa:
original
↓ primo encode lossy
WebP
↓ secondo encode lossy
AVIF
SSIMULACRA2 vede soltanto WebP → AVIF. Non sa nulla dell'originale che esisteva prima del WebP. Gli artefatti già presenti nel WebP diventano parte della reference.
Di conseguenza, un 60 può dirmi che l'AVIF non si è allontanato troppo dal WebP. Non può dirmi quanto l'AVIF finale si sia allontanato dal master perduto.
Il transcoding lossy crea un secondo budget di qualità
Immaginiamo che l'originale contenga un gradiente pulito. Il primo encoder introduce un po' di banding, ma il WebP appare ancora accettabile. Poi converto quel WebP in AVIF. SSIMULACRA2 può penalizzare la degradazione aggiuntiva introdotta dall'encoder AVIF, ma non può penalizzare il danno che fa già parte della reference.
Ecco perché codificare da un'immagine già lossy non equivale a codificare direttamente dalla migliore sorgente disponibile. Una discussione nel progetto](https://github.com/AOMediaCodec/libavif/discussions/2640%22>progetto) libavif evidenzia lo stesso principio generale: gli artefatti di compressione esistenti possono essere trasportati nel nuovo AVIF quando l'input è già compresso.
Questo non significa che AVIF amplifichi automaticamente ogni artefatto WebP, né che il transcoding sia sempre sbagliato. Significa che il secondo encoder inizia a lavorare quando una parte del budget qualitativo originale è già stata spesa.
La policy che ho finito per usare
- Sorgente canonica o di alta qualità: target 60, floor 58 per un singolo sample.
- WebP lossless: target 60, floor 58.
- Derivato noto con perdita: target 65, floor 63.
La distinzione importante non è JPEG contro WebP. È sorgente canonica contro derivato lossy noto.
WebP può essere lossless. La specifica](https://developers.google.com/speed/webp/docs/webp_lossless_bitstream_specification%22>specifica) WebP lossless descrive una modalità che ricostruisce esattamente i valori dei pixel, quindi non esiste una generazione precedente di perdita da compensare. Al contrario, un JPEG che so essere già passato attraverso più trasformazioni lossy merita la stessa cautela di un WebP precedentemente compresso.
Perché 65?
Non esiste una regola di SSIMULACRA2 secondo cui una seconda generazione lossy richieda esattamente cinque punti in più. Non ho trovato una regola simile perché non esiste. 65 è una policy ingegneristica, non una proprietà della metrica.
I riferimenti qualitativi pubblicati aiutano a contestualizzare la scelta. In modo approssimativo, 50 corrisponde a qualità media o discreta, mentre 70 corrisponde a qualità alta o buona. Questo colloca 60 in una zona di compressione web piuttosto aggressiva, non nella zona visivamente lossless.
Per una conversione diretta da una buona sorgente accetto di spendere quel budget percettivo in cambio di file più piccoli. Per una seconda generazione lossy volevo un budget minore per la distorsione aggiuntiva.
Ho considerato 70, ma avrebbe spinto tutte le immagini transcodificate in una regione qualitativa sensibilmente più rigida. Su pagine che caricano molte immagini, soprattutto su connessioni mobili, quei byte aggiuntivi contano. Non avevo prove sufficienti che imporre 70 a ogni immagine già compressa giustificasse il costo. Ho quindi scelto 65 come compromesso conservativo.
Perché 65/63 invece di 65/62?
La mia policy originale era 60/58: un singolo outlier rappresentativo poteva scendere di due punti sotto il target principale. Portando il target a 65, mantenere la stessa policy conduce naturalmente a 65/63.
60 - 58 = 2
65 - 63 = 2
Usare 62 creerebbe un'eccezione di tre punti. I sample normali diventerebbero più severi, mentre proprio il peggiore otterrebbe maggiore tolleranza. Non ho trovato un motivo tecnico per ampliare l'eccezione specificamente per input già sottoposti a compressione lossy.
Né 63 né 65 sono numeri magici. L'aspetto utile è la coerenza interna della policy.
Passare da 2 MB a 100 KB non descrive la qualità visiva
Non ricavo intenzionalmente le soglie SSIMULACRA2 dal rapporto di compressione. Ridurre un file da 2 MB a 100 KB sembra impressionante, ma la dimensione del file da sola dice sorprendentemente poco sulla degradazione percettiva.
Risoluzione, entropia dell'immagine, rumore, aree uniformi, line art, chroma subsampling e formato precedente influenzano fortemente l'efficienza di compressione. Lo studio](https://developers.google.com/speed/webp/docs/webp_study%22>studio) Google sulla compressione WebP confronta i codec a qualità approssimativamente equivalente invece di presumere che dimensioni uguali implichino qualità visiva uguale.
Quindi non uso regole come 20× più piccolo → target 65. Per la mia policy conta sapere se il file corrente è un derivato lossy noto, non quanto spettacolare sembri la riduzione di dimensione.
Se ho ancora l'originale, non transcodifico il WebP
Se possiedo sia l'originale di alta qualità sia il piccolo WebP lossy, genero AVIF direttamente dall'originale e uso la normale policy 60/58.
preferito:
original → AVIF
da evitare quando possibile:
original → WebP lossy → AVIF
Un target più severo nella seconda generazione non può recuperare le informazioni perse nel primo encode. 65 mantiene semplicemente l'AVIF più vicino al WebP; 70 lo manterrebbe ancora più vicino. Nessuno dei due ricrea l'originale perduto.
Il bug d'implementazione contava più di 62 contro 63
Durante la revisione della policy ho trovato un problema più pericoloso nella logica dell'encoder. Il codice aveva già target specifici per formato e un helper capace di restituire un target diverso per WebP. Cambiare WebP da 60 a 65 sembrava quindi banale.
Non lo era. La decisione adattiva sulla qualità continuava a usare il target globale e il worst-score globale. Il target specifico per formato veniva usato in seguito per etichettare i singoli risultati come pass o below-target, ma non controllava necessariamente la decisione che selezionava la qualità AVIF finale.
Questo crea un failure sottile: un sample WebP può essere correttamente classificato come sotto il suo target 65, mentre la ricerca adattiva accetta comunque quella quality perché la condizione globale di pass resta 60.
intended WebP target: 65
actual score: 61.2
format-aware label: below target
global search rule: pass if target is still 60
Una soglia non serve a nulla se non partecipa alla decisione che sceglie davvero l'output codificato.
È più sicuro rendere le soglie parte della policy del sample
Ora preferisco trattare le soglie come proprietà della sorgente, non come costanti decorative del formato. In pseudocodice semplificato:
if sample is a known lossy derivative:
target = 65
floor = 63
else:
target = 60
floor = 58
reject if any sample is below its floor
allow at most one sample below its target
La proprietà importante è che le stesse soglie usate per descrivere il risultato controllino anche se quel risultato viene accettato.
Anche la sampling policy conta
Non devo testare ogni immagine a ogni possibile qualità AVIF. La pipeline seleziona fino a dieci sample JPEG, PNG o WebP rappresentativi lungo la distribuzione dei byte per pixel.
Per raccolte con dieci immagini o meno, ogni sample deve raggiungere il target normale. Per raccolte più grandi, un sample può usare il floor inferiore, mentre tutti gli altri devono rispettare il target principale.
Il sampling rende la ricerca praticabile, ma è anche un motivo per non rendere la regola dell'outlier inutilmente permissiva. I sample selezionati sono rappresentativi, non una prova che ogni immagine non campionata si comporti allo stesso modo.
L'esperimento che potrebbe sostituire l'euristica
La risposta più solida richiederebbe di conservare i veri originali per un corpus rappresentativo e testare le catene complete:
A: original → AVIF, target 60
B: original → lossy WebP → AVIF, target 60
C: original → lossy WebP → AVIF, target 63
D: original → lossy WebP → AVIF, target 65
E: original → lossy WebP → AVIF, target 70
Per ogni variante registrerei dimensione finale, SSIMULACRA2 rispetto al vero original, SSIMULACRA2 rispetto al WebP intermedio e quality scelta dall'encoder. Ispezionerei manualmente anche le immagini più difficili.
Non ho eseguito questo esperimento controllato su un set sufficientemente rappresentativo di originali conservati, quindi non posso affermare che 65 sia ottimale in senso globale. È una limitazione importante.
AVIF non giustifica automaticamente un altro encode
Se l'unica sorgente rimasta è un WebP da 100 KB e l'AVIF che supera 65/63 pesa 96 KB, metterei in dubbio la conversione. Risparmiare 4 KB potrebbe non giustificare un'altra generazione lossy e maggiore complessità di elaborazione.
Se invece lo stesso WebP da 100 KB diventa un AVIF da 65 KB mantenendo la quality policy, il trade-off diventa molto più interessante sulle pagine ricche di immagini.
Una conversione di codec deve rispondere a due domande separate: la distorsione aggiuntiva è accettabile? E la riduzione di dimensione è abbastanza grande da contare? Superare la prima non garantisce automaticamente la seconda.
La regola che uso oggi
Se ho l'originale di migliore qualità, codifico direttamente da quello e uso 60/58 per questo workload web. Se il WebP è lossless, uso ancora 60/58. Se l'unico file rimasto è un derivato lossy noto, applico un budget più severo per la seconda generazione, attualmente 65/63. Se AVIF riduce appena la dimensione, considero di mantenere il WebP esistente.
La lezione più profonda non è che WebP richieda un numero SSIMULACRA2 speciale. Una full-reference quality metric risponde soltanto alla domanda rappresentata dalla sua immagine di riferimento.
Se la reference ha già perso informazione, un punteggio alto significa “vicino a questa reference”, non “vicino all'immagine che esisteva prima”. Quando ho iniziato a trattare la provenienza dell'immagine come parte della policy di compressione, le soglie hanno smesso di sembrare impostazioni arbitrarie del codec. Sono diventate budget per diverse generazioni di perdita.