Come configurare smartphone e PC. Portale informativo
  • casa
  • Errori
  • Quale metodo di compressione utilizza l'algoritmo jpeg? Il JPEG si comprime meglio del GIF

Quale metodo di compressione utilizza l'algoritmo jpeg? Il JPEG si comprime meglio del GIF

È facile calcolare che un'immagine a colori non compressa con una dimensione di 2000 * 1000 pixel avrà una dimensione di circa 6 megabyte. Se parliamo di immagini ottenute da fotocamere professionali o scanner ad alta risoluzione, le loro dimensioni possono essere ancora maggiori. Nonostante la rapida crescita della capacità dei dispositivi di memorizzazione, diversi algoritmi di compressione delle immagini sono ancora molto rilevanti.
Tutti gli algoritmi esistenti possono essere suddivisi in due grandi classi:

  • Algoritmi di compressione senza perdite;
  • Algoritmi di compressione con perdita.
Quando parliamo di compressione senza perdita di dati, intendiamo che esiste un algoritmo inverso all'algoritmo di compressione che consente di ripristinare con precisione l'immagine originale. Per algoritmi di compressione con perdita algoritmo inverso non esiste. Esiste un algoritmo che ripristina un'immagine che non necessariamente corrisponde esattamente a quella originale. Gli algoritmi di compressione e recupero sono selezionati per ottenere un rapporto di compressione elevato mantenendo la qualità visiva dell'immagine.

Algoritmi di compressione senza perdite

Algoritmo RLE
Tutti gli algoritmi della serie RLE si basano su un'idea molto semplice: i gruppi ripetuti di elementi vengono sostituiti da una coppia (numero di ripetizioni, elemento ripetuto). Consideriamo questo algoritmo usando l'esempio di una sequenza di bit. Questa sequenza alternerà gruppi di zeri e uno. Inoltre, i gruppi avranno spesso più di un elemento. Quindi la sequenza 11111 000000 11111111 00 corrisponderà al seguente insieme di numeri 5 6 8 2. Questi numeri indicano il numero di ripetizioni (il conteggio inizia da uno), ma anche questi numeri devono essere codificati. Assumeremo che il numero di ripetizioni sia compreso tra 0 e 7 (ovvero, 3 bit sono sufficienti per codificare il numero di ripetizioni). Quindi la sequenza considerata sopra viene codificata dalla seguente sequenza di numeri 5 6 7 0 1 2. È facile calcolare che sono necessari 21 bit per codificare la sequenza originale, e nella compressa Metodo RLE Nella forma, questa sequenza richiede 18 bit.
Sebbene questo algoritmo sia molto semplice, la sua efficienza è relativamente bassa. Inoltre, in alcuni casi, l'utilizzo di questo algoritmo non porta ad una diminuzione, ma ad un aumento della lunghezza della sequenza. Ad esempio, considera la seguente sequenza 111 0000 11111111 00. La sequenza RL corrispondente è simile a questa: 3 4 7 0 1 2. La lunghezza della sequenza originale è 17 bit, la lunghezza della sequenza compressa è 18 bit.
Questo algoritmo è più efficace per le immagini in bianco e nero. Viene spesso utilizzato anche come uno degli stadi intermedi di compressione di algoritmi più complessi.

Algoritmi del dizionario

L'idea alla base degli algoritmi del dizionario è che vengono codificate catene di elementi della sequenza originale. Questa codifica utilizza un dizionario speciale, ottenuto in base alla sequenza originale.
Esiste un'intera famiglia di algoritmi di dizionario, ma esamineremo l'algoritmo più comune LZW, che prende il nome dai suoi sviluppatori Lepel, Ziv e Welch.
Il dizionario in questo algoritmo è una tabella che viene riempita con catene di codifica durante l'esecuzione dell'algoritmo. Quando il codice compresso viene decodificato, il dizionario viene ripristinato automaticamente, quindi non è necessario trasmettere il dizionario insieme al codice compresso.
Il dizionario viene inizializzato con tutte le stringhe singleton, ad es. le prime righe del dizionario rappresentano l'alfabeto in cui codifichiamo. Durante la compressione viene ricercata la catena più lunga già registrata nel dizionario. Ogni volta che si incontra una catena che non è stata ancora scritta nel dizionario, questa viene aggiunta lì e viene emesso un codice compresso corrispondente alla catena già scritta nel dizionario. In teoria non vengono imposte restrizioni alla dimensione del dizionario, ma in pratica ha senso limitare questa dimensione, poiché nel tempo iniziano ad apparire catene che non si trovano più nel testo. Inoltre, quando raddoppiamo la dimensione della tabella, dobbiamo allocare un bit in più per memorizzare i codici compressi. Per prevenire tali situazioni, viene introdotto un codice speciale, che simboleggia l'inizializzazione della tabella con tutte le catene a elemento singolo.
Consideriamo un esempio di algoritmo di compressione. Comprimeremo la linea cuckoocuckoocuckoohood. Supponiamo che il dizionario contenga 32 posizioni, il che significa che ciascuno dei suoi codici occuperà 5 bit. Inizialmente, il dizionario è compilato come segue:

Questa tabella esiste sia dalla parte di chi comprime l'informazione, sia dalla parte di chi la decomprime. Ora esamineremo il processo di compressione.

La tabella mostra il processo di compilazione del dizionario. È facile calcolare che il codice compresso risultante richiede 105 bit e il testo originale (assumendo di impiegare 4 bit per codificare un carattere) richiede 116 bit.
In sostanza, il processo di decodifica si riduce alla decodifica diretta dei codici ed è importante che la tabella venga inizializzata allo stesso modo della codifica. Ora diamo un'occhiata all'algoritmo di decodifica.


Possiamo definire completamente la stringa aggiunta al dizionario al passo i-esimo solo a i+1. Ovviamente la riga i-esima deve terminare con il primo carattere della riga i+1. Quello. Abbiamo appena capito come ripristinare un dizionario. Di un certo interesse è la situazione in cui viene codificata una sequenza della forma cScSc, dove c è un carattere e S è una stringa, e la parola cS è già nel dizionario. A prima vista può sembrare che il decoder non riesca a risolvere questa situazione, ma in realtà tutte le righe di questo tipo devono terminare sempre con lo stesso carattere con cui iniziano.

Algoritmi di codifica statistica
Gli algoritmi di questa serie assegnano il codice compresso più breve agli elementi più frequenti delle sequenze. Quelli. sequenze della stessa lunghezza sono codificate con codici compressi di diversa lunghezza. Inoltre, quanto più spesso si verifica una sequenza, tanto più corto è il corrispondente codice compresso.
Algoritmo di Huffman
L'algoritmo di Huffman ti consente di costruire codici prefisso. Puoi pensare ai codici prefisso come a percorsi verso albero binario: un passaggio da un nodo al suo figlio sinistro corrisponde a uno 0 nel codice, e al suo figlio destro corrisponde a un 1. Se etichettiamo le foglie dell'albero con i simboli da codificare, otteniamo una rappresentazione ad albero binario di il codice prefisso.
Descriviamo l'algoritmo per costruire un albero di Huffman e ottenere i codici di Huffman.
  1. I caratteri dell'alfabeto in input formano un elenco di nodi liberi. Ogni foglia ha un peso tale uguale alla frequenza aspetto del simbolo
  2. Vengono selezionati due nodi dell'albero liberi con i pesi più piccoli
  3. Il loro genitore viene creato con un peso pari al peso totale
  4. Il genitore viene aggiunto all'elenco dei nodi liberi e i suoi due figli vengono rimossi da questo elenco
  5. A un arco che lascia l'arco principale viene assegnato il bit 1, all'altro viene assegnato il bit 0
  6. I passaggi a partire dal secondo vengono ripetuti finché nell'elenco dei nodi liberi rimane un solo nodo libero. Questa sarà considerata la radice dell'albero.
Usando questo algoritmo, possiamo ottenere i codici Huffman per un dato alfabeto, tenendo conto della frequenza di occorrenza dei caratteri.
Codifica aritmetica
Gli algoritmi di codifica aritmetica codificano stringhe di elementi in una frazione. In questo caso, viene presa in considerazione la distribuzione di frequenza degli elementi. Al momento gli algoritmi di codifica aritmetica sono protetti da brevetti, quindi esamineremo solo l'idea di base.
Supponiamo che il nostro alfabeto sia costituito rispettivamente da N simboli a1,...,aN e dalle loro frequenze di occorrenza p1,...,pN. Dividiamo la metà dell'intervallo ID tabella: 0
I restanti 64 byte devono riempire la tabella 8x8.



Dai un'occhiata più da vicino all'ordine in cui vengono compilati i valori della tabella. Questo ordine è chiamato ordine a zigzag:

Marker: SOF0 – DCT basale

Questo marcatore si chiama SOF0 e significa che l'immagine è codificata utilizzando il metodo base. È molto comune. Ma non meno popolare su Internet è il familiare metodo progressivo, quando viene prima caricata un'immagine a bassa risoluzione e poi un'immagine normale. Ciò ti consente di capire cosa viene mostrato lì senza aspettare Pieno carico. La specifica definisce molti altri metodi, a mio avviso, non molto comuni.

FF C0 00 11 08 00 10 00 10 03 01 22 00 02
11 01 03 11 01

Lunghezza: 17 byte.
Precisione: 8 bit. Nel metodo base è sempre 8. A quanto ho capito, questa è la profondità di bit dei valori del canale.
Altezza immagine: 0x10 = 16
Larghezza figura: 0x10 = 16
Numero di componenti: 3. Molto spesso si tratta di Y, Cb, Cr.

1° componente:
ID: 1
Diradamento orizzontale (H 1): 2
[_2] Diradamento verticale (V 1): 2
ID tabella di quantizzazione: 0

2° componente:
ID: 2
Diradamento orizzontale (H 2): 1
[_1] Diradamento verticale (V 2): 1

3° componente:
ID: 3
Diradamento orizzontale (H 3): 1
[_1] Diradamento verticale (V 3): 1
ID tabella di quantizzazione: 1

Ora guarda come determinare quanto è sottile un'immagine. Troviamo H max =2 e V max =2. Il canale i verrà assottigliato di H max /H i volte in orizzontale e di V max /V i volte in verticale.

Marcatore: DHT (tabella Huffman)

In questa sezione vengono memorizzati i codici e i valori ottenuti dalla codifica Huffman.

FF DO4 00 15 00 01 01 00 00 00 00
00 00 00 00 00 00 00 00 00 00 03 02

lunghezza: 21 byte.
classe: 0 (0 - tabella coefficienti DC, 1 - tabella coefficienti AC).
[_0] ID tabella: 0
Lunghezza del codice Huffman: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Numero di codici:
Il numero di codici indica il numero di codici di quella lunghezza. Tieni presente che la sezione memorizza solo le lunghezze dei codici, non i codici stessi. Dobbiamo trovare i codici da soli. Quindi, abbiamo un codice di lunghezza 1 e uno di lunghezza 2. Totale 2 codici, non ci sono altri codici in questa tabella.
A ogni codice è associato un valore e sono elencati nel file come segue. I valori sono a byte singolo, quindi leggiamo 2 byte.
- valore del 1° codice.
- valore del 2° codice.

Costruzione di un albero di codici di Huffman

Dobbiamo costruire un albero binario dalla tabella che abbiamo ricevuto nella sezione DHT. E da questo albero riconosciamo ogni codice. Aggiungiamo i valori nell'ordine in cui sono indicati nella tabella. L'algoritmo è semplice: non importa in quale nodo ci troviamo, proviamo sempre ad aggiungere un valore al ramo sinistro. E se è occupata, allora a destra. E se non c’è spazio lì, torniamo a un livello più alto e proviamo da lì. Devi fermarti a un livello pari alla lunghezza del codice. I rami di sinistra corrispondono al valore 0, quelli di destra - 1.
Commento:
Non è necessario ricominciare dall'inizio ogni volta. Aggiunto un valore: torna a un livello superiore. Esiste la filiale giusta? Se sì, sali di nuovo. In caso contrario, crea un ramo a destra e vai lì. Quindi, da questo punto, inizia la ricerca per aggiungere il valore successivo.

Alberi per tutte le tabelle in questo esempio:


UPD (grazie): I nodi del primo albero (DC, id=0) devono avere valori 0x03 e 0x02

Nei cerchi ci sono i significati dei codici, sotto i cerchi ci sono i codici stessi (lasciami spiegare che li abbiamo ottenuti percorrendo il percorso dall'alto verso ciascun nodo). È con questi codici (di questa e di altre tavole) che viene codificato il contenuto stesso della figura.

Indicatore: SOS (inizio scansione)

Il byte nel marcatore significa “SÌ! Infine, siamo passati direttamente all’analisi della sezione dell’immagine codificata!” Tuttavia, la sezione si chiama simbolicamente SOS.

 FF DA 00 0C 03 01 00 02 11
03 11 00 3F 00

Lunghezza della parte dell'intestazione (non dell'intera sezione): 12 byte.
Numero di componenti di scansione. Ne abbiamo 3, uno ciascuno per Y, Cb, Cr.

1° componente:
Numero componente immagine: 1 (Y)
ID tabella Huffman per coefficienti DC: 0
[_0] ID tabella Huffman per coefficienti AC: 0

2° componente:
Numero componente immagine: 2 (Cb)

[_1]

3° componente:
Numero componente immagine: 3 (Cr)
ID della tabella di Huffman per i coefficienti DC: 1
[_1] ID della tabella di Huffman per i coefficienti AC: 1

Questi componenti si alternano ciclicamente.

Qui è dove finisce la parte dell'intestazione, da qui fino alla fine (marcatore) ci sono i dati codificati.


0

Trovare il coefficiente DC.
1. Lettura di una sequenza di bit (se incontriamo 2 byte, allora questo non è un marcatore, ma solo un byte). Dopo ogni bit ci spostiamo lungo l'albero di Huffman (con il corrispondente identificatore) lungo il ramo 0 o 1, a seconda del bit letto. Ci fermiamo se ci troviamo al nodo finale.
10 1011101110011101100001111100100

2. Prendiamo il valore del nodo. Se è uguale a 0, allora il coefficiente è uguale a 0, lo scriviamo nella tabella e procediamo alla lettura degli altri coefficienti. Nel nostro caso - 02. Questo valore è la lunghezza del coefficiente in bit. Cioè leggiamo i prossimi 2 bit, questo sarà il coefficiente.
10 10 11101110011101100001111100100

3. Se la prima cifra del valore nella rappresentazione binaria è 1, la lasciamo così com'è: DC_coef = valore. Altrimenti trasformiamo: DC_coef = valore-2 valore lunghezza +1 . Scriviamo il coefficiente nella tabella all'inizio dello zigzag, nell'angolo in alto a sinistra.

Trovare i coefficienti AC.
1. Simile al passaggio 1, trovare il coefficiente DC. Continuiamo a leggere la sequenza:
10 10 1110 1110011101100001111100100

2. Prendiamo il valore del nodo. Se è 0, significa che i restanti valori della matrice devono essere riempiti con zeri. Quindi viene codificata la matrice successiva. I primi che leggeranno fin qui e me ne scriveranno in un messaggio personale riceveranno un vantaggio in karma. Nel nostro caso, il valore del nodo è 0x31.
Primo bocconcino: 0x3 - questo è esattamente il numero di zeri che dobbiamo aggiungere alla matrice. Questi sono 3 coefficienti zero.
Secondo nibble: 0x1 - lunghezza del coefficiente in bit. Leggi la parte successiva.
10 10 1110 1 110011101100001111100100

3. Simile al passaggio 3 per trovare il coefficiente DC.

Come già capisci, devi leggere i coefficienti AC finché non troviamo un valore di codice zero o finché la matrice non viene riempita.
Nel nostro caso otterremo:
10 10 1110 1 1100 11 101 10 0 0 0 1 11110 0 100
e matrice:





Hai notato che i valori sono riempiti secondo lo stesso schema a zigzag?
Il motivo per utilizzare questo ordine è semplice: poiché maggiori sono i valori di v e u, meno significativo è il coefficiente S vu nella trasformata discreta del coseno. Pertanto, a tassi di compressione elevati, i coefficienti insignificanti vengono impostati su zero, riducendo così la dimensione del file.

[-4 1 1 1 0 0 0 0] [ 5 -1 1 0 0 0 0 0]
[ 0 0 1 0 0 0 0 0] [-1 -2 -1 0 0 0 0 0]
[ 0 -1 0 0 0 0 0 0] [ 0 -1 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0] [-1 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0] [ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0] [ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0] [ 0 0 0 0 0 0 0 0]

[-4 2 2 1 0 0 0 0]
[-1 0 -1 0 0 0 0 0]
[-1 -1 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]

Oh, ho dimenticato di dire che i coefficienti DC codificati non sono i coefficienti DC stessi, ma le loro differenze tra i coefficienti della tabella precedente (lo stesso canale)! Le matrici devono essere corrette:
CD per il 2°: 2 + (-4) = -2
CD per il 3°: -2 + 5 = 3
CD per il 4°: 3 + (-4) = -1

[-2 1 1 1 0 0 0 0] [ 3 -1 1 0 0 0 0 0] [-1 2 2 1 0 0 0 0]
………

Ora è tutto in ordine. Questa regola si applica fino alla fine del file.

... e secondo la matrice per Cb e Cr:

[-1 0 0 0 0 0 0 0]
[ 1 1 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]

Poiché esiste una sola matrice, i coefficienti DC possono essere lasciati invariati.

Calcoli

Quantizzazione

Ricordi che la matrice attraversa una fase di quantizzazione? Gli elementi della matrice devono essere moltiplicati termine per termine con gli elementi della matrice di quantizzazione. Non resta che scegliere quello di cui hai bisogno. Per prima cosa abbiamo scansionato il primo componente, il suo componente immagine = 1. Il componente immagine con questo ID utilizza una matrice di quantizzazione pari a 0 (la nostra è la prima delle due). Quindi, dopo la moltiplicazione:


[ 0 120 280 0 0 0 0 0]
[ 0 -130 -160 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]

Allo stesso modo, otteniamo altre 3 matrici di canali Y...

[-320 110 100 160 0 0 0 0] [ 480 -110 100 0 0 0 0 0]
[ 0 0 140 0 0 0 0 0] [-120 -240 -140 0 0 0 0 0]
[ 0 -130 0 0 0 0 0 0] [ 0 -130 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0] [-140 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0] [ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0] [ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0] [ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0] [ 0 0 0 0 0 0 0 0]

[-160 220 200 160 0 0 0 0]
[-120 0 -140 0 0 0 0 0]
[-140 -130 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]

...e per matrice per Cb e Cr.

[-170 0 0 0 0 0 0 0] [ 0 0 0 0 0 0 0 0]
[ 180 210 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0] [ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0] [ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0] [ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0] [ 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0] [ 0 0 0 0 0 0 0 0]

Trasformata discreta inversa del coseno

La formula non dovrebbe essere difficile*. S vu è la nostra matrice di coefficienti risultante. u - colonna, v - riga. s yx - direttamente i valori del canale.

*In generale questo non è del tutto vero. Quando sono riuscito a decodificare e visualizzare l'immagine 16x16 sullo schermo, ho scattato l'immagine 600x600 (a proposito, questa era la copertina dell'album preferito di Mind.In.A.Box - Lost Alone). Non ha funzionato subito: sono emersi vari bug. Ben presto ho potuto ammirare l'immagine caricata correttamente. L'unica cosa che mi ha davvero sconvolto è stata la velocità di download. Ricordo ancora che ci sono voluti 7 secondi. Ma questo non sorprende, se usi sconsideratamente la formula sopra, per calcolare un canale di un pixel dovrai trovare 128 coseni, 768 moltiplicazioni e alcune addizioni. Pensaci: quasi mille operazioni difficili su un solo canale di un pixel! Fortunatamente c'è spazio per l'ottimizzazione (dopo molti esperimenti, ho ridotto il tempo di caricamento al limite di precisione del timer di 15 ms, e successivamente ho cambiato l'immagine in una foto con un'area 25 volte più grande. Forse ne scriverò in un articolo a parte).

Scriverò il risultato del calcolo solo della prima matrice del canale Y (i valori sono arrotondati):


[ 87 72 50 36 37 55 79 95]
[-10 5 31 56 71 73 68 62]
[-87 -50 6 56 79 72 48 29]

E i restanti 2:
Cb Cr
[ 60 52 38 20 0 -18 -32 -40] [ 19 27 41 60 80 99 113 120]
[ 48 41 29 13 -3 -19 -31 -37] [ 0 6 18 34 51 66 78 85]
[ 25 20 12 2 -9 -19 -27 -32] [-27 -22 -14 -4 7 17 25 30]
[ -4 -6 -9 -13 -17 -20 -23 -25] [-43 -41 -38 -34 -30 -27 -24 -22]
[ -37 -35 -33 -29 -25 -21 -18 -17] [-35 -36 -39 -43 -47 -51 -53 -55]
[ -67 -63 -55 -44 -33 -22 -14 -10] [ -5 -9 -17 -28 -39 -50 -58 -62]
[ -90 -84 -71 -56 -39 -23 -11 -4] [ 32 26 14 -1 -18 -34 -46 -53]
[-102 -95 -81 -62 -42 -23 -9 -1] [ 58 50 36 18 -2 -20 -34 -42]

  1. Oh, vado a mangiare!
  2. Sì, non mi muovo affatto, di cosa stiamo parlando.
  3. Una volta ottenuti i valori di colore YCbCr, non resta che convertirli in RGB, in questo modo: YCbCrToRGB(Y ij , Cb ij , Cr ij) , Y ij , Cb ij , Cr ij - le nostre matrici risultanti.
  4. 4 matrici Y e un Cb e un Cr ciascuna, poiché abbiamo assottigliato i canali e 4 pixel Y corrispondono a un Cb e Cr. Pertanto, calcola in questo modo: YCbCrToRGB(Y ij , Cb , Cr )
Se hai scelto 1 e 4, allora sono felice per te. O hai capito bene, o presto ti godrai il cibo.

YCbCr in RGB

R = Y + 1.402 * Cr
G = Y - 0,34414 * Cb - 0,71414 * Cr
B = Y + 1,772 * Cb
Non dimenticare di aggiungere 128. Se i valori superano l'intervallo, assegna valori limite. La formula è semplice, ma consuma anche una parte del tempo del processore.

Ecco le tabelle risultanti per i canali R, G, B per il quadrato 8x8 in alto a sinistra del nostro esempio:
255 248 194 148 169 215 255 255
255 238 172 115 130 178 255 255
255 208 127 59 64 112 208 255
255 223 143 74 77 120 211 255
237 192 133 83 85 118 184 222
177 161 146 132 145 162 201 217
56 73 101 126 144 147 147 141
0 17 76 126 153 146 127 108

231 185 117 72 67 113 171 217
229 175 95 39 28 76 139 189
254 192 100 31 15 63 131 185
255 207 115 46 28 71 134 185
255 241 175 125 112 145 193 230
226 210 187 173 172 189 209 225
149 166 191 216 229 232 225 220
72 110 166 216 238 231 206 186

255 255 249 203 178 224 255 255
255 255 226 170 140 187 224 255
255 255 192 123 91 138 184 238
255 255 208 139 103 146 188 239
255 255 202 152 128 161 194 232
255 244 215 200 188 205 210 227
108 125 148 172 182 184 172 167
31 69 122 172 191 183 153 134

FINE

In generale, non sono uno specialista di JPEG, quindi difficilmente riesco a rispondere a tutte le domande. È solo che quando scrivevo il mio decoder, spesso ho dovuto affrontare vari problemi incomprensibili. E quando l'immagine veniva visualizzata in modo errato, non sapevo dove avevo commesso l'errore. Forse ha interpretato i pezzi in modo errato o forse ha utilizzato il DCT in modo errato. Mi mancava davvero un esempio passo passo, quindi spero che questo articolo possa esserti d'aiuto durante la scrittura di un decodificatore. Penso che copra la descrizione del metodo di base, ma non puoi comunque farlo da solo. Ti propongo i link che mi hanno aiutato:

Fotografie e immagini differiscono l'una dall'altra non solo nel contenuto, ma anche in altre caratteristiche "informatiche". Ad esempio, per dimensione.

Succede che sembrano esserci due disegni identici, ma uno è tre volte più grande dell'altro.

Le immagini differiscono anche in termini di qualità. Penso che ti sia imbattuto in foto più di una volta Pessima qualità. Questo è visibile ad occhio nudo. Ad esempio, due fotografie identiche, ma una di qualità migliore e l'altra di qualità peggiore.

E succede che il disegno sembra privo di colori. Ecco un esempio.

E il formato o il tipo di file è responsabile di tutto questo.

In effetti, le immagini sono disponibili in una varietà di formati. E ce ne sono moltissimi. Non li considereremo tutti, ma parleremo di quelli più comuni. Questi sono formati come bmp, gif, jpg, png, tiff.

Differiscono l'uno dall'altro, prima di tutto, per la qualità. E la qualità differisce nel numero (saturazione) dei colori.

Ad esempio, dipingo un'immagine utilizzando colori diversi. E poi all'improvviso alcuni finiscono e devi finire di dipingere con quello che hai. Naturalmente, cercherò di fare tutto il possibile in modo che ciò non influisca molto sul risultato, ma l'immagine non risulterà comunque come vorrei: più sbiadita, sfocata.

Con i formati immagine è così. Alcuni lasciano tutti i colori, mentre altri ne tagliano alcuni. E a volte questo fa sì che l'immagine si deteriori.

Questo è un esempio piuttosto approssimativo. In effetti, tutto è un po' più complicato, ma penso che tu abbia capito il punto principale.

Formati di immagine comuni

BMP è un formato per i disegni realizzati nel programma Paint. Può essere utilizzato per memorizzare immagini disegnate sul tuo computer. Ma questo tipo di file non viene utilizzato su Internet a causa delle sue grandi dimensioni. Quindi, se desideri pubblicare un'immagine disegnata con Paint su un blog o un social network, deve essere di tipo diverso: gif, jpg o png.

GIF è un formato immagine popolare su Internet. Puoi salvarli senza perdere la qualità, ma con un numero limitato di colori - 256. La GIF ha guadagnato particolare popolarità grazie al fatto che può creare piccole immagini animate (in movimento).

JPG è un formato per fotografie e dipinti con un gran numero di colori. Puoi salvare un'immagine al suo interno sia senza perdita di qualità che con perdita.

PNG è un formato immagine moderno. Si ottiene questo tipo di immagine taglia piccola e senza perdita di qualità. Molto comodo: il file è piccolo e la qualità è buona. Supporta anche la trasparenza.

TIFF: le immagini sono di ottima qualità, senza compressione, di conseguenza la dimensione di tali file è enorme. TIFF viene utilizzato quando la qualità è di grande importanza. Ad esempio, quando si creano biglietti da visita, opuscoli, copertine di riviste.

Quale formato scegliere

  • BMP: se si tratta di un disegno realizzato in Paint e lo manterrai solo sul computer.
  • GIF - se un'animazione o un disegno con un numero limitato di colori per la pubblicazione su Internet.
  • PNG - se si tratta di un'immagine con molti colori o alcune parti trasparenti.
  • JPG (jpeg) - se una fotografia.
  • TIFF - immagine per la stampa (biglietti da visita, opuscoli, poster, ecc.).

Ciao, cari amici. Oggi parleremo di quale formato immagine è meglio utilizzare sul sito, quali formati di file grafici sono disponibili oggi per il sito e se è necessario perseguire nuovi formati grafici.

Ricevo molte domande come questa; molti dei miei studenti mi chiedono se possono utilizzare i nuovi formati SVG e WebP e qual è il posto migliore per utilizzare queste immagini. Certo, puoi usare nuovi formati, devi solo capire quale formato è più adatto a cosa.

Oggi le immagini su un sito web ne sono parte integrante. A partire da graphic design e il caricamento di immagini negli articoli, la grafica accompagna la maggior parte dei siti della rete. Ma la bellezza ha un prezzo

Le immagini non ottimizzate sono uno dei fattori che rallentano un sito web, come indicato dai servizi di verifica.

Pertanto, dovrai sempre scegliere quale formato scegliere per l'immagine. Le sue dimensioni e qualità dipenderanno da questo. E usare le immagini taglia più piccola e senza perdere qualità, dovresti sapere alcune cose.

Quali immagini per i siti web utilizzo oggi?

Tutte le immagini per i siti web sono suddivise in:

  • raster (esempio: JPG, JPEG, GIF, PNG),
  • vettore (esempio - SVG).

Raster Le immagini sono costituite da pixel che memorizzano valori di colore e trasparenza. Questi formati includono immagini in articoli, pulsanti, icone ed elementi di design. Queste immagini sono popolari tra gli sviluppatori e i proprietari di siti web. Lo svantaggio principale delle immagini raster è che non si adattano bene.

Cioè, quando la dimensione dell'immagine aumenta, si verifica una perdita di qualità.

Vettore le immagini sono costituite da linee e punti di passaggio. Le informazioni sull'immagine vengono memorizzate in istruzioni di rendering matematico, che consentono di ridimensionare tali immagini quanto desiderato senza perdita di qualità.

Tutte queste immagini possono e vengono utilizzate sui siti Web moderni. Devi solo capirlo prima di caricarlo sul sito!

Descrizione dei formati di immagine più diffusi per il sito

Dalla descrizione di questi formati capirai dove e quale formato è meglio utilizzare nel sito.

JPEG

JPEG o JPG è uno dei formati di immagine più popolari per i siti Web. Il formato supporta milioni di colori, il che gli conferisce una posizione di leadership nella presentazione di fotografie e immagini sul sito.

Le immagini in questo formato sono ottimizzate abbastanza bene praticamente senza perdita di qualità, il che consente di ottenere un file più piccolo senza perdita visiva di qualità. Va ricordato che ogni ottimizzazione successiva riduce la qualità.

I file di questo formato sono supportati da tutti i dispositivi e browser, il che conferma ancora una volta la sua popolarità e ti consente di non preoccuparti di problemi di visualizzazione sui siti web.

Il grande svantaggio di questo formato è la mancanza di trasparenza. Cioè, non sarà possibile combinare immagini in questo formato. Per tali compiti è meglio utilizzare il seguente formato.

PNG

Questo formato utilizza un algoritmo di compressione senza perdita di dati. In termini di numero di colori e livello di trasparenza, è disponibile in due tipi: 8 e 24 bit. Entrambi sostengono la trasparenza.

La tecnologia a 8 bit non è molto popolare, ma la versione a 24 bit è ampiamente utilizzata varie immagini In linea. Grazie alla trasparenza, ti consente di creare immagini combinate. Spesso utilizzato per creare pulsanti e icone animati in cui è necessario un effetto di trasparenza.

Le immagini in formato PNG possono essere ottimizzate e modificate più volte: manterranno la qualità originale.

Il formato è inoltre supportato da tutti i browser e dispositivi, garantendone la visualizzazione su qualsiasi schermo.

La qualità delle immagini sembra migliore di JPG, ma il peso del file sarà maggiore. Questo deve essere preso in considerazione quando si inseriscono file sul sito.

GIF

È un formato a 8 bit che supporta 256 colori, trasparenza e animazione. Grazie al supporto di un numero limitato di colori, anche il peso del file è minimo.

Il formato non è adatto per fotografie e immagini con vasta gamma colori.

Ma è ampiamente utilizzato nella creazione di banner, pulsanti, icone e così via.

Nei siti web moderni questo formato viene utilizzato sempre meno.

Parliamo poi dei formati relativamente recenti SVG e WebP, che non sono così popolari, ma stanno guadagnando popolarità e supporto e sono perfettamente adatti ai requisiti di velocità di caricamento e adattabilità del sito web.

SVG

Questo è il formato file vettoriali SU Basato su XML. Il formato ha iniziato a guadagnare popolarità abbastanza recentemente, poiché in precedenza era scarsamente supportato nei browser. E a causa di problemi di visualizzazione, nessuno aveva fretta di usarlo.

Oggi SVG è supportato da tutti i browser moderni. Ma si verificano ancora problemi con il display.

Questo formato viene spesso utilizzato per immagini semplici, come loghi, elementi di design e così via. Non adatto per fotografie.

Il formato SVG è leggero, altamente scalabile, fornisce immagini nitide su qualsiasi risoluzione dello schermo, supporta l'animazione, può essere controllato tramite CSS e inserito in HTML, riducendo il numero di richieste.

WebP

Un formato open source sviluppato da Google appositamente per Internet. Oggi YouTube utilizza la conversione delle miniature dei video in WebP.

Il formato fornisce una compressione superiore e supporta la trasparenza. Combina i vantaggi dei formati JPG e PNG senza aumentare le dimensioni del file.

Ma, nonostante i vantaggi del formato, non è supportato da tutti i browser, ad esempio IE, Edge, Firefox e Safari.

Esistono modi per aggirare queste restrizioni, ma impediscono l'utilizzo del formato ovunque.

Conclusione

Amici, spero di aver spiegato tutto chiaramente e che ora sappiate quale formato di immagine è meglio utilizzare sul sito e perché non insisto sull'utilizzo di un formato particolare, ma consiglio un approccio integrato.

Forse quando WebP otterrà un supporto diffuso, passeremo tutti ad esso e sostituiremo jpg e png sui nostri siti.

Discutiamo nei commenti quali formati utilizzate sui vostri siti, cosa vi piace e cosa non vi piace.

Per oggi è tutto, aspetto i vostri commenti.

Cordiali saluti, Maxim Zaitsev.

    CON I tre formati di file più popolari sono JPEG, RAW, TIFF. A volte puoi sentire disaccordi tra i fotografi: quale formato di file è migliore per la fotografia, in quale formato è meglio scattare foto, perché le fotocamere moderne ti consentono di scattare fotografietografia in uno qualsiasi di questi formati e talvolta in diversi contemporaneamente!

    Il formato file in cui viene archiviata un'immagine è essenzialmente un compromesso tra la qualità dell'immagine e la dimensione del file.

    Probabilmente sai già che un'immagine raster è composta da pixel. Il modo in cui è organizzato un file raster e in quale forma sono archiviate le informazioni sui pixel determina il formato del file. La qualità dell'immagine di un file raster è determinata da due parametri principali: la dimensione dei pixel (ovvero il numero totale di pixel) e la precisione del colore del pixel nel rappresentare il colore reale.Con la dimensione dei pixel è chiaro: maggiore è il numero di pixel (o “più piccolo” il pixel), meglio è.E la precisione della riproduzione del colore dipende dal numero di colori per pixel o dalla profondità del colore.

    Profondità colore (qualità della resa cromatica, profondità in bit dell'immagine): la quantità di memoria nel numero di bit utilizzata per archiviare e rappresentare il colore durante la codifica di un pixel di grafica raster o immagine video. Il numero di bit indica il numero di gradazioni (passi di tono) in ciascuna componente di colore o, semplicemente, il numero di colori. Aggiungere 1 bit significa aggiungere un altro bit al file codice binario cromaticità.

    • Colore a 1 bit (21 = 2 colori) colore binario, spesso rappresentato da bianco e nero (o nero e verde)
    • Colore a 2 bit (22 = 4 colori) CGA, scala di grigi NeXTstation
    • Colore a 3 bit (23 = 8 colori) molti personal computer legacy con uscita TV
    • Il colore a 4 bit (24 = 16 colori) è noto come EGA e, in misura minore, come standard VGA ad alta risoluzione
    • Colore a 5 bit (25 = 32 colori) Chipset originale Amiga
    • Colore a 6 bit (26 = 64 colori) Chipset Amiga originale
    • Colore a 8 bit (28 = 256 colori) Workstation Unix legacy, VGA a bassa risoluzione, Super VGA, AGA
    • Colore a 12 bit (212 = 4.096 colori) su alcuni sistemi Silicon Graphics, sistemi NeXTstation e sistemi in modalità Amiga HAM.

    Ad esempio, lavoriamo nello spazio colore RGB. Ciò significa che ci sono tre canali da cui si forma il colore finale del pixel: il canale rosso (Rad), il canale verde (Green) e il canale blu (Blue). Supponiamo che i canali siano a quattro bit. Ciò significa che ciascun canale ha la capacità di visualizzare 16 colori. Di conseguenza, tutto l'RGB sarà a 12 bit e sarà in grado di essere visualizzato

    C=16x16x16=4096 colori

    La profondità del colore in questo caso è di 12 bit.

    Quando si parla di RGB a 24 bit, si intendono canali a 8 bit (256 colori ciascuno) con un numero totale di opzioni colore per pixel

    C=256x256x256=16777216 colori.

    La cifra è impressionante. Questo numero di colori per ciascun pixel soddisfa i requisiti del fotografo più esigente.

    Un po' sui formati stessi.

    Formato TIFF

    TIFF sta per Tagged Image File Format ed è uno standard per il settore della stampa e della stampa.

    Di conseguenza, questo è ciò che accade:

    1. Se la tua fotocamera è così semplice da scattare solo JPEG e desideri ottenerla massima qualità, imposta la dimensione massima e la compressione minima e non tormentarti con il fatto di non avere altri formati. Nella maggior parte dei casi, un'immagine RAW resa manualmente scrupolosamente corrisponde al JPEG catturato automaticamente dalla fotocamera.

    2. Probabilmente non dovresti scattare fotografie in TIFF. La registrazione in questo formato è più difficile, ma non vi è alcuna differenza evidente rispetto al JPEG di alta qualità.

    3. Se hai l'opportunità di scattare foto, lavora con esso. Sentirai tu stesso se è giusto per te. In alcuni casi, solo RAW consente di scattare una foto unica con un ingrandimento elevato durante la stampa.

    Resta ancora una soluzione, si potrebbe dire universale. Esiste una modalità che ti consente di scattare fotogrammi in due formati contemporaneamente: RAW+ JPEG. Riprendi scene importanti in questa modalità. La moderna memorizzazione delle informazioni digitali, sia schede di memoria che dischi rigidi, rende possibile ciò. In questo caso riceverai un JPEG per utilizzare immediatamente la foto, senza perdere tempo nella revisione. E, se ne hai bisogno, affida il file RAW a uno specialista per l'elaborazione.

    Foto. Formati di file.

    È facile calcolare che un'immagine a colori non compressa con una dimensione di 2000 * 1000 pixel avrà una dimensione di circa 6 megabyte. Se parliamo di immagini ottenute da fotocamere professionali o scanner ad alta risoluzione, le loro dimensioni possono essere ancora maggiori. Nonostante la rapida crescita della capacità dei dispositivi di memorizzazione, diversi algoritmi di compressione delle immagini sono ancora molto rilevanti.
    Tutti gli algoritmi esistenti possono essere suddivisi in due grandi classi:

    • Algoritmi di compressione senza perdite;
    • Algoritmi di compressione con perdita.
    Quando parliamo di compressione senza perdita di dati, intendiamo che esiste un algoritmo inverso all'algoritmo di compressione che consente di ripristinare con precisione l'immagine originale. Non esiste un algoritmo inverso per gli algoritmi di compressione con perdita. Esiste un algoritmo che ripristina un'immagine che non necessariamente corrisponde esattamente a quella originale. Gli algoritmi di compressione e recupero sono selezionati per ottenere un rapporto di compressione elevato mantenendo la qualità visiva dell'immagine.

    Algoritmi di compressione senza perdite

    Algoritmo RLE
    Tutti gli algoritmi della serie RLE si basano su un'idea molto semplice: i gruppi ripetuti di elementi vengono sostituiti da una coppia (numero di ripetizioni, elemento ripetuto). Consideriamo questo algoritmo usando l'esempio di una sequenza di bit. Questa sequenza alternerà gruppi di zeri e uno. Inoltre, i gruppi avranno spesso più di un elemento. Quindi la sequenza 11111 000000 11111111 00 corrisponderà al seguente insieme di numeri 5 6 8 2. Questi numeri indicano il numero di ripetizioni (il conteggio inizia da uno), ma anche questi numeri devono essere codificati. Assumeremo che il numero di ripetizioni sia compreso tra 0 e 7 (ovvero, 3 bit sono sufficienti per codificare il numero di ripetizioni). Quindi la sequenza discussa sopra viene codificata dalla seguente sequenza di numeri 5 6 7 0 1 2. È facile calcolare che la codifica della sequenza originale richiede 21 bit e nella forma compressa RLE questa sequenza richiede 18 bit.
    Sebbene questo algoritmo sia molto semplice, la sua efficienza è relativamente bassa. Inoltre, in alcuni casi, l'utilizzo di questo algoritmo non porta ad una diminuzione, ma ad un aumento della lunghezza della sequenza. Ad esempio, considera la seguente sequenza 111 0000 11111111 00. La sequenza RL corrispondente è simile a questa: 3 4 7 0 1 2. La lunghezza della sequenza originale è 17 bit, la lunghezza della sequenza compressa è 18 bit.
    Questo algoritmo è più efficace per le immagini in bianco e nero. Viene spesso utilizzato anche come uno degli stadi intermedi di compressione di algoritmi più complessi.

    Algoritmi del dizionario

    L'idea alla base degli algoritmi del dizionario è che vengono codificate catene di elementi della sequenza originale. Questa codifica utilizza un dizionario speciale, ottenuto in base alla sequenza originale.
    Esiste un'intera famiglia di algoritmi di dizionario, ma esamineremo l'algoritmo più comune LZW, che prende il nome dai suoi sviluppatori Lepel, Ziv e Welch.
    Il dizionario in questo algoritmo è una tabella che viene riempita con catene di codifica durante l'esecuzione dell'algoritmo. Quando il codice compresso viene decodificato, il dizionario viene ripristinato automaticamente, quindi non è necessario trasmettere il dizionario insieme al codice compresso.
    Il dizionario viene inizializzato con tutte le stringhe singleton, ad es. le prime righe del dizionario rappresentano l'alfabeto in cui codifichiamo. Durante la compressione viene ricercata la catena più lunga già registrata nel dizionario. Ogni volta che si incontra una catena che non è stata ancora scritta nel dizionario, questa viene aggiunta lì e viene emesso un codice compresso corrispondente alla catena già scritta nel dizionario. In teoria non vengono imposte restrizioni alla dimensione del dizionario, ma in pratica ha senso limitare questa dimensione, poiché nel tempo iniziano ad apparire catene che non si trovano più nel testo. Inoltre, quando raddoppiamo la dimensione della tabella, dobbiamo allocare un bit in più per memorizzare i codici compressi. Per prevenire tali situazioni, viene introdotto un codice speciale, che simboleggia l'inizializzazione della tabella con tutte le catene a elemento singolo.
    Consideriamo un esempio di algoritmo di compressione. Comprimeremo la linea cuckoocuckoocuckoohood. Supponiamo che il dizionario contenga 32 posizioni, il che significa che ciascuno dei suoi codici occuperà 5 bit. Inizialmente, il dizionario è compilato come segue:

    Questa tabella esiste sia dalla parte di chi comprime l'informazione, sia dalla parte di chi la decomprime. Ora esamineremo il processo di compressione.


    La tabella mostra il processo di compilazione del dizionario. È facile calcolare che il codice compresso risultante richiede 105 bit e il testo originale (assumendo di impiegare 4 bit per codificare un carattere) richiede 116 bit.
    In sostanza, il processo di decodifica si riduce alla decodifica diretta dei codici ed è importante che la tabella venga inizializzata allo stesso modo della codifica. Ora diamo un'occhiata all'algoritmo di decodifica.



    Possiamo definire completamente la stringa aggiunta al dizionario al passo i-esimo solo a i+1. Ovviamente la riga i-esima deve terminare con il primo carattere della riga i+1. Quello. Abbiamo appena capito come ripristinare un dizionario. Di un certo interesse è la situazione in cui viene codificata una sequenza della forma cScSc, dove c è un carattere e S è una stringa, e la parola cS è già nel dizionario. A prima vista può sembrare che il decoder non riesca a risolvere questa situazione, ma in realtà tutte le righe di questo tipo devono terminare sempre con lo stesso carattere con cui iniziano.

    Algoritmi di codifica statistica
    Gli algoritmi di questa serie assegnano il codice compresso più breve agli elementi più frequenti delle sequenze. Quelli. sequenze della stessa lunghezza sono codificate con codici compressi di diversa lunghezza. Inoltre, quanto più spesso si verifica una sequenza, tanto più corto è il corrispondente codice compresso.
    Algoritmo di Huffman
    L'algoritmo di Huffman consente di costruire codici prefisso. Possiamo pensare ai codici prefisso come percorsi in un albero binario: andare da un nodo al suo figlio sinistro corrisponde a uno 0 nel codice, e al suo figlio destro corrisponde a un 1. Se etichettiamo le foglie dell'albero con i simboli da codificare, otteniamo una rappresentazione ad albero binario del codice del prefisso.
    Descriviamo l'algoritmo per costruire un albero di Huffman e ottenere i codici di Huffman.
  1. I caratteri dell'alfabeto in input formano un elenco di nodi liberi. Ogni foglio ha un peso pari alla frequenza di occorrenza del simbolo
  2. Vengono selezionati due nodi dell'albero liberi con i pesi più piccoli
  3. Il loro genitore viene creato con un peso pari al peso totale
  4. Il genitore viene aggiunto all'elenco dei nodi liberi e i suoi due figli vengono rimossi da questo elenco
  5. A un arco che lascia l'arco principale viene assegnato il bit 1, all'altro viene assegnato il bit 0
  6. I passaggi a partire dal secondo vengono ripetuti finché nell'elenco dei nodi liberi rimane un solo nodo libero. Questa sarà considerata la radice dell'albero.
Usando questo algoritmo, possiamo ottenere i codici Huffman per un dato alfabeto, tenendo conto della frequenza di occorrenza dei caratteri.
Codifica aritmetica
Gli algoritmi di codifica aritmetica codificano stringhe di elementi in una frazione. In questo caso, viene presa in considerazione la distribuzione di frequenza degli elementi. Al momento gli algoritmi di codifica aritmetica sono protetti da brevetti, quindi esamineremo solo l'idea di base.
  • Esercitazione


Hai capito correttamente dal titolo che questa non è una descrizione molto ordinaria dell'algoritmo JPEG (ho descritto in dettaglio il formato del file nell'articolo "Decodifica JPEG for Dummies"). Innanzitutto, il metodo scelto per presentare il materiale presuppone che non sappiamo nulla non solo del JPEG, ma anche della trasformata di Fourier e della codifica di Huffman. In generale, ricordiamo poco delle lezioni. Hanno semplicemente scattato la foto e hanno iniziato a pensare a come comprimerla. Pertanto, ho cercato di esprimere chiaramente solo l'essenza, ma in cui il lettore svilupperà una comprensione abbastanza profonda e, soprattutto, intuitiva dell'algoritmo. Formule e calcoli matematici: come minimo, solo quelli importanti per capire cosa sta succedendo.

Conoscere l'algoritmo JPEG è molto utile non solo per la compressione delle immagini. Utilizza la teoria da elaborazione digitale segnali, analisi matematica, algebra lineare, teoria dell'informazione, in particolare la trasformata di Fourier, codifica senza perdite, ecc. Pertanto, la conoscenza acquisita può essere utile ovunque.

Se lo desideri, ti suggerisco di eseguire tu stesso gli stessi passaggi parallelamente all'articolo. Verificare in che misura il ragionamento di cui sopra è adatto immagini diverse, prova ad apportare le tue modifiche all'algoritmo. È molto interessante. Come strumento, posso consigliare la meravigliosa combinazione di Python + NumPy + Matplotlib + PIL(Pillow). Quasi tutto il mio lavoro (compresa la grafica e l'animazione) è stato svolto utilizzandoli.

Attenzione, traffico! Molte illustrazioni, grafici e animazioni (~ 10 Mb). Per ironia della sorte, nell'articolo su JPEG ci sono solo 2 immagini su cinquanta con questo formato.

Qualunque sia l'algoritmo di compressione delle informazioni, il suo principio sarà sempre lo stesso: trovare e descrivere modelli. Maggiore è il numero di modelli, maggiore è la ridondanza, minore è l'informazione. Gli archiviatori e i programmatori sono solitamente “su misura” per un tipo specifico di informazioni e sanno dove trovarle. In alcuni casi, uno schema è immediatamente visibile, come nell'immagine di un cielo azzurro. Ogni riga della sua rappresentazione digitale può essere descritta in modo abbastanza accurato da una linea retta.

Ci addestreremo sui gatti procione. L'immagine grigia qui sopra è presa come esempio. Combina bene sia le aree omogenee che quelle contrastanti. E se impariamo a comprimere il grigio, non ci saranno problemi con il colore.

Rappresentazione vettoriale

Innanzitutto, controlliamo quanto sono dipendenti due pixel vicini. È logico supporre che molto probabilmente saranno molto simili. Controlliamo questo per tutte le coppie di immagini. Contrassegniamoli sul piano delle coordinate con punti in modo che il valore del punto lungo l'asse X sia il valore del primo pixel e lungo l'asse Y sia il secondo. Per la nostra immagine che misura 256 x 256, otteniamo 256*256/2 pixel:


Com'era prevedibile, la maggior parte dei punti si trovano sopra o vicino alla linea y=x (e ce ne sono ancora di più di quelli che si possono vedere nella figura, poiché si sovrappongono più volte e, inoltre, sono traslucidi). Se così fosse sarebbe più semplice lavorare ruotandoli di 45°. Per fare ciò, è necessario esprimerli in un sistema di coordinate diverso.


I vettori fondamentali del nuovo sistema sono ovviamente: . Siamo costretti a dividere per la radice di due per ottenere un sistema ortonormale (le lunghezze dei vettori base sono pari a uno). Qui viene mostrato che un certo punto p = (x, y) nel nuovo sistema sarà rappresentato come un punto (a 0 , a 1). Conoscendo i nuovi coefficienti, possiamo facilmente ottenere quelli vecchi capovolgendoli. Ovviamente la prima (nuova) coordinata è la media, e la seconda è la differenza di xey (ma divisa per radice 2). Immagina che ti venga chiesto di lasciare solo uno dei valori: uno 0 o un 1 (ovvero, equiparare l'altro a zero). È meglio scegliere uno 0, poiché molto probabilmente il valore di 1 sarà attorno allo zero. Questo è cosa succede se ripristiniamo l'immagine solo da 0:


Ingrandimento 4x:


Questa compressione non è molto impressionante, a dire il vero. È meglio dividere in modo simile l'immagine in triplette di pixel e presentarli in uno spazio tridimensionale.

Questo è lo stesso grafico, ma da diversi punti di vista. Le linee rosse sono gli assi che si sono suggeriti. Corrispondono ai vettori: . Lascia che ti ricordi che devi dividere per alcune costanti in modo che le lunghezze dei vettori diventino uguali a uno. Quindi, espandendo su questa base, otteniamo 3 valori: 0, 1, 2 e 0 è più importante di 1 e 1 è più importante di 2. Se scartiamo un 2, il grafico si “appiattirà” nella direzione del vettore e 2. Questo foglio tridimensionale già piuttosto sottile diventerà piatto. Non perderà molto, ma ci libereremo di un terzo dei valori. Confrontiamo le immagini ricostruite dalle triple: (a 0 , 0, 0), (a 1 , a 2 , 0) e (a 0 , a 1 , a 2). IN ultima versione Non abbiamo buttato via nulla, quindi prenderemo l'originale.


Ingrandimento 4x:


Il secondo disegno è già buono. Le aree nitide sono state leggermente attenuate, ma nel complesso l'immagine è stata conservata molto bene. E ora dividiamo per quattro allo stesso modo e determiniamo visivamente la base nello spazio quadridimensionale... Oh, beh, sì. Ma puoi indovinare quale sarà uno dei vettori base: (1,1,1,1)/2. Pertanto, si può guardare la proiezione dello spazio quadridimensionale sullo spazio perpendicolare al vettore (1,1,1,1) per identificarne altri. Ma questo non è il modo migliore.
Il nostro obiettivo è imparare come trasformare (x 0 , x 1 , ..., x n-1) in (a 0 , a 1 , ..., a n-1) in modo che ogni valore di a i sia meno importante rispetto ai precedenti. Se riusciamo a farlo, forse gli ultimi valori della sequenza possono essere eliminati del tutto. Gli esperimenti di cui sopra suggeriscono che è possibile. Ma non puoi fare a meno di un apparato matematico.
Quindi, dobbiamo trasformare i punti in una nuova base. Ma prima devi trovare una base adeguata. Torniamo al primo esperimento di abbinamento. Consideriamolo in generale. Abbiamo definito i vettori base:

Abbiamo espresso il vettore attraverso di loro P:

o in coordinate:

Per trovare uno 0 e un 1 è necessario proiettare P SU e 0 e e 1 rispettivamente. E per questo devi trovare il prodotto scalare

simile:

Nelle coordinate:

Spesso è più conveniente effettuare la trasformazione in forma matriciale.

Quindi A = EX e X = E T A. Questa è una forma bella e conveniente. La matrice E si chiama matrice di trasformazione ed è ortogonale, la incontreremo più avanti.

Transizione dai vettori alle funzioni.

È conveniente lavorare con vettori di piccole dimensioni. Tuttavia, vogliamo trovare pattern in blocchi più grandi, quindi invece di vettori N-dimensionali è più conveniente operare con le sequenze che rappresentano l'immagine. Chiamerò tali successioni funzioni discrete, poiché il ragionamento seguente vale anche per le funzioni continue.
Tornando al nostro esempio, immaginiamo una funzione f(i), definita solo in due punti: f(0)=x e f(1)=y. Allo stesso modo, definiamo le funzioni di base e 0 (i) ed e 1 (i) in base alle basi e 0 e e 1 . Noi abbiamo:

Questa è una conclusione molto importante. Ora nella frase “espansione di un vettore in vettori ortonormali” possiamo sostituire la parola “vettore” con “funzione” e ottenere l'espressione completamente corretta “espansione di una funzione in funzioni ortonormali”. Non importa che abbiamo ottenuto una funzione così breve, poiché lo stesso ragionamento vale per un vettore N-dimensionale, che può essere rappresentato come una funzione discreta con N valori. E lavorare con le funzioni è più chiaro che con i vettori N-dimensionali. Al contrario, puoi rappresentare una funzione come un vettore. Inoltre, il solito funzione continua può essere rappresentato come un vettore a dimensione infinita, sebbene non nello spazio euclideo, ma nello spazio di Hilbert. Ma non andremo lì; saremo interessati solo alle funzioni discrete.
E il nostro problema di trovare una base si trasforma nel problema di trovare un sistema adeguato di funzioni ortonormali. Nel ragionamento seguente, si presuppone di aver già in qualche modo determinato un insieme di funzioni di base, in base alle quali scomporremo.
Diciamo che abbiamo qualche funzione (rappresentata, ad esempio, da valori) che vogliamo rappresentare come somma di altre. Puoi rappresentare questo processo in forma vettoriale. Per scomporre una funzione è necessario “proiettarla” sulle funzioni base una per una. In senso vettoriale, il calcolo della proiezione fornisce l'avvicinamento minimo del vettore originale a un altro in termini di distanza. Ricordando che la distanza viene calcolata utilizzando il teorema di Pitagora, una rappresentazione simile sotto forma di funzioni fornisce la migliore approssimazione della radice quadrata di una funzione rispetto a un'altra. Pertanto, ciascun coefficiente (k) determina la “vicinanza” della funzione. Più formalmente, k*e(x) è la migliore approssimazione quadratica media a f(x) tra l*e(x).
L'esempio seguente mostra il processo di approssimazione di una funzione utilizzando solo due punti. Sulla destra c'è una rappresentazione vettoriale.


In relazione al nostro esperimento di divisione in coppie, possiamo dire che questi due punti (0 e 1 lungo l'ascissa) sono una coppia di pixel vicini (x, y).
La stessa cosa, ma con l'animazione:


Se prendiamo 3 punti, allora dobbiamo considerare Vettori 3D, tuttavia l'approssimazione sarà più accurata. E per una funzione discreta con N valori, devi considerare i vettori N-dimensionali.
Avendo un insieme di coefficienti ottenuti, puoi facilmente ottenere la funzione originale sommando le funzioni base prese con i coefficienti corrispondenti. L’analisi di questi coefficienti può rivelare molto informazioni utili(a seconda della base). Un caso particolare di queste considerazioni è il principio di espansione in serie di Fourier. Dopotutto, il nostro ragionamento è applicabile a qualsiasi base e quando si espande in una serie di Fourier, ne viene presa una completamente specifica.

Trasformate discrete di Fourier (DFT)

Nella parte precedente siamo giunti alla conclusione che sarebbe carino scomporre una funzione nei suoi componenti. All'inizio del XIX secolo anche Fourier pensava a questo. È vero, non aveva la foto di un procione, quindi ha dovuto studiare la distribuzione del calore lungo l'anello di metallo. Poi scoprì che è molto conveniente esprimere la temperatura (e la sua variazione) in ciascun punto dell'anello come somma di sinusoidi con periodi diversi. “Fourier ha scoperto (consiglio di leggerlo, è interessante) che la seconda armonica decade 4 volte più velocemente della prima, e le armoniche di ordine superiore decadono a una velocità ancora maggiore.”
In generale, si scoprì presto che le funzioni periodiche possono essere perfettamente scomposte nella somma delle sinusoidi. E poiché in natura esistono molti oggetti e processi descritti da funzioni periodiche, è apparso un potente strumento per la loro analisi.
Forse uno dei processi periodici più visivi è il suono.

  • 1° grafico: tono puro con una frequenza di 2500 hertz.
  • 2°: rumore bianco. Cioè, rumore con frequenze distribuite uniformemente su tutta la gamma.
  • 3° - la somma dei primi due.
Se mi avessero dato i valori dell'ultima funzione in quel momento in cui non sapevo della serie di Fourier e mi avessero chiesto di analizzarli, allora sarei sicuramente rimasto confuso e non avrei potuto dire nulla di utile. Ebbene sì, una sorta di funzione, ma come fai a capire che c'è qualcosa di ordinato lì? Ma se avessi indovinato di ascoltare l'ultima funzione, il mio orecchio avrebbe colto un tono puro in mezzo al rumore. Anche se non molto buono, poiché durante la generazione ho selezionato appositamente tali parametri in modo che sul grafico riassuntivo il segnale si dissolvesse visivamente nel rumore. Per quanto ho capito, non è ancora chiaro esattamente come apparecchio acustico lo fa. Tuttavia, recentemente è diventato chiaro che non decompone il suono in onde sinusoidali. Forse un giorno capiremo come ciò accade e appariranno algoritmi più avanzati. Bene, per ora lo stiamo facendo alla vecchia maniera.
Perché non provare a utilizzare le sinusoidi come base? In effetti, lo abbiamo già fatto. Ricordiamo la nostra scomposizione in 3 vettori base e presentiamoli nel grafico:


Sì, sì, lo so, sembra un aggiustamento, ma con tre vettori è difficile aspettarsi di più. Ma ora è chiaro come ottenere, ad esempio, 8 vettori base:


Non bene controllo complesso mostra che questi vettori sono perpendicolari a due a due, cioè ortogonali. Ciò significa che possono essere utilizzati come base. La trasformazione su tale base è ampiamente conosciuta ed è chiamata trasformata discreta del coseno (DCT). Penso che sia chiaro dai grafici sopra come si ottiene la formula di trasformazione DCT:

Questa è sempre la stessa formula: A = EX con una base sostituita. I vettori base della DCT specificata (sono anche vettori riga della matrice E) sono ortogonali, ma non ortonormali. Questo va ricordato durante la trasformazione inversa (non mi soffermerò su questo, ma per chi fosse interessato, la DCT inversa ha termine 0.5*a 0 , poiché il vettore base zero è maggiore degli altri).
L'esempio seguente mostra il processo di approssimazione dei totali parziali ai valori originali. Ad ogni iterazione, la base successiva viene moltiplicata per il coefficiente successivo e aggiunta alla somma intermedia (cioè la stessa dei primi esperimenti sul procione: un terzo dei valori, due terzi).


Tuttavia, nonostante alcune argomentazioni sull'opportunità di scegliere una base del genere, non esistono ancora argomenti reali. Infatti, a differenza del suono, la fattibilità della scomposizione di un'immagine in funzioni periodiche è molto meno ovvia. Tuttavia, l'immagine può effettivamente essere troppo imprevedibile anche in una piccola area. Pertanto, l'immagine è divisa in pezzi abbastanza piccoli, ma non assolutamente piccoli, affinché la scomposizione abbia un senso. In JPEG, l'immagine viene “tagliata” in quadrati 8x8. All'interno di un pezzo del genere, le fotografie sono generalmente molto uniformi: lo sfondo più piccole fluttuazioni. Tali aree sono meravigliosamente avvicinate dalle sinusoidi.
Bene, diciamo che questo fatto è più o meno intuitivo. Ma c'è una brutta sensazione riguardo alle transizioni di colore improvvise, perché cambiare lentamente le funzioni non ci salverà. Dobbiamo aggiungere varie funzioni ad alta frequenza che fanno il loro lavoro, ma appaiono lateralmente su uno sfondo omogeneo. Prendiamo un'immagine 256x256 con due aree contrastanti:


Scomponiamo ogni riga utilizzando DCT, ottenendo così 256 coefficienti per riga.
Quindi lasciamo solo i primi n coefficienti e impostiamo il resto a zero e, quindi, l'immagine verrà presentata come una somma delle sole prime armoniche:


Il numero nell'immagine è il numero di quote rimaste. Nella prima immagine rimane solo il valore medio. Sul secondo è già stata aggiunta una sinusoide a bassa frequenza, ecc. A proposito, presta attenzione al bordo: nonostante tutta la migliore approssimazione, accanto alla diagonale sono chiaramente visibili 2 strisce, una più chiara, l'altra più scura. Parte dell'ultima immagine ingrandita 4 volte:

E in generale, se lontano dal confine vediamo uno sfondo iniziale uniforme, allora avvicinandosi ad esso l'ampiezza inizia a crescere, raggiunge infine il valore minimo, per poi diventare improvvisamente massimo. Questo fenomeno è noto come effetto Gibbs.


L'altezza di queste gobbe, che compaiono in prossimità delle discontinuità della funzione, non diminuirà all'aumentare del numero degli addendi delle funzioni. In una trasformazione discreta scompare solo quando vengono preservati quasi tutti i coefficienti. Più precisamente, diventa invisibile.
L'esempio seguente è del tutto simile alla scomposizione dei triangoli sopra, ma su un vero procione:


Quando si studia la DCT si può avere la falsa impressione che siano sempre sufficienti solo i primi coefficienti (a bassa frequenza). Questo è vero per molte fotografie, quelle il cui significato non cambia radicalmente. Tuttavia, al confine delle aree contrastanti, i valori “salteranno” rapidamente e anche gli ultimi coefficienti saranno elevati. Pertanto, quando senti parlare delle proprietà di conservazione dell'energia del DCT, tieni conto del fatto che si applica a molti tipi di segnali incontrati, ma non a tutti. Ad esempio, pensa a come sarebbe una funzione discreta, i cui coefficienti di espansione sono uguali a zero, tranne l'ultimo. Suggerimento: pensa alla scomposizione in forma vettoriale.
Nonostante i difetti, la base scelta è una delle migliori tra le fotografie reali. Vedremo un piccolo confronto con gli altri un po' più tardi.

DCT contro tutto il resto

Quando ho studiato la questione delle trasformazioni ortogonali, a dire il vero, non ero molto convinto dall'argomentazione secondo cui tutto intorno è una somma vibrazioni armoniche, quindi è necessario scomporre le immagini in sinusoidi. O forse alcune funzioni passo-passo sarebbero migliori? Pertanto, ho cercato risultati di ricerca sull'ottimalità della DCT su immagini reali. Il fatto che “è la DCT a trovarsi più spesso nelle applicazioni pratiche a causa della proprietà di “compattazione energetica”” è scritto ovunque. Questa proprietà fa sì che la massima quantità di informazione sia contenuta nei primi coefficienti. E perché? Non è difficile condurre una ricerca: ci armiamo di un mucchio di immagini diverse, di basi conosciute diverse e iniziamo a calcolare la deviazione standard dall’immagine reale per un diverso numero di coefficienti. Ho trovato un piccolo studio in un articolo (immagini utilizzate) su questa tecnica. Mostra i grafici della dipendenza dell'energia immagazzinata dal numero dei primi coefficienti di espansione per basi diverse. Se guardavi le classifiche, eri convinto che DCT occupi costantemente un onorevole... ehm... 3° posto. Come mai? Che tipo di conversione KLT è questa? Stavo elogiando il DCT, e poi...
KLT
Tutte le trasformazioni, tranne KLT, sono trasformazioni con base costante. E in KLT (trasformata di Karhunen-Loeve) viene calcolata la base ottimale per diversi vettori. Viene calcolato in modo tale che i primi coefficienti forniscano l'errore quadratico medio più piccolo in totale per tutti i vettori. In precedenza abbiamo eseguito manualmente un lavoro simile, determinando visivamente la base. All'inizio sembra una buona idea. Potremmo, ad esempio, dividere l'immagine in piccole sezioni e calcolare la propria base per ciascuna. Ma non c'è solo la preoccupazione di memorizzare questa base, ma anche l'operazione di calcolo è piuttosto laboriosa. Ma DCT perde solo poco e inoltre DCT ha algoritmi di conversione veloci.
DFT
DFT (trasformata discreta di Fourier) - trasformazione discreta Fourier. Con questo nome a volte viene indicata non solo una trasformazione specifica, ma anche l'intera classe delle trasformazioni discrete (DCT, DST...). Diamo un'occhiata alla formula DFT:

Come puoi immaginare, questa è una trasformazione ortogonale con una sorta di base complessa. Poiché una forma così complessa si presenta un po' più spesso del solito, ha senso studiarne la derivazione.
Può sembrare che qualsiasi segnale armonico puro (con frequenza intera) con decomposizione DCT dia solo un coefficiente diverso da zero corrispondente a questa armonica. Questo non è vero perché oltre alla frequenza è importante anche la fase di questo segnale. Ad esempio, l'espansione del seno in coseno (in modo simile nell'espansione discreta) sarà così:

Questo per quanto riguarda le armoniche pure. Ne ha generati un sacco di altri. L'animazione mostra i coefficienti DCT di un'onda sinusoidale diverse fasi.


Se ti è sembrato che le colonne ruotassero attorno ad un asse, non ti è sembrato.
Quindi ora non espanderemo semplicemente la funzione alla somma delle sinusoidi frequenze diverse, ma anche spostato in qualche fase. Sarà più conveniente considerare lo sfasamento usando l'esempio del coseno:

Una semplice identità trigonometrica fornisce un risultato importante: lo sfasamento è sostituito dalla somma di seno e coseno, presi con i coefficienti cos(b) e sin(b). Ciò significa che le funzioni possono essere espanse nella somma di seni e coseni (senza fasi). Questa è una forma trigonometrica comune. Tuttavia, il complesso viene utilizzato molto più spesso. Per ottenerlo è necessario utilizzare la formula di Eulero. Sostituendo semplicemente le formule derivate di seno e coseno, otteniamo:


Ora per un piccolo cambiamento. Il trattino basso è il numero coniugato.

Otteniamo l'uguaglianza finale:

c è un coefficiente complesso, la cui parte reale è uguale al coefficiente coseno e la parte immaginaria è uguale al coefficiente seno. E l'insieme dei punti (cos(b), sin(b)) è un cerchio. In tale registrazione, ogni armonica entra nell'espansione sia con una frequenza positiva che negativa. Pertanto, in varie formule di analisi di Fourier, la somma o l'integrazione avviene solitamente da meno a più infinito. Spesso è più conveniente eseguire i calcoli in questa forma complessa.
La trasformazione decompone il segnale in armoniche con frequenze da uno a N oscillazioni nella regione del segnale. Ma la frequenza di campionamento è N per area del segnale. E secondo il teorema di Kotelnikov (noto anche come teorema di Nyquist-Shannon), la frequenza di campionamento dovrebbe essere almeno il doppio della frequenza del segnale. Se così non fosse, l'effetto è la comparsa di un segnale con una falsa frequenza:


La linea tratteggiata mostra il segnale ricostruito in modo errato. Hai riscontrato spesso questo fenomeno nella vita. Ad esempio, il divertente movimento delle ruote di un'auto in un video o l'effetto moiré.
Ciò porta al fatto che la seconda metà delle ampiezze del complesso N sembra essere costituita da altre frequenze. Queste false armoniche della seconda metà lo sono immagine riflessa non lo portano prima Informazioni aggiuntive. Pertanto, ci rimangono N/2 coseni e N/2 seni (che formano una base ortogonale).
Ok, c'è una base. I suoi componenti sono armoniche con un numero intero di oscillazioni nella regione del segnale, il che significa che i valori estremi delle armoniche sono uguali. Più precisamente, sono quasi uguali, poiché l'ultimo valore non è preso interamente dal bordo. Inoltre, ogni armonica è quasi specularmente simmetrica rispetto al suo centro. Tutti questi fenomeni sono particolarmente forti in basse frequenze, che sono importanti per noi durante la codifica. Anche questo è negativo perché i confini dei blocchi saranno visibili nell'immagine compressa. Lasciatemi illustrare la base DFT con N=8. Le prime 2 righe sono componenti del coseno, le ultime sono seno:


Prestare attenzione alla comparsa di componenti duplicati man mano che la frequenza aumenta.

Puoi pensare mentalmente a come potrebbe essere scomposto un segnale i cui valori diminuiscono gradualmente da un valore massimo all'inizio a un valore minimo alla fine. Un'approssimazione più o meno adeguata potrebbe essere fatta solo dagli armonici verso la fine, il che per noi non è un granché. La figura a sinistra è un'approssimazione di un segnale single-ended. A destra - simmetrico:


Le cose vanno estremamente male con il primo.
Quindi forse possiamo farlo come in DCT: ridurre le frequenze di 2 o un altro numero di volte, in modo che il numero di alcune oscillazioni sia frazionario e i confini siano in fasi diverse? Quindi i componenti saranno non ortogonali. E non c'è niente da fare al riguardo.

Ora legale
Cosa succede se usiamo i seni invece dei coseni nella DCT? Otterremo la Trasformata Sinusoidale Discreta (DST). Ma per il nostro compito non sono tutti interessanti, poiché sia ​​il periodo intero che quello semi-seno sono vicini allo zero ai confini. Cioè, otterremo all'incirca la stessa scomposizione inappropriata di quella di DFT.
Ritornando al DCT
Come va alle frontiere? Bene. Ci sono antifasi e nessuno zero.
Tutto il resto
Trasformate non di Fourier. Non lo descriverò.
WHT: la matrice è composta solo da componenti di gradini con valori -1 e 1.
Haar è anche una trasformata wavelet ortogonale.
Sono inferiori al DCT, ma sono più facili da calcolare.

Quindi ti è venuta l'idea di inventare la tua trasformazione. Ricorda questo:

  1. La base deve essere ortogonale.
  2. Con una base fissa, non puoi battere KLT per la qualità della compressione. Nel frattempo, nelle fotografie reali, il DCT è quasi altrettanto buono.
  3. Utilizzando l'esempio di DFT e DST, è necessario ricordare i confini.
  4. E ricorda che DCT ha un altro buon vantaggio: vicino ai confini dei suoi componenti, le derivate sono uguali a zero, il che significa che la transizione tra i blocchi vicini sarà abbastanza fluida.
  5. Le trasformate di Fourier hanno algoritmi veloci con complessità O(N*logN), in contrasto con il calcolo semplice: O(N 2).
Non sarà facile, vero? Tuttavia per alcuni tipi di immagini è possibile selezionare una base migliore rispetto a quella DCT.

Trasformazioni 2D

Ora proviamo a condurre un simile esperimento. Prendiamo, ad esempio, un pezzo di un'immagine.


Il suo grafico 3D:


Esaminiamo DCT(N=32) attraverso ciascuna riga:


Ora voglio che tu scorra ogni colonna dei coefficienti risultanti, cioè dall'alto verso il basso. Ricorda che il nostro obiettivo è lasciare meno valori possibili, eliminando quelli non significativi. Probabilmente hai intuito che i valori di ciascuna colonna dei coefficienti risultanti possono essere espansi esattamente allo stesso modo dei valori dell'immagine originale. Nessuno ci limita nella scelta di una matrice di trasformazione ortogonale, ma lo rifaremo utilizzando DCT(N=8):


Il coefficiente (0,0) si è rivelato troppo grande, quindi nel grafico è ridotto di 4 volte.
Allora, cos'è successo?
L'angolo in alto a sinistra è i coefficienti più significativi dell'espansione dei coefficienti più significativi.
L'angolo inferiore sinistro rappresenta i coefficienti più insignificanti dell'espansione dei coefficienti più significativi.
L'angolo in alto a destra rappresenta i coefficienti di espansione più significativi dei coefficienti più insignificanti.
L'angolo in basso a destra rappresenta i coefficienti più insignificanti dell'espansione dei coefficienti più insignificanti.
È chiaro che la significatività dei coefficienti diminuisce se ci si sposta diagonalmente dall'alto a sinistra verso il basso a destra. Cos'è più importante: (0, 7) o (7, 0)? Cosa significano?
Innanzitutto, per righe: A 0 = (EX T) T = XE T (trasposto, poiché la formula è A=EX per le colonne), quindi per colonne: A=EA 0 = EXE T . Se calcoli attentamente, ottieni la formula:

Pertanto, se un vettore viene scomposto in sinusoidi, la matrice viene scomposta in funzioni della forma cos(ax)*cos(by). Ogni blocco 8x8 in JPEG è rappresentato come una somma di 64 funzioni nel formato:


In Wikipedia e altre fonti, tali funzioni sono presentate in una forma più conveniente:


Pertanto, i coefficienti (0, 7) o (7, 0) sono ugualmente utili.
Tuttavia, in realtà questa è una normale scomposizione unidimensionale in 64 basi 64-dimensionali. Tutto quanto sopra si applica non solo alla DCT, ma anche a qualsiasi scomposizione ortogonale. Procedendo per analogia, nel caso generale si ottiene una trasformazione ortogonale N-dimensionale.
Ed ecco una trasformazione 2D di un procione (DCT 256x256). Anche in questo caso con i valori azzerati. Numeri - il numero di coefficienti non azzerati tra tutti (sono stati mantenuti i valori più significativi, situati nell'area triangolare nell'angolo in alto a sinistra).


Ricorda che il coefficiente (0, 0) si chiama DC, i restanti 63 si chiamano AC.

Scelta della dimensione del blocco

Un amico chiede perché JPEG utilizza il partizionamento 8x8. Dalla risposta votata negativamente:
Il DCT tratta il blocco come se fosse periodico e deve ricostruire il salto risultante ai confini. Se prendi blocchi 64x64, molto probabilmente avrai un enorme salto ai confini e avrai bisogno di molti componenti ad alta frequenza per ricostruirlo con una precisione soddisfacente.
Ad esempio, DCT funziona bene solo su funzioni periodiche e, se vai in grande, probabilmente otterrai un salto gigantesco ai confini del blocco e avrai bisogno di molti componenti ad alta frequenza per coprirlo. Questo non è vero! Questa spiegazione è molto simile a DFT, ma non a DCT, poiché copre perfettamente tali salti con le prime componenti.
Nella stessa pagina c'è una risposta dalle FAQ MPEG, con i principali argomenti contro i blocchi di grandi dimensioni:
  • Poco profitto se suddiviso in grandi blocchi.
  • Crescente complessità computazionale.
  • Alta probabilità grande quantità confini netti in un blocco, che causerà l'effetto Gibbs.
Ti suggerisco di ricercarlo tu stesso. Iniziamo con Primo.


L'asse orizzontale mostra la quota dei primi coefficienti non azzerati. Verticale: deviazione standard dei pixel dall'originale. La deviazione massima possibile è considerata pari a uno. Naturalmente, una foto chiaramente non è sufficiente per un verdetto. Inoltre, non mi comporto in modo del tutto corretto, mi limito a resettare a zero. In un JPEG reale, a seconda della matrice di quantizzazione, vengono azzerati solo piccoli valori dei componenti ad alta frequenza. Pertanto, i seguenti esperimenti e conclusioni hanno lo scopo di far emergere i principi e i modelli.
Puoi confrontare la divisione in diversi blocchi con il 25% dei coefficienti a sinistra (da sinistra a destra, poi da destra a sinistra):

I blocchi di grandi dimensioni non vengono mostrati, poiché visivamente sono quasi indistinguibili da 32x32. Ora vediamo la differenza assoluta con l'immagine originale (amplificata di 2 volte, altrimenti non si vede nulla):

8x8 dà miglior risultato rispetto al 4x4. Un ulteriore aumento delle dimensioni non offre più un vantaggio chiaramente visibile. Anche se prenderei seriamente in considerazione 16x16 invece di 8x8: aumentare la complessità del 33% (più sulla complessità nel paragrafo successivo) dà un miglioramento piccolo ma ancora visibile per lo stesso numero di coefficienti. Tuttavia, la scelta di 8x8 sembra abbastanza ragionevole e potrebbe rappresentare la via d'uscita. JPEG è stato pubblicato nel 1991. Penso che tale compressione fosse molto difficile per i processori dell'epoca.

Secondo discussione. Una cosa da tenere a mente è che aumentare la dimensione del blocco richiederà più calcoli. Stimiamo quanto. La complessità della conversione, come già sappiamo abbastanza bene: O(N 2), poiché ogni coefficiente è composto da N termini. Ma in pratica viene utilizzato un efficiente algoritmo Fast Fourier Transform (FFT). La sua descrizione va oltre lo scopo di questo articolo. La sua complessità: O(N*logN). Per un'espansione bidimensionale è necessario utilizzarlo due volte N volte. Quindi la complessità della DCT 2D è O(N 2 logN). Ora confrontiamo la complessità del calcolo di un'immagine con un blocco e diversi piccoli:

  • Un blocco (kN)x(kN): O((kN) 2 log(kN)) = O(k 2 N 2 log(kN))
  • k*k blocchi N*N: O(k 2 N 2 logN)
Ciò significa che, ad esempio, il calcolo per una partizione 64x64 è due volte più complesso di quello per una partizione 8x8.

Terzo discussione. Se nell'immagine è presente un bordo di colori netto, ciò influenzerà l'intero blocco. Forse sarebbe meglio che questo isolato fosse abbastanza piccolo, perché in molti isolati vicini probabilmente non esisterà più tale confine. Tuttavia, lontano dai confini, l’attenuazione avviene abbastanza rapidamente. Inoltre, il confine stesso avrà un aspetto migliore. Controlliamolo utilizzando un esempio con un gran numero di transizioni di contrasto, ancora una volta, con solo un quarto dei coefficienti:


Sebbene la distorsione dei blocchi 16x16 si estenda oltre quella dei blocchi 8x8, i caratteri sono più uniformi. Pertanto, solo i primi due argomenti mi hanno convinto. Ma in qualche modo mi piace di più la divisione 16x16.

Quantizzazione

A questo punto abbiamo un mucchio di matrici 8x8 con coefficienti di trasformata coseno. È tempo di sbarazzarsi dei coefficienti insignificanti. Esiste una soluzione più elegante del semplice azzeramento degli ultimi coefficienti, come abbiamo fatto sopra. Non ci accontentiamo di questo metodo, poiché i valori non azzerati vengono memorizzati con eccessiva precisione, e tra gli sfortunati potrebbero essercene di abbastanza importanti. La soluzione è utilizzare una matrice di quantizzazione. Le perdite si verificano proprio in questa fase. Ogni coefficiente di Fourier viene diviso per il numero corrispondente nella matrice di quantizzazione. Diamo un'occhiata a un esempio. Prendiamo il primo blocco dal nostro procione ed eseguiamo la quantizzazione. La specifica JPEG fornisce una matrice standard:


La matrice standard corrisponde al 50% di qualità in FastStone e IrfanView. Questa tabella è stata scelta in termini di equilibrio tra qualità e rapporto di compressione. Penso che il valore del coefficiente DC sia maggiore rispetto ai suoi vicini perché il DCT non è normalizzato e il primo valore è maggiore di quanto dovrebbe essere. I coefficienti ad alta frequenza vengono grossolani maggiormente a causa della loro minore importanza. Penso che tali matrici siano usate raramente ora, poiché il deterioramento della qualità è chiaramente evidente. Nessuno vieta di utilizzare la tua tabella (con valori da 1 a 255)
Durante la decodifica, avviene il processo inverso: i coefficienti quantizzati vengono moltiplicati termine per termine per i valori della matrice di quantizzazione. Ma poiché abbiamo arrotondato i valori, non saremo in grado di ripristinare con precisione i coefficienti di Fourier originali. Maggiore è il numero di quantizzazione, maggiore è l'errore. Pertanto, il coefficiente ricostruito è solo il multiplo più vicino.
Un altro esempio:

E per il dessert, considera una qualità del 5% (durante la codifica in Fast Stone).


Quando ripristiniamo questo blocco, otterremo solo il valore medio più il gradiente verticale (a causa del valore conservato di -1). Ma per questo vengono memorizzati solo due valori: 7 e -1. La situazione non è migliore con gli altri blocchi, ecco l'immagine ripristinata:

A proposito, qualità circa al 100%. Come puoi immaginare, in questo caso la matrice di quantizzazione è costituita interamente da unità, ovvero non avviene alcuna quantizzazione. Tuttavia, a causa dell'arrotondamento dei coefficienti all'intero più vicino, non è possibile ripristinare con precisione l'immagine originale. Ad esempio, il procione ha mantenuto esattamente il 96% dei pixel, ma il 4% era sfasato di 1/256. Naturalmente tali “distorsioni” non possono essere notate visivamente.
Oppure puoi guardare le matrici di quantizzazione di varie fotocamere.

Codifica

Prima di proseguire, dobbiamo utilizzare esempi più semplici per capire come possiamo comprimere i valori risultanti.

Esempio 0(per il riscaldamento)
Immagina una situazione del genere in cui il tuo amico ha dimenticato un pezzo di carta con un elenco a casa tua e ora ti chiede di dettarlo al telefono (non esistono altri metodi di comunicazione).
Elenco:

  • d9rg3
  • wfr43gt
  • wfr43gt
  • d9rg3
  • d9rg3
  • d9rg3
  • wfr43gt
  • d9rg3
Come renderesti più semplice il tuo compito? Non hai un desiderio particolare di dettare dolorosamente tutte queste parole. Ma ce ne sono solo due e si ripetono. Pertanto, in qualche modo detti semplicemente le prime due parole e accetti che d'ora in poi chiamerai "d9rg3" la prima parola e "wfr43gt" la seconda. Poi basterà dettare: 1, 2, 2, 1, 1, 1, 2, 1.

Indicheremo parole come A, B, C..., e le chiameremo simboli. Inoltre, sotto il simbolo può nascondersi qualsiasi cosa: una lettera dell'alfabeto, una parola o un ippopotamo nello zoo. La cosa principale è che simboli identici corrispondono a concetti identici e simboli diversi corrispondono a concetti diversi. Poiché il nostro compito è una codifica (compressione) efficiente, lavoreremo con i bit, poiché queste sono le unità più piccole di rappresentazione delle informazioni. Pertanto, scriviamo l'elenco come ABBAAABA. Invece di "prima parola" e "seconda parola", possono essere utilizzati i bit 0 e 1. Quindi ABBAAABA viene codificato come 01100010 (8 bit = 1 byte).

Esempio 1
Codifica ABC.
Non è possibile associare 3 caratteri diversi (A, B, C) a 2 possibili valori di bit (0 e 1). E se è così, puoi usare 2 bit per simbolo. Per esempio:

  • R: 00
  • B:01
  • C:10
La sequenza di bit associata ad un simbolo verrà chiamata codice. ABC sarà codificato in questo modo: 000110.

Esempio 2
Codifica AAAAAABC.
Usare 2 bit per carattere A sembra un po' uno spreco. E se provassi questo:

  • C:00

Sequenza codificata: 000000100.
Ovviamente questa opzione non è adatta, poiché non è chiaro come decodificare i primi due bit di questa sequenza: come AA o come C? È molto dispendioso utilizzare qualsiasi separatore tra i codici, penseremo a come aggirare questo ostacolo in modo diverso. Quindi il fallimento è dovuto al fatto che il codice di C inizia con il codice di A. Ma siamo determinati a codificare A con un bit, anche se B e C ne hanno due ciascuno. In base a questo desiderio diamo ad A il codice 0. Quindi i codici B e C non possono iniziare con 0. Ma possono iniziare con 1:
  • B: 10
  • C:11

La sequenza è codificata così: 0000001011. Prova a decodificarla mentalmente. Puoi farlo solo in un modo.
Abbiamo sviluppato due requisiti di codifica:
  1. Maggiore è il peso di un simbolo, più corto dovrebbe essere il suo codice. E viceversa.
  2. Per una decodifica univoca, un codice di carattere non può iniziare con il codice di nessun altro carattere.
Ovviamente l'ordine dei caratteri non è importante, a noi interessa solo la frequenza con cui si presentano. Pertanto ad ogni simbolo è associato un numero chiamato peso. Il peso di un simbolo può essere il seguente: dimensione relativa, che riflette la quota della sua occorrenza, e assoluta, pari al numero di caratteri. La cosa principale è che i pesi siano proporzionali alla presenza dei simboli.

Esempio 3
Consideriamo il caso generale di 4 simboli con qualsiasi peso.

  • R:pa
  • B:pb
  • C:pz
  • D:pd
Senza perdita di generalità, poniamo pa ≥ pb ≥ pc ≥ pd. Esistono solo due opzioni fondamentalmente diverse nella lunghezza del codice:


Quale è preferibile? Per fare ciò, è necessario calcolare la lunghezza risultante dei messaggi codificati:
W1 = 2*pa + 2*pb + 2*pc + 2*pd
W2 = pa + 2*pb + 3*pc + 3*pd
Se W1 è inferiore a W2 (W1-W2<0), то лучше использовать первый вариант:
W1-W2 = pa - (pc+pd)< 0 =>papà< pc+pd.
Se C e D insieme si presentano più spesso di altri, il loro vertice comune riceve il codice a un bit più corto. Altrimenti un bit va al carattere A. Ciò significa che l'unione dei caratteri si comporta come un carattere indipendente e ha un peso pari alla somma dei caratteri in ingresso.
In generale, se p è il peso di un carattere rappresentato dalla frazione della sua occorrenza (da 0 a 1), allora la migliore lunghezza del codice è s=-log 2 p.
Diamo un'occhiata a questo caso semplice(è facile immaginarlo sotto forma di albero). Quindi, dobbiamo codificare caratteri da 2 s con pesi uguali (1/2 s). A causa dell'uguaglianza dei pesi, le lunghezze dei codici saranno le stesse. Ogni carattere richiederà s bit. Ciò significa che se il peso di un simbolo è 1/2 s, allora la sua lunghezza è s. Se sostituiamo il peso con p, otteniamo la lunghezza del codice s=-log 2 p . Ciò significa che se un carattere ricorre il doppio della frequenza di un altro, la lunghezza del suo codice sarà più lunga di un bit. Tuttavia, questa conclusione è facile da trarre se si ricorda che l'aggiunta di un bit consente di raddoppiare il numero di opzioni possibili.
E un'altra osservazione: i due simboli con i pesi più piccoli hanno sempre il più grande, ma lunghezze uguali codici Inoltre, i loro pezzi, tranne l'ultimo, sono gli stessi. Se ciò non fosse vero, almeno un codice potrebbe essere abbreviato di 1 bit senza interrompere il prefisso. Ciò significa che i due simboli con il peso più piccolo nell'albero del codice hanno un genitore comune a un livello superiore. Puoi vederlo negli esempi C e D sopra.

Esempio 4
Proviamo a risolvere il seguente esempio, sulla base delle conclusioni ottenute nell'esempio precedente.

  1. Tutti i simboli sono ordinati in ordine decrescente di peso.
  2. Gli ultimi due simboli sono combinati in un gruppo. A questo gruppo viene assegnato un peso pari alla somma dei pesi di questi elementi. Questo gruppo partecipa all'algoritmo insieme ai simboli e ad altri gruppi.
I passaggi vengono ripetuti finché rimane un solo gruppo. All'interno di ciascun gruppo, a un carattere (o sottogruppo) viene assegnato il bit 0 e a un altro carattere il bit 1.
Questo algoritmo è chiamato codifica di Huffman.
L'illustrazione mostra un esempio con 5 caratteri (A: 8, B: 6, C: 5, D: 4, E: 3). Sulla destra c'è il peso del simbolo (o del gruppo).

Codifichiamo i coefficienti

Torniamo indietro. Ora abbiamo molti blocchi con 64 coefficienti ciascuno, che devono essere salvati in qualche modo. La soluzione più semplice è utilizzare un numero fisso di bit per coefficiente, ovviamente senza successo. Costruiamo un istogramma di tutti i valori ottenuti (ovvero la dipendenza del numero di coefficienti dal loro valore):


Nota: la scala è logaritmica! Potete spiegare il motivo della comparsa di un cluster di valori superiori a 200? Questi sono coefficienti DC. Poiché sono molto diversi dagli altri, non sorprende che siano codificati separatamente. Ecco solo DC:


Si noti che la forma del grafico ricorda i grafici dei primi esperimenti di accoppiamento e triplicazione dei pixel.
In generale, i valori del coefficiente DC possono variare da 0 a 2047 (più precisamente da -1024 a 1023, poiché JPEG sottrae 128 da tutti i valori originali, il che corrisponde a sottrarre 1024 da DC) e sono distribuiti abbastanza uniformemente con piccoli picchi. Quindi la codifica di Huffman non sarà di grande aiuto qui. E immagina quanto sarà grande l'albero del codice! E durante la decodifica dovrai cercare significati in esso. È molto caro. Pensiamo ulteriormente.
Il coefficiente DC è il valore medio di un blocco 8x8. Immaginiamo una transizione sfumata (anche se non ideale), che si trova spesso nelle fotografie. I valori DC stessi saranno diversi, ma rappresenteranno una progressione aritmetica. Ciò significa che la loro differenza sarà più o meno costante. Costruiamo un istogramma delle differenze:


Questo è meglio, perché i valori sono generalmente concentrati intorno allo zero (ma l'algoritmo di Huffman darà ancora una volta un albero troppo grande). Piccoli valori (by valore assoluto) sono comuni, quelli grandi sono rari. E poiché i valori piccoli occupano pochi bit (se rimuovi gli zeri iniziali), una delle regole di compressione funziona bene: assegnare simboli con pesi grandi codici brevi(e viceversa). Attualmente siamo limitati dal mancato rispetto di un'altra regola: l'impossibilità di decodifica univoca. In generale, questo problema può essere risolto nei seguenti modi: preoccuparsi del codice delimitatore, indicare la lunghezza del codice, utilizzare codici prefisso (li conosci già - questo è il caso in cui nessun codice inizia con un altro). Andiamo con la seconda opzione semplice, cioè ogni coefficiente (più precisamente, la differenza tra quelli vicini) verrà scritto così: (lunghezza)(valore), secondo questo segno:


Cioè, i valori positivi sono codificati direttamente dalla loro rappresentazione binaria, mentre i valori negativi sono codificati allo stesso modo, ma con l'1 iniziale sostituito da 0. Resta da decidere come codificare le lunghezze. Poiché ci sono 12 valori possibili, è possibile utilizzare 4 bit per memorizzare la lunghezza. Ma è qui che è meglio usare la codifica Huffman.


Sono presenti più valori con lunghezze 4 e 6, quindi hanno ottenuto i codici più brevi (00 e 01).


Potrebbe sorgere la domanda: perché nell'esempio il valore 9 ha il codice 1111110 e non 1111111? Dopotutto, puoi tranquillamente aumentare il "9" a un livello più alto, accanto allo "0"? Il fatto è che in JPEG non è possibile utilizzare un codice composto solo da uno: tale codice è riservato.
C'è un'altra caratteristica. I codici ottenuti dall'algoritmo di Huffman descritto potrebbero non coincidere in bit con i codici in JPEG, sebbene la loro lunghezza sarà la stessa. Utilizzando l'algoritmo di Huffman, si ottengono le lunghezze dei codici e si generano i codici stessi (l'algoritmo è semplice: inizia con codici brevi e aggiungili uno per uno all'albero il più a sinistra possibile, preservando la proprietà del prefisso ). Ad esempio, per l'albero sopra l'elenco viene memorizzato: 0,2,3,1,1,1,1,1. E, naturalmente, viene memorizzato un elenco di valori: 4,6,3,5,7,2,8,1,0,9. Durante la decodifica, i codici vengono generati allo stesso modo.

Ora è tutto in ordine. Abbiamo scoperto come vengono archiviati i controller di dominio:
[Codice Huffman per la lunghezza della differenza CC (in bit)]
dove DC diff = corrente DC - DC precedente

Guardiamo AC:


Poiché il grafico è molto simile al grafico delle differenze CC, il principio è lo stesso: [codice Huffman per la lunghezza CA (in bit)]. Ma non proprio! Poiché la scala sul grafico è logaritmica, non si nota immediatamente che ci sono circa 10 volte più valori zero rispetto ai valori 2, i successivi più frequenti. Questo è comprensibile: non tutti sono sopravvissuti alla quantizzazione. Torniamo alla matrice dei valori ottenuti durante la fase di quantizzazione (utilizzando la matrice di quantizzazione FastStone, 90%).

Poiché ci sono molti gruppi di zeri consecutivi, nasce l'idea di scrivere solo il numero di zeri nel gruppo. Questo algoritmo di compressione è chiamato RLE (codifica Run-length). Resta da scoprire la direzione della tangenziale di quelli “consecutivi”: chi c'è dietro chi? Scrivere da sinistra a destra e dall'alto verso il basso non è molto efficace, poiché i coefficienti diversi da zero sono concentrati vicino all'angolo in alto a sinistra e più si avvicinano all'angolo in basso a destra, più sono gli zeri.


Pertanto, JPEG utilizza un ordine chiamato "Zig-zag", mostrato nella figura a sinistra. Questo metodo distingue bene i gruppi di zeri. Nella foto a destra c'è un metodo di bypass alternativo, non legato al JPEG, ma dal nome curioso (prova). Può essere utilizzato in MPEG per la compressione video interlacciata. La scelta dell'algoritmo di attraversamento non influisce sulla qualità dell'immagine, ma può aumentare il numero di gruppi di zeri codificati, che in definitiva possono influire sulla dimensione del file.
Modifichiamo la nostra voce. Per ogni coefficiente AC diverso da zero:
[Numero di zeri prima di AC] [Codice Huffman per la lunghezza AC (in bit)]
Penso che si capisca subito che anche il numero di zeri è perfettamente codificato da Huffman! Questa è una risposta molto vicina e buona. Ma può essere leggermente ottimizzato. Immagina di avere un coefficiente AC, davanti al quale c'erano 7 zeri (ovviamente, se scritti a zigzag). Questi zeri sono lo spirito dei valori che non sono sopravvissuti alla quantizzazione. Molto probabilmente, anche il nostro coefficiente è stato gravemente danneggiato ed è diventato piccolo, il che significa che la sua lunghezza è breve. Ciò significa che il numero di zeri davanti ad AC e la lunghezza di AC sono quantità dipendenti. Pertanto lo scriviamo così:
[Codice Huffman per (Numero di zeri prima di AC, lunghezza di AC (in bit)]
L'algoritmo di codifica rimane lo stesso: quelle coppie (numero di zeri prima di AC, lunghezza di AC) che si verificano frequentemente riceveranno codici brevi e viceversa.

Costruiamo un istogramma della dipendenza dalla quantità per queste coppie e un albero di Huffman.


La lunga “dorsale montuosa” conferma la nostra ipotesi.

Caratteristiche di implementazione in JPEG:
Tale coppia occupa 1 byte: 4 bit per il numero di zeri e 4 bit per la lunghezza di AC. 4 bit sono valori da 0 a 15. Per la lunghezza di AC questo è più che sufficiente, ma possono esserci più di 15 zeri? Quindi vengono utilizzate più coppie. Ad esempio, per 20 zeri: (15, 0)(5, AC). Cioè, il sedicesimo zero è codificato come coefficiente diverso da zero. Poiché ci sono sempre molti zeri verso la fine del blocco, viene utilizzata la coppia (0,0) dopo l'ultimo coefficiente diverso da zero. Se viene riscontrato durante la decodifica, i valori rimanenti sono 0.

Abbiamo scoperto che ogni blocco è codificato e archiviato in un file come questo:
[Codice Huffman per la lunghezza del differenziale DC]
[Codice Huffman per (numero di zeri prima di AC 1, lunghezza di AC 1]

[Codice Huffman per (numero di zeri prima di AC n, lunghezza di AC n]
Dove AC i sono coefficienti AC diversi da zero.

Immagine a colori

Il modo in cui viene rappresentata un'immagine a colori dipende dal modello di colore selezionato. La soluzione semplice è utilizzare RGB e codificarli ciascuno canale di colore immagini separatamente. Quindi la codifica non sarà diversa dalla codifica di un'immagine grigia, solo 3 volte più lavoro. Ma la compressione dell'immagine può essere aumentata se ricordiamo che l'occhio è più sensibile ai cambiamenti di luminosità rispetto ai colori. Ciò significa che il colore può essere immagazzinato con perdite maggiori rispetto alla luminosità. RGB non ha un canale di luminosità separato. Dipende dalla somma dei valori di ciascun canale. Pertanto, il cubo RGB (questa è una rappresentazione di tutti i valori possibili) viene semplicemente "posizionato" sulla diagonale: più è alto, più è luminoso. Ma non si fermano qui: il cubo viene leggermente premuto dai lati e risulta più simile a un parallelepipedo, ma questo solo per tenere conto delle caratteristiche dell'occhio. Ad esempio, è più sensibile al verde che al blu. Ecco come è apparso il modello YCbCr.


(Immagine da Intel.com)
Y è la componente di luminanza, Cb e Cr sono le componenti della differenza di colore blu e rosso. Pertanto, se si desidera comprimere maggiormente l'immagine, RGB viene convertito in YCbCr e i canali Cb e Cr vengono assottigliati. Cioè, sono divisi in piccoli blocchi, ad esempio 2x2, 4x2, 1x2, e viene calcolata la media di tutti i valori di un blocco. O, in altre parole, riducono la dimensione dell'immagine di questo canale di 2 o 4 volte in verticale e/o in orizzontale.


Ogni blocco 8x8 è codificato (DCT + Huffman) e le sequenze codificate sono scritte in questo ordine:

È curioso che le specifiche JPEG non limitino la scelta del modello, ovvero l'implementazione del codificatore può dividere arbitrariamente l'immagine in componenti di colore (canali) e ciascuno verrà salvato separatamente. Sono a conoscenza dell'uso della scala di grigi (1 canale), YCbCr (3), RGB (3), YCbCrK (4), CMYK (4). I primi tre sono supportati quasi da tutti, ma ci sono problemi con gli ultimi a 4 canali. FastStone, GIMP li supportano correttamente e standard Programmi Windows, paint.net estrae correttamente tutte le informazioni, ma poi butta via il 4° canale nero, quindi (Antelle ha detto che non lo buttano via, leggete i suoi commenti) mostrano un'immagine più chiara. A sinistra c'è un classico JPEG YCbCr, a destra c'è un JPEG CMYK:



Se differiscono nel colore o è visibile solo un'immagine, molto probabilmente hai IE (qualsiasi versione) (UPD. nei commenti dicono "o Safari"). Puoi provare ad aprire l'articolo in browser diversi.

E un'altra cosa

In poche parole sulle funzionalità aggiuntive.
Modalità progressiva
Scomponiamo le tabelle risultanti dei coefficienti DCT nella somma delle tabelle (approssimativamente così (DC, -19, -22, 2, 1) = (DC, 0, 0, 0, 0) + (0, -20 , -20, 0, 0) + (0, 1, -2, 2, 1)). Per prima cosa codifichiamo tutti i primi termini (come abbiamo già imparato: Huffman e zigzag traversal), poi il secondo, ecc. Questo trucco è utile quando Internet è lento, poiché prima vengono caricati solo i coefficienti DC, che servono per costruire un'immagine approssimativa con 8x8 “pixel”. Quindi arrotondare i coefficienti AC per affinare la figura. Poi correzioni grossolane, poi correzioni più precise. E così via. I coefficienti vengono arrotondati, poiché nelle prime fasi del caricamento la precisione non è così importante, ma l'arrotondamento ha un effetto positivo sulla lunghezza dei codici, poiché ogni fase utilizza la propria tabella Huffman.
Modalità senza perdite
Compressione senza perdite. Niente DCT. Viene utilizzata la previsione del 4° punto basata su tre vicini. Gli errori di previsione sono codificati da Huffman. Secondo me viene usato un po’ più spesso che mai.
Modalità gerarchica
Dall'immagine vengono creati diversi livelli diverse risoluzioni. Il primo strato grossolano viene codificato come al solito, quindi viene codificata solo la differenza (raffinazione dell'immagine) tra gli strati (finta per essere una wavelet Haar). Per la codifica viene utilizzato DCT o Lossless. Secondo me viene usato un po’ meno spesso che mai.
Codifica aritmetica
L'algoritmo di Huffman produce codici ottimali in base al peso dei caratteri, ma questo è vero solo per una mappatura fissa da carattere a codice. L'aritmetica non ha un legame così rigido, che consente l'uso di codici come se avessero un numero frazionario di bit. Afferma di ridurre le dimensioni dei file in media del 10% rispetto a Huffman. Non diffuso a causa di problemi di brevetto, non supportato da tutti.

Spero che ora tu capisca l'algoritmo JPEG in modo intuitivo. Grazie per aver letto!

AGGIORNAMENTO
vanwin ha suggerito di indicare il software utilizzato. Ho il piacere di annunciare che tutto è disponibile e gratuito:

  • Python + NumPy + Matplotlib + PIL(Cuscino). Lo strumento principale. L'ho trovato cercando “Alternativa gratuita a Matlab”. Raccomando! Anche se non hai familiarità con Python, in un paio d'ore imparerai come fare calcoli e costruire bellissimi grafici.
  • JpegSnoop. Mostra informazioni dettagliate sul file jpeg.
  • sìEd. Editor di grafici.
  • Inkscape. Ci ho fatto delle illustrazioni, come un esempio dell'algoritmo di Huffman. Ho letto diverse lezioni, si è rivelato molto interessante.
  • Editor delle equazioni di Daum. Stavo cercando un editor di formule visive, dato che non sono molto bravo con Latex. Daum Equation è un plugin per Chrome che ho trovato molto comodo. Oltre a toccare il mouse, puoi modificare Latex.
  • FastStone. Penso che non ci sia bisogno di presentarlo.
  • PicPick. Alternativa gratuita a SnagIt. Si trova nel vassoio, fa uno screenshot di ciò che dicono e dove lo dicono. Inoltre tutti i tipi di gadget, come righelli, pipette, goniometri, ecc.

Tag: aggiungi tag

"Implementazione di algoritmi

JPEG e JPEG2000"

Completato:

studente del gruppo 819

Ugarov Dmitrij

Principi di funzionamento degli algoritmi JPEG e JPEG2000

1. Algoritmo JPEG

JPEG (Joint Photographic Experts Group) è un metodo ampiamente utilizzato per comprimere le immagini fotografiche. Il formato file che contiene dati compressi è solitamente chiamato anche JPEG; Le estensioni più comuni per tali file sono .jpeg, .jfif, .jpg, .JPG o .JPE. Tuttavia, tra queste, .jpg è l'estensione più popolare su tutte le piattaforme.

L'algoritmo JPEG è un algoritmo di compressione con perdita di qualità.

Area di applicazione

Il formato è un formato di compressione con perdita, quindi non è corretto pensare che JPEG memorizzi dati a 8 bit per canale (24 bit per pixel). D'altra parte, poiché i dati sottoposti a compressione da Formato JPEG e i dati decompressi sono solitamente rappresentati in 8 bit per canale, a volte viene utilizzata questa terminologia. È supportata anche la compressione delle immagini mezzitoni in bianco e nero.

Quando si salva un file JPEG, è possibile specificare il grado di qualità, e quindi il grado di compressione, che di solito viene specificato in alcune unità convenzionali, ad esempio da 1 a 100 o da 1 a 10. Un numero maggiore corrisponde a una qualità migliore , ma la dimensione del file aumenta. Di solito, la differenza di qualità tra 90 e 100 non viene praticamente percepita a occhio. Va ricordato che l'immagine ripristinata bit per bit è sempre diversa dall'originale. Un malinteso comune è questo Qualità JPEG identico alla quota di informazioni archiviate.

Fasi di codifica

Il processo di compressione JPEG comprende una serie di passaggi:

1. Converti l'immagine nello spazio colore ottimale;

In caso di utilizzo spazio colore luminosità/crominanza (YCbCr) raggiunge il miglior rapporto di compressione. In questa fase di codifica, il modello di colore RGB viene convertito in YCbCr utilizzando le relazioni appropriate:

Y = 0,299*R + 0,587*G + 0,114*B

Cb = - 0,1687*R – 0,3313*G + 0,5*B

Cr = 0,5*R – 0,4187*G – 0,0813*B.
Durante la decodifica è possibile utilizzare l'apposita trasformata inversa:
R = Y + 1.402*Cr

G = Y – 0,34414*Cb – 0,71414*Cr

B = Y + 1.772*Cb.
Nota relativa a Y,Cb,Cr nel sistema visivo umano:

L'occhio, in particolare la retina, ha due tipi di cellule come analizzatori visivi: cellule per la visione notturna, che percepiscono solo sfumature di grigio (dal bianco brillante al nero scuro) e cellule per la visione diurna, che percepiscono la tinta del colore. Le prime celle, che producono il colore RGB, rilevano un livello di luminosità simile al valore Y. Altre celle, responsabili della percezione della tonalità, determinano il valore associato alla differenza di crominanza.


2. Sottocampionamento delle componenti del colore calcolando la media di gruppi di pixel;

La maggior parte delle informazioni visive a cui l'occhio umano è più sensibile sono costituite da componenti di luminanza (Y) in scala di grigi ad alta frequenza dello spazio colore YCbCr. Le altre due componenti cromatiche (Cb e Cr) contengono informazioni cromatiche ad alta frequenza alle quali l'occhio umano è meno sensibile. Pertanto, una certa parte di esso può essere scartata e, quindi, il numero di pixel presi in considerazione per i canali di colore può essere ridotto.

1) tipo 4:2:0 (quando l'immagine è divisa in quadrati di 2x2 pixel e in ognuno di essi tutti i pixel ricevono gli stessi valori dei canali Cb e Cr, e la luminosità Y rimane diversa per ciascuno)

2) tipo 4:2:2 (la combinazione per componenti di cromaticità avviene solo orizzontalmente in gruppi di due pixel).

3) Il tipo 4:4:4 significa che ogni pixel in ogni riga ha il proprio valore univoco dei componenti Y, Cb e Cr. (Fig. 1a)

4) digitare 4:2:2. Sottocampionando orizzontalmente il segnale di crominanza con un fattore 2, otteniamo da un flusso YCbCr 4: 4: 4 un flusso YCbCr 4: 2: 2. La voce “4:2:2” significa che in un'unica riga ci sono 4 valori di luminosità per 2 valori di cromaticità (vedi Fig. 1 b). Il segnale 4:2:2 YCbCr è leggermente inferiore in termini di qualità dell'immagine al segnale 4:4:4 YCbCr, ma la larghezza di banda richiesta è ridotta del 33% rispetto all'originale.

3. Applicazione di trasformate coseno discrete per ridurre la ridondanza dei dati di immagine;

La fase principale dell'algoritmo è la trasformata discreta del coseno (DCT o DCT), che è un tipo di trasformata di Fourier. Viene utilizzato quando si lavora con le immagini per vari scopi, non solo per scopi di compressione. Il passaggio alla rappresentazione in frequenza dei valori dei pixel ci consente di dare uno sguardo diverso all'immagine, elaborarla e, ciò che ci interessa, comprimerla. Inoltre, conoscendo i coefficienti di conversione, possiamo sempre eseguire l'azione opposta: restituire l'immagine originale.

Il DCT applicato direttamente ad un blocco (nel nostro caso 8x8 pixel) dell'immagine apparirà così:

dove x, y sono le coordinate spaziali del pixel (0..7),

f(x,y) - valori dei pixel del macroblocco originale (ad esempio luminosità)

u,v - coordinate dei pixel nella rappresentazione della frequenza (0..7)

w(u) =1/SQRT(2) per u=0, negli altri casi w(u)=1 (SQRT - radice quadrata)

w(v) =1/SQRT(2) per v=0, negli altri casi w(v)=1

Oppure in forma matriciale:

4. Quantizzazione di ciascun blocco di coefficienti DCT utilizzando funzioni di ponderazione ottimizzate tenendo conto della percezione visiva umana;

La trasformata discreta del coseno prepara le informazioni per la compressione e l'arrotondamento con perdita. Ad ogni elemento della matrice da trasformare corrisponde un elemento della matrice quantizzazione. La matrice risultante si ottiene dividendo ciascun elemento della matrice in trasformazione per il corrispondente elemento della matrice di quantizzazione e arrotondando poi il risultato all'intero più vicino. Quando si compila una matrice di quantizzazione, i suoi elementi grandi si trovano nell'angolo in basso a sinistra, in modo che quando si divide per essi, i dati in questo angolo dopo una trasformazione discreta del coseno (proprio quelli il cui arrotondamento sarà meno doloroso) vengono arrotondati in modo più approssimativo. Rispettivamente informazioni perse meno importante per noi di quello rimanente.


5. Fase di compressione secondaria

La fase finale del codificatore JPEG sta codificando la matrice risultante.

5.1 Permutazione a zigzag di 64 coefficienti DCT

Quindi, dopo aver eseguito una trasformazione DCT su un blocco di valori 8x8, abbiamo nuovo blocco 8x8. Quindi, questo blocco 8x8 viene attraversato secondo uno schema a zigzag come questo:

(I numeri nel blocco 8x8 indicano l'ordine in cui scansioniamo la matrice bidimensionale 8x8)

0, 1, 5, 6,14,15,27,28,

2, 4, 7,13,16,26,29,42,

3, 8,12,17,25,30,41,43,

9,11,18,24,31,40,44,53,

10,19,23,32,39,45,52,54,

20,22,33,38,46,51,55,60,

21,34,37,47,50,56,59,61,

35,36,48,49,57,58,62,63

Come puoi vedere, prima c'è l'angolo in alto a sinistra (0,0), poi il valore in (0,1), poi (1,0), poi (2,0), (1,1), (0, 2), (0,3), (1,2), (2,1), (3,0), ecc.

Dopo aver proceduto a zigzag attraverso la matrice 8x8, ora abbiamo un vettore con 64 coefficienti (0..63). Lo scopo di questo vettore a zigzag è che stiamo esaminando i coefficienti DCT 8x8 in ordine crescente di frequenze spaziali. Quindi, otteniamo un vettore ordinato secondo criteri di frequenza spaziale: il primo valore sul vettore (indice 0) corrisponde alla frequenza più bassa nell'immagine - è indicato con il termine DC. All'aumentare dell'indice sul vettore otteniamo valori corrispondenti a frequenze più alte (un valore con indice 63 corrisponde all'ampiezza della frequenza più alta nel blocco 8x8). Il resto dei coefficienti DCT sono indicati con AC.

5.2 Codifica RunLength zero (RLE)

Ora abbiamo un vettore con una lunga sequenza di zeri. Possiamo usarlo codificando zeri consecutivi. IMPORTANTE: ne vedremo il motivo in seguito, ma qui stiamo saltando la codifica del primo coefficiente vettoriale (il coefficiente DC), che è codificato diversamente. Considera il vettore 64 originale come un vettore 63 (questo è un vettore 64 senza il primo coefficiente)

Diciamo che abbiamo 57,45,0,0,0,0,23,0,-30,-16,0,0,1,0,0,0,0,0,0, solo 0,... .0

Ecco come viene eseguita la compressione JPEG RLC per questo esempio:

(0,57); (0,45); (4.23); (1,-30); (0,-16); (2.1); EOB

Come puoi vedere, codifichiamo per ogni valore diverso da 0 il numero di zeri LEADER consecutivi prima del valore, quindi aggiungiamo il valore. Un'altra nota: EOB è l'abbreviazione di End of Block, è un valore codificato speciale (marker). Se abbiamo raggiunto una posizione su un vettore da cui abbiamo solo zeri vettoriali fino alla fine, assegneremo quella posizione con un EOB e completeremo la compressione RLC del vettore quantizzato.

[Nota che se il vettore quantizzato non ha terminazione zero (ha l'ultimo elemento diverso da 0), non avremo un token EOB.]

(0,57); (0,45); (4,23); (1,-30); (0,-16); (2,1); (0,0)

Un'altra cosa BASIC: diciamo da qualche parte sul vettore quantizzato che abbiamo:

57, diciotto zeri, 3, 0.0 ,0.0 2, trentatré zeri, 895, EOB

La codifica JPG Huffman impone la limitazione secondo cui il numero di zeri iniziali deve essere codificato poiché un valore a 4 bit non può superare 15.

Quindi, l'esempio precedente dovrebbe essere codificato come:

(0,57); (15,0) (2,3); (4,2); (15,0) (15,0) (1,895), (0,0)

(15,0) è un valore codificato speciale che indica che seguono 16 zeri consecutivi.

5.3 Passaggio finale: codifica Huffman

Prima una nota IMPORTANTE: invece di memorizzare il valore effettivo, lo standard JPEG specifica che memorizziamo la dimensione in bit minima alla quale possiamo mantenere questo valore (questa è chiamata la categoria di questo valore) e quindi una rappresentazione codificata in bit di questo valore come questo:

7,..,-4,4,..,7 3 000,001,010,011,100,101,110,111

15,..,-8,8,..,15 4 0000,..,0111,1000,..,1111

31,..,-16,16,..,31 5 00000,..,01111,10000,..,11111

63,..,-32,32,..,63 6 .

127,..,-64,64,..,127 7 .

255,..,-128,128,..,255 8 .

511,..,-256,256,..,511 9 .

1023,..,-512,512,..,1023 10 .

2047,..,-1024,1024,..,2047 11 .

4095,..,-2048,2048,..,4095 12 .

8191,..,-4096,4096,..,8191 13 .

16383,..,-8192,8192,..,16383 14 .

32767,..,-16384,16384,..,32767 15 .

Successivamente per l'esempio precedente:

(0,57); (0,45); (4,23); (1,-30); (0,-8); (2,1); (0,0)

codifichiamo solo il valore di destra di queste coppie, ad eccezione delle coppie che sono token speciali come (0,0) o (se necessario) (15,0)

45, allo stesso modo, verrebbe codificato come (6.101101)

30 -> (5,00001)

E ora scriveremo di nuovo la stringa di coppie:

(0,6), 111001; (0,6), 101101; (4,5), 10111; (1,5), 00001; (0,4), 0111; (2,1), 1; (0,0)

Coppie di 2 valori racchiusi tra parentesi possono essere rappresentate in un byte, poiché in effetti ciascuno dei 2 valori può essere rappresentato in un blocco di 4 bit (il conteggio degli zeri iniziali è sempre inferiore a 15 e uguale a la categoria [numeri codificati nel file JPG - nell'area -32767..32767]). In questo byte, il bit alto rappresenta il numero di zeri precedenti e il bit basso rappresenta la categoria del nuovo valore diversa da 0.

Il passaggio finale della codifica consiste nel codificare Huffman questo byte e quindi registrare in un file JPG, come flusso di bit, il codice Huffman di questo byte, seguito dalla rappresentazione bit per bit di questo numero.

Ad esempio, per il byte 6 (equivalente a (0,6)) abbiamo il codice Huffman = 111000;

21 = (1,5) - 11111110110

4 = (0,4) - 1011

33 = (2,1) - 11011

0 = EOB= (0,0) - 1010

Il bitstream finale scritto nel file JPG su disco per l'esempio precedente è di 63 coefficienti (ricorda che abbiamo saltato il primo coefficiente) -

111000 111001 111000 101101 1111111110011001 10111 11111110110 00001

1011 0111 11011 1 1010
Vantaggi e svantaggi

Gli svantaggi del formato includono il fatto che a livelli elevati di compressione, la struttura dei dati a blocchi si fa sentire, l'immagine viene "divisa in quadrati" (ciascuno di 8x8 pixel). Questo effetto è particolarmente evidente nelle aree con bassa frequenza spaziale (transizioni di immagini fluide, ad esempio, un cielo limpido). Nelle aree con elevata frequenza spaziale (ad esempio, bordi contrastanti dell'immagine), compaiono "artefatti" caratteristici: una struttura irregolare di pixel con colore e/o luminosità distorti. Inoltre, piccoli dettagli di colore scompaiono dall'immagine. Anche questo non dobbiamo dimenticarlo questo formato non supporta la trasparenza.

Tuttavia, nonostante i suoi difetti, il JPEG è diventato molto diffuso grazie al suo elevato rapporto di compressione rispetto alle alternative esistenti al momento della sua introduzione.

2. Algoritmo JPEG2000

L'algoritmo JPEG-2000 è stato sviluppato dallo stesso gruppo di esperti di fotografia che ha sviluppato JPEG. La formazione del JPEG come standard internazionale è stata completata nel 1992. Nel 1997 divenne chiaro che era necessario un nuovo standard più flessibile e potente, che fu finalizzato entro l’inverno del 2000.

Le principali differenze tra l'algoritmo in JPEG 2000 e l'algoritmo in JPEG sono le seguenti:

1) Migliore qualità dell'immagine con un elevato grado di compressione. Oppure, che è la stessa cosa, un rapporto di compressione più elevato con la stessa qualità per rapporti di compressione elevati. In effetti, ciò significa una notevole riduzione delle dimensioni della grafica di "qualità Web" utilizzata dalla maggior parte dei siti.

2)Supporto per la codifica di singole aree con migliore qualità. È noto che alcune aree dell'immagine sono critiche per la percezione umana (ad esempio gli occhi in una fotografia), mentre la qualità di altre può essere sacrificata (ad esempio lo sfondo). Con l'ottimizzazione “manuale”, il tasso di compressione viene aumentato fino a quando non si perde la qualità in alcune parti importanti dell'immagine. Ora diventa possibile impostare la qualità nelle aree critiche, comprimendo maggiormente le altre aree, ad es. otteniamo un rapporto di compressione finale ancora maggiore con una qualità dell'immagine soggettivamente uguale.

3) L'algoritmo di compressione principale è stato sostituito da wavelet. Oltre all'aumento specificato del rapporto di compressione, ciò ha permesso di eliminare il blocco di 8 pixel che si verifica quando si aumenta il rapporto di compressione. Inoltre, lo sviluppo regolare dell'immagine è ora inizialmente incluso nello standard (il JPEG progressivo, utilizzato attivamente su Internet, è apparso molto più tardi del JPEG).

4) Per aumentare il rapporto di compressione, l'algoritmo utilizza la compressione aritmetica. Lo standard JPEG originariamente includeva anche la compressione aritmetica, ma fu successivamente sostituito dalla meno efficiente compressione Huffman perché la compressione aritmetica era protetta da brevetti. Ora il brevetto principale è scaduto e c'è la possibilità di migliorare l'algoritmo.

5) Supporta la compressione senza perdite. Oltre alla consueta compressione con perdita, il nuovo JPEG ora supporterà la compressione senza perdita. Diventa così possibile utilizzare JPEG per comprimere immagini mediche, nella stampa, salvando il testo per il riconoscimento da parte dei sistemi OCR, ecc.

6)Supporta la compressione di immagini a bit singolo (2 colori). Per il salvataggio di immagini a bit singolo (disegni a inchiostro, testo scansionato, ecc.), in precedenza era ampiamente consigliato il formato GIF, poiché la compressione DCT è molto inefficace per le immagini con transizioni di colore nette. In JPEG, quando compressa, un'immagine da 1 bit veniva convertita in 8 bit, ovvero aumentato di 8 volte, dopodiché è stato effettuato un tentativo di compressione, spesso meno di 8 volte. Ora possiamo raccomandare JPEG 2000 come algoritmo universale.

7) La trasparenza è supportata a livello di formato. Sarà ora possibile applicare facilmente uno sfondo durante la creazione di pagine WWW non solo in GIF, ma anche in JPEG 2000. Inoltre, non è supportato solo 1 bit di trasparenza (il pixel è trasparente/opaco), ma un canale separato, che ti consentirà di impostare una transizione graduale da un'immagine opaca a uno sfondo trasparente.

Inoltre, il livello del formato supporta l'inclusione delle informazioni sul copyright nell'immagine, il supporto per la tolleranza agli errori di bit durante la trasmissione e la trasmissione e può essere interrogato per la decompressione o l'elaborazione fondi esterni(plug-in), è possibile includere nell'immagine la descrizione, le informazioni di ricerca, ecc.

Fasi di codifica

Il processo di compressione JPEG2000 comprende una serie di passaggi:

1. Converti l'immagine nello spazio colore ottimale.
In questa fase della codifica, il modello di colore RGB viene convertito in YUV utilizzando le relazioni appropriate:

Durante la decompressione viene applicata la corrispondente trasformazione inversa:

2. Trasformata wavelet discreta.

Discreto conversione wavelet(DWT) può anche essere di due tipi: nel caso della compressione con perdita e nel caso della compressione senza perdita.

Questa trasformazione nel caso unidimensionale è un prodotto scalare dei coefficienti corrispondenti e una stringa di valori. Ma perché molti coefficienti sono zero, quindi la trasformazione wavelet diretta e inversa può essere scritta con le seguenti formule (per trasformare gli elementi estremi di una linea si utilizza la sua espansione di 2 pixel in ciascuna direzione, i cui valori sono simmetrici rispetto a valori degli elementi della linea rispetto ai suoi pixel estremi):
y(2*n + 1) = x(2*n + 1) - (int)(x(2*n) + x(2*n + 2)) / 2

y(2*n) = x(2*n) + (int)(y(2*n - 1) + y(2*n + 1) + 2) / 4

e viceversa

x(2*n) = y(2*n) - (int)(y(2*n - 1) + y(2*n + 1) + 2) / 4

x(2*n + 1) = y(2*n + 1) + (int)(x(2*n) + x(2*n + 2)) / 2.

3. Quantizzazione dei coefficienti.

Proprio come l'algoritmo JPEG, la quantizzazione viene utilizzata quando si codifica un'immagine nel formato JPEG2000. La trasformata wavelet discreta, come la sua analoga, ordina i coefficienti per frequenza. Ma, a differenza del JPEG, nel nuovo formato esiste una matrice di quantizzazione per l'intera immagine.


4. Fase di compressione secondaria

. Come il JPEG, l'ultimo passaggio dell'algoritmo di compressione nel nuovo formato è la codifica senza perdita di dati. Ma, a differenza del formato precedente, JPEG2000 utilizza un algoritmo di compressione aritmetica.

Implementazione del software

In questo lavoro vengono implementati gli algoritmi JPEG e JPEG2000. Entrambi gli algoritmi implementano la codifica diretta e inversa (l'ultimo stadio di compressione secondaria è assente). Il calcolo JPEG richiede molto tempo (circa 30 secondi) a causa del calcolo “diretto” del DCT. Se è necessario aumentare la velocità del lavoro, è necessario inizialmente calcolare la matrice DCT (le modifiche dovrebbero essere apportate nella classe DCT).

Passiamo al programma:


  1. Dopo il lancio, appare una finestra dove

ed è possibile salvarlo facendo clic sul pulsante (2) e inserendo il nome desiderato nella finestra di dialogo.

  • Con un fattore di qualità sufficientemente grande, l'immagine cambierà notevolmente. Se si tratta di un algoritmo JPEG, i blocchi di dimensione 8x8 saranno chiaramente visibili (nel caso dell'algoritmo JPEG2000, non ci sarà divisione in blocchi).
  • Prima:

    Dopo:



    I migliori articoli sull'argomento