1. Tratto, tradotto ed editato da Wikipedia
Compressione dei file
1. INTRODUZIONE ED ENTROPIA
Il primo che iniziò a parlare di compressione dei dati fu Claude Shannon dei Bell
Laboratories, negli anni Quaranta. Comprimere significa andare a ridurre lo spazio che le
informazioni occupano, senza andare a intaccare il contenuto informativo vero e proprio,
rinunciando, per esempio, a elementi non significativi. Per fare una analogia, è come quando
si pone qualcosa sottovuoto: si elimina tutta l’aria presente senza comunque intaccare il
contenuto.
Si distinguono due tipi di compressione: “senza perdita di dati” (lossless) e “con perdita
di dati” (lossy).
Nel primo caso, entra in gioco il concetto di entropia, preso a piene mani dalla
termodinamica e applicato all’informatica. Si dice che un insieme di dati possiede una
maggiore o minore entropia a seconda della quantità di informazioni che
contiene. Per comprendere meglio questo concetto, si guardi il seguente esempio:
AAAAAAAAAABBBBBBBBBB
ABCDEFGHIJKLMNOQRSTU
È evidente che la seconda sequenza contiene un quantitativo informativo superiore rispetto
alla prima, proprio per la grande varietà di caratteri al suo interno. Questo significa
che in fase di compressione, sarà molto più difficile comprimere la seconda sequenza rispetto
alla prima, che può essere compressa, per esempio come 10A+10B, occupando 6 spazi
piuttosto che 20.
L’entropia è solitamente espressa in bit/carattere, con, di conseguenza, un massimo di
8. Quando l’entropia vale 8 significa che il file è casuale o compresso. Per esempio, un
file BMP ha entropia uguale a 4,7, un file JPEG ha entropia pari a 7.9. Altre strategie per
vedere se un flusso è casuale o compresso sono:
o test del Chi quadro: se il risultato è compreso tra il 5% e il 95%, allora il
flusso è casuale
o media aritmetica: somma tutti i byte e li divide per la lunghezza; più il
valore è vicino al numero 127.5, più è casuale
o test del pi-greco Montecarlo: più il valore si avvicina a 3.14, più il flusso
di dati è compresso
2. o coefficiente di correlazione: si analizza quanto è prevedibile un byte
conoscendone il precedente; più il coefficiente di correlazione è vicino a 1, tanto
più quel byte è predicibile quindi tanto più è vicino allo 0, tanto più è casuale.
La compressione che usa questa tecnica è detta Run-Lenght Encoding. Sulla base di tale
algoritmo, possono partire delle ricerche che individuano dei pattern all’interno della stringa.
Per esempio:
ABBABBABBABBABB
che diventa 5ABB. Questa tecnica è detta LZW, dalle iniziali del ricercatore che la ideò.
Questa tecnica viene ancora oggi utilizzata per i file GIF.
Nel secondo caso, si effettua un doppio procedimento. Consideriamo, per esempio, una
immagine. Per comprimerla, si potrebbero eliminare dei dettagli insignificanti o delle
sfumature (che sono comunque delle informazioni), per poi applicare l’algoritmo di
compressione. Di conseguenza, l’unica cosa che cambia rispetto alla compressione senza
perdita è che prima viene applicato un algoritmo di “semplificazione
dell’informazione”. Più sarà la quantità di informazioni a cui si rinuncia, più alto
sarà il grado di compressione.
Funzionano secondo questo procedimento, per esempio i file JPEG e MPEG.
Una delle nuove frontiere della compressione è quelli degli algoritmi frattali. Questi algoritmi
cercano dei pattern in modo simile a LZW solo che, qui, non è necessario che gli elementi
ripetuti siano identici; l’importante è che siano “affini”, cioè ottenibili tramite trasformazioni
elementari. Si tenta, dunque, di ricondurre tutto il set informativo e delle informazioni base,
principali, che possono poi essere combinate per ottenere tutte le altre.
2. RUN-LIGHT ENCODING
È storicamente il primo algoritmo di compressione per le immagini inventato e fa parte dei
lossless. Questa metodologia si basa sul fatto che un’immagine abbia pochi colori: si cercano,
quindi, di comprimere una serie di elementi uguali sostituendoli a un solo
elemento, accompagnato dal numero di volte che esso si ripete. Tale strategia si basa sulla
cosiddetta ridondanza spaziale. L’elemento e il numero di volte con cui compare sono
separati da un carattere speciale, dipendente dall’implementazione dell’algoritmo
utilizzata.
Attualmente è usato solo sulle immagini bitmap, in quanto è stato soppiantato da tecniche
più moderne per immagini più complesse.
Tratto, tradotto ed editato da Wikipedia
3. Tratto, tradotto ed editato da Wikipedia
3. CODIFICAZIONE ENTROPICA
La codificazione entropica è un altro schema di compressione lossless. Algoritmi basati
su questa tecnica creano e assegnano un codice prefisso a ciascun simbolo unico che
si presenta in input: questi poi comprimono i dati sostituendo ciascun simbolo
dell’input a lunghezza fissa con la corrispondente parola del codice prefisso a
lunghezza variabile.
Secondo il teorema della codifica di sorgente di Shannon (o primo teorema di
Shannon), la lunghezza ottimale del codice per un simbolo è − log𝑏𝑏 𝑃𝑃 dove b è il numero di
simboli usati per formare i codici e P è la probabilità relativa al simbolo. La lunghezza di
ciascuna parola del codice è proporzionale a tale quantità: simboli più comuni hanno
codici più brevi.
3.1 CODIFICA DI HUFFMAN
Questo algoritmo di codifica è atto a massimizzare l’entropia: si basa sul principio di trovare
il sistema ottimale per codificare stringhe, basato sulla frequenza relativa di
ciascun carattere. La codifica di Huffman usa un preciso metodo per rappresentare ciascun
simbolo, dove il simbolo più frequente è rappresentato dal codice più breve possibile. È
dimostrato che la codifica di Huffman è quella più efficiente per compressioni di questo tipo.
La codifica viene costruita sulla base di un albero binario di simboli, in questo modo:
o si ordinano i simboli in una lista, in base alla loro frequenza
o finché la lista non contiene un solo simbolo, allora:
si considerano i due simboli meno frequenti e si crea un albero che
come figli ha tali elementi, con un genitore generico
si sommano le frequenze dei figli e il risultato lo si pone al genitore, in
modo che la lista resti ordinata
cancella il nodo figlio dalla lista
o a partire dal basso, si assegna 0 o 1 per ogni coppia di rami: la codifica sarà
ottenuta leggendo i bit a partire dalla radice
Le frequenze utilizzate possono essere quelle generiche (calcolate preventivamente
dall’applicazione) oppure quelle trovate effettivamente nel testo: in questo caso, bisogna
tener traccia anche di una tabella delle occorrenze.
L’algoritmo di Huffman è ottimale nel momento in cui la probabilità di ciascun simbolo
in input è una potenza negativa del due. Le codifiche senza prefissi, infatti, tendono
ad essere inefficienti su piccoli alfabeti, quando le probabilità cadono tra potenze del due.
Esistono diverse varianti di questo tipo di codifica, come il “Template Huffman”:
effettivamente, si rappresentano le probabilità numeriche ma l’algoritmo non le richiede: esso
4. Tratto, tradotto ed editato da Wikipedia
vuole solo ordinare i pesi e sommarli; il Template Huffman permette, quindi, l’uso di pesi
non numerici.
ESEMPIO
Consideriamo la frase “Esempio di albero di Huffman”.
o Conteggio delle lettere:
o Ordinamento
o Albero binario
N1
R1
H1
L1
U1
P1
S1
E = 3 M = 2 I = 2 A = 2 R = 1 U = 1 M = 1
S = 1 P = 1 O = 2 L = 1 H = 1 F = 2 N = 1
E = 3 M = 2 I = 2 A = 2 F = 2 O = 2 M = 1
S = 1 P = 1 U = 1 L = 1 H = 1 R = 1 N = 1
5. Tratto, tradotto ed editato da Wikipedia
4. ALGORITMO LEMPEL – ZIV – WELCH (LZW)
LZW è un altro algoritmo lossless che viene utilizzato principalmente nella compressione dei
file GIF. È un algoritmo facile da implementare e applicabile a qualsiasi tipo di file
(immagine, audio, testo…).
L’input (encoder) si basa sul fatto che la stringa in ingresso è composta da un certo
numero di simboli provenienti da un alfabeto. È probabile che all’interno di tale
stringa si ripetano più volte le stesse sequenze di caratteri: LZW si basa su queste ripetizioni
per effettuare la compressione. Il grado di compressione dipende dai dati in ingresso ed è
molto efficiente per file testuali.
Le sottostringhe ridondanti vengono individuate dopo che l’intera stringa è stata ricevuta: a
ogni sequenza si associa un determinato simbolo, che nel complesso estendono
l’alfabeto iniziale. Pertanto, diventa possibile codificare sequenze di simboli di
lunghezza variabile con dei codici di lunghezza fissa.
4.1 CODIFICA
o sì inizializza il dizionario dei simboli con tutti i simboli dell’alfabeto sorgente
con la corrispondente codifica; si inizializza a “” la variabile prefisso; si legge
il primo carattere dello stream di input assegnandolo a una variabile buff
o se (prefisso + buff) è presente nel dizionario allora:
prefisso = prefisso + buff;
o altrimenti:
output = output + code(prefisso);
inserire la sequenza “prefisso + buff” nel dizionario e
codificarla;
prefisso = buff;
o se ci sono ancora dei caratteri nella stringa in input, si legge il carattere
successivo e si torna al punto precedente, altrimenti: output = output +
code(prefisso)
ESEMPIO
Passo 1 Prefisso = “” Simboli Code
Input ABABCBABAB A 0000
Buff A B 0001
Output C 0010
Inizializzazione delle variabili (prefisso e buff)
6. Tratto, tradotto ed editato da Wikipedia
Passo 2 Prefisso = “A” Simboli Code
Input ABABCBABAB A 0000
Buff A B 0001
Output C 0010
Prelievo del primo carattere, posto in buff. Prefisso+buff = A, già presente nel dizionario, vado avanti
Passo 3 Prefisso = “A” Simboli Code
Input ABABCBABAB A 0000
Buff B B 0001
Output 0000 C 0010
AB 0011
Prelievo del secondo carattere, posto in buff. Prefisso+buff = AB, non presente nel dizionario, lo si
inserisce con nuova codifica e l’output è la codifica del prefisso già presente (passo precedente); si
aggiorna il prefisso a B (nel passo successivo)
Passo 4 Prefisso = “B” Simboli Code
Input ABABCBABAB A 0000
Buff A B 0001
Output 0000 0001 C 0010
AB 0011
BA 0100
Prefisso+buff = BA, non presente nel dizionario; lo si inserisce con nuova codifica, si aggiorna
l’output aggiungendo a quello esistente, quello del prefisso; si aggiorna il prefisso a A
Passo 5 Prefisso = “A” Simboli Code
Input ABABCBABAB A 0000
Buff B B 0001
Output 0000 0001 C 0010
AB 0011
BA 0100
Prefisso+buff = AB, già presente, allora aggiorno prefisso a AB
Passo 6 Prefisso = “AB” Simboli Code
Input ABABCBABAB A 0000
Buff C B 0001
Output 0000 0001 0011 C 0010
AB 0011
BA 0100
ABC 0101
Prefisso+buff = ABC, aggiorno dizionario con nuova codifica e aggiorno output con la codifica di AB; il
prefisso ora vale C
7. Tratto, tradotto ed editato da Wikipedia
Passo 7 Prefisso = “C” Simboli Code
Input ABABCBABAB A 0000
Buff B B 0001
Output 0000 0001 0011 0010 C 0010
AB 0011
BA 0100
ABC 0101
BC 0110
Prefisso+buff = BC, aggiorno dizionario, aggiorno output con codifica di C; prefisso = B
Passo 8 Prefisso = “B” Simboli Code
Input ABABCBABAB A 0000
Buff A B 0001
Output 0000 0001 0011 0010 C 0010
AB 0011
BA 0100
ABC 0101
BC 0110
Prefisso+buff = BA, già presente in dizionario, prefisso = BA
Passo 8 Prefisso = “BA” Simboli Code
Input ABABCBABAB A 0000
Buff B B 0001
Output 0000 0001 0011 0010 0100 C 0010
AB 0011
BA 0100
ABC 0101
BC 0110
BAB 0111
Prefisso+buff = BAB, non presente in dizionario, associo nuova codifica, aggiorno output con codifica
di BA, prefisso = B
Passo 9 Prefisso = “B” Simboli Code
Input ABABCBABAB A 0000
Buff A B 0001
Output 0000 0001 0011 0010 0100 C 0010
AB 0011
BA 0100
ABC 0101
BC 0110
BAB 0111
Prefisso+buff = BA, già presente, prefisso = BA
8. Tratto, tradotto ed editato da Wikipedia
Passo 10 Prefisso = “BA” Simboli Code
Input ABABCBABAB A 0000
Buff B B 0001
Output 0000 0001 0011 0010 0100 0111 C 0010
AB 0011
BA 0100
ABC 0101
BC 0110
BAB 0111
Prefisso+buff = BAB, già presente in dizionario; stringa terminata, aggiorno output con codifica di BAB
4.2 DECODIFICA
o si inizializza il dizionario dei simboli con tutti i simboli dell’alfabeto sorgente
o si inizializza prefisso = “”
o si legge la prima stringa di bit e la si inserisce in una variabile CW
o si assegna la variabile PW = CW e si sposta CW in una posizione in avanti
o se CW è nel dizionario, allora:
output = decode(CW)
prefisso = decode(PW)
char = decode(CW) – primo carattere della stringa appena
decodificata
o altrimenti:
prefisso = decode(PW)
char = decode(PW) – primo carattere della stringa decodificata al
passo precedente
output = prefisso + char, che è stato aggiunto al dizionario
o continuare fino alla fine della stringa
ESEMPIO
Passo 1 Prefisso = “” PW = / CW = 0000 Simboli Code
Input 0000 0001 0011 0010 0100 0111 A 0000
Char B 0001
Output A C 0010
CW=0000 al quale è associato A, che viene stampato in output; PW nullo quindi prefisso nullo; char
nullo
9. Tratto, tradotto ed editato da Wikipedia
Passo 2 Prefisso = A
PW =
0000
CW = 0001 Simboli Code
Input 0000 0001 0011 0010 0100 0111 A 0000
Char B B 0001
Output AB C 0010
CW = 0001 = B che viene mandato in output; prefisso = PW = 0000 = A; char = CW = B
Passo 3 Prefisso = B
PW =
0001
CW = 0011 Simboli Code
Input 0000 0001 0011 0010 0100 0111 A 0000
Char A B 0001
Output ABAB C 0010
AB 0011
BA 0100
CW= 0011 = AB, non presente, prefisso = PW = B, char = primo carattere della stringa decodificata
precedentemente = A, aggiunta voce nel dizionario (AB), output = prefisso + char = BA
Passo 4 Prefisso = AB
PW =
0011
CW = 0010 Simboli Code
Input 0000 0001 0011 0010 0100 0111 A 0000
Char C B 0001
Output ABABC C 0010
AB 0011
BA 0100
CW= 0010 = C, prefisso = PW = AB, char = primo carattere della stringa decodificata = C
Passo 5 Prefisso = C
PW =
0010
CW = 0100 Simboli Code
Input 0000 0001 0011 0010 0100 0111 A 0000
Char B B 0001
Output ABABCBA C 0010
AB 0011
BA 0100
CW = 0100 = BA, aggiunto all’output, prefisso = PW = C, char = primo carattere BA = B
10. Tratto, tradotto ed editato da Wikipedia
Passo 6 Prefisso = BA
PW =
0100
CW = 0111 Simboli Code
Input 0000 0001 0011 0010 0100 0111 A 0000
Char B B 0001
Output ABABCBABAB C 0010
AB 0011
BA 0100
ABC 0101
CW = 0111 non presente in dizionario, quindi prefisso = PW = BA, char = primo carattere della stringa
decodificata al passo 5 = B, output = prefisso + char = BAB, nuova voce in dizionario ABC
11. Tratto, tradotto ed editato da Wikipedia
5. DICTIONARY CODER
La codifica a dizionario è una classe di algoritmi di compressione lossless dove si ricerca la
corrispondenza tra il testo da comprimere e un set di stringhe contenute in un
DB. Quando la corrispondenza viene trovata, si sostituisce la stringa trovata con quella
contenuta nel DB stesso.
Alcuni coder usano dei dizionari statici, che vengono decisi prima che l’implementazione
cominci: questo approccio è generalmente utilizzato in sistemi portatili e con memoria
limitata. L’approccio generale, invece, prevede un dizionario dinamico che parte da una
serie di stringhe già inserite prima dell’implementazione e poi si aggiorna via via che il
dizionario viene usato e che nuovi input vengono inseriti.
5.1 ALGORITMO LZ77
L’algoritmo LZ77 si basa su questo metodo: data una sorgente S, che genera una sequenza
X di simboli appartenenti ad un alfabeto A, il dizionario D è contenuto nella porzione
di tale sequenza precedentemente codificata; inoltre, inizialmente non è noto alcun modello
per S, ma solamente A ed una codifica dei suoi simboli. Operando in questo modo, ci si
svincola dal concetto di sorgente e si sfruttano prevalentemente le caratteristiche
di X che vengono individuate durante il processo di compressione. La codifica della
sequenza X avviene utilizzando una finestra scorrevole composta di due parti: un search
buffer, che contiene la porzione di X appena codificata, e un look-ahead buffer contenente
il successivo segmento della sequenza da codificare.
Il codificatore mantiene un puntatore i che identifica l’inizio del look-ahead buffer e, a
partire da esso, esamina all’indietro il search buffer al fine di individuare (se esiste) il più
lungo prefisso del look-ahead buffer che è contenuto anche nel search buffer. Un puntatore
j rappresenterà l’inizio di tale prefisso nel search buffer.
j i
… a a b c a b c a b c b c b …
Search buffer Look-ahead buffer
Si possono presentare due casi:
o la ricerca del prefisso va a buon fine e si produce una tripla (o, l, a) dove:
o = distanza i-j
l = lunghezza prefisso
a = simbolo dopo il prefisso nel look-ahead
12. Tratto, tradotto ed editato da Wikipedia
o
l a
… a a b c a b c a b c b c b …
Search buffer Look-ahead buffer
o la ricerca ha esito negativo, ovvero non esiste alcun prefisso utilizzabile nel
search buffer; in questo caso si produce una tripla (0, 0, a)
a
… a a b c a b c a b c b c b …
Search buffer Look-ahead buffer
Si può osservare che, sebbene nel primo caso non sia necessario memorizzare il terzo elemento
della tripla, questa scelta diventa obbligata quando si verifica la seconda situazione, per
consentire al codificatore di proseguire nell’analisi di X ed al tempo stesso
mantenere l’informazione associata ad a. La codifica di X viene effettuata mediante la
concatenazione di triple dei due tipi viste sopra.
La compressione si ottiene se i prefissi individuati sono sufficientemente lunghi: in
questo caso le triple verranno sicuramente rappresentate con un numero di bit inferiore
rispetto alla codifica banale dei prefissi stessi.
5.2 ALGORITMO LEMPEL – ZIV – MARKOV (LZM)
È un algoritmo di compressione lossless dove il flusso compresso è rappresentato da un flusso
di bit elaborato con un codificatore attivo binario di range (ABRC). Il flusso è
suddiviso in due pacchetti, di cui ognuno può descrivere sia un singolo byte che una sequenza
LZ77 di lunghezza o codifica implicita. Ogni parte di ogni pacchetto è modellata su contesti
differenti quindi le probabilità di ciascun bit sono correlate al valore del bit stesso.
Ci sono sette tipi di pacchetto:
Packet Code Packet Name Packet Description
0 + bytecode LIT Singolo byte codificato con ABRC
10 + len + dist MATCH Sequenza LZ77 con sequenza e distanza
1100 SHORTREP Un byte LZ77 con distanza della sequenza
precedente
1101 + len LONGREP[0] Una sequenza LZ77 con distanza della sequenza
precedente
1110 + len LONGREP[1] Una sequenza LZ77 con distanza della sequenza
penultima
13. Tratto, tradotto ed editato da Wikipedia
6. TRASFORMATA DI BURROWS – WHEELER
La trasformata di Burrows – Wheeler (BWT) è un algoritmo lossless più efficace del
RLE ed è anche reversibile, senza il bisogno di conservare ulteriori dati. In questo
algoritmo, quando una stringa di caratteri viene sottoposta alla BWT, nessuno di questi
cambia valore perché la trasformazione permuta solo il loro ordine.
La trasformata è fatta ordinando tutte le rotazioni del testo e poi prendendo soltanto l'ultima
colonna. Per esempio, il testo "^BANANA@" viene trasformato in "BNN^AA@A"
attraverso questi passi (i caratteri rossi ^ e @ indicano rispettivamente il puntatore di inizio
stringa e quello di fine stringa):
Rotazioni Ordine alfabetico
Passo1
1 ^ B A N A N A @
12
Passo2 1 A N A N A @ ^ B
2 @ ^ B A N A N A 2 A N A @ ^ B A N
3 A @ ^ B A N A N 3 A @ ^ B A N A N
4 N A @ ^ B A N A 4 B A N A N A @ ^
5 A N A @ ^ B A N 5 N A N A @ ^ B A
6 N A N A @ ^ B A 6 N A @ ^ B A N A
7 A N A N A @ ^ B 7 ^ B A N A N A @
8 B A N A N A @ ^ 8 @ ^ B A N A N A
Leggendo l’ultima colonna dall’alto verso il basso si ottiene la parola codificata.
BWT non genera un output più facilmente comprimibile dell'originale, anche
perché ciò si potrebbe ottenere mettendo semplicemente in ordine alfabetico i caratteri, ma
è la sua reversibilità il vero punto di forza: il documento originale si ricostruisce a partire
dai soli caratteri dell'ultima colonna.
Non si fa altro che ordinare alfabeticamente l’ultima colonna e aggiungere accanto la stessa
colonna, ripetendo il ciclo fino ad ottenere lo stesso numero di colonne. L’ultima riga
restituirà la parola decodificata.
14. Tratto, tradotto ed editato da Wikipedia
7. TRASFORMATA DISCRETA DEL COSENO
La trasformata discreta del coseno (DCT) è un algoritmo lossy ed è il più diffuso
metodo di compressione spaziale, capace di rilevare le variazioni di informazione tra
un’area e quella contigua, trascurando le ripetizioni.
La DCT è molto simile alla trasformata di Fourier, con la
differenza che fa solo uso di numeri reali. Infatti, esprime una
sequenza finita di punti espressi in somma di coseni
oscillanti a frequenze diverse. La DCT è uno degli strumenti
matematici più importanti nell’informatica perché trova
applicazioni non solo nella compressione dei file ma anche per le
soluzioni numeriche di equazioni differenziali.
Esempio di grafico prima e dopo l’applicazione della DCT, che concentra le
informazioni alle frequenze più basse
La DCT monodimensionale per una successione di lunghezza N, è espressa da:
𝐶𝐶(𝑢𝑢) = 𝛼𝛼(𝑢𝑢) � 𝑓𝑓(𝑥𝑥) cos �
𝜋𝜋(2𝑥𝑥 + 1)𝑢𝑢
2𝑁𝑁
�
𝑁𝑁−1
𝑥𝑥=0
𝑝𝑝𝑝𝑝𝑝𝑝 𝑢𝑢 = 0, … , 𝑁𝑁 − 1 𝑐𝑐𝑐𝑐𝑐𝑐 𝛼𝛼(𝑢𝑢) = �
�1/𝑁𝑁 𝑠𝑠𝑠𝑠 𝑢𝑢 = 0
�2/𝑁𝑁 𝑠𝑠𝑠𝑠 𝑢𝑢 ≠ 0
7.1 NOZIONI SULLE IMMAGINI
La precisione di un’immagine è espressa dal numero di bit usati per rappresentare
ogni pixel (bpp) e determina il numero di colori che l’immagine stessa può contenere. In
base alla precisione si possono classificare:
o immagini binarie, rappresentate da un 1bpp (2 colori)
o immagini computerizzate, rappresentate da 4bpp (16 colori)
o immagini su scala di grigio, rappresentate da 8bpp (256 colori)
o immagini a colori, rappresentate da 16 bpp o più
Per rappresentare il colore si possono usare due sistemi:
o RGB, dove il colore è ottenuto attraverso i tre colori primari (Red, Green,
Blue)
o HSI, dove il colore è scomposto in tre componenti: Hue, rappresentante il
colore, Saturation, rappresentante la quantità di colore presente, Intensity
indicante la luminosità; lo spazio in cui tre valori sono resi graficamente in un
bi-cono in cui l’asse centrale rappresenta i valori di grigio
15. Tratto, tradotto ed editato da Wikipedia
7.2 JPEG
JPEG, acronimo di Joint Photographic Expert Group, è il nome di un comitato che ha
definito il primo standard di compressione dell’immagine digitale a tono continuo,
sia a livelli di grigio che a colori (da 256 a 16.7 milioni)
Per la precisione, JPEG specifica solo come un’immagine possa essere trasformata in byte,
ma non come incapsulare questa informazione in un formato di file. JPEG si basa sull’uso
della DCT e della quantizzazione e analizza l’immagine pixel per pixel, da sinistra a destra
e dall’alto verso il basso.
CODIFICA
o i pixel vengono raggruppati in blocchi di 8x8
o gli elementi di ciascun blocco sono shiftati da interi
senza segno (nel range [0; 2𝑃𝑃
− 1]) a interi con segno (nel
range [−2𝑃𝑃
; 2𝑃𝑃
− 1]), con p = precisione dell’immagine
o ogni blocco è visto come un segnale discreto di 64
campioni, pertanto tale segnale viene inviato in input
alla trasformata FDCT (Forward Discrete Cosine
Transform), la cui definizione matematica è:
𝐹𝐹(𝑢𝑢, 𝑣𝑣) =
1
4
𝐶𝐶(𝑢𝑢)𝐶𝐶(𝑣𝑣) � � 𝑓𝑓(𝑥𝑥, 𝑦𝑦) cos
(2𝑥𝑥 + 1)𝑢𝑢𝑢𝑢
16
cos
(2𝑦𝑦 + 1)𝑣𝑣𝑣𝑣
16
7
𝑦𝑦=0
7
𝑥𝑥=0
con 𝐶𝐶(𝑢𝑢)𝐶𝐶(𝑣𝑣) così definiti:
𝐶𝐶(𝑢𝑢), 𝐶𝐶(𝑣𝑣) = �
1
√2
𝑠𝑠𝑠𝑠 𝑢𝑢, 𝑣𝑣 = 0
1 𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎 𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎
16. Tratto, tradotto ed editato da Wikipedia
L’output della FDCT è un insieme di 64
coefficienti che rappresentano le ampiezze dei
segnali base in cui il segnale originario è stato
scomposto. Il coefficiente con valore di
frequenza nullo è detto Direct Coefficient
(DC), i restanti sono gli Alternate
Coefficient (AC). Ciò che avviene con la
FDCT è una compressione dell’immagine dove
le componenti con più alta energia sono quelle
a bassa frequenza. I coefficienti DCT possono
essere visti come dei “pesi” associati a
opportune funzioni periodiche che formano una base nello spazio
delle frequenze e permettono di ricostruire così il segnale spaziale,
lavorando su blocchi 8x8.
Fino a questo punto, la compressione è lossless.
Esempio di compressione DCT
o i 64 coefficienti sono quantizzati uniformemente mediante la tabella di
quantizzazione di 64 elementi rappresentata da inter nell’intervallo [1-255];
ciò serve ridurre l’ampiezza di quei coefficienti il cui contributo è nullo
o molto basso sulla qualità dell’immagine (in pratica, si riduce il numero
di bit per campione); essendoci scarto di informazione, la compressione diventa
lossy
o si realizza un mapping molti-uno attraverso l’uso dell’equazione:
17. Tratto, tradotto ed editato da Wikipedia
𝐹𝐹𝑞𝑞(𝑢𝑢, 𝑣𝑣) = 𝑅𝑅𝑅𝑅𝑅𝑅𝑅𝑅𝑅𝑅 �
𝐹𝐹(𝑢𝑢, 𝑣𝑣)
𝑄𝑄(𝑢𝑢, 𝑣𝑣)
�
dove 𝑄𝑄(𝑢𝑢, 𝑣𝑣) rappresenta il coefficiente di quantizzazione associato al
coefficiente DCT; esso è costante per il DC e variabile per l’AC
Esempio di quantizzazione a livelli differenti
o tutti i coefficienti quantizzati vengono messi a
zig-zag in modo da porre per primi quelli a più
bassa frequenza
o si utilizza l’Entropy Encoder che comprime
maggiormente i coefficienti attraverso una
codifica di Huffman; ogni AC viene
rappresentato da una coppia
(RUNLENGHT, SIZE) dove il primo
rappresenta il numero di valori nulli che
precedono un valore non nullo, mentre il secondo rappresenta il numero
di bit necessari a codificare l’ampiezza; ogni DC, invece, viene
rappresentato da (SIZE, AMPLITUDE)
o la sequenza di simboli generata viene convertita in binario
DECODIFICA
o l’Entropy Encoder rigenera i simboli in coppia a partire dai bit
o si trasformano i coefficienti quantizzati in coefficienti DCT attraverso
l’equazione:
𝐹𝐹𝑞𝑞
𝐼𝐼 𝐼𝐼 𝐼𝐼(𝑢𝑢, 𝑣𝑣) = 𝐹𝐹𝑞𝑞(𝑢𝑢, 𝑣𝑣)𝑄𝑄(𝑢𝑢, 𝑣𝑣)
o si trasformano i coefficienti in pixel, usando la formula:
𝑓𝑓(𝑥𝑥, 𝑦𝑦) =
1
4
� � 𝐶𝐶(𝑢𝑢)𝐶𝐶(𝑣𝑣)𝐹𝐹(𝑢𝑢, 𝑣𝑣) cos
(2𝑥𝑥 + 1)𝑢𝑢𝑢𝑢
16
cos
(2𝑦𝑦 + 1)𝑣𝑣𝑣𝑣
16
7
𝑦𝑦=0
7
𝑥𝑥=0
18. Tratto, tradotto ed editato da Wikipedia
7.3 MPEG
MPEG, acronimo di Moving Picture Expert Group, è un insieme di diversi standard
ciascuno dei quali si occupa di una particolare codifica. I principali sono i seguenti:
MPEG-1 1993 Compressione audio/video limitata a 1.5 Mbit/s, creata per
archiviazione digitale su CD; qualità simile alle VHS
MPEG-2 1995 Compressione audio/video di alta qualità con supporto al Blue-Ray;
adoperamento di tecniche di interlacciamento; usato per le televisioni
MPEG-4 1998 Miglioramento di MPEG-2 per l’ottimizzazione di grafica
computerizzata
CODIFICA MPEG-2
Il flusso video MPEG-2 è organizzato con una sintassi stratificata. In una struttura
gerarchica "dall'alto in basso", la video sequenza è partizionata in gruppi multipli di
immagini (GOPs), che rappresentano degli insiemi di video-trame contigui nell'ordine di
trasmissione. Lo strato successivo è costituito da una trama singola, composta da più
porzioni.
Ogni porzione contiene uno o più macro blocchi, consistenti di quattro blocchi di luminanza
(Y) e di due blocchi di crominanza (U,V). Infine, il blocco è l'unità di base di codifica di
dimensione 8x8 pixel. Per ottenere un alto rapporto di compressione, ci si serve sia delle
ridondanze spaziali che di quelle temporali.
La ridondanza spaziale viene ridotta dall'utilizzo di un sotto-campionamento delle
componenti di crominanza(U,V), in accordo con la sensibilità dell'apparato visivo umano.
Successivamente, viene utilizzata la DCT sui blocchi con componenti Y e U,V. I coefficienti
DCT sono quantizzati e infine codificati utilizzando un codice a lunghezza variabile. Il
sotto-campionamento si compone di una tripla:
(𝑎𝑎: 𝑏𝑏: 𝑐𝑐)
dove 𝑎𝑎 = campionamento orizzontale della componente di luminanza,
𝑏𝑏 = sottocampionamento orizzontale delle componenti CB e CR rispetto al
19. Tratto, tradotto ed editato da Wikipedia
campionamento operato sulla luminanza, c = sotto-campionamento verticale. La
struttura 4:2:0 è solitamente usata in JPEG, 4:2:2 in MPEG-2.
La ridondanza temporale viene ridotta da una predizione temporale di alcune trame
derivate da altre trame moto-compensate. L'errore di predizione viene quindi
codificato.
CODIFICA MPEG-4
Questa codifica si basa su tre assunti fondamentali: accesso casuale ai dati, codifica
ibrida e manipolazione del bit-stream in uscita. La metodologia usata offre grande
robustezza agli errori e scalabilità del contenuto. Per la precisione, MPEG-4 considera una
scena come se fosse composta da video-oggetti, ognuno con un proprio momento,
20. Tratto, tradotto ed editato da Wikipedia
struttura e contorno. Ogni oggetto è codificato con una diversa stringa di bit.
L’informazione è strutturata su più layer:
o Video Session (VS):
compone le sequenze video
incorporando oggetti dalle
altre 3 classi
o Video Object (VO):
oggetto all’interno di una
scena
o Video Object Layer
(VOL): esalta la risoluzione
spaziale e temporale di
ciascun VO
o Video Object Plane (VOP): è un’istanza di VO in un dato istante
Un elemento di un layer superiore può contenere più di livello inferiore. I frame possono
essere codificati in due modalità: interframe, con riferimento alle immagini precedenti, e
intraframe, indipendente dagli altri frame. I VOP, quindi, possono essere distinti in:
o I-trama: codificati senza nessun
riferimento alle altre trame
o P-trama: codificati riferendosi al
precedente I o P
o B-trama: codificati riferendosi sia alle
trame precedenti che successive
L’ordine con cui le trame video vengono trasferite è: I-P-
B-B-B-P-B-B-B. Il decoder deve semplicemente
riordinare le trame secondo l’ordine di display.
La predizione consiste nel fornire un vettore di movimento
(motion-vector) che dichiari come gli oggetti si sono spostati dal
quadro I al quadro P. Il MV è parte dello stream MPEG ed è suddiviso
in una parte orizzontale ed una verticale. Un valore positivo corrisponde
ad un movimento a destra o verso il basso; uno negativo ad uno
spostamento a sinistra o in alto rispettivamente.
Il modello precedente assume che ogni differenza tra oggetti
può essere resa disponendo i pixel in zone differenti. In realtà
ciò non è sempre vero. �Per ovviare a tale inconveniente si
21. Tratto, tradotto ed editato da Wikipedia
utilizza una matrice di compensazione dell’errore di predizione.
22. Tratto, tradotto ed editato da Wikipedia
8. ARTEFATTO DI COMPRESSIONE
Un artefatto di compressione è il risultato di una compressione di dati aggressiva
applicata a file immagine, audio o video, che rende il risultato sgradevole all’utente. Gli
artefatti in dati time-dependent, come audio o video, sono spesso dati dall’errore
risiedente nel processo di compressione con perdite (conseguenza della
quantizzazione).
Esempi di artefatti, in JPEG, sono la
comparsa di contorni in zone uniformi, un
rumore scalettato lungo i bordi o un effetto
scacchiera nelle regioni più dense. In MPEG,
si può generare un “effetto dipinto” dove
diversi frame contigui contengono lo stesso
difetto, portando quindi a una sensazione di
pittura della schermata.
Esempio di effetto dipinto
Diversi approcci sono stati proposti per ridurre gli effetti della compressione sulle immagini,
ma per poter utilizzare tecniche standard di compressione/decompressione e
mantenere i benefici della compressione (per esempio, minori costi di trasmissione
e di immagazzinamento), molti di questi metodi si concentrano sul "post processing",
cioè, nella rielaborazione delle immagini in fase di ricezione o visualizzazione. Nessuna di
queste tecniche di post-processing ha dimostrato di essere efficace in tutte le situazioni e di
conseguenza nessuna tecnica ha ottenuto un largo consenso.
8.1 ARTEFATTO NELLA CODIFICA AUDIO
Gli artefatti peggiori sono quelli nei file audio, perché possono disturbare l’udito. Per ridurli,
si può ridurre il bitrate campionando a una frequenza più bassa: ciò significa
rimuovere le frequenze più elevate (affinché si rispetti il teorema di Shannon); se il filtro
anti-aliasing non funzionasse correttamente ci sarebbe sovrapposizione di
frequenze nell’intorno della frequenza di Nyquist.
23. Tratto, tradotto ed editato da Wikipedia
Esempio di alias e anti-alias nelle immagini; l'alias è la scalettatura visiva dei bordi che si verifica in
un'immagine quando la risoluzione dell'immagine è troppo bassa; l'anti-alias è l'attenuazione dei bordi
scalettati nelle immagini digitali che si ottiene facendo una media dei colori dei pixel del bordo.
Un'altra tecnica consiste nel cercare di rimuovere suoni che l'orecchio umano
tipicamente non può percepire. Se una persona non può percepire la differenza, i dati
risultanti saranno più semplici (e di conseguenza potranno avere una compressione migliore
usando tecniche lossless). Per esempio, l'orecchio umano in genere non è in grado di percepire
un suono debole simultaneamente ad un suono simile ma di intensità maggiore. Una tecnica
di compressione con perdita può identificare questo suono debole e cercare di rimuoverlo.
Poiché nessun algoritmo è perfetto e altri compromessi possono essere applicati per eliminare
dati aggiuntivi per ridurre il data rate, ciò comporta in alcuni casi l'eliminazione di
suoni percepibili. Ma poiché questi suoni sono comunque teoricamente difficili da
percepire, il risultato sarà generalmente di suono appiattito, o "sporcato".
24. Tratto, tradotto ed editato da Wikipedia
9. COMPRESSIONE FRATTALE
La compressione frattale è un metodo di compressione lossy utilizzato per la compressione
delle immagini attraverso l’utilizzo dei frattali. Il metodo ha una efficacia maggiore
nel momento in cui le immagini raffigurano paesaggi naturali, diventando migliore anche
della compressione JPEG. Tuttavia, tale metodologia non ha mai raggiunto un uso diffuso:
in primis, per una questione di brevetti e poi, perché è un metodo di compressione molto
lento. Inoltre, la compressione frattale richiede un consistente intervento umano: il
processo è semplice da automatizzare ma per nulla semplice da ottimizzare; questo perché le
immagini naturali hanno proprietà matematiche eterogenee.
È dimostrato da un teorema (teorema di Barnsley) che per ogni immagine naturale
esiste una rappresentazione frattale semplice, ma non fornisce un algoritmo generale
per la costruzione di queste rappresentazioni.
Attualmente, la compressione frattale è diventata ancora meno usata per via della
conversione wavelet, ovvero la rappresentazione di un segnale mediante l'uso di una
forma d'onda oscillante di lunghezza finita o a decadimento rapido.