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

FeatureValue
Full nameJPEG Image (Joint Photographic Experts Group)
File extension.jpeg (identical to .jpg)
MIME typeimage/jpeg
Format typeLossy-compressed raster bitmap (DCT-based)
DeveloperJoint Photographic Experts Group (ISO/IEC + ITU-T)
Introduced1992
StandardISO/IEC 10918-1; ITU-T Recommendation T.81
Open standardYes — baseline coding patents have expired
Color modelYCbCr (from RGB); grayscale and CMYK also possible
Bit depth8 bits per channel (24-bit colour); 12-bit in the extended mode
CompressionLossy, discrete cosine transform (DCT) with quantization
Entropy codingHuffman (baseline); optional arithmetic coding
Chroma subsampling4:4:4, 4:2:2, 4:2:0 (4:2:0 is the common default)
TransparencyNone — no alpha channel
AnimationNone
Max dimensions65535×65535 pixels (16-bit width/height fields)
Byte orderBig-endian (marker and segment lengths)
Magic numberFF D8 FF (SOI marker + start of next marker)
ContainerJFIF (APP0) or Exif (APP1); both wrap the same coded scan
MetadataExif, IPTC, XMP, embedded ICC profile
Related extensions.jpg .jfif .heic .webp
Specificationw3.org/Graphics/JPEG/itu-t81.pdf
File signature (magic bytes)
FF D8 FF

Offset 0. FF D8 is the SOI (Start Of Image) marker; the following FF begins the next marker. The 4th byte names the variant: E0 = JFIF (APP0), E1 = Exif (APP1, most camera and phone photos), DB = a bare quantization table. Every valid JPEG ends with the EOI marker FF D9.

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.

MarkerBytesMeaning
SOIFF D8Start Of Image
APP0FF E0JFIF header (density, thumbnail)
APP1FF E1Exif or XMP metadata
DQTFF DBDefine Quantization Table
SOF0FF C0Start Of Frame, baseline DCT
SOF2FF C2Start Of Frame, progressive DCT
DHTFF C4Define Huffman Table
DRIFF DDDefine Restart Interval
SOSFF DAStart Of Scan (entropy data follows)
EOIFF D9End 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.

SubsamplingChroma resolutionEffect
4:4:4Full — one Cb/Cr sample per pixelNo colour loss, largest file
4:2:2Halved horizontallyCommon in video capture
4:2:0Halved horizontally and verticallyDefault 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