Le immagini JPEG sono ovunque nella nostra vita digitale, ma dietro a questa facciata di consapevolezza si nascondono algoritmi che eliminano dettagli non percepibili dall'occhio umano. Il risultato è una qualità visiva eccezionale con la dimensione del file più ridotta possibile, ma come funziona esattamente tutto questo? Scopriamo insieme cosa non vedono i nostri occhi!

È facile dare per scontata la possibilità di inviare una foto a un amico e non preoccuparsi di quale dispositivo, browser o sistema operativo stia utilizzando; tuttavia, non è sempre stato così. All'inizio degli anni '80, i computer potevano memorizzare e visualizzare immagini digitali, ma c'erano molte idee concorrenti su quale fosse il modo migliore per farlo. Non si poteva semplicemente inviare un'immagine da un computer all'altro e sperare che funzionasse.
Per affrontare questo problema, nel 1986 è stato istituito un comitato di esperti provenienti da tutto il mondo chiamato «» (Joint Photographic Experts Group, JPEG), fondato nell'ambito della collaborazione tra l'Organizzazione Internazionale per la Normazione (ISO) e la Commissione Elettrotecnica Internazionale (IEC) – due organizzazioni internazionali di normazione con sede a Ginevra (Svizzera).
Un gruppo di persone chiamato JPEG ha creato lo standard di compressione delle immagini digitali JPEG nel 1992. Chiunque abbia utilizzato Internet probabilmente ha incontrato immagini codificate in JPEG. È il modo più comune di codificare, inviare e archiviare immagini. Dalle pagine web alle email e ai social media, JPEG è utilizzato miliardi di volte al giorno – praticamente ogni volta che guardiamo un'immagine online o la inviamo. Senza JPEG, il web sarebbe meno vivace, più lento e probabilmente avrebbe meno foto di gattini!
Questo articolo tratta di come decodificare un'immagine JPEG. In altre parole, riguarda ciò che è necessario per trasformare i dati compressi memorizzati nel computer in un'immagine che appare sullo schermo. È importante sapere questo non solo perché è essenziale per comprendere la tecnologia che utilizziamo quotidianamente, ma anche perché, rivelando i livelli di compressione, apprendiamo meglio la percezione e la visione, oltre a quali dettagli i nostri occhi sono più sensibili.
Inoltre, giocare con le immagini in questo modo è molto divertente.

Guardando dentro al JPEG
Nel computer tutto è memorizzato come una sequenza di numeri binari. Di solito, questi bit, zeri e uni, sono raggruppati in otto formando dei byte. Quando apri un'immagine JPEG sul computer, qualcosa (il browser, il sistema operativo, o qualcos'altro) deve decodificare i byte, ripristinando l'immagine originale come un elenco di colori che possono essere visualizzati.
Se scarichi questa adorabile e la apri in un editor di testo, vedrai un sacco di simboli senza senso.

Qui utilizzo Notepad++ per esaminare il contenuto del file, poiché i normali editor di testo, come il Blocco Note di Windows, rovinerebbero il file binario dopo il salvataggio, facendolo smettere di soddisfare il formato JPEG.
Aprendo un'immagine in un editor di testo, confondi il computer, proprio come confondi il tuo cervello quando ti strofini gli occhi e inizi a vedere macchie colorate!
Queste macchie che vedi sono conosciute come , e non sono il risultato dell'impatto di uno stimolo luminoso o allucinazioni generate dalla mente. Si verificano perché il tuo cervello interpreta che qualsiasi segnale elettrico nei nervi ottici trasmette informazioni sulla luce. Il cervello deve fare tali ipotesi, poiché non può sapere se il segnale è un suono, una visione o qualcos'altro. Tutti i nervi del corpo trasmettono impulsi elettrici assolutamente identici. Premendo sugli occhi, invii segnali che non sono visivi, ma attivano i recettori dell'occhio, che il tuo cervello interpreta - in questo caso, erroneamente - come qualcosa di visivo. Sei letteralmente in grado di vedere la pressione!
È interessante pensare a quanto i computer assomiglino al cervello, ma è anche un'analogia utile che illustra quanto sia importante l'interpretazione dei dati – sia che vengano trasmessi nel corpo tramite i nervi, sia che siano memorizzati in un computer. Tutti i dati binari sono composti da zeri e uni, i componenti di base capaci di trasmettere qualsiasi tipo di informazione. Il computer spesso deduce come interpretarli usando suggerimenti, come le estensioni dei file. E adesso lo costringiamo a interpretarli come testo, dato che è ciò che si aspetta un editor di testo.
Per capire come decodificare un JPEG, dobbiamo vedere i segnali originali – i dati binari. Questo può essere fatto tramite un editor esadecimale, oppure direttamente sulla ! Qui c'è un'immagine, accanto alla quale nel campo di testo sono elencati tutti i suoi byte (esclusi gli header), presentati in forma decimale. Puoi modificarli e lo script li ricoderà, generando una nuova immagine al volo.

È possibile scoprire molto semplicemente giocando con questo editor. Ad esempio, riesci a dire in quale ordine sono memorizzati i pixel?
In questo esempio è strano che cambiare alcuni numeri non influisca affatto sull'immagine, mentre, ad esempio, se sostituisci il numero 17 con 0 nella prima riga, la foto si rovinerebbe completamente!

Altri cambiamenti, come la sostituzione del 7 nella riga 1988 con il numero 254, cambiano il colore, ma solo dei pixel successivi.

Forse la cosa più strana è che alcuni numeri cambiano non solo il colore, ma anche la forma dell'immagine. Cambia 70 nella riga 12 in 2 e guarda l'ultima riga dell'immagine per vedere cosa intendo.

E indipendentemente dall'immagine JPEG che usi, troverai sempre queste misteriose sequenze a scacchiera durante la modifica dei byte.
Giocando con l'editor, è difficile capire come venga ricreata la foto da questi byte, poiché la compressione JPEG è composta da tre diverse tecnologie applicate in sequenza a livelli. Esamineremo ciascuna di esse separatamente per rivelare il comportamento misterioso che stiamo osservando.
Tre livelli di compressione JPEG:
- .
- .
- , e
Per darvi un'idea delle dimensioni della compressione, notate che l'immagine sopra rappresenta 79.819 numeri, cioè circa 79 KB. Se l'avessimo memorizzata senza compressione, sarebbero stati necessari tre numeri per ogni pixel - per le componenti rossa, verde e blu. Questo avrebbe comportato 917.700 numeri, ovvero circa 917 KB. Di conseguenza, grazie alla compressione JPEG, il file finale è stato ridotto di oltre 10 volte!
In realtà, quest'immagine può essere compressa molto di più. Qui sotto ci sono due immagini affiancate - quella a destra è stata compressa a 16 KB, quindi 57 volte meno rispetto alla versione non compressa!

Se si osserva attentamente, si noterà che queste immagini non sono identiche. Entrambe sono immagini con compressione JPEG, tuttavia quella a destra è molto più leggera. Inoltre, appare leggermente peggio (guardate i quadrati dei colori sullo sfondo). Per questo motivo, il JPEG è anche conosciuto come compressione con perdita; durante il processo di compressione, l'immagine cambia e perde alcuni dettagli.
1. Sottocampionamento del colore
Ecco l'immagine con l'applicazione solo del primo livello di compressione.

(Versione interattiva – in articolo). Rimuovere un numero distrugge tutti i colori. Tuttavia, se si rimuovono esattamente sei numeri, questo influisce praticamente poco sull'immagine.
Adesso i numeri sono un po’ più facili da decifrare. Sono quasi una semplice lista di colori, dove ogni byte modifica esattamente un pixel, ma è già due volte più piccolo di un’immagine non compressa (che occuperebbe circa 300 KB a questa dimensione ridotta). Indovinate perché?
Si può notare che questi numeri non rappresentano le standard componenti rosso, verde e blu, poiché se si sostituiscono tutti i numeri con zeri, otteniamo un'immagine verde (anziché bianca).

Questo perché questi byte rappresentano Y (luminosità),

Cb (bluastra relativa),

e Cr (rossore relativo) dell'immagine.

Perché non usare RGB? Infatti, è così che funzionano la maggior parte degli schermi moderni. Il tuo monitor può visualizzare qualsiasi colore, inclusi rosso, verde e blu con diverse intensità per ogni pixel. Il bianco si ottiene accendendo tutti e tre alla massima luminosità, mentre il nero si ottiene spegnendoli.

Questo è anche molto simile alla funzione dell'occhio umano. I recettori del colore dei nostri occhi si chiamano «», e si dividono in tre tipi, ognuno dei quali è più sensibile al rosso, al verde o al blu [i coni di tipo S sono sensibili nella gamma blu-viola (S da Short — spettro a onde corte), i tipo M — nel giallo-verde (M da Medium — onde medie), e L tipo — nel giallo-rosso (L da Long — onde lunghe) parti dello spettro. La presenza di questi tre tipi di coni (e bastoncelli, sensibili nella parte verde smeraldo dello spettro) conferisce all'essere umano la visione a colori. / nota del traduttore]. , un altro tipo di fotorecettori nei nostri occhi, è in grado di rilevare le variazioni di luminosità, ma è molto più sensibile al colore. Nei nostri occhi ci sono circa 120 milioni di bastoncelli e solo 6 milioni di coni.
I nostri occhi notano quindi molto meglio i cambiamenti di luminosità piuttosto che le variazioni di colore. Separando il colore dalla luminosità, è possibile rimuovere un po' di colore senza che nessuno se ne accorga. La sottodimensione del colore è il processo di rappresentazione dei componenti colorati di un'immagine a una risoluzione inferiore rispetto a quella dei componenti di luminosità. Nell'esempio sopra, ogni pixel ha esattamente un componente Y, mentre ogni gruppo di quattro pixel ha esattamente un componente Cb e uno Cr. Pertanto, l'immagine contiene quattro volte meno informazioni sul colore rispetto all'originale.
Lo spazio colore YCbCr non è utilizzato solo nel JPEG. È stato inizialmente progettato nel 1938 per le trasmissioni televisive. Non tutti avevano un televisore a colori, quindi la separazione tra colore e luminosità ha permesso a tutti di ricevere lo stesso segnale, mentre i televisori in bianco e nero utilizzavano solo il componente di luminosità.
Pertanto, la rimozione di un numero dall'editor distrugge completamente tutti i colori. I componenti sono memorizzati come Y Y Y Y Cb Cr (in realtà, non necessariamente in questo ordine – l'ordine di archiviazione è stabilito nell'intestazione del file). La rimozione del primo numero farà sì che il primo valore Cb venga percepito come Y, Cr come Cb, e in generale si otterrà un effetto domino che altera tutti i colori dell'immagine.
La specifica JPEG non obbliga all'uso di YCbCr. Ma nella maggior parte dei file viene utilizzata, poiché fornisce immagini di qualità migliore dopo la sottocampionatura rispetto a RGB. Ma non devi credermi sulla parola. Guarda tu stesso nella tabella qui sotto come apparirà la sottocampionatura di ciascun componente sia in RGB che in YCbCr.

(Versione interattiva – in articoli).
La rimozione del blu non è così evidente come quella del rosso o del verde. Questo perché, sui sei milioni di coni presenti nei tuoi occhi, circa il 64% sono sensibili al rosso, il 32% al verde e il 2% al blu.
La sottocampionatura del componente Y (in basso a sinistra) è la più visibile. Anche una piccola variazione è evidente.
La conversione dell'immagine da RGB a YCbCr non riduce la dimensione del file, ma facilita la ricerca di dettagli meno evidenti che possono essere rimossi. La compressione con perdita avviene nella seconda fase. Alla base c'è l'idea di rappresentare i dati in una forma più comprimibile.
2. Trasformata discreta del coseno e campionamento
Questo livello di compressione definisce principalmente l'essenza del JPEG. Dopo la conversione dei colori in YCbCr, i componenti vengono compressi separatamente, quindi possiamo concentrarci solo sul componente Y. Ecco come appaiono i byte del componente Y dopo l'applicazione di questo livello.

(Versione interattiva – in articoli). Nella versione interattiva, cliccare su un pixel scorre l'editor alla riga che lo rappresenta. Prova a rimuovere i numeri dalla fine o ad aggiungere alcuni zeri a un numero specifico.
A prima vista, sembra una compressione molto scarsa. Nell'immagine ci sono 100.000 pixel e per rappresentare la loro luminosità (componenti Y) servono 102.400 numeri — è peggio di non comprimere affatto!
Tuttavia, si prega di notare che la maggior parte di questi numeri è uguale a zero. Inoltre, tutti questi zeri alla fine delle righe possono essere rimossi senza alterare l'immagine. Rimangono circa 26.000 numeri, che è quasi quattro volte in meno!
A questo livello si trova il segreto dei modelli scacchistici. A differenza di altri effetti che abbiamo visto, l'emergere di questi modelli non è un glitch. Sono i mattoni di tutta l'immagine. Ogni riga dell'editor contiene esattamente 64 numeri, i coefficienti della trasformata discreta del coseno (DCT), corrispondenti alle intensità di 64 modelli unici.
Questi modelli sono formati sulla base del grafico del coseno. Ecco come appaiono alcuni di essi:

8 su 64 coefficienti
Di seguito è riportata un'immagine che mostra tutti i 64 modelli.

(Versione interattiva – in articoli).
Questi modelli hanno un significato particolare, poiché formano la base delle immagini di dimensione 8x8. Se non sei familiare con l'algebra lineare, questo significa che qualsiasi immagine di dimensione 8x8 può essere ottenuta da questi 64 modelli. La DCT è il processo di suddivisione delle immagini in blocchi 8x8 e di trasformazione di ciascun blocco in una combinazione di questi 64 coefficienti.
Il fatto che qualsiasi immagine possa essere composta da 64 modelli definiti sembra magia. Tuttavia, è lo stesso che dire che qualsiasi luogo sulla Terra può essere descritto da due numeri: latitudine e longitudine [con indicazione degli emisferi / nota del traduttore]. Spesso consideriamo la superficie terrestre come bidimensionale, quindi ci servono solo due numeri. Un'immagine 8x8 ha 64 dimensioni, perciò ci servono 64 numeri.
Non è ancora chiaro come questo ci aiuti in termini di compressione. Se abbiamo bisogno di 64 numeri per rappresentare un'immagine 8x8, perché questo modo è migliore che semplicemente memorizzare 64 componenti di luminosità? Facciamo questo per lo stesso motivo per cui abbiamo trasformato tre numeri RGB in tre numeri YCbCr: ci permette di eliminare dettagli impercettibili.
È difficile vedere quali dettagli vengano rimossi in questa fase, poiché JPEG applica DCT a blocchi di 8x8. Tuttavia, non c'è divieto nell'applicarlo a un'intera immagine. Ecco come appare la DCT sulla componente Y applicata a un'intera immagine:

Si possono rimuovere più di 60.000 numeri senza praticamente vedere cambiamenti nell'immagine.

Tuttavia, si noti che se azzeriamo i primi cinque numeri, la differenza sarà evidente.

I numeri all'inizio indicano i cambiamenti a bassa frequenza nell'immagine e i nostri occhi li percepiscono meglio. I numeri più vicini alla fine indicano cambiamenti ad alta frequenza, che sono più difficili da notare. Per "vedere ciò che è invisibile all'occhio", possiamo isolare questi dettagli ad alta frequenza annullando i primi 5000 numeri.

Vediamo tutte le aree dell'immagine in cui si verifica il maggiore cambiamento da un pixel all'altro. Si evidenziano gli occhi del gatto, i suoi baffi, la coperta di peluche e le ombre nell'angolo in basso a sinistra. Possiamo spingerci oltre, annullando i primi 10.000 numeri:

20 000:

40 000:

60 000:

Questi dettagli ad alta frequenza JPEG vengono rimossi nella fase di compressione. La conversione dei colori in coefficienti DCT è senza perdita. Le perdite si verificano nella fase di discretizzazione, dove vengono eliminati valori ad alta frequenza o vicini a zero. Quando riduci la qualità del salvataggio JPEG, il programma aumenta la soglia dei valori rimossi, il che porta a una riduzione delle dimensioni del file, ma rende l'immagine più pixelata. Per questo motivo, l'immagine nella prima sezione, che era 57 volte più piccola, appariva in questo modo. Ogni blocco 8x8 era rappresentato da un numero molto inferiore di coefficienti DCT rispetto a una versione di qualità superiore.
È possibile creare un effetto sorprendente, come la trasmissione fluida delle immagini. Puoi visualizzare un'immagine sfocata che diventa sempre più dettagliata man mano che vengono scaricati più coefficienti.
Ecco, giusto per curiosità, cosa succede utilizzando solo 24.000 numeri:

Oppure solo 5000:

Molto sfocato, ma sembra riconoscibile!
3. Codifica delle lunghezze delle sequenze, delta e Huffman
Fino ad ora, tutte le fasi di compressione sono state lossy. L'ultima fase, al contrario, è lossless. Essa non elimina informazioni, ma riduce significativamente la dimensione del file.
Come possiamo comprimere qualcosa senza scartare informazioni? Immagina come descriveremmo un semplice rettangolo nero di 700 x 437.
JPEG utilizza a tal fine 5000 numeri, ma possiamo ottenere risultati molto migliori. Riesci a immaginare uno schema di codifica che descriva un'immagine simile con il minor numero possibile di byte?
Lo schema minimo che sono riuscito a ideare utilizza quattro: tre per indicare il colore e il quarto per indicare quanti pixel hanno quel colore. L'idea di rappresentare valori ripetitivi in questo modo compresso si chiama codifica delle lunghezze delle serie. Essa è lossless, poiché possiamo ripristinare i dati codificati nel loro stato originale.
La dimensione del file JPEG con un rettangolo nero è molto maggiore di 4 byte: ricordate che a livello DCT la compressione viene applicata a blocchi di 8x8 pixel. Pertanto, abbiamo bisogno di almeno un coefficiente DCT per ogni 64 pixel. Ne serve uno perché, invece di memorizzare un coefficiente DCT seguito da 63 zeri, la codifica a lunghezza variabile ci consente di memorizzare un numero e designare che "tutti gli altri sono zeri".
La codifica delta è una tecnica in cui ogni byte contiene la differenza rispetto a un certo valore, piuttosto che un valore assoluto. Pertanto, la modifica di determinati byte cambia il colore di tutti gli altri pixel. Ad esempio, invece di memorizzare
12 13 14 14 14 13 13 14
Potremmo iniziare con 12 e poi semplicemente indicare quanto aggiungere o sottrarre per ottenere il successivo numero. E questa sequenza in codifica delta assume la forma:
12 1 1 0 0 -1 0 1
I dati trasformati non risultano più piccoli di quelli originali, ma è già più facile comprimerli. L'applicazione della codifica delta prima della codifica a lunghezza variabile può aiutare notevolmente, mantenendo comunque la compressione senza perdite.
La codifica delta è una delle poche tecniche utilizzate al di fuori dei blocchi 8x8. Dei 64 coefficienti DCT, uno è semplicemente una funzione d'onda costante (colore uniforme). Rappresenta la luminosità media di ciascun blocco per i componenti di luminosità o la media di blu per i componenti Cb, e così via. Il primo valore di ogni blocco DCT è chiamato valore DC, e ogni valore DC viene sottoposto a codifica delta rispetto al precedente. Pertanto, la variazione di luminosità del primo blocco influenzerà tutti i blocchi.
Rimane l'ultima mistero: come mai la modifica di un singolo numero rovinerebbe completamente l'immagine? Finora non ci sono state caratteristiche di questo tipo nei livelli di compressione. La risposta si trova nell'intestazione JPEG. I primi 500 byte contengono metadati sull'immagine – larghezza, altezza, ecc., e finché non abbiamo lavorato con essi.
Senza un'intestazione è praticamente impossibile (o molto difficile) decodificare un JPEG. Semberebbe come se cercassi di descriverti un dipinto e iniziassi a inventare parole per trasmettere le mie impressioni. La descrizione sarà probabilmente piuttosto concisa, poiché posso inventare parole con esattamente il significato che voglio trasmettere, ma per gli altri non avranno senso.
Può sembrare sciocco, ma è proprio così che funziona. Ogni immagine JPEG è compressa con codici specifici per essa. Il dizionario dei codici è memorizzato nell'intestazione. Questa tecnica è chiamata "codifica di Huffman", e il dizionario è la tabella di Huffman. Nell'intestazione, la tabella è segnata da due byte: 255 e poi 196. Ogni componente di colore può avere la sua tabella.
Modifiche alle tabelle influenzeranno radicalmente qualsiasi immagine. Un buon esempio è sostituire il 1 alla riga 15 con 12.

Questo accade perché nelle tabelle viene specificato come leggere i singoli bit. Fino ad ora abbiamo lavorato solo con numeri binari in forma decimale. Ma questo ci nasconde il fatto che se vuoi memorizzare il numero 1 in un byte, esso apparirà come 00000001, poiché in ogni byte devono esserci esattamente otto bit, anche se ne serve solo uno.
Potenzialmente questa è una grande spreco di spazio, se hai molti numeri piccoli. Il codice di Huffman è una tecnica che ci permette di attenuare questo requisito, secondo cui ogni numero deve occupare otto bit. Questo significa che se vedi due byte:
234 115
Allora, a seconda della tabella di Huffman, questi possono essere tre numeri. Per estrarli, prima devi dividerli in singoli bit:
11101010 01110011
Poi ci riferiamo alla tabella per capire come raggrupparli. Ad esempio, questi potrebbero essere i primi sei bit (111010), o 58 in sistema decimale, seguiti da cinque bit (10011), o 19, e infine gli ultimi quattro bit (0011), o 3.
Pertanto, è molto difficile districarsi nei byte in questa fase della compressione. I byte non rappresentano ciò che sembra. Non entrerò nei dettagli del lavoro con la tabella in questo articolo, ma su questo argomento in rete .
Uno dei trucchi interessanti che si possono fare, sapendo questo, è separare l'intestazione dal JPEG e conservarla separatamente. Di fatto, risulterà che il file sarà leggibile solo da te. Facebook fa questo per ridurre ulteriormente le dimensioni dei file.
Cosa si può fare ancora – modificare leggermente la tabella di Huffman. Per gli altri sembrerà un'immagine danneggiata. E solo tu conoscerai la magica soluzione per ripararla.
Facciamo un riepilogo: di cosa abbiamo bisogno per decodificare un JPEG? È necessario:
- Estrarre la tabella (o le tabelle) di Huffman dall'intestazione e decodificare i bit.
- Estrarre i coefficienti della trasformata discreta del coseno per ogni componente di colore e luminosità per ogni blocco 8x8, eseguendo le trasformazioni inverse della codifica di run-length e delle delta.
- Combinare i coseni in base ai coefficienti per ottenere i valori dei pixel per ogni blocco 8x8.
- Scala i componenti dei colori, se è stata effettuata una subcampionatura (queste informazioni sono nell'intestazione).
- Converti i valori YCbCr ottenuti per ogni pixel in RGB.
- Mostra l'immagine sullo schermo!
Un lavoro serio per visualizzare una semplice foto di un gattino! Tuttavia, ciò che mi piace è che si può vedere quanto sia centrata sull'umanità la tecnologia JPEG. Essa si basa sulle caratteristiche della nostra percezione, permettendo di ottenere una compressione molto migliore rispetto alle tecnologie tradizionali. E ora, comprendendo come funziona JPEG, si può immaginare come queste tecnologie possano essere trasferite ad altri ambiti. Ad esempio, la codifica delta nei video può ridurre significativamente le dimensioni del file, poiché spesso ci sono intere aree che non cambiano da un fotogramma all'altro (ad esempio, lo sfondo).
, è aperto e contiene istruzioni su come sostituire le immagini con le proprie.
Fonte: habr.com
