310 likes | 484 Views
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
E N D
Algorytm kodowania arytmetycznego Algorytmy Kompresji Danychwykład 4Roman 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, • alfabet źródła to {a, b, c} • Używamy stałego modelu danych • P(a)=0.6, P(b)=0.2, P(c)=0.2 • Przyjmujemy, iż długość ciągu jest znana dekoderowi.
0.6 1 c 0.8 b 0.6 a 0 0 • 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)
0.6 0.48 1 c c 0.8 0.48 b b 0.6 0.36 a a 0 0 0.36 • 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)
0.6 0.432 0.36 1 0.48 c c c 0.48 0.456 0.8 b b b 0.432 0.36 0.6 a a a 0.36 0 0 • 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)
0.6 0.42624 1 0.48 0.432 0.432 c c c c c 0.48 0.42912 0.4176 0.456 0.8 b b b b b 0.4032 0.6 0.432 0.36 0.42624 a a a a a 0.4176 0.36 0 0 0.36 0.4176 ... 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 • np. 0.42 • (a wiedząc, że liczba jest z [0, 1), wystarczy wyprowadzić znaczące cyfry mantysy: 42)
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
0.6 0.42 0.42 0.42 0.42 0.42 0.42624 0.432 0.432 1 0.48 c c c c c 0.456 0.48 0.4176 0.8 0.42912 b b b b b 0.42624 0.6 0.4032 0.432 0.36 a a a a a 0.4176 0 0.4176 0.36 0.36 0 • 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: • 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
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
0.6 0.42624 1 0.48 0.432 0.432 c c c c c 0.48 0.42912 0.4176 0.456 0.8 b b b b b 0.4032 0.6 0.432 0.36 0.42624 a a a a a 0.4176 0.36 0 0 0.36 0.4176 Koncepcja implementacji dla liczb o ograniczonej precyzji • Część cyfr liczby z wnętrza przedziału można wyprowadzać już w trakcie kodowania • 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)
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=2m –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 odejmijM/2 od obu kresów bieżącegoPrzedziału elseif bieżącyPrzedział [M/4, 3M/4] licznikBitów ++ // tylko tu możliwe przepełnienie odejmijM/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 ++ ifkres 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 arytmetycznyMQ 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 • unikamy mnożenia; nowa długość podprzedziału dla prawdopodobieństwa p, to nie p * stara_długość, a po prostu p • 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 • 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 • co nadal jest szybkie • 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 • 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 n-arnym; gdy aktualna długość przedziału spada • wyprowadzamy pokrywające się najbardziej znaczące cyfry kresów • odpowiednio skalujemy przedział mnożąc jego kresy przez podstawę systemu liczbowego • wygląda znajomo? • 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 • 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] • komórka 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 • np. i = 6 = 110b, j = 1 • 2j = i & – i
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 siO(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 siO(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)