JPEG codec
Defines an array -> bytes codec that encodes a chunk as a baseline (sequential DCT, Huffman-coded) JPEG image.
JPEG is a lossy image compression format, so this codec MUST NOT be used where exact values must be preserved (e.g. label / segmentation data).
It is intended for uint8 image data (grayscale or RGB) where a controlled loss of precision is acceptable in exchange for a high compression ratio.
Codec name
The value of the name member in the codec object MUST be jpeg.
Configuration parameters
quality(integer, required): the JPEG encoding quality, in the range1–100. Higher values preserve more detail at the cost of a larger encoded size. It is an encoder setting: this specification does not prescribe how it maps to quantization tables, so its exact effect depends on the encoder, and different encoders MAY produce different output for the same value. Decoding does not depend on it, since the quantization tables are stored in the JPEG stream. The range deliberately deviates from the0–100quality scale of the IJG libjpeg library [IJG-libjpeg] (which treats0as1), because not all implementations accept0(e.g. the TurboJPEG API of libjpeg-turbo requires1–100). The parameter has no default when parsing a configuration. Implementations that offer a default for convenience when building a configuration SHOULD use95, so that configurations created with different implementations agree.encoded_color_space(string, required): the color space of the samples as stored in the JPEG stream. It also fixes the component count, so a configuration can be fully validated without looking at the chunk data. One of:grayscale: 1-component data, stored as-is.ycbcr: the RGB input is converted to YCbCr before encoding (the JFIF-standard color space, and a prerequisite for chroma subsampling). Suitable for natural color images.rgb: the three components are stored as-is, with no color-space conversion. Suitable for independent scientific channels (fluorescence, multispectral, …) whose channels are not real colors. The encoder MUST write an APP14 Adobe marker indicating an "unknown" transform, so that decoders do not apply an inverse YCbCr transform.
This parameter is REQUIRED and has no default: for 3-component data,
ycbcrandrgbare meant for different kinds of data, and silently applying YCbCr would destroy fidelity for data whose channels are not colors.For maximum interoperability, note that encoders and decoders are only guaranteed to support grayscale (1-component) data and
ycbcr;rgb(storing color components without conversion) is not supported by all implementations and SHOULD be used only when portability is not a concern. Some decoders ignore the APP14 Adobe marker and always apply the inverse YCbCr transform to 3-component data (e.g. the jpgjs decoder used by Neuroglancer), which would silently produce wrong values forrgbdata. An implementation that cannot decodergbMUST therefore reject a configuration withencoded_color_space: rgbwhen parsing it, and a decoder that cannot honor the color transform stored in a JPEG stream MUST reject that chunk rather than return incorrectly converted data. This parameter is defined as an open-ended color space, rather than a simple on/off transform, so that additional color spaces (e.g. XYB, CMYK) can be added later as a backwards-compatible extension.decoded_color_space(string, optional): the color space of the chunk data, i.e. the input to encoding and the output of decoding. One ofgrayscale,rgborycbcr. Defaults tograyscaleifencoded_color_spaceisgrayscale, and torgbotherwise, so the default depends only on the configuration, not on the chunk data. Together withencoded_color_spaceit determines the color conversion applied during encoding, and its inverse during decoding. The JPEG stream does not record this value, so decoders need it to return the original color space:decoded_color_spaceencoded_color_spaceConversion grayscalegrayscalenone rgbycbcrRGB → YCbCr rgbrgbnone ycbcrycbcrnone (data is already YCbCr) Any other combination MUST be rejected. Implementations are only guaranteed to support
grayscaleandrgb→ycbcr.subsampling(array, required ifencoded_color_spaceisycbcr, optional otherwise): the subsampling applied to the components of the encoded color space, expressed declaratively as the per-component JPEG sampling factors. It is an array with one entry per component, and each entry is a two-element array[horizontal, vertical]giving that component's horizontal and vertical sampling factor as integers in the range1–4. The factors are relative: a component is subsampled by the ratio of its factors to the largest factor across all components, so a component with factor[1, 1]is stored at half the resolution (in each direction) of a component with factor[2, 2]. All-equal factors (e.g.[[1, 1], [1, 1], [1, 1]]) therefore mean no subsampling. Forycbcr, this is how the chroma components (Cb, Cr) are subsampled relative to luma (Y). The second and third components MUST have factor[1, 1], and the first component's factor MUST be greater than or equal to it in each direction.
subsampling is only meaningful together with encoded_color_space: ycbcr, where it MUST be present; the common 4:2:0 scheme is [[2, 2], [1, 1], [1, 1]]. With encoded_color_space: rgb or grayscale, no subsampling is applied, and subsampling defaults to [[1, 1], [1, 1], [1, 1]] for rgb and [[1, 1]] for grayscale. In these cases subsampling SHOULD be omitted; if present, it MUST be equal to the default.
The common human-readable J:a:b chroma-subsampling notation maps to subsampling as follows. Implementations MUST support at least these schemes:
J:a:b | subsampling | Chroma resolution vs. luma |
|---|---|---|
4:4:4 | [[1, 1], [1, 1], [1, 1]] | full (no subsampling) |
4:2:2 | [[2, 1], [1, 1], [1, 1]] | half horizontally |
4:4:0 | [[1, 2], [1, 1], [1, 1]] | half vertically |
4:2:0 | [[2, 2], [1, 1], [1, 1]] | half horizontally and vertically |
Because encoded_color_space fixes the component count (1 for grayscale, 3 otherwise), an implementation MUST reject an invalid configuration when parsing it, rather than silently ignoring the problem. Examples are a combination of color spaces not listed in the table above, a missing subsampling together with encoded_color_space: ycbcr, a subsampling array whose length does not match the component count, or a subsampling with encoded_color_space: rgb or grayscale that differs from its default. When encoding, it MUST additionally reject a chunk whose component count (see below) does not match the configuration.
Supported data types
uint8
Other data types are not supported by this codec.
Supported chunk shapes
JPEG stores images with either 1 component (grayscale) or 3 components (color). The component count is derived from the chunk shape:
| Chunk shape | Result |
|---|---|
(N,) (1D) | rejected — reshape to 2D first |
(H, W) | 1-component (grayscale) |
(H, W, 1) | 1-component (grayscale) |
(H, W, 3) | 3-component |
(H, W, 2) or (H, W, C) with C ≥ 4 | rejected — reshape/shard so the channel axis is 1 or 3 |
| 4D or higher | rejected — transpose/reshape down to 2D or 3D |
(H, W, 1) is accepted as grayscale so that data can be sharded over a size-1 channel axis.
When using different chunk shapes, the reshape codec should be used prior to the jpeg codec to change the chunk shapes into compatible shapes.
The chunk is flattened in C order (last axis varies fastest), so the channel axis must be the innermost axis. If it is not, place a transpose codec before jpeg to move it there.
The spatial axes give the JPEG image height (H) and width (W). Each MUST NOT exceed 65535, since the JPEG format stores each dimension as a 16-bit value. A decoder reshapes the decoded samples by the chunk shape.
JPEG encodes samples in blocks called minimum coded units (MCUs). An MCU spans 8 × h_max samples horizontally and 8 × v_max samples vertically, where h_max and v_max are the largest horizontal and vertical sampling factors in subsampling (both 1 for rgb and grayscale). For example, 4:4:4 gives 8×8, 4:2:0 gives 16×16, and a factor of 4 gives 32. When H or W is not a multiple of the block size, the encoder pads the image up to the next block boundary and the decoder crops back to the stored dimensions, so the chunk shape still round-trips exactly; however, the padded samples share quantized blocks with the real edge samples and can introduce extra artifacts near the right and bottom edges. To avoid this, W SHOULD be a multiple of 8 × h_max and H a multiple of 8 × v_max. 16 covers all schemes listed above, and 32 covers every allowed sampling factor. Because Zarr pads boundary chunks to the full chunk shape, choosing such a chunk shape ensures every encoded image — including boundary chunks — is block-aligned.
Examples
Natural color (RGB → YCbCr, 4:2:0)
A 3-component chunk (shape (H, W, 3)) holding a natural color image. It is converted to YCbCr and the chroma is subsampled with the common 4:2:0 scheme.
{
"codecs": [
{
"name": "jpeg",
"configuration": {
"quality": 90,
"encoded_color_space": "ycbcr",
"subsampling": [[2, 2], [1, 1], [1, 1]]
}
}
]
}Grayscale
A 1-component chunk (shape (H, W) or (H, W, 1)).
{
"codecs": [
{
"name": "jpeg",
"configuration": {
"quality": 90,
"encoded_color_space": "grayscale"
}
}
]
}Natural color, no chroma subsampling (4:4:4)
The same shape at higher fidelity: YCbCr conversion but every component kept at full resolution.
{
"codecs": [
{
"name": "jpeg",
"configuration": {
"quality": 95,
"encoded_color_space": "ycbcr",
"subsampling": [[1, 1], [1, 1], [1, 1]]
}
}
]
}Independent scientific channels (rgb, no color transform)
No color-space conversion is applied, and the components MUST NOT be subsampled.
{
"codecs": [
{
"name": "jpeg",
"configuration": {
"quality": 90,
"encoded_color_space": "rgb"
}
}
]
}Data already in YCbCr
A 3-component chunk whose samples are already YCbCr. No conversion is applied, but the JPEG still records YCbCr as its color space, and decoding returns YCbCr samples.
{
"codecs": [
{
"name": "jpeg",
"configuration": {
"quality": 90,
"encoded_color_space": "ycbcr",
"decoded_color_space": "ycbcr",
"subsampling": [[2, 2], [1, 1], [1, 1]]
}
}
]
}Channel axis not innermost
For a chunk laid out as (3, H, W), place a transpose codec first to move the channel axis last.
{
"codecs": [
{ "name": "transpose", "configuration": { "order": [1, 2, 0] } },
{
"name": "jpeg",
"configuration": {
"quality": 90,
"encoded_color_space": "ycbcr",
"subsampling": [[2, 2], [1, 1], [1, 1]]
}
}
]
}Format and algorithm
The output is a standard JFIF/JPEG bitstream using baseline (Color Transform, Block Splitting, DCT, Quantization, Huffman) as defined by [ITU-T81] and [ITU-T871].
Encoding
- The component count (1 or 3) is determined from the chunk shape as described above. Unsupported shapes, and shapes whose component count does not match
encoded_color_space, are rejected. - The
uint8chunk data is read in C order into a contiguous buffer; the spatial axes give the imageheight(H) andwidth(W). - For 3-component data, the samples are converted from
decoded_color_spacetoencoded_color_spaceas given in the table above, and forycbcrthesubsamplingis applied. Forrgb, the APP14 Adobe marker indicating an "unknown" transform is written. - The samples are encoded as a baseline JPEG at the configured
quality.
Decoding
- The JPEG bitstream is decoded to its
uint8samples in raster order.encoded_color_spaceandsubsamplingare ignored, since the stream itself records them (the APP14 marker, when present, indicates the stored color space). The samples are converted from the stored color space todecoded_color_space. - For grayscale, the single-component samples are read directly. For color, the three interleaved components are read per pixel.
- The samples are reshaped to the chunk shape.
Interoperability notes
This codec matches neuroglancer
precomputed'sjpegencoding for 1- and 3-channeluint8data withencoded_color_space: grayscaleorycbcr, with no data reordering. Neuroglancer does not supportrgb.This works because Zarr's C-order over
[z, y, x, channel](reshaped to(z·y, x, channel)with thereshapecodec) visits samples in the same order (channel, then x, then y, then z) as neuroglancer's Fortran-order over[x, y, z].Because JPEG is lossy, decoded values are generally not bit-identical to the original values. Implementations and users should choose
qualityaccordingly.
References
[ITU-T81] ITU-T Recommendation T.81 | ISO/IEC 10918-1. Information technology – Digital compression and coding of continuous-tone still images – Requirements and guidelines. September 1992. URL: https://www.w3.org/Graphics/JPEG/itu-t81.pdf
[ITU-T871] ITU-T Recommendation T.871 | ISO/IEC 10918-5. Information technology – Digital compression and coding of continuous-tone still images: JPEG File Interchange Format (JFIF). May 2011. URL: https://www.itu.int/rec/T-REC-T.871
[IJG-libjpeg] Independent JPEG Group. Using the IJG JPEG library (libjpeg.txt). URL: http://apodeline.free.fr/DOC/libjpeg/libjpeg-3.html
