JPEG File Documentation
Summary
A JPEG Image is a lossy, DCT-compressed raster photo, the most common picture format on the web, cameras and phones. The extension .jpeg and its shorter twin .jpg are byte-for-byte the same format; you can rename one to the other freely. Its MIME type is image/jpeg, and the standard is ISO/IEC 10918-1 (1992). Because compression is lossy, every re-save discards a little more detail.
Technical details
| Feature | Value |
|---|---|
| Full name | JPEG Image (Joint Photographic Experts Group) |
| File extension | .jpeg (identical to .jpg) |
| MIME type | image/jpeg |
| Format type | Lossy-compressed raster bitmap (DCT-based) |
| Developer | Joint Photographic Experts Group (ISO/IEC + ITU-T) |
| Introduced | 1992 |
| Standard | ISO/IEC 10918-1; ITU-T Recommendation T.81 |
| Open standard | Yes — baseline coding patents have expired |
| Color model | YCbCr (from RGB); grayscale and CMYK also possible |
| Bit depth | 8 bits per channel (24-bit colour); 12-bit in the extended mode |
| Compression | Lossy, discrete cosine transform (DCT) with quantization |
| Entropy coding | Huffman (baseline); optional arithmetic coding |
| Chroma subsampling | 4:4:4, 4:2:2, 4:2:0 (4:2:0 is the common default) |
| Transparency | None — no alpha channel |
| Animation | None |
| Max dimensions | 65535×65535 pixels (16-bit width/height fields) |
| Byte order | Big-endian (marker and segment lengths) |
| Magic number | FF D8 FF (SOI marker + start of next marker) |
| Container | JFIF (APP0) or Exif (APP1); both wrap the same coded scan |
| Metadata | Exif, IPTC, XMP, embedded ICC profile |
| Related extensions | .jpg .jfif .heic .webp |
| Specification | w3.org/Graphics/JPEG/itu-t81.pdf |
What is a JPEG file?
JPEG stands for Joint Photographic Experts Group, the committee that wrote the standard, and by extension the coding scheme itself. It is a lossy raster image format: a photograph is compressed by throwing away detail the eye is least likely to notice, which shrinks the file dramatically at the cost of pixel-exact fidelity. The standard was published in 1992 as ISO/IEC 10918-1 and, word for word, as ITU-T Recommendation T.81. That document defines the coding process. It does not define a file on disk. The bytes you actually get are wrapped in one of two interchange formats layered on top: JFIF (the original 1992 wrapper) or Exif (the camera/phone wrapper that carries date, exposure and GPS metadata).
The .jpeg and JPG extensions name the identical format. There is no byte difference between them. The three-letter .jpg spelling exists only because early DOS and Windows filesystems limited extensions to three characters under the 8.3 naming rule, so .jpeg was truncated. You can rename one to the other with no effect on the data. What follows is about the real mechanics: the markers that frame the file, the colour transform, the 8×8 DCT, quantization (where the loss happens), and the entropy coding that packs the result.
Markers and segment framing
A JPEG file is a stream of markers. Every marker is two bytes: FF followed by a code byte that is neither 00 nor FF. Some markers stand alone (SOI, EOI). Most introduce a segment: the two marker bytes are followed by a 2-byte big-endian length, and that length counts itself plus the payload but not the marker. So a parser reads FF xx, reads the 16-bit length L, then consumes L−2 payload bytes and looks for the next FF.
FF D8 SOI Start Of Image (no length, no payload)
FF E0 LL LL ... APP0 JFIF header segment (or E1 = Exif)
FF DB LL LL ... DQT Define Quantization Table
FF C0 LL LL ... SOF0 Start Of Frame, baseline sequential
FF C4 LL LL ... DHT Define Huffman Table
FF DA LL LL ... SOS Start Of Scan --> entropy-coded data follows
<compressed scan bytes, no length field>
FF D9 EOI End Of Image
The first two bytes are always the SOI marker FF D8. The fourth byte is what distinguishes the two common wrappers: FF E0 is APP0, whose payload begins with the ASCII string JFIF\0 (4A 46 49 46 00); FF E1 is APP1, whose payload begins with Exif\0\0 (45 78 69 66 00 00) and then a TIFF-structured block of Exif tags. The APPn markers occupy the range FF E0 through FF EF, sixteen application slots. A file can carry several: Exif in APP1, an ICC colour profile split across APP2 segments, XMP packets in APP1 as well.
| Marker | Bytes | Meaning |
|---|---|---|
| SOI | FF D8 | Start Of Image |
| APP0 | FF E0 | JFIF header (density, thumbnail) |
| APP1 | FF E1 | Exif or XMP metadata |
| DQT | FF DB | Define Quantization Table |
| SOF0 | FF C0 | Start Of Frame, baseline DCT |
| SOF2 | FF C2 | Start Of Frame, progressive DCT |
| DHT | FF C4 | Define Huffman Table |
| DRI | FF DD | Define Restart Interval |
| SOS | FF DA | Start Of Scan (entropy data follows) |
| EOI | FF D9 | End Of Image |
An annotated header hex dump
Here are the opening bytes of a typical JFIF file and, below it, the opening of a camera file that leads with Exif. The offsets are byte positions from the start of the file.
Offset Bytes Meaning
000000 FF D8 SOI, start of image
000002 FF E0 00 10 APP0 marker, segment length 0x0010 = 16
000006 4A 46 49 46 00 "JFIF\0" identifier
00000B 01 01 JFIF version 1.01
00000D 00 density units: 0 = aspect ratio only
00000E 00 01 00 01 X density 1, Y density 1
000012 00 00 thumbnail width 0, height 0 (no thumb)
000014 FF DB 00 43 DQT marker, length 0x0043 = 67
...
Offset Bytes Meaning
000000 FF D8 SOI
000002 FF E1 XX XX APP1 marker, segment length
000006 45 78 69 66 00 00 "Exif\0\0" identifier
00000C 49 49 2A 00 TIFF header, "II" = little-endian, 0x002A
000010 08 00 00 00 offset to first IFD (Image File Directory)
... Exif tags: make, model, exposure, GPS
One quirk shows up here: the Exif metadata block is internally a little-endian (or big-endian) TIFF structure, so byte order inside the APP1 payload is set by that II/MM tag and is independent of the big-endian marker framing around it. The FF DB at offset 0x14 in the first dump is the first quantization table, which is where compression settings begin.
YCbCr, chroma subsampling and the MCU
Before any transform, the encoder converts RGB into YCbCr: one luma channel Y (brightness) and two chroma channels Cb and Cr (blue-difference and red-difference). The point of the split is that human vision is far more sensitive to brightness than to colour, so the two chroma channels can be sampled at lower resolution with little visible effect. That reduction is chroma subsampling, written as a three-part ratio.
| Subsampling | Chroma resolution | Effect |
|---|---|---|
4:4:4 | Full — one Cb/Cr sample per pixel | No colour loss, largest file |
4:2:2 | Halved horizontally | Common in video capture |
4:2:0 | Halved horizontally and vertically | Default for photos, smallest |
With 4:2:0, one Cb value and one Cr value cover a 2×2 block of luma samples, so the two colour channels together carry a quarter of the samples of the luma channel. This is why sharp coloured edges (red text on grey, for instance) can look smeared in a JPEG even at high quality: the colour detail was thrown out before the DCT ran.
Because the channels can have different sample resolutions, the encoder groups them into a minimum coded unit (MCU). For 4:2:0 an MCU is a 16×16 pixel region: four 8×8 luma blocks plus one 8×8 Cb block and one 8×8 Cr block covering the same area. For 4:4:4 an MCU is a single 8×8 region with one block per channel. The whole image is coded MCU by MCU, left to right, top to bottom.
The 8×8 forward DCT
Each channel is tiled into 8×8 blocks of samples. Before transforming, every sample has 128 subtracted from it (the level shift), moving 8-bit values from the 0–255 range into −128–127 so they centre on zero. The block then goes through a two-dimensional forward discrete cosine transform, which re-expresses those 64 spatial samples as 64 frequency coefficients.
The coefficient in the top-left corner is the DC coefficient: it is proportional to the average brightness of the whole block. The other 63 are AC coefficients, each describing how much of a particular horizontal-and-vertical wave pattern is present, from slow gradients near the top-left to fine detail in the bottom-right. The DCT itself is lossless in principle (it just rewrites the same information in a different basis). Nothing has been discarded yet. What it buys is a concentration of visually important energy into the low-frequency coefficients near the DC term, leaving many high-frequency coefficients close to zero.
Quantization and the DQT: where quality is set
This is the lossy step. Each of the 64 DCT coefficients is divided by a matching entry in an 8×8 quantization table and rounded to the nearest integer. Large divisors in the high-frequency positions push small coefficients to zero, which is exactly the detail the eye tolerates losing. The table is stored in the file in a DQT segment (FF DB), so a decoder can reverse the division by multiplying back.
coefficient_quantized = round( coefficient_dct / Q[i] ) // encode, lossy
coefficient_restored = coefficient_quantized * Q[i] // decode
The quality setting you move in an editor does one thing: it scales this table. A high quality number uses small divisors, so few coefficients round to zero and little is lost. A low number multiplies the table up, zeroing far more coefficients and shrinking the file, at the price of visible 8×8 blocking and ringing around edges. Because the loss is baked into the quantized coefficients, it is permanent, and re-encoding a JPEG quantizes an already-quantized image again. That compounding is generation loss: each save degrades a little further, which is why JPEG is an export format, not a working master. Lossless PNG or a camera RAW file is the right place to edit.
Zig-zag ordering and run-length of AC zeros
After quantization each 8×8 block is read out in a zig-zag order rather than row by row. The zig-zag path walks from the DC term diagonally outward, which tends to visit coefficients from lowest frequency to highest.
0 1 5 6 14 15 27 28
2 4 7 13 16 26 29 42
3 8 12 17 25 30 41 43
9 11 18 24 31 40 44 53
10 19 23 32 39 45 52 54
20 22 33 38 46 51 55 60
21 34 37 47 50 56 59 61
35 36 48 49 57 58 62 63
The payoff is that all the coefficients that quantization drove to zero end up clustered at the tail of the sequence. The AC coefficients are then encoded as run/size pairs: a count of how many zeros precede the next non-zero value, together with the bit-length of that value. A single special symbol, end-of-block, says “every remaining coefficient in this block is zero,” so a mostly-flat block collapses to a handful of symbols.
Huffman coding, the DHT and byte stuffing
The run/size symbols are finally compressed with Huffman coding: frequent symbols get short bit codes, rare ones get longer codes. The code tables live in DHT segments (FF C4). A baseline file carries up to four tables (a DC and an AC table for luma, and a DC and AC pair for chroma). DC coefficients get special treatment: only the difference from the previous block’s DC value is coded, because neighbouring blocks usually have similar average brightness, so the differences are small and compress well.
The entropy-coded data begins right after the SOS segment (FF DA) and, unlike every other part of the file, has no length field. It simply runs until the next marker. That creates a problem: what if a genuine FF byte appears in the compressed bitstream? The standard solves it with byte stuffing. Any FF produced by the entropy coder is written as FF 00, and a decoder reading the scan treats FF 00 as a literal FF data byte, not a marker. Only an FF followed by a non-zero code byte is a real marker, which is how the decoder knows it has reached the end of the scan (or a restart marker inside it).
Baseline versus progressive
The frame marker decides the coding mode. Baseline sequential (SOF0, FF C0) codes each block completely in one pass, MCU after MCU, so the image decodes top to bottom in a single sweep. Progressive (SOF2, FF C2) splits the same coefficients across several scans, each with its own SOS segment, so the picture appears at once at low fidelity and sharpens as more scans arrive.
Progressive mode uses two techniques, often combined. Spectral selection sends a range of the zig-zag coefficient indices in each scan: the DC and first few AC coefficients first (a blurry whole image), then higher-frequency bands later. Successive approximation sends the most significant bits of the coefficients first and refines with lower-order bits in later scans. The pixel data is identical to a baseline file at the end; progressive only reorders how the same coefficients are transmitted, which usually also compresses a few percent smaller. The trade-off is that a decoder must buffer and revisit the whole coefficient array across scans instead of finishing each block once.
Frequently asked questions
Is JPEG the same as JPG?
Yes, exactly the same. Both extensions name a file that starts with the SOI marker FF D8 FF and is coded per ISO/IEC 10918-1. There is no structural difference at all; .jpg is only the three-letter form left over from the DOS 8.3 filename limit. Renaming photo.jpeg to photo.jpg changes nothing in the bytes.
Why does re-saving a JPEG lose quality?
Because quantization is lossy and it runs again on every save. Opening a JPEG decodes and de-quantizes it, but the detail already zeroed out is gone. Saving re-applies the DCT and quantizes a second time, rounding coefficients that were already rounded, so new blocking and ringing accumulate. This generation loss is cumulative and cannot be undone. Keep an original in PNG, TIFF or RAW if you plan to edit repeatedly.
What does the quality setting actually change?
It scales the quantization tables stored in the DQT segments. A higher quality number means smaller divisors, so fewer DCT coefficients round to zero and more detail survives; a lower number multiplies the divisors up, zeroing more coefficients for a smaller file and more visible artefacts. It does not change dimensions or colour depth, only how aggressively frequency detail is discarded.
References
- ITU-T T.81 — the JPEG standard (ISO/IEC 10918-1)
- JPEG.org — official committee overview of JPEG
- Library of Congress — JPEG File Interchange Format (JFIF)
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.