All formats

jai-imageio-jpeg2000

LanguageJava 8+
LicenseJJ2000 license and BSD-3-Clause (Sun); not OSI-approved, not GPL-compatible
Repositorygithub.com/jai-imageio/jai-imageio-jpeg2000
DocumentationJavadoc of J2KImageReadParam and J2KImageWriteParam in the sources
Depends onjai-imageio-core
Read / writeyes / yes; JP2 and J2K codestreams

jai-imageio-jpeg2000 is the JPEG 2000 part of Sun’s Java Advanced Imaging Image I/O Tools, built on JJ2000, the Java reference implementation of JPEG 2000. It registers itself with ImageIO, so once the jar is on the classpath, ImageIO.read returns a BufferedImage for JPEG 2000 files. Apache PDFBox and many document tools use it to decode JPXDecode images in PDFs.

The project gets only build fixes, no new development. It’s pure Java and slow on large images, and it has no HTJ2K support. The JJ2000 license grants rights only for products that conform to the JPEG 2000 standard and warns about patents; it isn’t compatible with the GPL.

Installation#

Maven:

XML
<dependency>
    <groupId>com.github.jai-imageio</groupId>
    <artifactId>jai-imageio-jpeg2000</artifactId>
    <version>1.4.0</version>
</dependency>

Gradle:

groovy
implementation 'com.github.jai-imageio:jai-imageio-jpeg2000:1.4.0'

Basic usage: read JPEG 2000 and save as PNG#

Java
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;

public class Jp2ToPng {
    public static void main(String[] args) throws IOException {
        BufferedImage image = ImageIO.read(new File("in.jp2"));
        if (image == null) {
            throw new IOException("no ImageIO reader for this file");
        }
        System.out.println(image.getWidth() + " x " + image.getHeight());
        ImageIO.write(image, "png", new File("out.png"));
    }
}

How to write a lossless JPEG 2000 file#

The writer is registered under the format name "jpeg2000". Default settings are lossless:

Java
BufferedImage image = ImageIO.read(new File("in.png"));
ImageIO.write(image, "jpeg2000", new File("out.jp2"));

How to write lossy JPEG 2000 with a bit rate#

Use J2KImageWriteParam to set the target bits per pixel. Setting a rate switches to the lossy 9/7 wavelet:

Java
import com.github.jaiimageio.jpeg2000.J2KImageWriteParam;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;

BufferedImage image = ImageIO.read(new File("in.png"));
ImageWriter writer = ImageIO.getImageWritersByFormatName("jpeg2000").next();
J2KImageWriteParam param = (J2KImageWriteParam) writer.getDefaultWriteParam();
param.setEncodingRate(1.0);           // 1 bit per pixel
param.setNumDecompositionLevels(5);
param.setWriteCodeStreamOnly(false);  // true writes a raw .j2k codestream

try (ImageOutputStream out = ImageIO.createImageOutputStream(new File("out.jp2"))) {
    writer.setOutput(out);
    writer.write(null, new IIOImage(image, null, null), param);
} finally {
    writer.dispose();
}

How to decode at lower resolution#

J2KImageReadParam.setResolution picks the resolution level. It skips only the last wavelet steps, the whole codestream is still parsed; add setDecodingRate(bitsPerPixel) to read fewer bytes:

Java
import com.github.jaiimageio.jpeg2000.J2KImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;

try (ImageInputStream in = ImageIO.createImageInputStream(new File("big.jp2"))) {
    ImageReader reader = ImageIO.getImageReaders(in).next();
    reader.setInput(in);
    J2KImageReadParam param = (J2KImageReadParam) reader.getDefaultReadParam();
    param.setResolution(2);   // resolution level, 0 is the smallest
    BufferedImage preview = reader.read(0, param);
    System.out.println(preview.getWidth() + " x " + preview.getHeight());
    reader.dispose();
}

How to get the image size without decoding#

Java
try (ImageInputStream in = ImageIO.createImageInputStream(new File("in.jp2"))) {
    ImageReader reader = ImageIO.getImageReaders(in).next();
    reader.setInput(in);
    System.out.println(reader.getWidth(0) + " x " + reader.getHeight(0));
    reader.dispose();
}

Tips#