Skip to content

JPEG 2000

currentMigrated from docs/images/JPEG2000.mdx at 37c3317fd058; modified for Encode Wiki · CC BY-SA 4.0

JPEG 2000 (ISO/IEC 15444, 2000) replaces JPEG’s 8×8 DCT with a wavelet transform. One codestream can be lossless or lossy, decodes at lower resolutions or quality layers by simply reading less of the file, and has no block artefacts (it blurs at low rates instead). Only Safari ever displayed it, and Safari 18 dropped it. Nothing about it beats modern formats for photo delivery, but it is firmly established in a few places.

  • Digital cinema: the DCI specification mandates JPEG 2000 for the picture in a DCP. Containers are 2048×1080 (2K, 24 or 48 fps) or 4096×2160 (4K, 24 fps), 12 bits per sample, with a ceiling of 250 Mbit/s. That is a lossy, “visually lossless” encode, not mathematically lossless.
  • Medical imaging: DICOM defines JPEG 2000 transfer syntaxes, including a lossless-only one.
  • Archives and libraries: JP2 is used for lossless masters and access copies of scans; the resolution scalability suits large images.
  • Geospatial and satellite imagery: huge images with random-access decoding, usually through GDAL.

For anything else, use JPEG, AVIF, WebP or JPEG XL. No current browser decodes JPEG 2000.

PieceWhat it is
.j2k / .j2cRaw codestream, no colour or metadata boxes
.jp2Part 1 file wrapper with colour space/ICC and channel definitions (use this by default)
JPX (Part 2)Extensions: extended colour, multiple codestreams. Many decoders do not read it
MJ2 (Part 3)Motion JPEG 2000, a sequence of independently coded frames
HTJ2K (Part 15)High Throughput block coder, much faster to encode and decode; can be converted to and from classic Part 1 codestreams without loss. Older decoders cannot read it

Lossless coding uses the reversible 5/3 wavelet; lossy uses the irreversible 9/7. Bit depth and component sampling are per component, so 16-bit, 4:2:0 and more are all legal.

ToolNotes
OpenJPEGBSD-licensed reference software; opj_compress/opj_decompress. Latest release 2.5.4 (September 2025); the repository’s README now describes it as unmaintained. This is what ImageMagick, GDAL and FFmpeg’s libopenjpeg wrapper use.
KakaduCommercial SDK (kdu_compress) with the most complete feature set and HTJ2K.
GrokAGPL-3.0 codec with HTJ2K, random-access decoding and a transcoder; integrated into GDAL.
OpenJPHHTJ2K only.
FFmpegNative jpeg2000 encoder and the libopenjpeg wrapper.

DCP mastering needs more than a JPEG 2000 file (X′Y′Z′ conversion, MXF wrapping, naming, subtitles and audio). Use a DCP tool such as DCP-o-matic instead of encoding frames by hand.

Lossless JP2 from a PNG, TIFF, PPM or BMP input:

Terminal window
opj_compress -i input.png -o output.jp2
opj_decompress -i output.jp2 -o restored.png

The default is lossless (reversible 5/3, one layer). For lossy, switch to the irreversible transform and give a compression ratio:

Terminal window
opj_compress -i input.png -o output.jp2 -I -r 20
OptionWhat it does
-r 20Target compression ratio (source size divided by output size), not a quality score. A comma list such as -r 20,10,1 makes quality layers; a final 1 keeps all coded data (lossless only with the reversible transform).
-q 40Target PSNR in dB per layer. It steers rate allocation and says little about how the image looks; -r is easier to reason about.
-IIrreversible 9/7 wavelet. Lossy even without rate limits.
-n 6Resolution levels (default 6). Lower it for small images, otherwise the encode fails with “Number of resolutions is too high”.
-p RPCLPacket order. RPCL suits progressive-by-resolution delivery; the default LRCP is progressive by quality.
-t 1024,1024Tile size. Tiling enables partial decoding of huge images but costs some efficiency.
-threads ALL_CPUSMultithreaded encode.

Decoding at reduced size is cheap: opj_decompress -i big.jp2 -o half.png -r 1 skips the highest resolution level.

FFmpeg works for single frames and sequences: ffmpeg -i input.png -c:v jpeg2000 output.jp2 (native) or -c:v libopenjpeg.

  • Compression ratio, not quality: -r and -q are rate targets. Judge lossy output by eye or with SSIMULACRA2, not by the PSNR you asked for.
  • Profile mismatches: a lossless JP2 does not make a DCI or IMF delivery. Those profiles restrict size, precision, colour and rate. opj_compress has -cinema2K, -cinema4K and -IMF switches that enforce parts of them, but they are not a full DCP workflow.
  • Decoder support: JPX extensions and HTJ2K are not read by every decoder. For long-term archives, stay with plain Part 1 JP2.
  • Raw codestreams (.j2k) carry no ICC profile. Use .jp2 when colour matters.
  • Invalid options are not fatal: opj_compress prints a warning, ignores the option and encodes anyway, so read its output.

This table lists every option and default for OpenJPEG 2.5.4. The sections above are the practical summary. Reading option tables explains the Default column.

Defaults are for the ordinary profile; the cinema and IMF profiles override some of them. Informational options and paths have no numeric default. opj_compress -h prints the option list and exits with status 1.

All OpenJPEG opj_compress parameters and defaults
Parameter / aliasesType / syntaxDefaultEffect and limits
-ifile pathrequired unless -ImgDirInput image; the extension selects the reader (PNM, PGX, PNG, BMP, TIFF, RAW, RAWL, TGA). RAW and RAWL need -F.
-ofile pathrequired with -iOutput file: .jp2 writes a JP2 container, .j2k or .j2c a raw codestream.
-ImgDirdirectory pathunsetEncode every image in this directory. Needs -OutFor; excludes -i and -o.
-OutForenumrequired with -ImgDirOutput suffix for -ImgDir batches: JP2, J2K or J2C.
-Fraw geometry descriptorrequired for RAW/RAWL; sampling 1x1 if omittedRaw input layout: width,height,components,bitdepth,s or u, then optional @dxXdy per component. Required for RAW and RAWL.
-hactionnot requestedPrint the help text and exit with status 1.
-rdecreasing number listone untruncated layer (internal rate 0)Target compression ratio per quality layer, in decreasing order; a final 1 means all data. Excludes -q.
-qincreasing number list, dBunsetTarget PSNR in dB per quality layer, in increasing order; a final 0 means lossless. Excludes -r.
-ninteger6Resolution levels (DWT decompositions plus one), 1–33; limited by tile size.
-TargetBitDepth, -Xpositive integer0 (keep input precision)Reduce TIFF input to this many bits per component. Other input formats ignore it.
-bwidth,height64,64Code-block width,height: powers of two from 4 to 1024, with area at most 4096.
-cprecinct-size list32768x32768 at each resolutionPrecinct sizes as [w,h] pairs (powers of two), highest resolution first; the last pair is halved for each lower level.
-twidth,heightone tile covering the imageTile width,height. Choose -n so the smallest tile still fits all levels.
-penumLRCPPacket progression order: LRCP, RLCP, RPCL, PCRL or CPRL.
-sdx,dy1,1Reference-grid subsampling dx,dy; it does not resize the image. Values above 2 may error.
-POC, -Pprogression-change listunsetProgression-order changes as Tn=resStart,compStart,layerEnd,resEnd,compEnd,order, joined by /. Tile numbers start at 1.
-SOP, -SflagdisabledWrite a start-of-packet marker before each packet, for error resilience.
-EPH, -EflagdisabledWrite an end-of-packet-header marker after each packet header.
-PLTflagdisabledWrite packet-length (PLT) markers in tile-part headers, for random access.
-TLMflagdisabledWrite tile-part-length (TLM) markers in the main header.
-Minteger bitmask0Code-block mode bitmask: 1 bypass, 2 reset, 4 terminate each pass, 8 vertical causal, 16 predictable termination, 32 segmentation symbols. Add values to combine.
-TP, -uenumdisabledSplit each tile into tile-parts by resolution (R), layer (L) or component (C).
-ROI, -Rcomponent index and upshiftdisabled (component index -1)c=component,U=shift (0–37): upshift one whole component. Not a spatial region of interest.
-dx,y0,0Image origin x,y on the reference grid.
-Tx,y0,0Tile-grid origin x,y; neither value may exceed the image origin.
-Iflagdisabled (reversible 5/3 transform)Use the lossy irreversible 9/7 wavelet instead of the reversible 5/3.
-mct, -Yinteger enum1 for at least 3 components; otherwise 0Component transform: 0 none, 1 RGB to YCC, 2 custom matrix (needs -m, which does not work). Subsampled RAW forces 0.
-mfile pathunset (option not registered)Custom MCT matrix file. Listed in the help but missing from the option string, so it is rejected as invalid.
-cinema2Kinteger enumdisabledDigital Cinema 2K profile at 24 or 48 fps; overrides other coding options.
-cinema4Kflagdisabled; profile assumes 24 fps when selectedDigital Cinema 4K profile at 24 fps; overrides other coding options.
-IMFprofile descriptordisabled; mainlevel 0, sublevel 0, no frame-rate limitIMF profile 2K, 4K, 8K or the _R variants, with optional mainlevel (0–11), sublevel (0–9) and framerate.
-GuardBits, -Gintegerautomatic: 2 ordinarily, 1 for cinema 2KQuantization guard bits, 0–7. The default is 2, or 1 for cinema 2K.
-jpip, -JflagdisabledAdd a JPIP index box to JP2 output; the help says it supports only RPCL order.
-Cstringgenerated OpenJPEG version commentComment text for the COM marker, replacing the generated OpenJPEG comment.
-threadsinteger or ALL_CPUS0 (no workers unless OPJ_NUM_THREADS overrides)Encoder thread count or ALL_CPUS. OPJ_NUM_THREADS sets the default. Needs a threaded build.
-flegacy integer matrix stringdisabledHidden legacy fixed-layer allocation matrix. Fragile; use -r or -q instead.
-xfile path (ignored)unsetIndex file path. Ignored with a warning because index generation is broken.
-WJPWL comma-separated descriptor (conditional)disabled (needs USE_JPWL)JPWL error-protection descriptor. Needs a build with USE_JPWL.

-m appears in the help and has a source branch but is missing from the registered option string, so it prints Invalid option m and is ignored. -mct 2 (custom matrix) is therefore not usable. -x warns that index generation is broken and ignores its path. Neither is fatal, so read the log. The help says code blocks are limited to 64×64, but any power-of-two shape with area up to 4,096 (for example 128×32) works. OPJ_NUM_THREADS sets the default worker count when threading is built in; a positive -threads overrides it.