Articoli approfonditi sulla tecnologia che plasma il futuro.

Come funziona davvero la compressione JPEG

Una guida visiva alla compressione JPEG: trasformata DCT, tabelle di quantizzazione e perché alcune immagini si comprimono meglio di altre.

Un'immagine di fiore che si dissolve in blocchi di pixel e poi in motivi ondulati a spirale

JPEG è il blatta dei formati di file. È stato standardizzato nel 1992, è precedente al browser web e ha resistito a ogni tentativo di sostituirlo. WebP, AVIF, HEIC: comprimono tutti meglio, eppure JPEG resta il formato immagine più usato su internet. In parte è inerzia. Ma in parte è perché la pipeline di compressione di JPEG è davvero geniale: una lezione magistrale di elaborazione dei segnali applicata, che vale la pena capire anche se non scriverai mai un encoder.

La maggior parte degli sviluppatori tratta JPEG come una scatola nera: metti un'immagine dentro, ottieni un file più piccolo, alzi il cursore della qualità finché non sembra accettabile. Ma capire cosa succede dentro la scatola cambia il modo in cui pensi a immagini, compressione e compromessi tra dimensione del file e qualità visiva.

La pipeline in breve

La compressione JPEG è una pipeline di cinque fasi, ciascuna con un trucco ingegnoso. La magia sta nel fatto che si combinano: ogni fase prepara la successiva a essere più efficace.

  1. Conversione dello spazio colore — da RGB a YCbCr (separa la luminosità dal colore)
  2. Sottocampionamento della crominanza — riduce la risoluzione dei canali colore
  3. Discrete Cosine Transform (DCT) — converte i dati spaziali in dati di frequenza
  4. Quantizzazione — scarta i dettagli ad alta frequenza (questo è il passaggio con perdita)
  5. Codifica entropica — compressione senza perdita dei dati quantizzati

I passaggi 1 e 2 sfruttano il funzionamento della visione umana. I passaggi 3 e 4 sfruttano le proprietà delle immagini naturali. Il passaggio 5 è compressione dati standard. Ogni fase, presa singolarmente, è semplice. L'intuizione sta nella sequenza.

Passo 1: conversione dello spazio colore

La tua fotocamera cattura le immagini in RGB: canali rosso, verde e blu, ciascuno con 8 bit per pixel. La prima mossa di JPEG è convertire tutto in YCbCr: un canale di luminanza (luminosità) e due canali di crominanza (colore). Non è ancora compressione, non si perde nessun dato. È una trasformazione di coordinate, come ruotare gli assi.

Perché farlo? Perché l'occhio umano è molto più sensibile alla luminosità che al colore. Puoi vedere dettagli finissimi in una fotografia in bianco e nero, ma la tua capacità di percepire la risoluzione del colore è molto più grossolana. Separando luminosità e colore, JPEG può trattarli in modo diverso: preserva il dettaglio della luminosità e riduce in modo aggressivo quello del colore.

RGB to YCbCr conversion (simplified):
Y  =  0.299R + 0.587G + 0.114B    (luminance — brightness)
Cb = -0.169R - 0.331G + 0.500B    (blue chrominance)
Cr =  0.500R - 0.419G - 0.081B    (red chrominance)
Note: green dominates the luminance calculation because
human eyes have far more green-sensitive cone cells.
This isn't arbitrary — it matches the physiology.

Passo 2: sottocampionamento della crominanza

Qui avviene la prima vera riduzione dei dati. Poiché gli umani non percepiscono il colore alla piena risoluzione spaziale, JPEG riduce la risoluzione dei canali Cb e Cr, tipicamente della metà in entrambe le dimensioni. Si chiama sottocampionamento 4:2:0. Ogni quattro pixel di luminanza c'è un solo campione di crominanza.

Il risultato: hai appena dimezzato i dati grezzi (da 3 canali a piena risoluzione a 1 canale pieno più 2 canali a un quarto della risoluzione) e l'immagine appare praticamente identica all'originale. Provaci: prendi una fotografia qualsiasi, riduci di 4 volte la risoluzione dei canali colore e faticherai a vedere la differenza. Il cervello ricostruisce il dettaglio del colore a partire dall'informazione di luminanza.

Non funziona allo stesso modo per tutte le immagini. Le fotografie si comprimono benissimo perché le scene naturali hanno gradienti di colore morbidi. Ma le immagini con bordi di colore netti, come testo su sfondo colorato, un cerchio rosso su bianco o la pixel art, mostrano frange di colore dopo il sottocampionamento. È per questo che JPEG non è adatto al testo: i bordi di colore netti si sfocano.

Passo 3: la Discrete Cosine Transform

È qui che JPEG diventa interessante. L'immagine viene divisa in blocchi di 8×8 pixel e ogni blocco viene trasformato dal dominio spaziale (i valori di luminosità dei pixel) al dominio delle frequenze (quanto è presente ciascuna frequenza spaziale).

Pensala così: un blocco 8×8 di pixel può essere rappresentato come somma pesata di 64 pattern di base. Il primo pattern è un blocco piatto (il valore medio). I successivi aggiungono gradienti orizzontali e verticali. I pattern con numeri più alti aggiungono dettagli sempre più fini, oscillazioni rapide di luminosità.

An 8x8 DCT block conceptually:
DC ──► Low frequency ──────────────► High frequency
┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐
│ AVG │  →  │  →  │  →  │  →  │  →  │  →  │  →  │ Low freq
│     │     │     │     │     │     │     │     │   ↓
│  ↓  │  ↘  │     │     │     │     │     │     │
│     │     │  ↘  │     │     │     │     │     │
│     │     │     │  ↘  │     │     │     │     │
│     │     │     │     │  ↘  │     │     │     │
│     │     │     │     │     │  ↘  │     │     │
│     │     │     │     │     │     │  ↘  │     │
│     │     │     │     │     │     │     │ FINE │ High freq
└─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘
Top-left = low frequency (smooth gradients)
Bottom-right = high frequency (sharp edges, fine detail)
Natural images: most energy concentrated in top-left
Text/line art: significant energy in bottom-right

La DCT in sé è senza perdita: puoi ricostruire perfettamente il blocco 8×8 originale dai suoi 64 coefficienti. Ma ecco la proprietà cruciale: nelle immagini naturali (fotografie, gradienti, texture organiche) gran parte dell'energia si concentra nei coefficienti a bassa frequenza. I coefficienti ad alta frequenza, che rappresentano i dettagli fini, tendono a essere piccoli. È questo che rende efficace il passaggio successivo.

Perché 8×8? È un compromesso. Blocchi più grandi (16×16, 32×32) concentrerebbero meglio l'energia, ma costano di più in calcolo e causano artefatti di 'blocchettatura' visibili con qualità bassa. Blocchi più piccoli (4×4) ridurrebbero la blocchettatura, ma non concentrerebbero l'energia altrettanto bene. L'8×8 si è rivelato un punto di equilibrio che regge da oltre 30 anni.

Passo 4: quantizzazione, dove i dati muoiono

È il passaggio con perdita. Ogni coefficiente DCT viene diviso per un valore corrispondente di una tabella di quantizzazione e poi arrotondato all'intero più vicino. Divisori grandi (per i coefficienti ad alta frequenza) portano i valori piccoli a zero. Quegli zeri sono persi per sempre: è qui che l'informazione viene scartata in modo permanente.

Example quantization (quality ~50):
DCT coefficient: 42.7    Quantization divisor: 16    Result: round(42.7/16) = 3
DCT coefficient: 8.3     Quantization divisor: 40    Result: round(8.3/40)  = 0
DCT coefficient: -2.1    Quantization divisor: 99    Result: round(-2.1/99) = 0
The quality slider in your image editor controls the quantization table.
Quality 100: divisors close to 1 (almost no rounding, huge file)
Quality 75:  moderate divisors (good balance, typical web use)
Quality 50:  large divisors (noticeable artifacts, small file)
Quality 10:  very large divisors (blocky mess, tiny file)

La tabella di quantizzazione è il fattore più importante per la qualità di un JPEG. Lo standard JPEG definisce una tabella predefinita, ma gli encoder possono usare qualsiasi tabella. Gli encoder moderni come MozJPEG usano tabelle ottimizzate che estraggono più qualità da file più piccoli, regolando i divisori in base alla sensibilità percettiva umana: più aggressivi sulle frequenze a cui siamo meno sensibili.

Dopo la quantizzazione, un tipico blocco 8×8 a qualità moderata ha forse 6-10 coefficienti diversi da zero su 64. Gli altri sono zero. Questa enorme riduzione dei valori non nulli è ciò che rende efficace l'ultimo passaggio di compressione.

Passo 5: codifica entropica

L'ultimo passaggio è una compressione senza perdita dei coefficienti quantizzati. JPEG usa una combinazione di run-length encoding (RLE) e codifica di Huffman. I coefficienti vengono letti in un pattern a zigzag dal blocco 8×8, partendo dal coefficiente DC (in alto a sinistra) e attraversando frequenze crescenti. Così tutti gli zeri finali si raggruppano, e l'RLE li gestisce in modo efficiente: invece di memorizzare '0, 0, 0, 0, 0, 0, ..., 0', memorizza 'quarantasette zeri'.

La codifica di Huffman assegna sequenze di bit più corte ai valori più comuni e più lunghe a quelli rari. Dato che la maggior parte dei coefficienti non nulli sono piccoli interi (1, -1, 2, -2), ottengono codici molto brevi. Il risultato: il blocco 8×8 quantizzato, che partiva da 64 valori, di solito si comprime in 20-40 byte, a seconda del contenuto dell'immagine e dell'impostazione di qualità.

Perché alcune immagini si comprimono meglio di altre

Capire la pipeline spiega comportamenti di compressione che altrimenti sembrerebbero misteriosi.

  • Le fotografie si comprimono bene perché hanno gradienti di colore morbidi (il sottocampionamento funziona benissimo) e la maggior parte dell'energia sta nei coefficienti DCT a bassa frequenza (la quantizzazione azzera molti coefficienti senza impatto visibile).
  • Testo e disegni al tratto si comprimono male perché i bordi netti producono coefficienti DCT ad alta frequenza significativi, che non possono essere azzerati senza artefatti visibili. I confini dei blocchi 8×8 diventano visibili come aloni attorno ai bordi del testo.
  • Il rumore distrugge la compressione perché il rumore del sensore è dato casuale ad alta frequenza. Ogni pixel rumoroso aggiunge coefficienti ad alta frequenza diversi da zero, che resistono alla quantizzazione. Una foto rumorosa a qualità 80 può essere 3-5 volte più grande di una foto pulita della stessa scena alla stessa qualità.
  • I gradienti sono quasi gratis perché un gradiente morbido su un blocco 8×8 è rappresentato da appena 2-3 coefficienti DCT. Gli altri sono zero anche senza quantizzazione.
  • Risalvare degrada la qualità perché ogni salvataggio applica di nuovo la quantizzazione e gli errori di arrotondamento si accumulano. Salvare un JPEG 10 volte a qualità 75 è sensibilmente peggio che salvarlo una volta sola a qualità 75. Per questo dovresti sempre modificare partendo dal file sorgente originale.

Il cursore della qualità non è lineare

La maggior parte degli sviluppatori tratta il cursore di qualità JPEG come un compromesso lineare: la qualità 50 è metà della qualità 100 e produce metà della dimensione del file. Nessuna delle due cose è vera.

La qualità 100 non è 'senza perdita': applica comunque la quantizzazione, solo con divisori piccoli. Il file è enorme (spesso più grande di un PNG) e visivamente indistinguibile dalla qualità 95. Per le fotografie, la qualità 95 è indistinguibile dalla 90. La differenza percettiva tra qualità 85 e 75 è maggiore di quella tra 100 e 85. Sotto la qualità 50 gli artefatti diventano gravi e la riduzione della dimensione rallenta.

Il punto dolce per le immagini web è di solito tra qualità 75 e 85. Sotto 75 gli artefatti sono visibili alla normale dimensione di visualizzazione. Sopra 85 la dimensione del file cresce molto con un miglioramento percettivo trascurabile. La qualità predefinita 75 di MozJPEG è ben scelta: colpisce il ginocchio della curva qualità-dimensione per la maggior parte delle fotografie.

Gli encoder JPEG moderni sono più intelligenti di quanto pensi

Lo standard JPEG definisce il formato di decodifica, non l'encoder. Questo significa che le implementazioni di encoder possono differire enormemente in efficienza di compressione, pur producendo file conformi allo standard. MozJPEG, sviluppato da Mozilla, produce file del 5-15% più piccoli di libjpeg alla stessa qualità visiva, grazie a diverse tecniche.

  • Trellis quantization — invece di arrotondare ogni coefficiente DCT in modo indipendente, la trellis quantization considera come le decisioni di arrotondamento influenzano la codifica entropica e prende scelte globalmente migliori.
  • Tabelle di Huffman ottimizzate — le tabelle di Huffman predefinite di JPEG sono generiche. MozJPEG ne genera di personalizzate, calibrate sulla distribuzione effettiva dei coefficienti di ogni immagine.
  • Codifica progressiva — i JPEG progressivi memorizzano i coefficienti in più passaggi (prima la versione grossolana, poi i raffinamenti). Spesso comprimono meglio della codifica sequenziale, perché il passaggio grossolano ha distribuzioni di coefficienti più prevedibili.
  • Tabelle di quantizzazione adattive — invece di una sola tabella per tutta l'immagine, gli encoder adattivi regolano la quantizzazione per regione: più aggressivi nelle aree complesse (dove gli artefatti si notano meno) e più conservativi nelle aree lisce.

Se servi immagini sul web, passare dall'encoder libjpeg predefinito a MozJPEG è una delle vittorie di performance più facili che si possano ottenere. La maggior parte delle CDN di immagini e delle librerie di elaborazione lo supporta. La codifica è più lenta (irrilevante per immagini statiche pre-elaborate) e l'output è più piccolo alla stessa qualità.

JPEG contro i nuovi formati

WebP, AVIF e HEIC usano tecniche di compressione più moderne: blocchi di trasformata più grandi, filtri di deblocking in-loop, intra-predizione (usare i blocchi vicini per predire il blocco corrente). A parità di qualità visiva comprimono del 20-50% meglio di JPEG. AVIF in particolare, basato sul codec video AV1, è impressionantemente efficiente.

Allora perché JPEG è ancora ovunque? Per il supporto universale. Ogni browser, ogni visualizzatore di immagini, ogni sistema operativo, ogni fotocamera, ogni telefono, ogni software che gestisce immagini supporta JPEG. WebP è quasi universale nei browser ma manca di supporto nativo nei sistemi operativi. Il supporto di AVIF è ancora incoerente. HEIC è soprattutto nell'ecosistema Apple. Per un formato che deve funzionare ovunque, JPEG è ancora l'unica scelta sicura.

Il consiglio pratico: usa AVIF o WebP con fallback JPEG se la tua infrastruttura di distribuzione lo supporta (la maggior parte delle CDN lo fa tramite content negotiation). Se puoi usare un solo formato, un JPEG codificato con MozJPEG a qualità 80 è difficile da battere per le fotografie. Per screenshot, diagrammi e immagini ricche di testo, il PNG resta la scelta migliore — oppure esplora approcci più creativi alla rappresentazione delle immagini.

Il blocco 8×8 che ha conquistato il mondo

La pipeline di compressione di JPEG è un esempio da manuale di ingegneria che lavora con la percezione umana invece che contro di essa. Ogni fase sfrutta una proprietà specifica (l'insensibilità al colore, la distribuzione delle frequenze nelle immagini naturali, il mascheramento percettivo del rumore di quantizzazione) e insieme formano un sistema che riduce i file di 10-20 volte con un impatto visivo minimo. Trentaquattro anni dopo, gli stessi blocchi DCT 8×8 che giravano sull'hardware del 1992 trasportano ancora la maggior parte delle immagini su internet. È il segno di un design che ha azzeccato i fondamenti.