Technische Universität Chemnitz Fakultät für Informatik Professur Theoretische Informatik Proseminar WS 2002/2003 Thema: Datenkompression Dynamisches / Adaptives Huffman-Verfahren Danny Grobe Rainer Kuhn Dynamisches / Adaptives Huffman-Verfahren Huffman-Kode: Von David A. Huffman stammt ein Algorithmus, welcher zu einer gegebenen Nachricht einen Präfixkode erzeugt, der die Länge einer kodierten Nachricht unter der Annahme einer shannonschen Quelle minimiert. Der durch diesen Algorithmus konstruierte Kode wird als Huffman-Kode zu einer Nachricht bezeichnet. Die Huffman- Kodierung einer Nachricht ist eine binäre Kodierung der Nachricht, deshalb sei im folgenden angenommen, dass die Zeichen der Nachricht durch den Huffman-Kode über dem Alphabet {0, 1} kodiert werden. Der Algorithmus wurde im Jahre 1952 von Huffman in "A method for the construction of minimum redundancy codes" veröffentlicht. Arten der Huffmankodierung: Die Huffman-Kodierung kann statisch, dynamisch oder adaptiv vorgenommen werden. Bei der statischen Kodierung ist die Codetabelle dem Coder und dem Decoder bekannt. Bei der dynamischen Kodierung wird der Prefixcode für den jeweiligen Datenstrom (Block) neu berechnet. In diesem Fall muss neben dem kodierten Signal jedoch zusätzlich die gültige Codeworttabelle übertragen werden. Die adaptive Variante geht von einer initialen Codetabelle (oftmals leer) aus, aktualisiert diese aber während des Kodierens. Der Decoder aktualisiert seine Codetabelle entsprechend. Minimaleigenschaft: Bei der der Huffman-Kodierung will man unter der Annahme der shannonschen Quelle die Länge der kodierten Nachricht möglichst gering halten. Bezeichnet h(x) die absolute Häufigkeit des Zeichens x in der Nachricht N über dem Alphabet A und l(x) die Länge der Kodierung des Zeichens, dann ist die Länge l(N) der kodierten Nachricht offenbar durch gegeben. Betrachtet man zum Beispiel die Nachricht "baa", so gilt: h(a)= 2 und h(b)= 1. Wird dabei "a" mit einer Länge von 3 und "b" mit einer Länge von 4 kodiert, so erhält man: Die kodierte Nachricht hat also eine Länge von 10. Ziel bei der Konstruktion eines HuffmanKodes zu einer endlichen Nachricht ist die Minimierung der Länge l(N) durch geeignete Wahl der l(x). Unter der Annahme einer shannonschen Quelle ist für jedes Zeichen, welches in der Nachricht auftreten kann, also aus dem Alphabet A, über welchem die Nachricht kodiert ist, stammt, die relative Auftrittswahrscheinlichkeit durch Abzählung oder Abschätzung ermittelbar. Mithilfe dieser Wahrscheinlichkeiten p(x) ermittelt man die Entropie H, den mittleren Informationsgehalt pro Zeichen, der Nachr icht zu wobei ld der Logarithmus zur Basis zwei ist. Unter der Betrachtung der Entropie lässt sich die Minimaleigenschaft des gesuchten Kodes anders formulieren: Die durchschnittliche Kodierungslänge D des Kodes, welche durch gegeben ist, wobei l(x) die Kodierungslänge des Zeichens x und p(x) die Auftrittswahrscheinlichkeit des Zeichens x aus dem Alphabet A sei, soll minimiert werden. Man sucht also den Kode derart, dass es keinen Kode gibt, welche eine geringere durchschnittliche Kodierungslänge der Zeichen hat. Dabei kann die durchschnittliche Kodierungslänge D nicht geringer als die theoretische Grenze der Entropie H werden. Im Idealfall hat also der gesuchte Kode eine durchschnittliche Kodierungslänge, welche der Entropie entspricht, wobei dieser Fall auftritt, wenn die Wahrscheinlichkeiten Potenzen von zwei sind. Betrachtet man dabei speziell eine endliche Nachricht und setzt die Auftrittswahrscheinlichkeiten zu den Häufigkeiten der Zeichen in der Nachricht, so ergibt sich die zuerst betrachtete Festlegung der Minimalbedingung, welche die kodierte Länge der Nachricht minimieren soll. Baumdarstellung: Kodes lassen sich sehr anschaulich mithilfe von Bäumen darstellen. Jeder Knoten hat eine bestimmte Anzahl von Nachfolger, wobei diese Anzahl durch die Anzahl der Zeichen im Kodealphabet beschränkt ist. Nachrichten werden sehr oft über dem Alphabet {0, 1} kodiert, in diesem Fall entsteht ein binärer Baum. Jede Kante des Baumes wird derart mit einem Zeichen aus dem Kodealphabet versehen, dass jeder Knoten höchstens eine abgehende Kante mit einem bestimmten Zeichen des Kodealphabets besitzt. Hätte ein Knoten zwei abgehe nde Kanten mit gleicher Bezeichnung, so könnte man die Knoten, an welchen die Kanten enden, zu einem Knoten zusammenfassen. Dies könnte aber dazu führen, dass eine bestimmte Folge von Kodezeichen mehrere Zeichen kodiert, was natürlich nicht zugelassen ist. Binäre Kodierung in Baumdarstellung Die Zuordnung des Kodes zu einem Zeichen ist einfach gegeben durch die Folge der Kanten, wenn man von der Wurzel aus zu dem Zeichen geht, dabei ist der Weg eindeutig, denn ein Baum ist frei von Zyklen. Finden des Kodes eines bestimmten Zeichens, "C" wird durch "110" kodiert Durch die Darstellung mithilfe eines Baumes hat man offenbar eine weitere Charakterisierung für die Erfüllung der Präfixbedingung gewonnen: Eine Zuordnung ist genau dann ein Präfixkode, falls alle Knoten, welche zu Zeichen gehören, Blätter des Baumes sind, also keine Nachfolger haben. Aufwandsbetrachtung: Eine für die Konstruktion eines Huffman-Kodes günstige Datenstruktur ist die Halde, welche bei einer Größe von m Elementen für Einfügen und Löschen nur O(log(m)) Schritte benötigt. Mithilfe einer Halde lässt sich die Konstruktion des Huffman-Kodes wie folgt durchführen Man speichert in der Halde nur die noch zu betrachtenden Knoten mit deren Häufigkeiten ab, so sind zu Anfang die n in der Nachricht vorkommenden, verschiedenen Zeichen in der Halde. In jedem Schritt werden die beiden Knoten mit minimaler Gewichtung von der Halde entfernt und zu einem neuen Knoten, welcher in die Halde eingefügt wird, zusammengefasst. Da jedes Einfügen und Löschen in O(log(n)) Schritten durchgeführt werden kann und O(n) Schritte ausreichen, da anfangs n Elemente vorhanden sind und in jedem Schritt eines weniger auf der Halde bleibt, kann man den Huffman-Kode in O(n*log(n)) Schritten konstruieren, wenn man annimmt, dass die Häufigkeiten der n Zeichen bekannt sind. Anwendung von Huffman-Kodes: Für Kodierungen von Zeichen verwendet ma n im Allgemeinen keine Kodes, welche verschiedene Längen haben, weil diese einen sehr hohen Aufwand zur Folge haben. Deshalb werden Huffman-Kodes nur in Ausnahmefällen eingesetzt, um eine geeignete Kodierung für Zeichen zu finden, wie zum Beispiel beim Morsealphabet. Eine weitere Einsatzmöglichkeit von Huffman-Kodes eröffnet sich durch ihre Eigenschaft, die Länge kodierter Nachrichten zu minimieren. Dabei werden Huffman-Kodes nur im Verbund mit anderen Methoden zur Kompression eingesetzt, vor allem bei Bild- oder Audiodaten. Dies ist darauf zurückzuführen, dass bei der Kodierung mit Huffman-Kodes angenommen wird, dass eine shannonsche Nachricht vorliegt. Da dies fast nie gegeben ist und darüber hinaus für häufig vorkommende Daten, wie zum Beispiel Text, Bilder oder Musik, wesentlich bessere Methoden zur Kompression zur Verfügung stehen, wird der Huffman-Kode nie alleine zur Kompression von Daten verwendet. Huffman-Kodes sind als Ergänzung der Kompression in verschiedenen Dateiformaten aufzufinden. Im JPEG-Format (Joint Graphic Experts Group) für Bilddaten kommt neben einer diskreten Kosinustransformation (DCT) auch die HuffmanKodierung zum Einsatz, um Bilder mit möglichst wenig Speicherverbrauch zu speichern. Im PNG-Format (Portable Network Graphic-Format) wird die Huffman-Kodierung eingesetzt, um Bilder ohne Verlust zu komprimieren. Im MP3-Format für Audiodaten kommt neben einigen anderen Verfahren die Huffman-Kodierung zum Einsatz, um schon verlustbehaftete Daten weiter ohne Verlust zu komprimieren (circa 20% Kompression möglich). Bei diesem Format ergänzen sich die Huffman-Kodierung und andere Verfahren derart, dass oft immer ein Verfahren eine gute Kompression leistet. Im MPEG-Format für Bilddaten ist neben verlustbehafteten Methoden die Huffman-Kodierung zur nachträglichen Reduktion der Daten eingesetzt. Die verbreiteten Komprimierungsprogramme bzip und bzip2 verwenden neben einem Burrows-Wheeler-Algorithmus ebenfalls die Huffman-Kodierung, um Daten beliebigen Typs ohne Verlust zu komprimieren. Dynamisches Huffman-Verfahren: Der Text wird in Blöcke unterteilt, welche mit dem normalen Huffman-Verfahren komprimiert werden. Dabei wird für jeden Block eine eigene Codetabelle ermittelt, mit dieser dann der Block komprimiert wird. Beides (Codetabelle und komprimierter Block) werden zusammen übermittelt. Beim Dekomprimieren wird die jeweilige Codetabelle benutzt um den dazugehörigen Block zu entschlüsseln. Die dekomprimierten Blöcke ergeben in der richten Reihenfolge aneinandergereiht wieder den Ausgangstext. Komprimieren eines Blockes nach Standard-Huffman: - Ermitteln der absoluten Zeichenhäufigkeiten über den ganzen Block Wald erstellen mit Zeichen und zugehörigen Häufigkeiten Solange bis nur noch 1 fertiger Baum entstanden ist o immer die zwei Bäume mit den minimalen Häufigkeiten durch einen Parentknoten verknüp fen, dem die Summe der Häufigkeiten der beiden Teilbäume zugewiesen wird. è Baum aus dem man die Kodes für jedes Zeichen ablesen kann è Kodetabelle è Kodetabelle abspeichern und ersetzen der Zeichen durch den erhaltenen Bitcode Vorteil der Unterteilung in Blöcke: Die dynamische Huffmancodierung bildet das Verfahren auf eine Blocklänge größer als eins ab. Der einfache Ansatz kann bei einer ungleichmäßigen Wahrscheinlichkeitsverteilung, also wenn auf ein Symbol ein großes Gewicht fällt und andere dafür kaum vorkommen, stark von der Entropie abweichen. Diesem kann man entgegenwirken, wenn man Blöcke aus mehreren Symbolen zur Codelänge benutzt. Allgemein kann man so eine durchschnittliche Codelänge erreichen, die näher an der Entropie liegt. Beispiel für das Komprimieren eines Blockes: Inhalt des Blockes: „halloballo“ Ermittelte Zeichen, die im Text vorkommen und deren absolute Häufigkeit: Zeichen Habsolut h 1 a 2 l 4 o 2 b 1 o 111 b 101 Resultierender Huffmanbaum: 10 6 2 4 1 2 4 2 1 h a l o b Resultierende Codetabelle: Zeichen Code h 100 a 110 l 0 Komprimierter Text: 100 110 0 0 111 101 110 0 0 111 Länge: 22 bit Bei der 7bit-ASCII-Codierung hätte sich eine Länge von 10 Zeichen * 7 bit = 70 bit ergeben. Komprimierungsleistung: 69 % (bei Vernachlässigung der Codetabelle) Adaptives Huffman-Verfahren: Bei diesem Verfahren kennt man die Häufigkeiten der Symbole zuerst nicht, allerdings wird beim enkodieren der Codebaum adaptiv aus den vorkommenden Symbolen aufgebaut, ausgehend von dem Fall der Gleichverteilung. So passen sich die Codes im Laufe der Datei immer besser an die Häufigkeiten an. Da derselbe Prozess auch beim Decodieren verwendet wird, kann dieser Prozess auch eindeutig Rückgängig gemacht werden. Der Huffmanbaum wird folgendermaßen aufgebaut: - - - Anfangsbaum der nur aus NYT (Escape)-Knoten besteht, der Gewicht NULL hat Zeichen einlesen wenn Zeichen noch nicht im Baum enthalten dann: o Suche NYT und erhöhe dessen Wichtung und die der Knoten auf dem Pfad dort hin um 1 o Anhängen von 2 Blättern an das NYT-Blatt à wird zum Knoten und ist daher unmarkiert o linkes Blatt = neues NYT mit Wichtung 0 o rechtes Blatt = neues Zeichen mit Wichtung 1 o Ausgabe des Bitcodes der zum neuen Zeichen führt, gefolgt vom neuen Zeichen sonst: o suche Zeichen o alle Knotenwichtungen auf dem Pfad zwischen Wurzel und Zeichen um 1 erhöhen o Ausgabe des Bitcodes Überprüfung der Wichtungen: o Die Knotenwichtungen einer Ebene müssen immer kleiner sein als die der übergeordneten Ebene, sonst tausche die Teilbäume ! o Wichtungen von Knoten einer Ebene müssen von links nach rechts aufsteigend sortiert sein, sonst sortierendes Tauschen! NYT bedeutet „not yet transmitted“, das heißt, dass an seiner Stelle Zeichen eingefügt werden, die bis jetzt noch nicht aufgetreten sind. Im Folgenden soll anhand des Textes „halloballo“ die Funktionsweise des adaptiven Huffmanverfahrens nahe gebracht werden. NYT Begonnen wird mit einem Baum, der nur aus dem NYT-Knoten mit der Wichtung 0 besteht. Als erstes Zeichen wurde das „h“ transferiert. Jedes neue Zeichen wird rechts an den NYTKnoten gehangen mit der Wichtung 1. Außerdem wird links ein neues Blatt angehangen, das der neue NYT-Knoten mit der Wichtung 0 wird. Der alte NYT-Knoten verliert seine Markierung und erhält die Summe der Wichtungen seiner Söhne. Ausgabe: h Wieder wird ein neues Zeichen eingefügt, das „a“. Damit der Decoder weiß, dass ein neues Zeichen folgt, wird ihm der Pfad zum NYT-Knoten übermittelt, gefolgt vom neuen Zeichen. Die Wichtungen aller Knoten werden von unten nach oben aktualisiert, so dass jeder Knoten die Summe der Wichtungen seiner Söhne als Wichtung erhält. Dieser Schritt wird nach jeder Änderung am Baum durchgeführt. Ausgabe: 0 a Nach dem Einfügen des „l“ am NYT-Knoten und der Aktualisierung der Wichtungen erweist es sich als notwendig, die beiden Teilbäume zu vertauschen, da rechts immer der Sohn mit der höheren Wichtung hängen muss. Ausgabe: 0 0 l Ein weiteres „l“ wurde übermittelt. Da das Zeichen im Baum bereits vorhanden ist, wird die Wichtung des „l“-Knotens um 1 erhöht. Der Weg zum „l“-Knoten wird ausgegeben und die Wichtungen aktualisiert. Da der „l“-Knoten nun die Wichtung 2 hat, somit „schwerer“ ist als der „h“-Knoten, und die Regel „ Für jeden Knoten k1 und k2 mit dem Level l1 und l2 muss gelten, wenn l1<l2, dann w(k1)>=w(k2).“ beachtet werden muss, war es notwendig den „l“-Knoten mit dem „h“-Knoten zu vertauschen. Ausgabe: 1 0 1 Das „o“ wird als neues Zeichen am NYT-Knoten angefügt und die Wichtungen aktualisiert. Danach ist es erforderlich den Knoten „a“ mit seinem linken Bruder zu vertauschen, da dieser nun eine höhere Wichtung besitzt als er. Es gilt: „Die Wichtung eines rechten Sohnes muss immer größer gleich der Wichtung des linken Sohnes sein.“ Ausgabe: 1 0 0 o Da nun alle Regeln erläutert wurden, beschränken wir uns nun nur noch auf die Ausgabe. Ausgabe: 1 1 0 0 b Ausgabe: 1 1 0 Ausgabe: 1 1 Ausgabe: 1 1 Ausgabe: 1 0 1 Komprimierter Text: h 0 a 0 0 l 1 0 1 1 0 0 o 1 1 0 0 b 1 1 0 1 1 0 1 1 1 1 1 0 1 Länge (ohne Buchstaben1 ): 26 bit Bei der 7bit-ASCII-Codierung hätte sich eine Länge von 10 Zeichen * 7 bit = 70 bit ergeben. Komprimierungsleistung: 63 % Vergleich der beiden Verfahren: Dynamisches Verfahren Adaptives Verfahren Vorteile einfaches Verfahren zur Erzeugung des Huffmanbaumes bei Beschädigung eines Blockes ist nur dieser beschädigt Daten müssen nur einmal eingelesen werden Nachteile zweimaliges Einlesen der Daten notwendig 1 stark anfällig für Fehler aufwendige Verwaltung des Huffmanbaumes Die Buchstaben werden nicht mit gezählt, da sie jeweils nur ein mal auftreten und dadurch bei längeren Texten nicht ins Gewicht fallen. Quellenverzeichnis: Sedgewick, R., Algorithmen, Addison-Wesley, Bonn, München, Paris [u.a.], 1992. Heun, V., Grundlegende Algorithmen, Vieweg, 1. Auflage, Oktober 2000 Internetquellen: Übersicht über Varianten von Huffmans Algorithmen http://www.dbg.rt.bw.schule.de/lehrer/ritters/info/kompr/huffman.htm Algorithmen à Datenkompression an der FH Flensburg http://www.iti.fh- flensburg.de/lang/algorithmen/code/huffman/huffman.htm Applet zum dynamischen Huffman-Verfahren http://www.cs.sfu.ca/cs/CC/365/li/squeeze/Huffman.html Applet zum adaptiven Huffman-Verfahren http://www.cs.sfu.ca/cs/CC/365/li/squeeze/AdaptiveHuff.html Funktions weise des adaptiven Huffman-Verfahrens http://www.cs.cf.ac.uk/Dave/Multimedia/node212.html http://www.cs.duke.edu/csed/curious/compression/adaptivehuff.html#encoding