All formats

OpenJPEG

LanguageC (usable from C++)
LicenseBSD-2-Clause
Repositorygithub.com/uclouvain/openjpeg
DocumentationAPI reference (Doxygen), wiki, and the opj_decompress.c / opj_compress.c sources
Depends onnothing for the library; libpng, libtiff, lcms2 for the command-line tools
Read / writeyes / yes; JP2 and J2K codestreams, Part 1 and some Part 2; HTJ2K decoding (since 2.5.0)

OpenJPEG is the JPEG 2000 implementation recognized by ISO/IEC and ITU-T as reference software. It’s the library behind JPEG 2000 support in Pillow, ImageMagick, libvips, GDAL, Poppler, MuPDF, Ghostscript and many DICOM toolkits. It decodes JP2, J2K/J2C and the codestream inside JPX files, and encodes JP2 and J2K, lossless and lossy.

The repository is marked “unmaintained”: there’s no committed maintainer team, but fixes still get merged and releases still appear. Decoding is slower than Grok or Kakadu, though multi-threaded decoding (opj_codec_set_threads) helps. Errors are reported through callbacks and boolean return values.

Installation#

Shell
# Debian / Ubuntu (library + opj_* tools)
sudo apt install libopenjp2-7-dev libopenjp2-tools
# Fedora
sudo dnf install openjpeg2-devel openjpeg2-tools
# macOS
brew install openjpeg
# Windows (vcpkg)
vcpkg install openjpeg

With CMake:

cmake
find_package(OpenJPEG REQUIRED)
target_include_directories(myapp PRIVATE ${OPENJPEG_INCLUDE_DIRS})
target_link_libraries(myapp PRIVATE openjp2)

Basic usage: decode a JP2 file#

The decoder gives you an opj_image_t with one OPJ_INT32 array per component. Components may have different sizes (subsampling) and bit depths, so check dx, dy and prec.

C
#include <openjpeg.h>
#include <stdio.h>

static void on_error(const char *msg, void *user) {
    (void)user;
    fprintf(stderr, "openjpeg: %s", msg);
}

int main(void) {
    opj_dparameters_t params;
    opj_set_default_decoder_parameters(&params);

    opj_stream_t *stream = opj_stream_create_default_file_stream("in.jp2", OPJ_TRUE /*read*/);
    if (!stream) return 1;

    // OPJ_CODEC_JP2 for .jp2/.jpx files, OPJ_CODEC_J2K for raw .j2k/.j2c codestreams
    opj_codec_t *codec = opj_create_decompress(OPJ_CODEC_JP2);
    opj_set_error_handler(codec, on_error, NULL);

    opj_image_t *image = NULL;
    opj_codec_set_threads(codec, 4);  // fails harmlessly if built without thread support
    int ok = opj_setup_decoder(codec, &params)
          && opj_read_header(stream, codec, &image)
          && opj_decode(codec, stream, image)
          && opj_end_decompress(codec, stream);
    if (ok) {
        printf("%u x %u, %u components, color space %d\n",
               image->x1 - image->x0, image->y1 - image->y0, image->numcomps, image->color_space);
        for (OPJ_UINT32 c = 0; c < image->numcomps; c++) {
            opj_image_comp_t *comp = &image->comps[c];
            printf("component %u: %u x %u, %u bits%s, first sample %d\n", c, comp->w, comp->h,
                   comp->prec, comp->sgnd ? " signed" : "", comp->data[0]);
        }
    }
    opj_image_destroy(image);
    opj_destroy_codec(codec);
    opj_stream_destroy(stream);
    return ok ? 0 : 1;
}

Link with -lopenjp2.

How to detect JP2 vs J2K#

Pick the codec from the first bytes of the file:

C
#include <string.h>

OPJ_CODEC_FORMAT detect_format(const unsigned char *buf, size_t len) {
    static const unsigned char jp2_sig[12] = {0, 0, 0, 0x0C, 'j', 'P', ' ', ' ', 0x0D, 0x0A, 0x87, 0x0A};
    static const unsigned char j2k_sig[4] = {0xFF, 0x4F, 0xFF, 0x51};
    if (len >= 12 && memcmp(buf, jp2_sig, 12) == 0) return OPJ_CODEC_JP2;
    if (len >= 4 && memcmp(buf, j2k_sig, 4) == 0) return OPJ_CODEC_J2K;
    return OPJ_CODEC_UNKNOWN;
}

How to get the image size without decoding#

opj_read_header parses only the headers. The returned image has sizes and bit depths but no pixel data:

C
opj_image_t *image = NULL;
if (opj_setup_decoder(codec, &params) && opj_read_header(stream, codec, &image)) {
    printf("%u x %u, %u components, %u bits\n", image->x1 - image->x0,
           image->y1 - image->y0, image->numcomps, image->comps[0].prec);
}
opj_image_destroy(image);

opj_get_cstr_info(codec) returns more codestream details: tile size, number of resolution levels and quality layers.

How to decode at reduced resolution or a region#

Discarding resolution levels is the fastest way to make a thumbnail: each level halves the width and height.

C
opj_dparameters_t params;
opj_set_default_decoder_parameters(&params);
params.cp_reduce = 2;  // decode at 1/4 width and height
opj_setup_decoder(codec, &params);
opj_read_header(stream, codec, &image);

// decode only a region, in full-resolution coordinates
opj_set_decode_area(codec, image, 1000, 1000, 3000, 2500);
opj_decode(codec, stream, image);
opj_end_decompress(codec, stream);

params.cp_layer limits the number of quality layers decoded.

How to encode a JP2 file#

Create an opj_image_t, fill the component arrays, then compress. This writes 8-bit RGB with 20:1 lossy compression:

C
#include <openjpeg.h>
#include <string.h>

int save_rgb_jp2(const char *path, const unsigned char *rgb, int w, int h) {
    opj_cparameters_t params;
    opj_set_default_encoder_parameters(&params);
    params.tcp_numlayers = 1;
    params.tcp_rates[0] = 20;   // compression ratio; 0 = lossless
    params.cp_disto_alloc = 1;
    params.irreversible = 1;    // 9/7 wavelet (lossy); 0 = 5/3 (lossless)
    params.tcp_mct = 1;         // RGB -> YCbCr

    opj_image_cmptparm_t cmpt[3];
    memset(cmpt, 0, sizeof(cmpt));
    for (int i = 0; i < 3; i++) {
        cmpt[i].dx = cmpt[i].dy = 1;
        cmpt[i].w = w;
        cmpt[i].h = h;
        cmpt[i].prec = 8;
        cmpt[i].sgnd = 0;
    }
    opj_image_t *image = opj_image_create(3, cmpt, OPJ_CLRSPC_SRGB);
    if (!image) return 0;
    image->x0 = 0;
    image->y0 = 0;
    image->x1 = w;
    image->y1 = h;
    for (int i = 0; i < w * h; i++) {
        image->comps[0].data[i] = rgb[i * 3];
        image->comps[1].data[i] = rgb[i * 3 + 1];
        image->comps[2].data[i] = rgb[i * 3 + 2];
    }

    opj_codec_t *codec = opj_create_compress(OPJ_CODEC_JP2);  // OPJ_CODEC_J2K for a raw codestream
    opj_stream_t *stream = opj_stream_create_default_file_stream(path, OPJ_FALSE /*write*/);
    int ok = stream
          && opj_setup_encoder(codec, &params, image)
          && opj_start_compress(codec, image, stream)
          && opj_encode(codec, stream)
          && opj_end_compress(codec, stream);
    if (stream) opj_stream_destroy(stream);
    opj_destroy_codec(codec);
    opj_image_destroy(image);
    return ok;
}

For lossless output, set tcp_rates[0] = 0 and irreversible = 0. Several layers (e.g. tcp_numlayers = 3 with rates 40, 20, 10) make a file that can be decoded progressively.

How to convert from the command line#

Shell
opj_decompress -i in.jp2 -o out.png            # decode
opj_decompress -i in.jp2 -o thumb.png -r 3     # discard 3 resolution levels (1/8 size)
opj_decompress -i in.jp2 -o part.png -d 0,0,1024,1024   # decode a region
opj_compress -i in.png -o out.jp2              # lossless
opj_compress -i in.png -o out.jp2 -r 40,20,10 -I   # lossy, 3 quality layers, 9/7 wavelet
opj_dump -i in.jp2                             # print boxes and codestream parameters

Tips#