Table of Contents

JPEG 2000

Egami reads JPEG 2000 (ITU-T T.800 | ISO/IEC 15444-1, all of Part 1), written from the 2019 edition of T.800, T.801's JPX boxes and the PDF specification's JPXDecode filter. Jpeg2000Format.Info is registered in EgamiContext.Default; JPEG 2000 is never written. Every ISO/IEC 15444-4 conformance codestream decodes within its tolerances: the reversible ones to OpenJPEG 2.5.4's samples exactly, the irreversible ones within one level, at full resolution and with levels dropped. High-Throughput JPEG 2000 (ITU-T T.814 | ISO/IEC 15444-15, HTJ2K) is read too, from the 2019 edition of T.814: JPH files, HT codestreams (JHC) and HT codestreams in JP2 files.

Reading files

JP2 and JPX files start with the JP2 signature box and raw codestreams (J2K, J2C, a PDF's JPXDecode stream) with SOC and SIZ, so Raster.Load recognizes both. MinimumSize drops whole wavelet levels, JPEG 2000's own reduction (a quarter of the levels' work for each level dropped), and Region decodes only the tiles and code-blocks the rectangle needs, so a thumbnail of a large scan or a viewport into a map costs a fraction of the whole decode. Rows stream a row of tiles at a time.

// JP2, JPX and raw codestreams are detected like any other format, so is a PDF's JPXDecode stream.
using Raster photo = Raster.Load("photo.jp2");

// A thumbnail drops whole wavelet levels instead of decoding at full size, and a region decodes only the code-blocks
// it needs.
using Raster thumbnail = Raster.Load("photo.jp2", options: new ReaderOptions { MinimumSize = new Size(24, 18) });
using Raster corner = Raster.Load("photo.jp2", options: new ReaderOptions { Region = new Rect(0, 0, 32, 32) });

What it loads as

Jpeg2000ReaderOptions.Components chooses between two readings of the same samples.

Jpeg2000Components Pixels
Color (default) Palettes applied, channels placed by the channel definitions, sYCC and CIELab converted to sRGB; gray, RGB or CMYK, with alpha when a channel is opacity
Raw The codestream's components after its own component transform: 1 as gray, 2 as gray and alpha, 3 as RGB, 4 as CMYK, for a PDF that names its own color space

Depths widen to the next Egami type by bit replication, so the stored value is recoverable by a shift: 1 to 8 bits load as Gray8, Rgb8, Rgba8 or Cmyk8, 9 to 16 bits as their 16-bit forms, deeper ones as float types. Signed samples are offset by half their range, with the Jpeg2000.SignedSamples warning; SampleImage.Load gives them unshifted, as stored, with each component's precision and sign (see Stored samples). Premultiplied alpha loads straight. ICC profiles go to RasterMetadata.IccProfile and the pixels stay as stored, as for every format; XMP, the resolution box and COM markers are read.

Subsampled sYCC (4:2:0 and 4:2:2) is upsampled with the YUV kernels at JPEG 2000's chroma siting, or loads straight into a YuvRaster without upsampling; FastUpsampling repeats chroma as OpenJPEG does instead. MaxQualityLayers decodes only the first layers of a layered file, for a quick preview.

// A PDF that names its own color space wants the codestream's components as stored.
using Raster components = Raster.Load(jpxStream, options: new Jpeg2000ReaderOptions { Components = Jpeg2000Components.Raw });

// A quick preview of a layered file: only its first quality layer, chroma repeated as OpenJPEG does.
using Raster preview = Raster.Load("photo.jp2", options: new Jpeg2000ReaderOptions { MaxQualityLayers = 1, FastUpsampling = true });

What is decoded

Part Read
Codestream SIZ, CAP, CPF, COD, COC, QCD, QCC, RGN, POC, PPM, PPT and COM; TLM, PLM, PLT and CRG read past; SOP and EPH; tiles and tile-parts in any order
Tier 2 Packet headers with tag trees, all five progression orders, progression order changes, precincts of any size, packed packet headers
Tier 1 The three coding passes and every code-block style: arithmetic bypass, context reset, termination on each pass, vertically causal contexts, predictable termination, segmentation symbols; ROI by max-shift
Reconstruction Reversible 5/3 and irreversible 9/7 wavelets at any number of levels, odd sizes and offsets; RCT and ICT; subsampled and signed components
HT block coder T.814's cleanup pass (MEL, VLC, UVLC and MagSgn), SigProp and MagRef passes; placeholder passes; HT and T.800 code-blocks mixed in one tile-component
JP2, JPX and JPH ihdr, colr (enumerated, restricted and any ICC), pclr, cmap, cdef, res, XMP uuid; JPX's several colr boxes and rreq; the JPH brand; the first codestream
Refused Part 2 coding tools throw NotSupportedException naming the feature

Jpeg2000Details describes what was decoded: the Jpeg2000Container, the profile, each Jpeg2000Component's precision, sign and sampling, the wavelet, levels, layers and Jpeg2000ProgressionOrder, the tiling, the Jpeg2000ColorSpace, whether there is a palette, the Jpeg2000Channel definitions with their Jpeg2000ChannelType, the comments, and whether the codestream is an HTJ2K one (HighThroughput).

using Raster image = Raster.Load("photo.jp2");
var details = (Jpeg2000Details)image.Metadata.FormatDetails!;
Console.WriteLine($"{details.Container}, {(details.Reversible ? "lossless 5/3" : "lossy 9/7")}, {details.DecompositionLevels} levels");
Console.WriteLine($"{details.QualityLayers} layers in {details.ProgressionOrder} order, {details.TileCount} tiles, {details.ColorSpace}");
foreach (Jpeg2000Component component in details.Components)
{
    Console.WriteLine($"{component.Precision} bits{(component.IsSigned ? " signed" : "")}, sampled every {component.SubsamplingX}×{component.SubsamplingY}");
}

foreach (Jpeg2000Channel channel in details.Channels)
{
    Console.WriteLine($"Channel {channel.Channel}: {channel.Type} for color {channel.Association}");
}

High-Throughput JPEG 2000

HTJ2K replaces Part 1's bit-plane coder with a block coder that codes a code-block's samples in one cleanup pass, optionally refined by one SigProp and one MagRef pass, so decoding does several times less work per sample; everything else (tiles, packets, wavelets, the file format) is Part 1's. HT files load like any JPEG 2000 file, with the same options, reductions, regions and streaming, and Jpeg2000Details.HighThroughput tells them apart; a JPH file's container is Jpeg2000Container.Jph.

The ISO/IEC 15444-4 HTJ2K conformance codestreams decode to the samples of the Part 1 codestreams they were transcoded from (or, where their bit-depth cuts magnitudes short, with no more error than the set's statistics give), and to OpenJPEG 2.5.4's samples wherever OpenJPEG decodes them: reversible ones exactly, irreversible ones within one level. On a 12-megapixel photo an HT file decodes in about 55 % of the time of the same photo as a Part 1 file on one thread.

Speed and threads

Code-blocks are independent, so tier 1 runs across cores by EgamiContext.MaxParallelism, with the same output at any thread count; the wavelets and color conversions are vectorized. On a 10-megapixel photo Egami decodes lossy files faster than OpenJPEG on one thread and every file two to four times faster on all cores.

Damaged and hostile data

Every size in SIZ is checked against SafetyLimits before a tile is read; the number of tiles, components, levels, passes and packet lengths are bounded by the standard and by the data; the samples held count against MaxTotalBytes, and a codestream whose tiles and components would build far more structure than its samples fill is refused. A codestream that ends early decodes what it holds with the Jpeg2000.Truncated warning, and throws CorruptDataException in strict mode.