JPEG
JPEG ist kein Dateiformat, sondern ein Kompressionsverfahren — die übliche Datei nennt man JFIF. Der Datenstrom besteht aus „Markern“: jeder beginnt mit dem Byte 0xFF, gefolgt von einem Markercode.
Die Datei startet mit dem SOI-Marker (FF D8) und endet mit EOI (FF D9). Dazwischen liegen Segmente mit Tabellen (Quantisierung, Huffman), Rahmenparametern (Bildgröße) und den eigentlichen komprimierten Bilddaten.
JPEG ist verlustbehaftet: Das Bild wird in 8×8-Blöcke zerlegt, per DCT in Frequenzen transformiert, quantisiert (hier geht Information verloren) und anschließend entropiekodiert.
FF D8 FF → SOI-Marker + Beginn des nächsten Markers🧩 Aufbau der Datei
So liegen die Bestandteile einer JPEG-Datei nacheinander im Speicher.
📐 Ablaufdiagramm (ASCII)
Schematischer Gesamtaufbau auf einen Blick.
┌──────────────────────────────────────────────────────────────┐ │ FF D8 ← SOI (Start of Image) │ ├──────────────────────────────────────────────────────────────┤ │ Marker-Schema (Segmente mit Länge): │ │ FF <code> <len_hi> <len_lo> <daten …> │ │ (len zählt 2 Längen-Bytes mit, aber NICHT den Marker) │ ├──────────────────────────────────────────────────────────────┤ │ FF E0 APP0 → "JFIF", Version, DPI, Thumbnail │ │ FF DB DQT → Quantisierungstabelle(n) │ │ FF C0 SOF0 → Breite, Höhe, Komponenten (Baseline DCT) │ │ FF C4 DHT → Huffman-Tabelle(n) │ │ FF DA SOS → Scan-Kopf ┐ │ │ <entropie-codierte 8×8-Blöcke …> (FF00 = escaped FF) │ │ FF D9 EOI → Ende │ └──────────────────────────────────────────────────────────────┘
📊 Tabellen & Referenz
Wichtige JPEG-Marker
| Code | Name | Bedeutung |
|---|---|---|
| FF D8 | SOI | Start of Image (keine Länge) |
| FF E0 | APP0 | JFIF-Header (Version, DPI) |
| FF E1 | APP1 | Exif-/XMP-Metadaten |
| FF DB | DQT | Quantisierungstabelle |
| FF C0 | SOF0 | Baseline-DCT-Rahmenkopf |
| FF C2 | SOF2 | Progressive-DCT-Rahmenkopf |
| FF C4 | DHT | Huffman-Tabelle |
| FF DA | SOS | Start of Scan (Bilddaten folgen) |
| FF D9 | EOI | End of Image (keine Länge) |
Pipeline der Kompression
| Schritt | Operation |
|---|---|
| 1 | Farbraum RGB → YCbCr |
| 2 | Chroma-Subsampling (z. B. 4:2:0) |
| 3 | Aufteilung in 8×8-Blöcke |
| 4 | Diskrete Cosinus-Transformation (DCT) |
| 5 | Quantisierung (verlustbehaftet!) |
| 6 | Zickzack + Huffman-Entropiekodierung |
⌨️ C-Strukturen & Auslese-Code
Einfache C-Strukturen, die exakt dem Byte-Layout entsprechen, plus Beispielcode zum Einlesen.
#include <stdint.h>
#define JPEG_SOI 0xD8 /* Start of Image */
#define JPEG_EOI 0xD9 /* End of Image */
#define JPEG_SOF0 0xC0 /* Baseline DCT */
#define JPEG_DHT 0xC4 /* Huffman-Tabelle*/
#define JPEG_DQT 0xDB /* Quant.-Tabelle */
#define JPEG_SOS 0xDA /* Start of Scan */
#define JPEG_APP0 0xE0 /* JFIF */
/* Ein Marker mit Länge: FF <code> <length(2)> <payload...> */
typedef struct {
uint8_t prefix; /* immer 0xFF */
uint8_t code; /* Markercode, z. B. 0xC0 */
uint16_t length; /* Big-Endian, inkl. dieser 2 Bytes */
/* uint8_t payload[length - 2]; */
} jpeg_marker_t;ℹ️ Alle Längen in JPEG sind Big-Endian. SOI, EOI und RSTn haben KEINE Längen-/Datenfelder.
#pragma pack(push, 1)
typedef struct {
uint8_t precision; /* meist 8 Bit pro Sample */
uint16_t height; /* Bildhöhe (Big-Endian) */
uint16_t width; /* Bildbreite (Big-Endian) */
uint8_t num_components; /* 1 = Graustufen, 3 = YCbCr */
} jpeg_sof0_t;
typedef struct {
uint8_t id; /* Komponenten-ID (1=Y,2=Cb,3=Cr)*/
uint8_t sampling; /* hi nibble = H, lo nibble = V */
uint8_t quant_table_id; /* Index der DQT-Tabelle */
} jpeg_component_t;
#pragma pack(pop)#include <stdio.h>
#include <arpa/inet.h> /* ntohs */
int jpeg_scan_markers(FILE *f) {
int b;
if (fgetc(f) != 0xFF || fgetc(f) != JPEG_SOI) return -1; /* SOI? */
while ((b = fgetc(f)) != EOF) {
if (b != 0xFF) continue; /* auf 0xFF synchronisieren */
int code = fgetc(f);
if (code == JPEG_EOI) break; /* fertig */
if (code == JPEG_SOI || code == 0xFF) continue;
uint16_t len;
fread(&len, 2, 1, f);
len = ntohs(len) - 2; /* Längenbytes herausrechnen */
printf("Marker FF%02X, %u Byte Nutzdaten\n", code, len);
fseek(f, len, SEEK_CUR); /* Segment überspringen */
if (code == JPEG_SOS) break; /* danach kommen Bilddaten */
}
return 0;
}ℹ️ Innerhalb der Scan-Daten wird 0xFF als 0xFF 0x00 „gestuffed“, um echte Marker abzugrenzen.