LZW.html

 
ca de en es fr it nl no pl pt ru ro fi sv tr vo


 

Lempel-Ziv-Welch (skracane zwykle do LZW) – metoda strumieniowej bezstratnej kompresji słownikowej, będąca modyfikacją metody LZ78.

Pomysłodawcą algorytmu jest Terry A. Welch. Metodę opisał w 1984 roku, w artykule "A technique for high-performance data compression" opublikowanym w numerze 6. Computer (str. 8-19).

Metoda LZW jest względnie łatwa do zaprogramowania, daje bardzo dobre rezultaty. Wykorzystywany jest m.in. w programach ARC, PAK i UNIX-owym compress, w formacie zapisu grafiki GIF, w formatach PDF i Postscript (filtry kodujące fragmenty dokumentu) oraz w modemach (V.32bis). LZW było przez pewien czas algorytmem objętym patentem, co było przyczyną podjęcia prac nad nowym algorytmem kompresji obrazów, które zaowocowały powstaniem formatu PNG.

Spis treści

edytuj Zmiany w stosunku do LZ78

Przewaga LZW nad LZ78 to krótsze wyjście kodera: w metodzie LZ78 na wyjście wypisywane są pary (kod, symbol s), w metodzie LZW wypisuje się tylko kod.

Uzyskano to dzięki pierwszemu etapowi algorytmu, tj. wstępnemu wypełnieniu słownika alfabetem (wszystkimi symbolami). W metodzie LZ78 słownik początkowo jest pusty i przy kodowaniu trzeba brać pod uwagę, czy wczytany symbol s znajduje się w słowniku, czy może pojawił się po raz pierwszy.

edytuj Algorytm kompresji (kodowania)

W pojedynczym kroku algorytmu wyszukiwany jest w słowniku najdłuższy prefiks niezakodowanych jeszcze danych. Na wyjście wypisywany jest wówczas kod związany z tym słowem, zaś do słownika dodawana nowa pozycja: konkatenacja słowa i pierwszej niedopasowanej litery.

Algorytm przebiega następująco:

  1. Wypełnij słownik alfabetem źródła informacji.
  2. c := pierwszy symbol wejściowy
  3. Dopóki są dane na wejściu:
    • Wczytaj znak s.
    • Jeżeli ciąg c + s znajduje się w słowniku, przedłuż ciąg c, tj. c := c + s
    • Jeśli ciągu c + s nie ma w słowniku, wówczas:
      • wypisz kod dla c (c znajduje się w słowniku)
      • dodaj ciąg c + s do słownika
      • przypisz c := s.
  4. Na końcu wypisz na wyjście kod związany c.

O efektywności kompresji w dość dużym stopniu decyduje sposób zapisu kodów (liczb).

edytuj Algorytm dekompresji

Algorytm dekompresji jest nieco bardziej złożony, bowiem dekoder musi wykryć przypadki ciągów scscs (które nie znajdują się w słowniku), gdzie ciąg sc jest już w słowniku, a podciąg c jest dowolny, być może także pusty.

  1. Wypełnij słownik alfabetem źródła informacji.
  2. pk := pierwszy kod skompresowanych danych
  3. Wypisz na wyjście ciąg związany z kodem pk, tj. słownikpk
  4. Dopóki są jeszcze jakieś słowa kodu:
    • Wczytaj kod k
    • pc := słownikpk - ciąg skojarzony z poprzednim kodem
    • Jeśli słowo k jest w słowniku, dodaj do słownika ciąg (pc + pierwszy symbol ciągu słownikk), a na wyjście wypisz cały ciąg słownikk.
    • W przeciwnym razie (przypadek scscs) dodaj do słownika ciąg (pc + pierwszy symbol pc) i tenże ciąg wypisz na wyjście.
    • pk := k

edytuj Modyfikacje algorytmu

  • LZMW (V. Miller, M. Wegman, 1985) - zamiast dodawać do słownika słowa przedłużone o jedną literę, dodaje się połączenie poprzedniego i bieżącego słowa. Tzn. jeśli we wcześniejszej iteracji np. dopasowano słowo "wiki", natomiast w bieżącej znaleziono w słowniku prefiks "pedia", od razu dodawane jest słowo "wikipedia". W klasycznym LZW najprawdopodobniej (zależy to danych) w kilku krokach algorytmu dodane do słownika zostałyby "wikip", następnie "wikipe", itd.
  • LZAP (James Storer, 1988) - modyfikacja LZMW, w której oprócz konkatenacji poprzedniego i bieżącego słowa, dodaje się konkatenację wszystkich prefiksów bieżącego słowa (skrót AP pochodzi od all prefixes - wszystkie prefiksy). Czyli dla wcześniejszego przykładu zostaną dodane oprócz "wikipedia" także "wikip", "wikipe", "wikiped" oraz "wikipedi". To powoduje szybsze powiększanie słownika, nawet takimi słowami, które mogą nigdy nie pojawić się w kodowanych danych.

edytuj Przykład kompresji

Zostanie zakodowany ciąg składający się z 24 znaków: "abccd_abccd_acd_acd_acd_".

poprzedni ciąg (c) bieżący symbol (s) s + c indeks słownik komentarz

1. a
2. b
3. c
4. d
5. _

inicjalizacja słownika alfabetem
a b ab 1 - indeks 'a' 6. ab do słownika dodawany jest ciąg 'ab', c = 'b'
b c bc 2 - indeks 'b' 7. bc do słownika dodawany jest ciąg 'bc', c = 'c'
c c cc 3 - indeks 'c' 8. cc do słownika dodawany jest ciąg 'cc', c = 'c'
c d cd 3 - indeks 'c' 9. cd do słownika dodawany jest ciąg 'cd', c = 'd'
d _ d_ 4 - indeks 'd' 10. d_ do słownika dodawany jest ciąg 'd_', c = '_'
_ a _a 5 - indeks '_' 11. _a do słownika dodawany jest ciąg '_a', c = 'a'
a b ab 'ab' istnieje w słowniku
ab c abc 6 - indeks 'ab' 12. abc do słownika dodawany jest ciąg 'abc', c = 'c'
c c cc 'cc' istnieje w słowniku
cc d ccd 8 - indeks 'cc' 13. ccd do słownika dodawany jest ciąg 'ccd', c = 'd'
d _ d_ 'd_' istnieje w słowniku
d_ a d_a 10 - indeks 'd_' 14. d_a do słownika dodawany jest ciąg 'd_a', c = 'a'
a c ac 1 - indeks 'a' 15. ac do słownika dodawany jest ciąg 'ac', c = 'c'
c d cd 'cd' istnieje w słowniku
cd _ cd_ 9 - indeks 'cd' 16. cd_ do słownika dodawany jest ciąg 'cd_', c = '_'
_ a _a '_a' istnieje w słowniku
_a c _ac 11 - indeks '_a' 17. _ac do słownika dodawany jest ciąg '_ac', c = 'c'
c d cd 'cd' istnieje w słowniku
cd _ cd_ 'cd_' istnieje w słowniku
cd_ a cd_a 16 - indeks 'cd_' 18. cd_a do słownika dodawany jest ciąg 'cd_a', c = 'a'
a c ac 'ac' istnieje w słowniku
ac d acd 15 - indeks 'ac' 19. acd do słownika dodawany jest ciąg 'acd', c = 'd'
d _ d_ 10 - indeks 'd_' koniec kodowania

Zakodowane dane składają się z 15 indeksów: 1, 2, 3, 3, 4, 5, 6, 8, 10, 1, 9, 11, 16, 15, 10.

Jeśli przyjąć, że indeksy oraz symbole są zapisane na tej samej liczbie bitów, to współczynnik kompresji wyniesie ok. 37%. Jeśli natomiast przyjąć minimalną liczbę bitów potrzebną do zapisania danych, tj. 3 bity na symbol (w sumie 72 bity), 4 na indeks (w sumie 60 bitów), współczynnik kompresji wyniesie ok. 15%.

edytuj Przykładowy program

Poniższy program napisany w języku Python koduje dane metodą LZW (LZW_encode) a następnie dekoduje (LZW_decode) i na końcu stwierdza, czy proces kodowania i dekodowania przebiegł prawidłowo, wyświetlając przy okazji podsumowanie.

Przykładowe wynik działania programu, gdy kompresji zostało poddane źródło artykułu Python:

$ python LZW.py python-artykul.txt 
Liczba kodów: 8297
Maks. liczba bitów potrzebna do zapisania kodu: 14
Rozmiar danych wejściowych: 23805 bajtów
Rozmiar danych skompresowanych: 14520 bajtów
Stopień kompresji: 39.00%

Uwaga: jak wspomniano stopień kompresji zależy również od sposobu zapisu kodów - w tym programie do obliczeń rozmiaru danych skompresowanych i stopnia kompresji założono, że każdy kod zajmuje stałą liczbę bitów. W praktycznych aplikacjach rozwiązania mogą być inne.

# -*- coding: iso-8859-2 -*-
 
def LZW_encode(data):
 
	# krok. 1
	D = {}	# D - słownik: ciąg -> kod
	n = 0	# n - kod ciągu (liczba)
	for i in xrange(256):
		Dchr(i) = n
		n = n + 1
 
	# krok 2.
	c = data0
	result = 
 
	# krok 3.
	for s in data1::
		if c + s in D:
			# przypadek 1. (c+s w słowniku)
			c = c + s
		else:
			# przypadek 2.
			result.append(Dc)
			Dc + s = n
			n = n + 1
			c = s
 
	# krok 4.
	result.append(Dc)
 
	# zwrócenie wyniku
	return result
 
 
def LZW_decode(data):
	# krok 1.
	D = {}	# D - słownik: kod -> ciąg
	n = 0	# n - kod ciągu (liczba)
	for i in xrange(256):
		Dn = chr(i)
		n = n + 1
 
	# krok 2.
	pk = data0
 
	# krok 3.
	result = Dpk
 
	# krok 4.
	for k in data1::
		pc = Dpk
		if k in D:
			# przypadek pierwszy: D[k] istnieje
			Dn = pc + Dk0
			n = n + 1
			result.append(Dk)
		else:
			# przypadek specjalny: dekodowany jest
			# ciąg postaci scscs
			Dn = pc + pc0
			n = n + 1
			result.append( pc + pc0 )
		pk = k
 
	return ''.join(result)	
 
 
if __name__ == '__main__':
	import sys
	from math import log, ceil
 
	if len(sys.argv) < 2:
		print "Podaj nazwę pliku"
		sys.exit(1)
 
	data   = open(sys.argv1).read()
	comp   = LZW_encode(data)
	decomp = LZW_decode(comp)
 
	if data == decomp:
		k  = len(comp)
		n  = int(ceil(log(max(comp), 2.0)))
 
		l1 = len(data)
		l2 = (k*n + 7)/8
 
		print "Liczba kodów: %d" % k
		print "Maks. liczba bitów potrzebna do zapisania kodu: %d" % n
		print "Rozmiar danych wejściowych: %d bajtów" % l1
		print "Rozmiar danych skompresowanych: %d bajtów" % l2
		print "Stopień kompresji: %.2f%%" % (100.0*(l1-l2)/l1)
		# print data
		# print decomp
	else:
		print "Wystąpił jakiś błąd!"

edytuj Bibliografia

  • Adam Drozdek: Wprowadzenie do kompresji danych. Warszawa: Wydawnictwa Naukowo-Techniczne, 1999, ss. 83-88. ISBN 8320423031. 
  • Artur Przelaskowski: Kompresja danych: podstawy, metody bezstratne, kodery obrazów. Warszawa: BTC, 2005, s. 164. ISBN 83-60233-05-5. 

edytuj Zobacz też

edytuj Linki zewnętrzne

All Right Reserved © 2007, Designed by Stylish Blog.