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.