Kodowanie Shanona-Fano Kodowanie Huffmana

advertisement
Algorytm kodowania
arytmetycznego
Algorytmy Kompresji Danych
wykład 4
Roman Starosolski
Plan wykładu

Historia kodowania arytmetycznego

Idea kodowania arytmetycznego

Koncepcja implementacji dla liczb o ograniczonej
precyzji

Wybrane algorytmy



MQ-Coder
Range-Coder
Szybki model dla kodera arytmetycznego
Historia kodowania arytmetycznego

Historia



Shannon (1948) ― „Podstawowe twierdzenie ...”, wzmianka o
możliwości kodowania arytmetycznego
Elias (196x) ― dalsze prace
Jelinek (1968) ― dalsze prace

Rissanen i Pascoe (1976, niezależnie) ― idea implementacji w
arytmetyce stałopozycyjnej

IBM (198x) ― algorytmy zorientowane na sprzętowe kodowanie
alfabetu binarnego (w większości opatentowane)
Witten (1987) i inni ― implementacje programowe dla alfabetów
binarnych i nie-binarnych

„Podstawowe twierdzenie Shannona
o kodowaniu bezszumowym”
(jedna z postaci twierdzenia, wg.: Drozdek: „Wprowadzenie do kompresji danych”)
Dla bezpamięciowego źródła S o entropii H(S) możliwe
jest przypisanie ciągom k symboli źródła, słów kodu
przedrostkowego tak, że spełnione jest
H(S) ≤ Lk / k < H(S) + 1 / k



asymptotycznie, możliwe jest uzyskanie średniej długości kodu
(w przeliczeniu na pojedynczy symbol) równej entropii źródła
optymalna długość słowa kodowego dla symbolu o
prawdopodobieństwie p równa jest –log (p)
(czyli autoinformacji dla tego symbolu)
można zbudować koder entropijny o efektywności bliskiej 100%
Idea kodowania arytmetycznego

Cały ciąg zostanie zakodowany za pomocą jednej liczby rzeczywistej z
lewostronnie domkniętego przedziału zawartego w przedziale [0, 1)



(Jeżeli długość ciągu nie jest znana dekoderowi, to należy ją przetransmitować,
bądź uzupełnić alfabet źródła o symbol oznaczający koniec ciągu.)
Zaczynamy od przedziału [0, 1)
Czytając kolejne symbole ciągu zawężamy stopniowo początkowy
przedział;
dla kolejnego symbolu pobranego z ciągu
przedział dzieli się na lewostronnie domknięte podprzedziały o długościach
wprost proporcjonalnych do prawdopodobieństw poszczególnych symboli
alfabetu źródła
 wybiera się przedział odpowiadający odczytanemu symbolowi.


Wyprowadzamy dowolną liczbę z przedziału wyznaczonego dla ciągu
Przykład

komdujemy ciąg abaca,


Używamy stałego modelu danych


alfabet źródła to {a, b, c}
P(a)=0.6, P(b)=0.2, P(c)=0.2
Przyjmujemy, iż długość ciągu jest znana dekoderowi.

Zaczynamy od przedziału [0, 1). Dla kolejnego symbolu z ciągu
odczytaliśmy a

... przedział dzieli się na lewostronnie domknięte podprzedziały o długościach wprost
proporcjonalnych do prawdopodobieństw poszczególnych symboli alfabetu źródła,
a: [0, 0.6)

b: [0.6, 0.8)
c: [0.8, 1)
... wybiera się przedział odpowiadający temu symbolowi.
przedział dla ciągu a: [0, 0.6)
1
0.6
c
0.8
b
0.6
a
0
0

przedział dla ciągu a: [0, 0.6). Dla kolejnego symbolu z ciągu
odczytaliśmy b

... przedział dzieli się na lewostronnie domknięte podprzedziały o długościach wprost
proporcjonalnych do prawdopodobieństw poszczególnych symboli alfabetu źródła,
aa: [0, 0.36)

ab: [0.36, 0.48)
ac: [0.48, 0.6)
... wybiera się przedział odpowiadający temu symbolowi.
przedział dla ciągu ab: [0.36, 0.48)
1
0.6
c
c
0.8
0.48
b
b
0.6
0.36
a
a
0
0
0.48
0.36

przedział dla ciągu ab: [0.36, 0.48). Dla kolejnego symbolu z ciągu
odczytaliśmy a

... przedział dzieli się na lewostronnie domknięte podprzedziały o długościach wprost
proporcjonalnych do prawdopodobieństw poszczególnych symboli alfabetu źródła,
aba: [0.36, 0.432)

abb: [0.432, 0.456)
abc: [0.456, 0.48)
... wybiera się przedział odpowiadający temu symbolowi.
przedział dla ciągu aba: [0.36, 0.432)
1
0.6
0.48
c
c
c
0.8
0.48
b
b
0.6
0.36
a
a
0
0
0.432
0.456
b
0.432
a
0.36
0.36
... po przetworzeniu całego ciągu abaca

otrzymujemy przedział dla ciągu abaca: [0.4176, 0.432)

Wyprowadzamy dowolną liczbę z przedziału wyznaczonego dla ciągu
1

np. 0.42

(a wiedząc, że liczba jest z [0, 1), wystarczy wyprowadzić znaczące cyfry
mantysy: 42)
0.6
0.48
c
c
c
0.8
0.48
b
b
0.6
0.36
a
a
0
0
0.456
b
0.432
0.432
c
0.4176
b
0.4032
a
a
0.36
0.36
0.432
0.42624
c
0.42912
b
0.42624
a
0.4176
0.4176

Przedziału uzyskany po wykonaniu algorytmu
 długość
przedziału równa jest prawdopodobieństwu
wygenerowania odczytanego ciągu
 dla
każdego możliwego ciągu o długości n symboli
otrzymamy inny przedział


przedziały te nie pokrywają się
ich suma jest przedziałem [0, 1)
 dla
podprzedziału o długości p można znaleźć taką
liczbę, że binarne zakodowanie jej mantysy wymagać
będzie nie więcej niż
 –log(p)  + 1 bitów

Dekodowanie

Znając rozkład prawdopodobieństwa symboli alfabetu i długość
ciągu,
pobieramy zakodowany ciąg, czyli kod 42 → liczba 0.42
 zaczynamy od przedziału [0, 1)
 cyklicznie, aż nie zdekodujemy ciągu o danej długości:




1
analogicznie do kodowania, dzielimy przedział na podprzedziały dla
wszystkich symboli alfabetu
wybieramy podprzedział który zawiera liczbę 0.42
wyprowadzamy symbol odpowiadający temu podprzedziałowi
0.6
0.48
c
c
c
0.8
0.48
b
b
0.6
0.36
0.432
c
0.456
0.42
0.4176
b
b
0.432
0.4032
0.432
0.42
0.42624
c
0.42912
b
0.42624
0.42
a
a
a
a
a
0.42
0.42
0
0
0.36
0.36
0.4176
0.4176
Cechy kodowania arytmetycznego

efektywność bliska 100%, asymptotycznie 100%


bez ograniczeń co do rozkładu prawdopodobieństwa symboli
(ani co do rozmiaru alfabetu)
niezależność kodera od modelu
można stosować różne modele probabilistyczne
 można stosować wiele modeli


nadaje się do algorytmów adaptacyjnych


(trzeba uzupełnić alfabet o symbol EOF)
nie da się go zaimplementować wprost dla liczb o ograniczonej
precyzji
Koncepcja implementacji dla liczb
o ograniczonej precyzji

Część cyfr liczby z wnętrza przedziału można wyprowadzać już w trakcie kodowania
1

gdy początkowe cyfry górnego i dolnego kresu przedziału są takie same,
to takie będą również cyfry każdej liczby wewnątrz przedziału

można więc te cyfry wyprowadzić

(i odpowiednio przeskalować przedział aby korzystać z pełnej dostępnej dokładności)
0.6
0.48
c
c
c
0.8
0.48
b
b
0.6
0.36
a
a
0
0
0.456
b
0.432
0.432
c
0.4176
b
0.4032
a
a
0.36
0.36
0.432
0.42624
c
0.42912
b
0.42624
a
0.4176
0.4176
Koncepcja implementacji dla liczb o
ograniczonej precyzji

Model danych



model oparty o liczby całkowite zamiast prawdopodobieństw
zlicza liczby wystąpień symboli
do wyznaczenia podprzedziału dla kolejnego symbolu wystarczy
znać jego prawdopodobieństwo oraz łączne
prawdopodobieństwo wszystkich symboli poprzedzających go w
alfabecie
ponieważ łatwiej to robić w modelu, zazwyczaj to model
wyznacza skumulowane prawdopodobieństwa
Pi = ∑ki= 1 pi ,
gdzie pi to prawdopodobieństwo symbolu si , tj. i-tego symbolu
alfabetu; tu symbole numerujemy od 1
Koncepcja implementacji dla liczb o
ograniczonej precyzji

Reprezentacja przedziału

kresy pamiętamy na m-bitowych liczbach całkowitych, M = 2 m –1

przedziały traktujemy jako obustronnie domknięte,




kres górny danego podprzedziału jest o 1 mniejszy od kresu
dolnego następnego podprzedziału
w przypadku skalowania przedziału trzeba zadbać aby powyższa
własność została zachowana (po pomnożeniu kresów przez 2
odległość między nimi wzrośnie do 2)
zaczynamy od przedziału [0, M], tj. [0000...0 i 1111...1]
prawdopodobieństwo symbolu wyznaczone przez model nie może
spowodować wyznaczenia przedziału o długości 0
Normalizacja podprzedziału zawartego całkowicie w
górnej lub dolnej połówce przedziału [0, M]

wyprowadź 1 (górna), lub 0 (dolna) i przeskaluj

co zrobić gdy podprzedział zawiera środek przedziału [0, M]?
Normalizacja podprzedziału zawierającego środek
przedziału [0, M]

koduj alfabet binarny, to nie będzie takich problemów (przy
odpowiedniej normalizacji), albo

gdy długość podprzedziału będzie mniejsza od M/2


przeskaluj
zliczaj przeskalowania, ale nie wyprowadzaj bitów
(na podstawie: A. Drozdek, Wprowadzenie do kompresji danych, WNT, Warszawa, 1999)

Kodujemy symbol si (i-ty symbolu alfabetu), aktualny przedział to [L, R]
bieżącyPrzedział = [

L + Pi-1(R – L + 1) , L + Pi (R – L + 1)
]
Normalizacja przedziału (gdy przedział jest mały wykonywana kilkakrotnie)
while(1)
if bieżącyPrzedział  [0, M/2]
zwróc 0 i licznikBitów jedynek
licznikBitów = 0
elseif bieżącyPrzedział  [M/2, M]
zwróc 1 i licznikBitów zer
licznikBitów = 0
odejmij M/2 od obu kresów bieżącegoPrzedziału
elseif bieżącyPrzedział  [M/4, 3M/4]
licznikBitów ++ // tylko tu możliwe przepełnienie
odejmij M/4 od obu kresów bieżącegoPrzedziału
else
break
endif
pomnóż przez 2 oba kresy bieżącegoPrzedziału
dodaj 1 do górnego kresu bieżącegoPrzedziału
endwhile
(na podstawie: A. Drozdek, Wprowadzenie do kompresji danych, WNT, Warszawa, 1999)

Zakończenie kodowania
(wyprowadź resztę „stanu” kodera arytmetycznego,
czyli końcówkę rozwinięcia binarnego liczby z bieżącego przedeziału)
licznikBitów ++
if kres dolny bieżącegoPrzedziału < M/4
zwróc 0 i licznikBitów jedynek
else
zwróc 1 i licznikBitów zer
endif
Wybrane Implementacje

Witten, Neal, Cleary (1987)
(tzw. CACM, zasada działania pokazana na poprzednich slajdach)
ftp://ftp.cpsc.ucalgary.ca/pub/projects/ar.cod/cacm-87.shar

Moffat, Neal, Witten (1998)
(udoskonalony CACM, bez dzielenia, mniej mnożeń, shift, +, –)
http://www.cs.mu.oz.au/~alistair/arith_coder/

MQ Coder
(binarny, opatentowany /IBM i in./, użyty w JBIG2, JPEG2000 i innych)

Range Coder
(szybki, prosty, alfabety wielosymbolowe)
http://www.compressconsult.com/rangecoder/
Binarny koder arytmetyczny
MQ Coder

Kodujemy symbole alfabetu binarnego

bardzo proste modelowanie,



nie trzeba liczyć prawdopodobieństwa kumulatywnego
wystarczy szacować prawdopodobieństwo tylko jednego symbolu
bardzo proste kodowanie


nowy symbol to zmiana tylko jednego kresu przedziału
podprzedział o długości M/2 zawsze jest całkowicie zawarty w górnej lub
dolnej połówce przedziału [0, M] (przy odpowiedniej normalizacji)
MQ Coder – dalsze uproszczenia

normalizacja utrzymuje długość podprzedziału między 0.75 a 1.5, średnio 1.0


faktycznie kodujemy alfabet {MPS, LPS} – czyli bardziej i mniej prawdopodobny
symbol; to, czy MPS to 0 czy 1 jest określa flaga uaktualniana po każdym symbolu



unikamy mnożenia; nowa długość podprzedziału dla prawdopodobieństwa p,
to nie p * stara_długość, a po prostu p
pojawienie się LPS to zawsze skalowanie i wyprowadzenie bitu
MPS może, ale nie musi powodować skalowania i wyprowadzania bitów
model szacuje tylko prawdopodobieństwo LPS
jest ściśle powiązany z koderem i jest aktualizowany tylko w razie normalizacji
prawdopodobieństwo LPS pamiętane z niewielką precyzją (np. 7 bitów)
precyzja jednocześnie odpowiada za adaptacyjność modelu (szybkość zapominania)
 aktualizacja modelu również bez mnożeń,
stablicowane prawdopodobieństwa po normalizacji wywołanej przez LPS/MPS
(2 tablice indeksowane prawdopodobieństwem LPS)


MQ Coder

bardzo szybki

dla większych alfabetów symbol kodowany jest jako ciąg bitów


niewielkie wymagania pamięciowe

szczególnie istotne dla modelu; model dla kontekstu pamięta jedynie:

prawdopodobieństwo LPS (np. 7 bitów)
który bit jest LPS (1 bit)

można stosować złożone modele z wielką liczbą kontekstów


co nadal jest szybkie
mimo uproszczeń, efektywność kodowania bliska 100%
Range Coder – koncepcja

Martin (1979)

Koncepcja opracowana niezależnie od klasycznego kodowania
arytmetycznego

podział pewnego przedziału liczb analogicznie jak w kodowaniu
arytmetycznym, ale



przedział liczb całkowitych
odpowiednio duży przedział, np. [0, 1000000]
po przetworzeniu ciągu wyprowadzamy najbardziej znaczące cyfry pewnej
liczby z wnętrza przedziału
(nie wszystkie, wystarczające do jednoznacznego określenia przedziału)
Range Coder – realizacja

Zakodowanie ciągów dłuższych niż bardzo krótkie, wymagałoby przedziału zbyt
wielkiego by jego krańce reprezentować wprost jako liczby

używamy przedziału reprezentowalnego jako liczba o stałej precyzji w systemie narnym; gdy aktualna długość przedziału spada




Otrzymujemy koder podobny do kodera CACM

inna (szybsza) operacja normalizacji


nadal nie trywialna, mogą wystąpić niedomiary, zliczmy je (analogia do licznikaBitów)
alfabet kodu to cyfra w systemie n-arnym



wyprowadzamy pokrywające się najbardziej znaczące cyfry kresów
odpowiednio skalujemy przedział mnożąc jego kresy przez podstawę systemu liczbowego
wygląda znajomo?
n=256, a więc we/wy bajtowe, prostsze i szybsze niż bitowe w CACM
precyzja podziału przedziału na podprzedziały typowo mniejsza niż np. w CACM
Shindler: http://www.compressconsult.com/rangecoder
(koder, dekoder, prosty model bezpamięciowy, przykłady)
Szybki model dla kodera
arytmetycznego

Potrzebne operacje



dla si wyznacz Pi-1
dla si wyznacz pi
dla Pi-1 wyznacz si (dekodowanie)
powyższe łatwe do zrealizowania, gdy potrafimy szybko wyznaczyć
Ci – łączną liczbę wystąpień symboli sx, x ≤ i


uaktualnij model (po przetworzeniu si)
podziel liczniki w modelu (zwykle przez 2)
Struktura Fenwicka

dla alfabetu n symboli, tablica T [0..n-1]
o indeksie i zawiera sumę liczb wystąpień
symboli od i – 2j + 1 do i
gdzie j to pozycja najmniej znaczącej jedynki w
binarnie zakodowanym i
 komórka

np. i = 6 = 110b, j = 1

2j = i & – i
Struktura Fenwicka
i
i binarnie
j
2j
suma liczników w T [i]
0
0000
0
1
0–0
1
0001
0
1
1–1
2
0010
1
2
1–2
3
0011
0
1
3–3
4
0100
2
4
1–4
5
0101
0
1
5–5
6
0110
1
2
5–6
7
0111
0
1
7–7
8
1000
3
8
1–8
9
1001
0
1
9–9
10
1010
1
2
09 – 10
11
1011
0
1
11 – 11
12
1100
2
4
09 – 12
Struktura Fenwicka

Fenwick
 złożoność
pamięciowa: n
 złożoność czasowa
dla si wyznacz Pi-1
O(log2n)
 dla si wyznacz pi
O(1)
 dla Pi-1 wyznacz si
O(log2n)
 uaktualnij model
O(log2n)
 podziel liczniki w modelu O(log2n)

Struktura Moffata

W tablicy sumowanie „w przód”
 złożoność
pamięciowa: n (z sortowaniem 3n)
 złożoność czasowa
dla si wyznacz Pi-1
O(log2i)
 dla si wyznacz pi
O(1)
 dla Pi-1 wyznacz si
O(log2i)
 uaktualnij model
O(log2i)
 podziel liczniki w modelu O(log2n)

(w praktyce struktury podobnie szybkie, Moffata będzie szybsza gdy alfabet będzie
naturalnie posortowany wg rosnących częstości występowania symboli)
Download