2 Nachricht, Information und Codierung
2 Nachricht, Information und Codierung
2 Nachricht, Information und Codierung
Sie wollen auch ein ePaper? Erhöhen Sie die Reichweite Ihrer Titel.
YUMPU macht aus Druck-PDFs automatisch weboptimierte ePaper, die Google liebt.
88 3 <strong>Codierung</strong><br />
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯<br />
3.4 Datenkompression<br />
.<br />
.<br />
.<br />
3.4.5 Der LZW-Algorithmus<br />
Kompression von korrelierten Zeichengruppen<br />
Für die verlustfreie Kompression beliebiger Daten hat sich als sehr effizientes Verfahren der<br />
nach seinen Erfindern Lempel, Ziv <strong>und</strong> Welch benannte LZW-Algorithmus durchgesetzt<br />
[Ziv77]. Es handelt sich dabei um ein statistisches Verfahren, das aber anders als das Huffman-Verfahren<br />
oder die arithmetische <strong>Codierung</strong> nicht nur Einzelzeichen codiert, sondern<br />
Zeichengruppen unterschiedlicher Länge. Dadurch lassen sich nicht nur die Häufigkeiten von<br />
Einzelzeichen bei der <strong>Codierung</strong> berücksichtigen, sondern auch durch Korrelationen aufeinander<br />
folgender Zeichen bedingte Red<strong>und</strong>anzen. Der LZW-Algorithmus minimiert also auch<br />
Red<strong>und</strong>anzen, die dadurch entstehen, dass sich identische Zeichenfolgen (Strings) in den<br />
Eingabedaten mehrmals wiederholen. Dies führt zu einer umso besseren Kompressionswirkung,<br />
je häufiger solche Wiederholungen auftreten <strong>und</strong> je länger die sich wiederholenden<br />
Zeichengruppen sind. Das Ergebnis der Kompression besteht dann aus einer weit gehend<br />
unkorrelierten Zeichenfolge, die verlustfrei nicht mehr weiter komprimierbar ist.<br />
Das Prinzip des LZW-Algorithmus<br />
Der LZW-Algorithmus arbeitet mit einer Code-Tabelle in der jeder Eintrag aus einem String<br />
mit Zeichen des Quell-Alphabets <strong>und</strong> dem zugehörigen komprimierten Code besteht. Die<br />
Code-Tabelle wird am Anfang mit allen Einzelzeichen des Quell-Alphabets vorbesetzt <strong>und</strong><br />
während der Kompression nach <strong>und</strong> nach erweitert <strong>und</strong> an die Eingabe angepasst. Wegen<br />
dieser automatischen Anpassung benötigt der LZW im Voraus keinerlei <strong>Information</strong>en über<br />
die Statistik des Eingabetextes; er kann daher als Ein-Schritt-Verfahren realisiert werden.<br />
Auch muss die Code-Tabelle nicht zusammen mit den codierten Daten gespeichert bzw.<br />
übertragen werden, da sie im Decoder aus den codierten Daten in identischer Weise wieder<br />
neu erzeugt werden kann.<br />
Zu Beginn der <strong>Codierung</strong> muss jedes Zeichen des Eingabetextes einzeln codiert werden,<br />
weil ja die Code-Tabelle nur mit den Einzelzeichen des Quell-Alphabets vorbesetzt ist <strong>und</strong><br />
noch keine längeren Strings enthält. Zu Beginn ist also noch kein Kompressionseffekt zu<br />
erwarten. Im Laufe der Verarbeitung sammeln sich aber in der Tabelle immer mehr <strong>und</strong> immer<br />
längere mehrfach aufgetretene Strings an, von denen angenommen werden kann, dass<br />
sie im noch zu komprimierenden Text ebenfalls noch häufig auftreten werden. Dadurch steigt<br />
die Effizienz der Kompression immer weiter an, bis die Code-Tabelle vollständig gefüllt ist.<br />
Danach geht die Anpassungseigenschaft des Algorithmus verloren. Die Kompressionsrate<br />
bleibt dann zunächst gleich, sie kann sich aber auch wieder verschlechtern, wenn sich die<br />
Charakteristika der Eingabedaten ändern. Dem kann man durch Erstellen einer neuen Code-<br />
Tabelle entgegenwirken.<br />
Der Kompressions-Algorithmus<br />
Die <strong>Codierung</strong> einer Zeichenkette Z läuft nun nach folgendem Schema ab: zunächst wird das<br />
nächste Eingabezeichen c des Eingabestrings Z eingelesen <strong>und</strong> an den als Präfix bezeichneten<br />
Anfangs-Teilstring P des Strings Z angehängt, es wird also der String Pc gebildet. Zu<br />
Beginn wird der Präfix P mit dem leeren String vorbesetzt. Ist Pc in der Code-Tabelle bereits
3 <strong>Codierung</strong> 89<br />
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯<br />
vorhanden, so wird P=Pc gesetzt <strong>und</strong> das nächste Zeichen eingelesen. Andernfalls wird P<br />
ausgegeben, Pc in die Code-Tabelle eingetragen <strong>und</strong> der neue Präfix P=c gesetzt. Kommt<br />
der soeben eingetragene Teilstring Pc später im Text nochmals vor, so kann er durch ein<br />
einziges Code-Wort ersetzt werden. Darauf beruht letztlich die komprimierende Wirkung des<br />
LZW-Verfahrens.<br />
Der Kompressions-Algorithmus lautet damit in Pseudo-Code-Formulierung:<br />
LZW-Algorithmus zur Kompression eines Strings Z<br />
Initialisiere die Code-Tabelle mit den Einzelzeichen<br />
Weise dem Präfix P den Leerstring zu<br />
Wiederhole, solange Eingabezeichen vorhanden sind:<br />
Lies nächstes Eingabezeichen c aus dem Eingabestring Z<br />
Wenn Pc in der Code-Tabelle gef<strong>und</strong>en wird:<br />
setze P=Pc<br />
Sonst:<br />
Trage Pc in die nächste freie Position der Code-Tabelle ein<br />
Gib den Code für P aus<br />
setze P=c<br />
Ende der Schleife<br />
Gib den Code für das letzte Präfix P aus<br />
Beispiel: LZW-Kompression der Zeichengruppe ABABCBABAB.<br />
Als Beispiel wird die Zeichenkette Z=ABABCBABAB betrachtet. Die Code-Tabelle wird mit<br />
den Zeichen A, B, C des Quell-Alphabets <strong>und</strong> den entsprechenden Codes des Ausgabealphabets<br />
vorbesetzt. Wählt man für das Beispiel als maximale Länge der Code-Tabelle sieben<br />
Einträge, so benötigt man in der Ausgabe 3 Bit pro Code-Wort. Die Code-Tabelle wird<br />
also folgendermaßen vorbesetzt:<br />
Tabelle 3.4.6: Vorbesetzung der Code-Tabelle für die Kompression des Strings Z=ABABCBABAB mit<br />
dem LZW-Algorithmus.<br />
Präfix Ausgabe-Code<br />
A 0 = 000<br />
B 1 = 001<br />
C 2 = 010<br />
- 3 = 011<br />
- 4 = 100<br />
- 5 = 101<br />
- 6 = 110<br />
- 7 = 111<br />
Der <strong>Codierung</strong>svorgang läuft damit folgendermaßen ab:
90 3 <strong>Codierung</strong><br />
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯<br />
Tabelle 3.4.7: <strong>Codierung</strong> des Strings Z=ABABCBABAB mit dem LZW-Algorithmus. Das aktuell verarbeitete<br />
Zeichen ist unterstrichen dargestellt. Die codierte <strong>Nachricht</strong> lautet 013247.<br />
Schritt String Z Präfix P Eintrag in die Code-Tabelle Ausgabe<br />
0 ABABCBABAB - Vorbesetzung -<br />
1 ABABCBABAB A - -<br />
2 ABABCBABAB B AB=3 0<br />
3 ABABCBABAB A BA=4 1<br />
4 ABABCBABAB AB - -<br />
5 ABABCBABAB C ABC=5 3<br />
6 ABABCBABAB B CB=6 2<br />
7 ABABCBABAB BA - -<br />
8 ABABCBABAB B BAB=7 4<br />
9 ABABCBABAB BA - -<br />
10 ABABCBABAB BAB - -<br />
11 ABABCBABAB - - 7<br />
Nach Beendigung der <strong>Codierung</strong> hat der Inhalt der Code-Tabelle die Form:<br />
Tabelle 3.4.8: Code-Tabelle nach Beendigung der Kompression des Strings Z=ABABCBABAB.<br />
Präfix Ausgabe-Code<br />
A 0 = 000<br />
B 1 = 001<br />
C 2 = 010<br />
AB 3 = 011<br />
BA 4 = 100<br />
ABC 5 = 101<br />
CB 6 = 110<br />
BAB 7 = 111<br />
Methoden zur Optimierung des Verfahrens<br />
Weil jeder neue Eintrag in der Code-Tabelle nur eine Verlängerung eines bereits in der Code-Tabelle<br />
enthaltenen Strings darstellt, ist es nicht nötig, zu jedem Code den vollständigen<br />
String zu speichern. Es empfiehlt sich statt dessen, nur das letzte Zeichen des Strings zu<br />
speichern <strong>und</strong> einen Verweis auf den String, aus dem er hervorgegangen ist. Der Code ABC<br />
aus dem obigen Beispiel wird dann als 4C abgespeichert. Dadurch erfordert jeder Tabelleneintrag<br />
bei 8 Byte Eingabezeichen <strong>und</strong> 12 bis 16 Bit Code nur drei Byte: ein Byte für das<br />
letzte Zeichen <strong>und</strong> zwei für den Verweis. Meist werden 12 Bit Codes verwendet, entsprechend<br />
4096 Tabelleneinträgen oder 13 Bit Codes, entsprechend 8192 Einträgen. Bei einer<br />
Verlängerung der Code-Tabelle können zwar mehr <strong>und</strong> längere Teilstrings abgespeichert<br />
werden; dies führt jedoch nicht unbedingt zu einer Verbesserung der Kompressionsrate, weil<br />
eine größere Tabelle auch zu längeren Code-Wörtern führt. Insbesondere zu Beginn der<br />
Kompression, wenn noch Einzelzeichen codiert werden, führt dies zunächst nicht zu einer<br />
Kompression, sondern zu einer Verlängerung des Textes.<br />
Relativ einfach zu realisieren ist, dass nicht immer die volle Länge der Codes übertragen<br />
werden muss. Solange in der Tabelle nicht mehr als 512 Einträge sind, reichen 9 Bit für die<br />
Darstellung der Code-Wörter aus, zwischen 513 <strong>und</strong> 1024 Einträgen genügen 10 Bit usw.<br />
Sowohl der Kompressor als auch der Dekompressor (siehe unten) können anhand ihrer Code-Tabelle<br />
feststellen, mit welcher Wortlänge gerade gearbeitet wird <strong>und</strong> die Wortlänge erhöhen,<br />
sobald ein längerer Code in die Tabelle eingetragen wird. Oft wird auch die Erhöhung<br />
der Wortlänge durch ein eigenes, dafür reserviertes Code-Wort signalisiert, weil dann nicht<br />
schon beim Eintragen eines längeren Code-Wortes in die Tabelle umgeschaltet werden<br />
muss, sondern erst dann, wenn tatsächlich das erste längere Code-Wort verwendet wird.<br />
Wenn die Code-Tabelle vollständig gefüllt ist, kann man entweder mit dieser Tabelle weiterarbeiten<br />
oder aber die Tabelle löschen <strong>und</strong> mit einer neu initialisierten Tabelle fortfahren. Bei
3 <strong>Codierung</strong> 91<br />
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯<br />
der zweiten Strategie sinkt zwar die Kompressionsrate zunächst, aber die Code-Tabelle<br />
kann dafür wieder neu an die Eigenschaften der Eingabedaten angepasst werden. Dies erweist<br />
sich dann als sinnvoll, wenn damit zu rechnen ist, dass sich die Charakteristik der Daten<br />
ändern wird. Dies ist insbesondere bei der Kompression von Bilddaten der Fall. Eine<br />
Neuinitialisierung der Code-Tabelle muss in den komprimierten Daten allerdings durch Einfügen<br />
eines dafür reservierten Code-Worts kenntlich gemacht werden.<br />
Bei der Komprimierung der Daten muss bei jedem Schritt nach dem String Pc gesucht werden,<br />
also dem aktuellen Präfix plus nächstes Eingabezeichen. Eine sequentielle Suche würde<br />
sehr viel Zeit benötigen, so dass sich die Verwendung einer Hash-Tabelle (siehe Kapitel<br />
11.3) empfiehlt. Dazu wird neben der Code-Tabelle noch eine Hash-Tabelle zur Speicherung<br />
von Verweisen auf die Code-Tabelle aufgebaut.<br />
Eine weitere Verbesserung, allerdings auf Kosten der Ausführungszeit, kann erzielt werden,<br />
wenn man das Verfahren nicht einschrittig auslegt, sondern eine statistische Analyse vorschaltet.<br />
Besonders häufig auftretende Strings können so vorab ermittelt <strong>und</strong> bereits bei der<br />
Initialisierung der Code-Tabelle berücksichtigt werden.<br />
Der Dekompressions-Algorithmus<br />
Die Dekompression ist zunächst etwas unanschaulicher, aber auch nicht schwieriger zu implementieren<br />
als die Kompression. Zunächst wird wie bei der Kompression eine Code-Tabelle<br />
angelegt, <strong>und</strong> mit den Eingabezeichen vorbesetzt. Der Dekompressor liest nun ein Zeichen<br />
nach dem anderen ein, sucht den zugehörigen String in der Code-Tabelle auf <strong>und</strong> gibt ihn<br />
aus. Zusätzlich wird an den im vorherigen Schritt decodierten String das erste Zeichen des<br />
aktuell decodierten Strings angehängt <strong>und</strong> das Ergebnis in die nächste freie Position der<br />
Code-Tabelle eingetragen. Auf diese Weise wird schrittweise dieselbe Code-Tabelle aufgebaut,<br />
mit der auch der Kompressor gearbeitet hat. Es gibt dabei jedoch eine Komplikation:<br />
Wenn bei der Kompression ein String in die Code-Tabelle eingetragen <strong>und</strong> im nächsten<br />
Schritt bereits wieder verwendet wurde, so kann er bei der Dekompression an dieser Stelle<br />
noch nicht in der Tabelle enthalten sein. In diesem Fall ist aber klar, dass der fehlende Code<br />
einfach durch Verlängerung des Präfix um das erste Zeichen des zuvor ausgegebenen<br />
Strings entsteht. Der in die Code-Tabelle einzutragende String ist in diesem Sonderfall mit<br />
dem auszugebenden String identisch. Der Algorithmus lautet damit als Pseudo-Code:<br />
LZW-Algorithmus zur Dekompression einer <strong>Nachricht</strong><br />
Initialisiere die Code-Tabelle mit den Einzelzeichen<br />
Weise dem Präfix P den Leerstring zu<br />
Wiederhole, solange Eingabezeichen vorhanden sind:<br />
Lies nächstes Eingabezeichen c<br />
Wenn c in der Code-Tabelle enthalten ist:<br />
Gib den zu c gehörenden String aus<br />
Setze k = erstes Zeichen dieses Strings<br />
Trage Pk in die Code-Tabelle ein, falls noch nicht vorhanden<br />
Setze P auf den zu dem Code c gehörigen String<br />
Sonst (Sonderfall):<br />
setze k = erstes Zeichen von P<br />
Gib Pk aus<br />
Trage Pk in die Code-Tabelle ein<br />
Setze P=Pk<br />
Ende der Schleife<br />
Gib letztes Präfix aus
92 3 <strong>Codierung</strong><br />
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯<br />
Beispiel: Dekompression<br />
Als Beispiel wird nun das weiter oben gewonnene Kompressions-Ergebnis 013247 des<br />
Strings ABABCBABAB wieder dekomprimiert. Zunächst wird die leere Code-Tabelle mit den<br />
Zeichen A, B <strong>und</strong> C vorbesetzt. Der Dekompressor liest dann das erste Code-Zeichen (=0)<br />
ein, sucht das zugehörige Zeichen des Quell-Alphabetes in der Code-Tabelle (=A) <strong>und</strong> gibt<br />
dieses Zeichen aus. Anschließend wird das nächste Zeichen (=1) eingelesen, decodiert (=B)<br />
<strong>und</strong> ausgegeben. Zusätzlich wird jetzt der String AB, bestehend aus dem zuvor decodierten<br />
Zeichen A <strong>und</strong> dem soeben decodierten Zeichen B auf die Nächste freie Position, hier also<br />
3, der Code-Tabelle eingetragen. Das als Nächstes eingelesene Zeichen (=3) ergibt den<br />
Ausgabestring AB, der soeben erst in die Code-Tabelle eingetragen wurde. Zusätzlich wird<br />
der String BA, bestehend aus dem Zeichen B des vorhergehenden Schritts <strong>und</strong> dem ersten<br />
Zeichen des Strings AB, in die Code-Tabelle eingetragen. Die weiteren Schritte der Decodierung<br />
ergeben sich aus der nachstehenden Tabelle.<br />
Tabelle 3.4.9: Decodierung der komprimierten <strong>Nachricht</strong> 013247 mit dem LZW-Algorithmus. Das<br />
aktuell verarbeitete Zeichen ist jeweils unterstrichen dargestellt. Es wird wieder die ursprüngliche<br />
<strong>Nachricht</strong> ABABCBABAB aufgebaut.<br />
Schritt Code-String Eintrag in Code-Tabelle Ausgabe-String=Präfix<br />
0 013247 Vorbesetzung -<br />
1 013247 - A<br />
2 013247 AB B<br />
3 013247 BA AB<br />
4 013247 ABC C<br />
5 013247 CB BA<br />
6 013247 BAB BAB<br />
Man erkennt, dass der Dekompressor tatsächlich dieselben Strings in die Code-Tabelle einträgt<br />
wie der Kompressor, allerdings immer einen Schritt später. Der Dekompressor kann<br />
beispielsweise den String AB erst dann eintragen, wenn er auch den Code für B bereits verarbeitet<br />
hat, weil erst dann bekannt ist, dass bei der Komprimierung auf das Zeichen A ein B<br />
folgte. Dieses Nachhinken kann zu dem oben bereits erwähnten Sonderfall führen, dass ein<br />
benötigter Code in der Code-Tabelle noch nicht enthalten ist. In dem betrachteten Beispiel ist<br />
dies in Schritt 6 der Fall. Dort trifft der Dekompressor auf den Code 7, den er in der Code-<br />
Tabelle nicht findet, weil dafür noch kein String eingetragen worden ist. Wenn dieser Fall<br />
eintritt, ist aber bekannt, dass der fehlende String mit demselben Zeichen beginnen muss,<br />
wie der unmittelbar zuvor decodierte <strong>und</strong> ausgegebene String.
3 <strong>Codierung</strong> 93<br />
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯<br />
3.4.6 Datenreduktion durch unitäre Transformationen (JPEG)<br />
Die Fourier-Transformation<br />
In vielen technischen Anwendungen werden Daten, insbesondere Messdaten <strong>und</strong> Bilder, mit<br />
Hilfe der Fourier-Transformation in eine Frequenzdarstellung transformiert. In diesem Kapitel<br />
wird gezeigt, dass auf diese Weise auch eine sehr effiziente Datenkompression erreicht werden<br />
kann. Die Fourier-Transformation wird durch Integrale vermittelt, die im Falle diskreter<br />
Daten durch Summen ersetzt werden können; man spricht dann von der diskreten Fourier-<br />
Transformation, für die es sehr effiziente Algorithmen gibt. Der bekannteste ist der DFFT-<br />
Algorithmus (von Diskrete Fast Fourier Transform). Damit kann man eine aus N Punkten bestehende<br />
Datenmenge f n in ihre Entsprechung F n im Frequenzraum transformieren:<br />
F<br />
u<br />
=<br />
1<br />
N<br />
N<br />
∑ − 1<br />
f<br />
n<br />
n=<br />
0<br />
e<br />
−2πinu/N<br />
Fourier-Transformation<br />
Die Formel für die Rücktransformation lautet:<br />
N−1<br />
2 /N<br />
f<br />
n<br />
= ∑ Fue<br />
πinu Fourier-Rücktransformation<br />
u=<br />
0<br />
Die Summen in diesen Gleichungen lassen sich durch Multiplikation einer die Exponentialterme<br />
enthaltenden Matrix mit einem Vektor darstellen, dessen Komponenten die zu transformierenden<br />
Daten sind. Die einzelnen Komponenten des transformierten Vektors ergeben<br />
sich also durch Berechnung des Skalarproduktes aus der entsprechenden Matrixzeile mit<br />
dem Datenvektor. Betrachtet man die Zeilen der Matrix als Basisvektoren, so wird durch das<br />
Skalarprodukt diejenige Komponente des Datenvektors transformiert, die in Richtung des<br />
entsprechenden Basisvektors zeigt.<br />
Unitäre <strong>und</strong> orthogonale Transformationen<br />
Dieses Prinzip soll nun verallgemeinert werden. Dazu wird bei der Fourier-Transformation<br />
die Exponentialfunktion e -i2πnu/N durch eine zunächst beliebige, als Kern der Transformation<br />
bezeichnete Matrix K der Dimension N mit den Komponenten K nu ersetzt <strong>und</strong> bei der Rücktransformation<br />
durch die zu K inverse Matrix K -1 :<br />
F<br />
f<br />
1<br />
=<br />
N<br />
N−1<br />
∑<br />
fK<br />
u n nu<br />
n=0<br />
N−1<br />
−1<br />
n<br />
= ∑ FuK nu<br />
u=0<br />
allgemeine unitäre Transformation<br />
unitäre Rück-Transformation<br />
Damit sich eine sinnvolle Transformation ergibt, müssen die Basisvektoren des Kerns, also<br />
die Zeilen der Matrix K, einen Vektorraum mit Dimension N aufspannen. Dies ist dann der<br />
Fall, wenn alle N Zeilenvektoren (Basisvektoren) linear unabhängig, also in einer geometrischen<br />
Betrachtungsweise nicht parallel zueinander sind. Besonders einfach wird die mathematische<br />
Beschreibung, wenn die Basisvektoren nicht nur linear unabhängig, sondern orthogonal<br />
sind, also - geometrisch interpretiert - aufeinander senkrecht stehen. Für komplexe<br />
Matrizen bedeutet dies, dass (bis auf den vorgezogenen Normierungsfaktor 1/N) die inverse<br />
Matrix K -1 mit der konjugiert komplexen <strong>und</strong> transponierten Matrix K *T übereinstimmt:<br />
K<br />
− 1 T<br />
= K<br />
*
94 3 <strong>Codierung</strong><br />
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯<br />
Komplexe Matrizen mit dieser Eigenschaft werden als unitäre Matrizen bezeichnet, dementsprechend<br />
heißen auch die durch sie vermittelten Transformationen unitäre Transformationen.<br />
Insbesondere gehört auch die Fourier-Transformation zur Klasse dieser Transformationen.<br />
Im Falle reeller Matrizen stimmt die inverse Matrix mit der transponierten Matrix überein,<br />
man spricht dann von orthogonalen Matrizen <strong>und</strong> orthogonalen Transformationen. Ist die<br />
orthogonale Transformationsmatrix außerdem noch symmetrisch, so ist sie mit ihrer Inversen<br />
bzw. Transponierten identisch. Da das Rechnen mit komplexen Zahlen doch einen gewissen<br />
Aufwand bedeutet, werden in der Praxis orthogonale Transformationen mit reellen, möglichst<br />
auch noch symmetrischen Matrizen bevorzugt verwendet. Das bekannteste Beispiel für eine<br />
orthogonale Transformation ist wohl die Drehung von Koordinatensystemen, was bei der<br />
Robotersteuerung oder in CAD-Anwendungen zum täglichen Brot gehört.<br />
Im allgemeinen Fall ist die Berechnung der inversen Matrix recht aufwendig, die Bestimmung<br />
der transponierten Matrix dagegen trivial: man erhält die transponierte Matrix einfach durch<br />
Spiegelung an der Hauptdiagonalen. Damit ist auch sofort klar, dass orthogonale, symmetrische<br />
Matrizen zu sich selbst invers sind, so dass für die Hintransformation <strong>und</strong> die Rücktransformation<br />
identische Matrizen verwendet werden können.<br />
Hat man einen Datensatz durch eine unitäre bzw. orthogonale Transformation in eine andere<br />
Darstellung überführt, so ist noch stets die gleiche Datenmenge zu speichern, eine Kompression<br />
wurde dadurch also nicht bewirkt. Eine sehr effiziente Möglichkeit zur Datenreduktion<br />
liegt aber darin, dass bei geeigneter Wahl der Transformation manche Komponenten nur<br />
wenig <strong>Information</strong> tragen <strong>und</strong> daher weggelassen werden können (siehe Abbildung 3.4.4).<br />
Der Gr<strong>und</strong> dafür ist, dass man orthogonale Transformationen angeben kann, bei denen die<br />
Komponenten des Ergebnisses weitgehend unkorreliert sind, während die zu transformierenden<br />
Daten in der Regel sehr stark miteinander korreliert sind, da sie sich für gewöhnlich<br />
stetig ändern. Anders ausgedrückt: kennt man einige aufeinanderfolgende Werte der zu<br />
transformierenden Ausgangsdaten, so lässt sich der Wert des nächsten Wertes mit hoher<br />
Wahrscheinlichkeit voraussagen; für das Ergebnis einer geeigneten orthogonalen Transformation<br />
gilt das aber nicht mehr.<br />
f<br />
f 2<br />
f 1<br />
X<br />
f’<br />
f 1<br />
f 2<br />
X’<br />
Abbildung 3.4.4:<br />
Durch eine geeignete Koordinatentransformation<br />
wird erreicht, dass die X’-<br />
Komponenten für die beiden Daten f 1 <strong>und</strong> f 2<br />
zu 0 werden <strong>und</strong> daher nicht gespeichert<br />
werden müssen. Dies entspricht einer Datenkompression.<br />
Ordnet man den Zeilen des Kerns als Basisfunktionen Schwingungen mit ansteigender Frequenz<br />
zu, so wird ein Datenvektor durch Überlagerungen dieser Basisfunktionen ausgedrückt.<br />
Hohe Frequenzanteile in Daten entstehen durch scharfe Kanten <strong>und</strong> durch Rauschen.<br />
Werden nun die den hohen Frequenzanteilen entsprechenden Komponenten vernachlässigt,<br />
so führt dies zu einer Rauschunterdrückung, aber – da es sich hierbei im Gr<strong>und</strong>e<br />
um einen Tiefpass-Filter handelt – auch zu einer Kantenverschmierung.
3 <strong>Codierung</strong> 95<br />
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯<br />
Die Hadamard-Transformation<br />
Die Effizienz des Verfahrens hängt in erster Linie von den gewählten Basisfunktionen ab. Die<br />
einfachste Möglichkeit ergibt sich, wenn man als Basisfunktionen Rechteckschwingungen<br />
wählt, die in diesem Zusammenhang auch als Walsh-Funktionen bezeichnet werden. Die<br />
zugehörige Transformation ist als Hadamard-Transformation bekannt <strong>und</strong> besonders einfach<br />
<strong>und</strong> extrem schnell ausführbar, weil die Transformationsmatrix nur die Werte 1 <strong>und</strong> -1 enthält,<br />
so dass man bei der Transformation völlig ohne Multiplikationen auskommt. Als günstiger<br />
hat sich allerdings die Wahl von Sinus- oder Kosinusfunktionen als Basis erwiesen, da<br />
dann die Resultate in noch höherem Maße unkorreliert sind als bei der Hadamard-<br />
Transformation, so dass höhere Kompressionsraten erreichbar sind.<br />
Die Kosinus-Transformation<br />
Bei der Fourier-Transformation besteht der Kern aus einer komplexen Exponentialfunktion<br />
exp(i2πnu/N), die sich in einen reellen Kosinus-Anteil <strong>und</strong> einen imaginären Sinus-Anteil zerlegen<br />
lässt:<br />
e i2πnu/N = cos(2πnu/N) + i . sin(2πnu/N)<br />
Die Kosinus- oder Sinus-Funktionen alleine bilden in diesem Fall jedoch keine Basis, da die<br />
Kosinus-Funktionen gerade Funktionen <strong>und</strong> die Sinus-Funktionen ungerade Funktionen sind.<br />
Mit den Kosinus-Funktionen alleine kann man also nur gerade Funktionen darstellen, das<br />
Ergebnis ist dann rein reell. Mit den Sinus-Funktionen alleine sind nur ungerade Funktionen<br />
darstellbar, <strong>und</strong> zwar mit rein imaginärem Ergebnis. Für Funktionen bzw. Daten ohne diese<br />
besonderen Symmetrien wird also die komplexe Kombination aus Kosinus- <strong>und</strong> Sinus-<br />
Termen benötigt.<br />
Weil ein reelles Ergebnis in den meisten Anwendungsfällen bequemer zu handhaben ist,<br />
greift man zu einem Kunstgriff: Man symmetrisiert die zu transformierenden Daten durch<br />
Spiegeln an der vertikalen Koordinatenachse. Nun wird eine Fourier-Transformation über<br />
diesen um den Faktor zwei vergrößerten Datenvektor durchgeführt, wobei sich die Summation<br />
nun über 2N Terme erstreckt. Das Ergebnis ist jetzt aber rein reell <strong>und</strong> enthält nur Kosinus-Funktionen.<br />
Wegen der künstlich erzeugten geraden Symmetrie lassen sich viele Summanden<br />
zusammenfassen, so dass sich schließlich wieder nur genau so viele Terme ergeben,<br />
wie man bei Summation über die ursprünglichen Daten erhalten hätte, wobei sich aber<br />
die Wellenlängen der Kosinus-Funktionen verglichen mit der Fourier-Transformation verdoppelt<br />
haben. Das Ergebnis ist die rein reelle Kosinus-Transformation [Str02], die in der<br />
Praxis größte Bedeutung erlangt hat. Die Transformations-Formeln lauten:<br />
N−1<br />
u<br />
=<br />
u n [ + ]<br />
F c 2/N∑ fcos(2n 1) π u/2N)<br />
Kosinus-Transformation<br />
n=<br />
0<br />
N−1<br />
∑ [ π ]<br />
f = 2/ N c F cos (2n+<br />
1) u/2N)<br />
n u u<br />
u=<br />
0<br />
mit: u,n = 0,1,...N-1, c u = 1/√2 für u=0, c u = 1 für u>0.<br />
Kosinus-Rücktransformation<br />
Der Transformationskern enthält nun nicht mehr nur die Werte 1 <strong>und</strong> -1 wie bei der Hadamard-Transformation.<br />
Die Berechnung ist daher wegen der jetzt nötigen Multiplikationen entsprechend<br />
aufwendiger. Der Aufwand lohnt jedoch, da wie schon erwähnt, die Ergebnisse<br />
der Kosinus-Transformation noch weniger korreliert sind als bei der Hadamard-<br />
Transformation <strong>und</strong> somit eine noch effizientere Datenreduktion ermöglichen. Wegen der<br />
Verfügbarkeit von Signalprozessoren, die in der Lage sind, die nötigen Berechnungen sehr<br />
schnell durchzuführen, hat sich die Kosinus-Transformation als Standard durchgesetzt. Häufig<br />
verwendet man Kerne mit N=8:
96 3 <strong>Codierung</strong><br />
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯<br />
C<br />
un<br />
= 0.5 ⋅ c cos[(2n + 1) πu /16]<br />
=<br />
u<br />
⎛1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 ⎞<br />
1.3870 1.1759 0.7857 0.2759 -0.2759 -0.7857 -1.1759 -1.3870<br />
1.3066 0.5412 -0.5412 -1.3066 -1.3066 -0.5412 0.5412 1.3066<br />
1.1759 -0.2759 -1.3870 -0.7857 0.7857 1.3870 0.2759 -1.1759<br />
= 1.0000 -1.0000 -1.0000 1.0000 1.0000 -1.0000 -1.0000 1.0000<br />
0.7857 -1.3870 0.2759 1.1759 -1.1759 -0.2759 1.3870 -0.7857<br />
0.5412 -1.3066 1.3066 -0.5412 -0.5412 1.3066 -1.3066 0.5412<br />
⎝0.2759 -0.7857 1.1759 -1.3870 1.3870 -1.1759 0.7857 -0.2759⎠<br />
Die Transformation entspricht dann einer Multiplikation der Matrix C mit dem Vektor der Bilddaten<br />
(Zeile bzw. Spalte) f:<br />
u<br />
N<br />
=∑ −1<br />
F f C<br />
Fourier-Transformation<br />
n<br />
n=0<br />
N<br />
∑ − 1<br />
c<br />
u<br />
u=<br />
0<br />
nu<br />
f = F C Fourier-Rücktransformation<br />
n<br />
u<br />
−1<br />
nu<br />
Bei der Ausführung der Transformation, geht man am besten durch Erweiterung der Koeffizienten<br />
mit einer Potenz von 2, beispielsweise 4096, zu einer Integer-Darstellung über, so<br />
dass für alle Berechnungen Integer-Arithmetik genügt.<br />
JPEG-Kompression durch Quantisierung der Koeffizienten<br />
Die Kosinustransformation liefert als Ausgabe quadratische Matrizen mit einer zuvor festgelegten<br />
Komponentenzahl, üblicherweise 8×8. Es wird nun angestrebt, diese Einträge möglichst<br />
Platz sparend abzuspeichern, wozu ein Teil der <strong>Information</strong> so zu entfernen ist, dass<br />
es in den rekonstruierten Daten nur zu geringen, nicht relevanten Änderungen kommt. Bei<br />
der Entscheidung, welche Matrixkomponenten übertragen werden <strong>und</strong> mit wie vielen Bits sie<br />
dargestellt werden sollen, gibt es prinzipiell zwei verschiedene Möglichkeiten:<br />
Ein Ansatz besteht darin, die Entscheidung von der Position der Komponenten in der Matrix<br />
abhängig zu machen. Man geht dabei von der Überlegung aus, dass die niederfrequenten<br />
Anteile mehr zur <strong>Information</strong> beitragen als die zu höheren Frequenzen gehörenden Komponenten.<br />
Die Ergebnismatrix wird dementsprechend in verschiedene Zonen aufgeteilt, für die<br />
in einer Bit-Zuordnungstabelle oder Quantisierungstabelle festgelegt wird, wie viele Bits für<br />
die <strong>Codierung</strong> der Matrixkomponenten in den jeweiligen Zonen zu verwenden sind. Dabei<br />
werden für die niederfrequenten Matrixkomponenten mehr Bits reserviert als für die höherfrequenten<br />
<strong>und</strong> die höchsten Frequenzen werden oft ganz unterdrückt, was durch den Eintrag<br />
Null gekennzeichnet wird. Die folgende Tabelle zeigt eine mögliche Bit-Zuordnung für<br />
eine 8×8 Matrix, entsprechend einer Datenreduktion um etwa den Faktor 3.<br />
Tabelle 3.4.10: Eine datenkomprimierende Bitzuordnungstabelle (Quantisierungstabelle) für die 8×8<br />
Kosinus-Transformation. Der Kompressionsfaktor beträgt für dieses Beispiel ca. 3.<br />
8 8 7 7 6 5 4 4<br />
8 7 6 5 4 3 2 2<br />
7 6 5 3 2 2 1 1<br />
7 5 3 2 1 1 0 0<br />
6 4 2 1 1 0 0 0<br />
5 3 2 1 0 0 0 0<br />
4 2 1 0 0 0 0 0<br />
4 2 1 0 0 0 0 0
3 <strong>Codierung</strong> 97<br />
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯<br />
Der zweite Ansatz zur Datenkompression besteht darin, die Quantisierung nicht nach der<br />
Lage der Matrixelemente zu entscheiden, sondern nach deren Größe. Man geht hier von der<br />
Annahme aus, dass Matrixeinträge mit großen Beträgen auch viel <strong>Information</strong> tragen. Dies<br />
trägt der Tatsache Rechnung, dass scharfe Kanten in Messdaten oder Bildern im Verlauf der<br />
Daten auch zu signifikanten hochfrequenten Komponenten führen, deren Unterdrückung zu<br />
einer Kantenverschmierung führen würde. Alle Matrixelemente werden daher mit einem voreinstellbaren<br />
Schwellwert verglichen <strong>und</strong> nur übertragen, wenn sie größer sind als dieser<br />
Schwellwert. Allerdings muss dann auch die Position der Matrixelemente mit codiert werden.<br />
Fehlende Einträge werden bei der Rücktransformation wie beim ersten Verfahren durch Null<br />
ergänzt.<br />
Beide Methoden können zu Problemen führen. Das erste Verfahren berücksichtigt nicht,<br />
dass auch hochfrequente Matrixelemente wichtige <strong>Information</strong> tragen können. Das zweite<br />
Verfahren vermeidet zwar diesen Fehler, codiert aber statt dessen niederfrequente Anteile<br />
nur dann, wenn sie über dem Schwellwert liegen. Erinnert man sich daran, dass die erste<br />
Matrixkomponente den Mittelwert der codierten Daten repräsentiert, so wird deutlich, dass<br />
diese Komponente auch dann nicht ohne Qualitätsverlust weggelassen werden darf, wenn<br />
sie klein ist. Eine optimale Lösung muss demnach beide Methoden kombinieren <strong>und</strong> die Anzahl<br />
der Bits für die <strong>Codierung</strong> der einzelnen Matrix-Komponenten in Abhängigkeit von deren<br />
Position <strong>und</strong> Größe entscheiden.<br />
Die Kosinus-Transformation mit 8×8-Matritzen ist wesentlicher Bestandteil des genormten<br />
JPEG-Standards für die datenreduzierende <strong>Codierung</strong> von Bilddaten, bei der nach den oben<br />
beschriebenen Strategien kleine <strong>und</strong>/oder hochfrequente Komponenten auf 0 gesetzt werden.<br />
Dadurch ergeben sich häufig längere Sequenzen von Nullen, die durch eine Lauflängen-<strong>Codierung</strong><br />
komprimiert werden. Zusätzlich werden die Koeffizienten aufeinander folgender<br />
8×8-Bereiche mittels Differenz-<strong>Codierung</strong> weiter komprimiert. Im letzten Schritt steht<br />
dann eine Huffman-<strong>Codierung</strong> oder eine arithmetische <strong>Codierung</strong>, mit der die verbleibende<br />
Einzelzeichen-Red<strong>und</strong>anz eliminiert wird. Für Bilder ergeben sich dann bei Kompressionsraten<br />
um ca. den Faktor 10 gute visuelle Eindrücke, obwohl der <strong>Information</strong>sgehalt wesentlich<br />
reduziert wurde. Besonders für Internet-Anwendungen hat dieses Verfahren der Bildkompression<br />
weite Verbreitung gef<strong>und</strong>en.<br />
Weitere Kompressionsverfahren<br />
Von großer praktischer Bedeutung ist die Kompression bewegter Bilder nach dem MPEG-<br />
Standard (siehe [Wat01] <strong>und</strong> [Sym98]) Die Kompression von Einzelbildern erfolgt dabei wie<br />
beim JPEG-Verfahren durch Kosinustransformation [Str02]. Es werden jedoch nicht alle Bilder,<br />
sondern nur Stützbilder (beispielsweise jedes vierte) vollständig komprimiert, Zwischenbilder<br />
aber nur aus den Stützbildern interpoliert. Zusätzlich wird durch die Übernahme örtlich<br />
verschobener, aber sonst unveränderter Bildbereiche von einem Bild zum nächsten ein weiterer<br />
Kompressionseffekt erzielt. Insgesamt sind Kompressionsraten bis ca. um den Faktor<br />
100 möglich.<br />
Weitere Methoden der Bilddatenkompression sind die Kompression mit Hilfe der Wavelet-<br />
Transformation [Str02], bei der die zur Bildbeschreibung benötigte Funktionsbasis aus den<br />
Bildern selbst gewonnen wird sowie die fraktale Bildkompression [Fis96], bei der Bilder durch<br />
typische, kleine Bildausschnitte <strong>und</strong> deren Kombination zu fraktalen Mustern durch Überlagerung<br />
sowie unter Verwendung affiner Abbildungen approximiert werden.