Aufgabe 6: Ein JPEG-Decoder
Aufgabe 6: Ein JPEG-Decoder
Aufgabe 6: Ein JPEG-Decoder
Sie wollen auch ein ePaper? Erhöhen Sie die Reichweite Ihrer Titel.
YUMPU macht aus Druck-PDFs automatisch weboptimierte ePaper, die Google liebt.
¥¤<br />
¡<br />
<br />
©¨<br />
£¢<br />
<br />
§¦<br />
<strong>Aufgabe</strong> 6: <strong>Ein</strong> <strong>JPEG</strong>-<strong>Decoder</strong><br />
<strong>Ein</strong>leitung<br />
<strong>JPEG</strong> ist ein populärer Standard zur Kompression von Bildern. Neben verlustfreien Kompressionsalgorithmen<br />
werden auch verlustbehaftete benutzt. Bei diesen wird die besondere Struktur von Bildern<br />
so ausgenutzt, daß der Informationsverlust der Kompression dem menschlichen Betrachter nur wenig<br />
auffällt. <strong>JPEG</strong> is nach seinen Schöpfern benannt, der Joint Photographic Expert Group.<br />
In <strong>JPEG</strong> werden verschiedene Techniken kombiniert: Huffman-Kodierung, Quantisierung, Fourier-<br />
Transformation, Differentielle Kodierung und andere.<br />
Ziel dieser <strong>Aufgabe</strong> ist es, ein C-Programm zu schreiben, welches eine <strong>JPEG</strong>-Datei der einfachsten<br />
und häufigsten Art (nicht-differentielle Huffman-Kodierung, drei Farbebenen, keine Zusatzfunktionen)<br />
dekodiert und dekomprimiert. Als Ergebnis soll eine Datei im PPM (portable pixmap)-Format erzeugt<br />
werden.<br />
Digitalisierte Bilder: das PPM-Format<br />
<strong>Ein</strong> digitalisiertes Bild besteht aus einzelnen Bildpunkten, Pixel genannt. PPM ist eine ”naive”Form der<br />
Speicherung dieser Pixel ohne jegliche Komprimierung oder Umordnung der Daten. <strong>Ein</strong>e PPM-Datei<br />
beginnt mit einer Kennung. Das ist eine Zeile, die nur aus den 2 Zeichen ”P6” besteht. Es folgt eine<br />
Zeile, die aus 2 Zahlen besteht, der Breite w und Höhe h des Bildes in Pixel ( z.B. ”608 320”) und eine<br />
weitere Zeile, die für unsere Zwecke nur aus der Zahl ”255” besteht. An diese 3 Zeilen schließt sich ein<br />
Strom von 3 ∗ w ∗ h Bytes an. Jeweils 3 Bytes kodieren Farbe und Helligkeit eines Pixels. Dazu wird<br />
jedes Byte als vorzeichenlose ganze Zahl zwischen 0 und 255 interpretiert. Die 3 Zahlen beschreiben<br />
die Intensität des Rot-, Grün- und Blauanteils (RGB-Kodierung): (0, 0, 0) ist Schwarz, (255, 255, 255)<br />
Weiß, (230, 230, 0) ein helles Gelb etc. Die Pixeldaten stehen einfach ”in Leserichtung” (Zeile für Zeile<br />
von links nach rechts und oben nach unten) hintereinander.<br />
Huffman-Kodierung<br />
Die Huffman-Kodierung ist ein verlustfreier Kompressionsalgorithmus. <strong>Ein</strong> Alphabet soll so kodiert<br />
werden, daß die häufigere Zeichen durch kürzere Bit-Sequenzen dargestellt werden (ein Prinzip, von<br />
dem sich schon Samuel Morse bei der Erfindung seines Morsealphabets leiten ließ).<br />
Wie bei wohl allen Kompressionsalgorithmen ist das Entpacken wesentlich einfacher als das Packen.<br />
Letzteres erfordert eine umfangreiche Analyse der Input-Daten, ersteres bloß das Anwenden eines mitgelieferten<br />
Rezeptes.<br />
Das Rezept zur Huffman-Dekodierung läßt sich am einfachsten in Form einer Baumstruktur kodieren.<br />
Nehmen wir als Beispiel die Sprache ”Vokalisch”, deren Alphabet die 5 Buchstaben A,E,I,O,U umfaßt.<br />
Der häufigste Buchstabe sei E, gefolgt von O,A,I und U.<br />
0 1<br />
E<br />
A<br />
0<br />
<br />
U<br />
0 1<br />
0<br />
1<br />
1<br />
I<br />
O<br />
Der Datenstrom 101111101000 wird mit Hilfe des Huffman-Baums<br />
so dekodiert: Wir starten an der Wurzel des Baumes (wie in der<br />
Informatik üblich, steht der Baum auf dem Kopf) und nehmen die<br />
Bitfolge als Wegbeschreibung: 0 heißt den linken Weg nehmen, 1 den<br />
rechten Weg nehmen. Wenn wir eine Zweigspitze erreicht haben, steht<br />
ein Buchstabe fest und wir beginnen wieder an der Wurzel. Damit<br />
führt uns 1011 zu I, 11 zu O und 1010 zu U, 0 zu E und 0 zu E:<br />
IOUEE.<br />
Obwohl wir es mit einem Code variabler Länge zu tun haben, ist kein spezielles Trennzeichen nötig.<br />
Natürlich ist die Kompression nicht optimal. Sie könnte verbessert werden, indem man Silben aus<br />
2 Buchstaben in den Huffman-Baum aufnimmt, dann Silben aus 3 Buchstaben etc.<br />
1
Der <strong>JPEG</strong>-Algorithmus verwendet mehrere Huffman-Bäume, allerdings auf recht indirekte Art und<br />
Weise.<br />
Beschreibung des <strong>JPEG</strong>-Formats<br />
Im Folgenden wird das <strong>JPEG</strong>-Format beschrieben, wobei die Kapitel dieser Beschreibung (Farbmodell,<br />
Downsampling, Quantisierung, Fourier-Transformation, Huffman-Dekodierung, Analyse der Datei) vom<br />
Dekompressor in umgekehrter Reihenfolge angewandt werden müssen.<br />
Farbmodell<br />
<strong>JPEG</strong> verwendet nicht das RGB-Farbmodell, sondern eines, das YUV genannt wird: 3 Zahlen beschreiben<br />
Helligkeit (Y), Blauanteil (U) und Rotanteil (V) eines Pixels. Die Umrechnung in RGB ist durch<br />
die lineare Transformation ⎛<br />
⎝ R ⎞ ⎛<br />
G ⎠ = ⎝ 1 0 ⎞ ⎛<br />
8<br />
5<br />
1 − 1 3<br />
− 4 ⎠ ⎝ Y ⎞<br />
5<br />
U ⎠<br />
B 1 2 0 V<br />
möglich.<br />
Implementierungshinweis: Die YUV-Werte, die man nach Fourier-Transformation und Dequantisierung<br />
aus den <strong>JPEG</strong>-Daten erhält, sind vorzeichenbehaftet, liegen zwischen -128 und 127.<br />
Dasselbe gilt auch für die RGB-Werte, die obige Transformation liefert. Manchmal können sie auch<br />
über diesen Bereich hinausschießen. Um sie z.B. mit der Funktion putc (die ihr Argument zu einem<br />
unsigned char konvertiert) korrekt in eine PPM-Datei ausgeben zu können, empfiehlt es sich:<br />
a) 128 zu addieren,<br />
b) zu testen, ob der Wert nun zwischen 0 und 255 liegt und kleinere Werte durch 0, größere durch<br />
255 zu ersetzen.<br />
Downsampling<br />
• Zentrales Objekt der Kompressionsalgorithmen ist eine ”data unit”. Dies ist eine 8 × 8-Matrix von<br />
Y-, U- oder V-Werten. Im einfachsten Fall beschreibt sie auch einen Bildausschnitt von 8 × 8<br />
Pixeln. Durch eine (verlustbehaftete) Kompressionsmethode namens ”Downsampling” kann sie<br />
jedoch auch die Informationen für bis zu 32×32 Pixel enthalten. Dabei wird jeweils ein kleiner (z.B.<br />
2×2) Block von Pixeln durch ein ”gemitteltes”Pixel ersetzt. Üblicherweise wird das Downsampling<br />
nicht auf die Helligkeit (Y) angewendet, sondern nur auf die Farbkomponenten. Dadurch bleibt<br />
der wahrnehmbare Qualitätsverlust gering.<br />
• Für jede der 3 Farbkomponenten YUV wird das Downsampling beschrieben durch 2 Parameter<br />
(h, w) (height, width): Blöcke aus h × w Pixel wurden duch einen Pixel ersetzt. h und w können<br />
Werte zwischen 1 und 4 annehmen. Beim Entpacken muß also ein ”Upsampling” durchgeführt<br />
werden, bei dem die 8 × 8-Matrix zu einer( 8h × 8w-Matrix ) aufgebläht wird, indem man jeden<br />
<strong>Ein</strong>trag x durch eine h × w-Blockmatrix x x<br />
ersetzt. Für die Downsampling-Parameter<br />
h Y , h V , h U muß gelten kgV(h i ) = max(h i ) und kgV(w i ) = max(w i ). ∗<br />
x<br />
• Das Bild wird in kleine Rechtecke aus (8∗max(h i )×8∗max(w i )) Pixel zerlegt, die MCUs (Minimal<br />
Coding Units) genannt werden. Nehmen wir an, die Y-Information wird nicht ”downsampled”und<br />
die U- und V-Komponente werden um die Faktoren 2×2 bzw 4×4 ”downsampled”. Dann beschreibt<br />
eine MCU 32 × 32 Pixel des Bildes und sie ist kodiert durch 16 Y-data-units, 4 U-data-units und<br />
eine V-data-unit. In genau dieser Reihenfolge sind sie auch im Datenstrom gespeichert, wobei die<br />
16 Y-data-units (und 4 U-data-units) in ”Lesereihenfolge” zeilenweise von links nach rechts und<br />
oben nach unten abgespeichert sind.<br />
x<br />
∗ kgV: kleinstes gemeinsames Vielfaches. Dies bedeutet einfach, daß die elementaren Quadrate oder Rechtecke, in die das<br />
Bild für die verschiedenen Farbkomponenten zerschnitten wird, ”ineinander aufgehen” müssen.<br />
2
• Der Datenstrom, der das Gesamtbild beschreibt, besteht aus einer Folge von MCUs<br />
([16Y, 4U, 1V ], [16Y, 4U, 1V ], . . .), ebenfalls in ”Lesereihenfolge”.<br />
• Nach der Dekomprimierung hat man ein Bild, dessen Größe ein Vielfaches der MCU-Größe ist.<br />
Um es auf die Größe (H, W ) des Originals zu bringen, muß man eventuell rechts und unten ein<br />
paar Pixel wegschneiden.<br />
Fourier-Transformation und Quantisierung<br />
Der Schlüssel zur hohen Kompressionsrate von <strong>JPEG</strong> ist, daß die Bilddaten nicht direkt komprimiert,<br />
sondern vorher Fourier-transformiert werden. Die verwendete spezielle Form der Fourier-Transformation<br />
wird als ”Diskrete Cosinus-Transformation” (DCT) bezeichnet. Die DCT führt eine 8 × 8-Matrix von<br />
Farbwerten in eine 8 × 8-Matrix über, deren [0, 0]-Komponente den Mittelwert der Originaldaten beschreibt,<br />
die anderen beschreiben die Amplituden der verschiedenen Schwankungen um diesen Wert.<br />
Zur Rücktransformation sind folgende Schritte nötig:<br />
• Die <strong>JPEG</strong>-Datei enthält für jede Komponente (Y, U und V) eine Quantisierungstabelle aus 64<br />
ganzen Zahlen. Jede der 64 Zahlen du[i], i=0...63 einer data-unit ist mit der entsprechenden<br />
Zahl aus der Quantisierungstabelle zu multiplizieren. Das Ergebnis ist in Fließkommazahlen<br />
umzuwandeln.<br />
• Aus diesen 64 Zahlen wird jetzt eine 8 × 8-Matrix aufgebaut. Um eine optimale Kompression<br />
zu erreichen, sind diese Zahlen allerdings nicht zeilen- oder spaltenweise angeordnet, sondern im<br />
Zigzag. Das folgende Codestück beschreibt die notwendige Umordnung:<br />
const int zigzag[8][8] ={ { 0, 1, 5, 6,14,15,27,28},<br />
{ 2, 4, 7,13,16,26,29,42},<br />
{ 3, 8,12,17,25,30,41,43},<br />
{ 9,11,18,24,31,40,44,53},<br />
{10,19,23,32,39,45,52,54},<br />
{20,22,33,38,46,51,55,60},<br />
{21,34,37,47,50,56,59,61},<br />
{35,36,48,49,57,58,62,63}};<br />
for(i=0; i
• Der DC-Baum dient nur zum <strong>Ein</strong>lesen des ersten Wertes du[0]:<br />
• Lese Bits vom Datenstrom und laufe durch den DC-Baum, bis eine Spitze erreicht ist. Der<br />
an dieser Spitze gespeicherte Wert s gibt die Anzahl der nun einzulesenden Bits an.<br />
• Die nächsten s Bits aus dem Datenstrom sind als positive ganze Zahl n zu interpretieren.<br />
• Diese Zahl wird in eine vorzeichenbehaftete Zahl konvertiert nach der Vorschrift<br />
m = extend(n, s)<br />
{ n if n ≥ 2<br />
s−1<br />
extend(n, s) =<br />
n + 1 − 2 s otherwise<br />
• Dieser Wert m ist nur dann gleich du[0], wenn es sich um die erste data-unit des Bildes zu<br />
einer Farbkomponente handelt. Sonst ist m die Differenz zum Wert du[0] der vorhergehenden<br />
data-unit derselben Farbkomponente.<br />
• Zur Dekodierung der übrigen 63 Werte wird der AC-Baum verwendet. Die an dessen Spitzen<br />
gespeicherten Bytes (Zahlen x zwischen 0 und 255) sind als zwei 4-Bit-Zahlen (”nibbles”)<br />
(r, s) = (x/16, mod (x, 16)) zu interpretieren. Die Dekodierung erfordert folgende Schritte:<br />
1. Lese Bits aus dem Datenstrom ein, bis eine Spitze (r, s) des AC-Baumes ereicht ist.<br />
2. Wenn r = s = 0, fülle die restlichen Felder von du[] mit Nullen, die data-unit ist fertig<br />
gelesen.<br />
3. Wenn r > 0 ist, werden die nächsten r Felder in du[] mit Nullen gefüllt.<br />
4. Dann werden die die nächsten s Bits aus dem Datenstrom gelesen und das Ergebnis von<br />
extend(n, s) wird in das nächste freie Feld von du[] gespeichert.<br />
5. Wiederhole 1.-4., bis alle Felder von du[] gefüllt sind.<br />
Aufbau einer <strong>JPEG</strong>-Datei<br />
<strong>Ein</strong>e <strong>JPEG</strong>-Datei besteht aus einer Folge von Segmenten verschiedenen Typs. Segmente beginnen mit<br />
2 Startbytes, die den Segmenttyp kodieren, gefolgt von 2 Bytes, die die Länge L des Segments (ohne<br />
die beiden Startbytes) angeben und von L − 2 Datenbytes. <strong>Ein</strong>e Ausnahme sind die Segmente ”Start<br />
of Image” (SOI) am Dateianfang und ”End of Image” (EOI) am Dateiende, sie bestehen nur aus den<br />
2 Bytes FFD8 (SOI) und FFD9 (EOI). In der folgenden Übersicht steht ein ∗ für ein Halbbyte (4 Bits),<br />
auch nibble genannt.<br />
SOI Start of Image<br />
FFD8<br />
EOI End of Image<br />
FFD9<br />
COM Comment<br />
FFFE ∗ ∗ ∗∗<br />
L<br />
L-2 Bytes<br />
Kommentartext<br />
DQT Define Quantization Table<br />
FFDB ∗ ∗ ∗∗ ∗∗ 64 Bytes . . . ∗∗ 64 Bytes L = 2 + 65 ∗ n<br />
L i 1 QT 1 i n QT n<br />
SOF Start of Frame<br />
FFC0 ∗ ∗ ∗∗ ∗∗ ∗ ∗ ∗∗ ∗ ∗ ∗∗ ∗∗ 3 Bytes . . . 3 Bytes L = 2 + 6 + 3 ∗ ne<br />
L cd H W ne Ebene 1 Ebene ne<br />
Jede Ebene hat den Aufbau ∗∗ ∗ ∗ ∗∗<br />
i e w h n qt<br />
4
DHT Define Huffman Table<br />
FFC4 ∗ ∗ ∗∗ ∗ ∗ 16 Bytes data . . . ∗ ∗ 16 Bytes data<br />
L tc th ns[i] tc th ns[i]<br />
L = 2 + 1 + 16 + ∑ 15<br />
i=0 ns[i] + . . . + 1 + 16 + ∑ 15<br />
i=0 ns[i]<br />
SOS Start of Scan<br />
FFDA ∗ ∗ ∗∗ ∗∗ 2 Bytes . . . 2 Bytes ∗ ∗ ∗ ∗ ∗∗ data<br />
L nc Komponente 1 Komponente nc 3 Bytes<br />
Jede Komponente hat den Aufbau ∗∗ ∗ ∗<br />
i c td ta<br />
Es gibt noch eine Reihe weiterer Segmente. Wenn Segmente mit den Kennungen FFC1, FFC2, FFC3,<br />
FFC5, FFC6, FFC7, FFC9, FFCA, FFCB, FFCC, FFCD, FFCE oder FFCF auftauchen, soll unser<br />
Entpacker abbrechen. Sie bedeuten, daß noch andere Kompressionsalgorithmen (differentielle arithmetische<br />
Kodierung, differentielle Huffman-Kodierung u.a.) angewendet wurden.<br />
Andere Segmente der Form<br />
FF∗∗ ∗ ∗ ∗∗<br />
L-2 Bytes<br />
können ignoriert werden. Sie könenn z.B. ”thumbnails” (”daumennagelgroße” Varianten des Bildes, die<br />
ein Programm zur Vorschau o.ä. verwenden kann) enthalten.<br />
Beschreibung der Segmente<br />
DQT <strong>JPEG</strong>-Dateien können bis zu 4 Quantisierungstabellen enthalten (auch wenn die hier besprochene<br />
Variante nur 3 verwendet). Sie können durch mehrere DQT-Segmente oder gemeinsam in einem<br />
DQT-Segment gespeichert sein. Die Anzahl der Tabellen in einem DQT-Segment läßt sich aus der Längenangabe<br />
L ableiten. Jede Tabelle hat eine Nummer i n . Die Tabelle selbst besteht aus 64 Bytes, von<br />
denen jedes eine Zahl zwischen 0 und 255 repräsentiert.<br />
Implementierungshinweis:<br />
int i;<br />
i=getc(filepointer);<br />
liest ein Byte korrekt in diesem Sinne (als Zahl zwischen 0 und 255) ein, weil getc() das eingelesene<br />
Byte als unsigned char interpretiert. Ebenso kann man die durch 2 Bytes kodierten Zahlen (wie<br />
die Längenangabe L) einlesen:<br />
int i,L;<br />
i=getc(filepointer);<br />
L=256*i+getc(filepointer);<br />
SOF<br />
<strong>Ein</strong> Frame enthält globale Informationen über das Bild.<br />
• dc (1 Byte) enthält die Farbtiefe (color depth). Für unsere Zwecke sollte sie 8 sein: Jeder Farbwert<br />
wird mit 8 Bit (einem Byte) kodiert.<br />
• H und W (je 2 Byte) geben die tatsächliche Größe des Bildes (Höhe und Breite in Pixeln) an.<br />
• ne (1 Byte) gibt die Anzahl der Farbkomponenten an. Unser Dekoder soll nur mit genau 3 Farbkomponenten<br />
umgehen können. Es gibt auch schwarzweiße <strong>JPEG</strong>-Dateien mit nur einer Farbkomponente.<br />
• Es folgen jeweils 3 Bytes für jede Farbkomponente. Sie enthalten eine Nummer i e und die Downsampling-Parameter<br />
w und h dieser Farbkomponente sowie die Nummer der Quantisierungstabelle<br />
n qt , die für diese Komponente zu verwenden ist. Es ist die Tabelle zu verwenden, für die i n = n qt<br />
gilt.<br />
5
¡<br />
!<br />
"<br />
!<br />
"<br />
!<br />
"<br />
!<br />
"<br />
!<br />
"<br />
#<br />
$<br />
#<br />
$<br />
#<br />
$<br />
#<br />
$<br />
#<br />
$<br />
#<br />
$<br />
§¦<br />
©¨<br />
DHT<br />
Implementierungshinweis: Aus diesen Daten läßt sich die Größe einer MCU sowie die<br />
Anzahl der MCUs berechnen. Hierbei ist daran zu denken, daß man aufrunden muß, wenn H<br />
oder W nicht durch die Höhe oder Breite einer MCU teilbar ist. Die ”nibbles” h und w lassen<br />
sich so einlesen:<br />
int x,h,w;<br />
x=getc(filepointer);<br />
w=x/16;<br />
h=x%16;<br />
• Für jede Farbkomponente Y,U und V werden 2 Huffman-Bäume benötigt, ein DC- und ein AC-<br />
Baum. Die Bäume können in einem odere mehreren DHT-Feldern beschrieben sein. Sie sind numeriert<br />
durch die nibbles tc und th. Wenn tc ungleich 0 ist, dient der Baum als AC-Baum, wenn<br />
tc gleich 0 ist, als DC-Baum. th ist seine Nummer.<br />
• Es folgt eine 16 Bytes lange Liste ns[]. Die Bytes geben an, wieviele Huffman-Kodes der Länge<br />
1 Bit, 2 Bit, . . . 16 Bit abgespeichert sind. (Für unseren Vokal-Beispielbaum hieße das ns =<br />
[1, 1, 1, 2, 0, . . .].) Danach folgt eine Liste der an den Zweigspitzen zu speichernden Daten, nach<br />
aufsteigender Länge der Kodes sortiert. Diese Liste ist somit ∑ 15<br />
i=0<br />
ns[i] Bytes lang.<br />
• Die Daten sind Bytes, Zahlen zwischen 0 und 255. Bei den AC-Bäumen werden sie später als 2<br />
nibbles interpretiert.<br />
• Diese Daten bestimmen einen Baum eindeutig, wenn er so konstruiert wird, daß der Abstand<br />
Spitze-Wurzel von links nach rechts zunimmt.<br />
B<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
¢£¤¥<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
A D E F G<br />
<br />
%% && ''' (((<br />
<br />
&& ''' ((( %%<br />
%% && ((( '''<br />
((( ''' && %%<br />
%% && ((( )) ** '''<br />
)) ** ((( ''' && %%<br />
C<br />
** ))<br />
)) **<br />
** ))<br />
H<br />
++ ,,<br />
,, ++<br />
++ ,,<br />
,, ++<br />
.. /// 000 --<br />
++ ,,<br />
,, -- .. /// 000 ++ <br />
-- .. 000 ///<br />
000 /// .. --<br />
<strong>Ein</strong> Beispiel: Sei ns = [0, 1, 5, 1, 1] und die 8 Bytes<br />
in diesem Baum sind in der Reihenfolge BADEFG-<br />
CH gegeben. Damit sind die Huffman-Kodes B=00,<br />
A=010, D=011, E=100, F=101, G=110, C=1110,<br />
H=11110. Die letzte Spitze bleibt leer.<br />
Implementierungshinweis: Material zu Bäumen und anderen Datenstrukturen findet man<br />
z.B. in R. Sedgewick, Algorithmen in C. <strong>Ein</strong>e Möglichkeit ist, einen Knoten des Baumes als<br />
Struktur mit Zeigern auf die Unterknoten zu definieren und den Baum gemäß den DHT-Daten<br />
durch Aufrufe von createNode zu erzeugen. Dies kann z.B. auch rekursiv geschehen.<br />
struct node { unsigned char Item; struct node *l; struct node *r; };<br />
struct node * createNode(unsigned char a){<br />
struct node * x = malloc(sizeof(struct node));<br />
x->l=0;<br />
x->r=0;<br />
x->Item=a;<br />
return x;<br />
}<br />
SOS Das eigentliche Datenfeld. Bevor es losgeht mit dem Strom Huffman-kodierter und komprimierter<br />
MCUs, folgen nochmal Daten über die Farbkomponenten. Im nc-Byte steht nochmal deren Anzahl, dann<br />
folgt für jede Komponente ein 2-Byte-Feld, das aus 3 Zahlen besteht: einem Index i c und den Nummern<br />
td und ta der für diese Komponente zu verwendenden DC- und AC-Huffmanbäume. Die Daten mit dem<br />
Index i c ergänzen die Daten aus dem SOF-Segment mit dem gleichen Index i e .<br />
Es folgen noch 3 Bytes, deren Inhalt für die hier beschriebene <strong>JPEG</strong>-Variante irrelevant ist.<br />
Beim <strong>Ein</strong>lesen des folgenden Datenstroms ist eine Besonderheit zu beachten: Er endet, wenn ein neuer<br />
Segment-Markierer kommt, d.h., ein FF-Byte gefolgt von einem Byte ungleich 0. In der Regel wird das<br />
der EOI-Marker FFD9 und auch das Ende der Datei sein.<br />
Nun können solche Sequenzen natürlich auch als Teil der Bilddaten entstehen. Deshalb gilt eine<br />
Sonderregel: Wenn die Bilddaten ein FF-Byte enthalten, wird ihm ein zusätzliches 00-Byte angehängt.<br />
Dieses 00-Byte ist nicht Teil der Bilddaten und muß beim <strong>Ein</strong>lesen herausgefiltert werden.<br />
6