All formats

CoreJ2K

LanguageC# (.NET Standard 2.0/2.1, .NET 8+)
LicenseBSD-3-Clause
Repositorygithub.com/cinderblocks/CoreJ2K
DocumentationREADME and guides in the docs folder
Depends onnothing; optional adapter packages for SkiaSharp, ImageSharp, System.Drawing
Read / writeyes / yes; JP2 and J2K, all of Part 1, parts of Part 2

CoreJ2K is a managed JPEG 2000 encoder and decoder, a maintained fork of CSJ2K, which was a C# port of the Java reference implementation JJ2000. Because it has no native code, it works on every platform .NET supports, in trimmed and AOT apps (with explicit adapter registration) and in sandboxed environments where you can’t load native libraries.

Decoded images come back as an InterleavedImage (integer samples, any bit depth) that converts to SKBitmap, an ImageSharp Image or a System.Drawing.Bitmap through the adapter packages. It’s slower than OpenJPEG-based libraries, though decoding and encoding use multiple cores.

Installation#

Shell
dotnet add package CoreJ2K
dotnet add package CoreJ2K.Skia         # SkiaSharp integration
# or CoreJ2K.ImageSharp, CoreJ2K.Windows (System.Drawing)

Basic usage: decode JPEG 2000 and save as PNG#

C#
using CoreJ2K;
using SkiaSharp;

using var image = J2kImage.FromFile("in.jp2");
Console.WriteLine($"{image.Width} x {image.Height}, {image.NumberOfComponents} components, " +
                  $"{image.GetBitDepth(0)} bits");

using SKBitmap bitmap = image.As<SKBitmap>();
using var output = File.OpenWrite("out.png");
bitmap.Encode(output, SKEncodedImageFormat.Png, 100);

For 8-bit images, J2kImage.DecodeFileToImage<SKBitmap>("in.jp2") decodes straight into the bitmap with about 4 times less memory.

How to encode JPEG 2000#

ToBytes takes any image type that an adapter package supports, plus optional encoder parameters:

C#
using CoreJ2K;
using CoreJ2K.j2k.util;
using SkiaSharp;

using SKBitmap bitmap = SKBitmap.Decode("in.png");

// lossless JP2
var lossless = new ParameterList { ["lossless"] = "on", ["file_format"] = "on" };
File.WriteAllBytes("lossless.jp2", J2kImage.ToBytes(bitmap, lossless));

// lossy JP2 at 1 bit per pixel
var lossy = new ParameterList { ["rate"] = "1.0", ["file_format"] = "on" };
File.WriteAllBytes("lossy.jp2", J2kImage.ToBytes(bitmap, lossy));

Without file_format, the output is a raw J2K codestream. Other parameters: tiles ("1024 1024"), Wlev (decomposition levels), Ffilters ("w5x3" or "w9x7"), Aptype (progression order), Psop / Peph (error resilience markers).

How to decode at lower resolution#

C#
using CoreJ2K;
using CoreJ2K.Configuration;
using SkiaSharp;

var config = new J2KDecoderConfiguration()
    .WithResolutionLevel(2)   // 0 is the lowest resolution level
    .WithColorSpace(true);    // apply the JP2 color space
using var preview = J2kImage.FromFile("big.jp2", config);
using SKBitmap bitmap = preview.As<SKBitmap>();

How to read 16-bit samples#

InterleavedImage keeps the original sample values, so 12-bit and 16-bit medical or scientific images aren’t reduced to 8 bits:

C#
using var image = J2kImage.FromFile("ct-slice.j2k");
int[] samples = image.GetComponent(0);   // width * height values, original bit depth
int center = image.GetSample(image.Width / 2, image.Height / 2, 0);

Tips#