-
Notifications
You must be signed in to change notification settings - Fork 17
add jpeg codec #66
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
konstibob
wants to merge
8
commits into
zarr-developers:main
Choose a base branch
from
konstibob:main
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
add jpeg codec #66
Changes from 3 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
aa06756
add jpeg codec
konstibob 60174d2
adjusted jpeg codec
konstibob e21fc7c
adjusted chroma subsampling format
konstibob 85bf751
Apply suggestions from code review
konstibob f956d90
adjusted wording and code reviews
konstibob 38c490c
adjusted required params
konstibob 14e9a5e
add examples
konstibob 7e6865d
add examples and fixes
konstibob File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,105 @@ | ||
| # 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. | ||
|
|
||
| This codec is interoperable with the `jpeg` chunk encoding used by [neuroglancer](https://github.com/google/neuroglancer). | ||
|
konstibob marked this conversation as resolved.
Outdated
konstibob marked this conversation as resolved.
Outdated
|
||
|
|
||
| ## Codec name | ||
|
|
||
| The value of the `name` member in the codec object MUST be `jpeg`. | ||
|
|
||
| ## Configuration parameters | ||
|
|
||
| - `quality` (integer, optional, default `90`): the JPEG encoding quality, in the range `0`–`100`. Higher values preserve more detail at the cost of a larger encoded size. Decoding does not depend on it. | ||
|
konstibob marked this conversation as resolved.
Outdated
|
||
|
|
||
| - `encoded_color_space` (string): the color space of the samples as stored in the JPEG stream, for 3-component data. One of: | ||
| - `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. | ||
|
|
||
|
konstibob marked this conversation as resolved.
|
||
| This parameter is REQUIRED for 3-component data and has **no default**: the two color spaces are meant for different kinds of data, and silently applying YCbCr would destroy fidelity for data whose channels are not colors. It MUST NOT be set for grayscale data, whose encoded color space is always `grayscale` and is determined by the chunk shape. | ||
|
|
||
| For maximum interoperability, note that encoders and decoders are only guaranteed to support `grayscale` 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. 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. | ||
|
|
||
| - `subsampling` (array, optional): the chroma subsampling, 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 range `1`–`4`. A component with factor `[2, 2]` is stored at twice the horizontal and vertical resolution of a component with factor `[1, 1]`; the chroma components (Cb, Cr) are therefore subsampled relative to luma (Y) by the ratio of their sampling factors. The chroma components MUST have factor `[1, 1]`, and each luma factor MUST be greater than or equal to the corresponding chroma factor. | ||
|
konstibob marked this conversation as resolved.
Outdated
|
||
|
|
||
| For 3-component data, `subsampling` is only meaningful together with `encoded_color_space: ycbcr`, where it defaults to `[[2, 2], [1, 1], [1, 1]]` (the common `4:2:0` scheme). With `encoded_color_space: rgb` it MUST be `[[1, 1], [1, 1], [1, 1]]` (no subsampling) or omitted, since those components are independent and MUST NOT be subsampled. It MUST NOT be set for grayscale data. | ||
|
|
||
| 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 | | ||
|
|
||
| An implementation MUST reject a configuration whose parameters are invalid for the chunk's channel count (for example, 3-component data without `encoded_color_space`, a `subsampling` array whose length does not match the component count, or a `subsampling` other than `[[1, 1], [1, 1], [1, 1]]` together with `encoded_color_space: rgb`) rather than silently ignoring them. | ||
|
|
||
| ## Supported data types | ||
|
|
||
| - `uint8` | ||
|
|
||
| Other data types are not supported by this codec. | ||
|
|
||
| ## Supported chunk shapes | ||
|
konstibob marked this conversation as resolved.
|
||
|
|
||
| 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 (`encoded_color_space` REQUIRED) | | ||
| | `(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. | ||
|
konstibob marked this conversation as resolved.
|
||
|
|
||
| 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`](../transpose/) codec before `jpeg` to move it there. | ||
|
|
||
| The trailing 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. | ||
|
konstibob marked this conversation as resolved.
Outdated
|
||
|
|
||
| JPEG encodes samples in blocks — a *minimum coded unit* (MCU) of 8×8 samples, or up to 16×16 when chroma subsampling is used. 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, `H` and `W` SHOULD be multiples of `16` (which covers every subsampling mode; `8` suffices for `4:4:4`). Because Zarr pads boundary chunks to the full chunk shape, choosing a chunk shape whose spatial dimensions are multiples of `16` ensures every encoded image — including boundary chunks — is block-aligned. | ||
|
|
||
| These rules apply to the inner chunk shape when used inside [`sharding_indexed`](../sharding_indexed/). | ||
|
konstibob marked this conversation as resolved.
Outdated
|
||
|
|
||
| ## 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 | ||
|
|
||
| 1. The component count (1 or 3) is determined from the chunk shape as described above; unsupported shapes are rejected. | ||
| 2. The `uint8` chunk data is read in C order into a contiguous buffer; the trailing spatial axes give the image `height` (`H`) and `width` (`W`). | ||
|
konstibob marked this conversation as resolved.
|
||
| 3. For 3-component data, the samples are converted to the `encoded_color_space` (RGB → YCbCr for `ycbcr`, no conversion for `rgb`) together with the chroma `subsampling` (for `ycbcr`). For `rgb`, the APP14 Adobe marker indicating an "unknown" transform is written. | ||
| 4. The samples are encoded as a baseline JPEG at the configured `quality`. | ||
|
|
||
| ### Decoding | ||
|
|
||
| 1. The JPEG bitstream is decoded to its `uint8` samples in raster order. The inverse color transform is determined by the stream itself: the APP14 marker, when present, indicates whether an inverse YCbCr transform is applied. | ||
| 2. For grayscale, the single-component samples are read directly. For color, the three interleaved components are read per pixel. | ||
| 3. The samples are reshaped to the chunk shape. | ||
|
|
||
| ## Interoperability notes | ||
|
|
||
| - This codec matches neuroglancer `precomputed`'s `jpeg` encoding for 1- and 3-channel `uint8` data, with no data reordering. | ||
|
|
||
| - This works because Zarr's C-order over `[z, y, x, channel]` 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 `quality` accordingly. | ||
|
|
||
| ## 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 | ||
|
|
||
| ## Current maintainers | ||
|
|
||
| * [Konstantin Bobenko](https://github.com/konstibob) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| { | ||
| "$schema": "https://json-schema.org/draft/2020-12/schema", | ||
| "type": "object", | ||
| "properties": { | ||
| "name": { | ||
| "const": "jpeg" | ||
| }, | ||
| "configuration": { | ||
| "type": "object", | ||
| "properties": { | ||
| "quality": { | ||
| "type": "integer", | ||
| "minimum": 0, | ||
| "maximum": 100, | ||
| "default": 90 | ||
| }, | ||
| "encoded_color_space": { | ||
| "enum": ["ycbcr", "rgb"] | ||
| }, | ||
| "subsampling": { | ||
| "type": "array", | ||
| "items": { | ||
| "type": "array", | ||
| "items": { | ||
| "type": "integer", | ||
| "minimum": 1, | ||
| "maximum": 4 | ||
| }, | ||
| "minItems": 2, | ||
| "maxItems": 2 | ||
| }, | ||
| "minItems": 1, | ||
| "maxItems": 3 | ||
| } | ||
| }, | ||
| "additionalProperties": false | ||
|
konstibob marked this conversation as resolved.
Outdated
|
||
| } | ||
| }, | ||
| "required": ["name"], | ||
|
konstibob marked this conversation as resolved.
Outdated
|
||
| "additionalProperties": false | ||
| } | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.