How JPEG Encodes Image Width and Height
In digital image processing, extracting dimensions from a JPEG file without decoding pixel data relies on parsing specific metadata stored in the file stream. JPEG stores its structural information in segments marked by distinct two-byte codes, where the image dimensions—specifically height and width—reside within a Start of Frame (SOF) marker segment. This article explains how to locate the SOF marker and decode the binary values that define an image's pixel dimensions.
Locating the Start of Frame (SOF) Marker
JPEG files consist of a sequence of chunks called "marker segments."
Every marker begins with the byte 0xFF followed by a single
byte identifying the marker type.
Dimensions are contained within the Start of Frame (SOFn) header.
Depending on the encoding process used (such as baseline, progressive,
or lossless), the SOF marker byte varies between 0xC0 and
0xCF (excluding 0xC4, 0xC8, and
0xCC, which are used for table definitions). The two most
common markers are:
0xFF 0xC0(SOF0): Baseline Discrete Cosine Transform (DCT)0xFF 0xC2(SOF2): Progressive DCT
A parser scans past the Start of Image (SOI) marker
(0xFF 0xD8) and skips preceding markers (such as APPn or
DQT segments by reading their length fields) until it encounters an SOFn
marker.
Byte Layout of the SOF Segment
Once the SOF marker is identified, the frame header follows a strict byte-level structure:
- Marker Code (2 bytes):
0xFFfollowed by the SOF identifier (e.g.,0xC0). - Frame Header Length (2 bytes): The total length of
the SOF segment (excluding the initial
0xFF 0xXXmarker itself). - Data Precision (1 byte): Bits per sample (commonly
8for standard JPEG images, sometimes12). - Image Height (2 bytes): The number of lines in the image.
- Image Width (2 bytes): The number of samples per line.
- Number of Components (1 byte): Color channels
(e.g.,
1for grayscale,3for YCbCr,4for CMYK). - Component Specification Parameters (3 bytes per component): Component IDs and sampling factors.
Decoding the Height and Width Values
Both the height and width are stored as 16-bit unsigned integers in big-endian (network byte order) format, occupying four consecutive bytes immediately following the 1-byte data precision field:
- Height (Bytes 5 and 6 of the SOF payload): \[\text{Height} = (\text{Byte}_5 \times 256) + \text{Byte}_6\]
- Width (Bytes 7 and 8 of the SOF payload): \[\text{Width} = (\text{Byte}_7 \times 256) + \text{Byte}_8\]
For example, consider the following raw hex sequence representing an SOF0 segment:
FF C0 00 11 08 04 38 07 80 03 ...
FF C0: SOF0 Marker00 11: Header length (17 bytes)08: Precision (8 bits)04 38: Image Height ((0x04 << 8) | 0x38= 1080 pixels)07 80: Image Width ((0x07 << 8) | 0x80= 1920 pixels)
Edge Cases: The DNL Marker
Standard JPEG specifications require the width to be greater than
zero. If the height field is encoded as 0x00 0x00, the
actual height was unknown at the start of frame generation. In this rare
scenario, the height is defined later in the stream by a Define Number
of Lines (DNL) marker (0xFF 0xDC), which appears
immediately after the first scan data. Modern JPEG files almost
universally populate the height directly inside the SOF header.