glymur
| Language | Python 3 |
|---|---|
| License | MIT |
| Repository | github.com/quintusdias/glymur |
| Documentation | glymur.readthedocs.io |
| Depends on | NumPy, lxml, Pillow; the OpenJPEG shared library (loaded with ctypes) |
| Read / write | yes / yes; JP2, J2K, JPX; full box access; HTJ2K decoding with a recent OpenJPEG |
glymur reads JPEG 2000 into NumPy arrays and writes NumPy arrays to JPEG 2000. Image access uses array slicing: a step in the slice selects a lower resolution level, a range selects a region, and only the needed data is decoded. It also parses every JP2/JPX box, so you can read and add XML, XMP, ICC profiles, GeoJP2 and resolution boxes, and print the codestream markers.
glymur loads the OpenJPEG library at run time with ctypes; it doesn’t bundle it. Install OpenJPEG with your system package manager or conda.
Installation#
pip install glymur
# plus the OpenJPEG library:
sudo apt install libopenjp2-7 # Debian / Ubuntu
brew install openjpeg # macOS
# or: conda install -c conda-forge glymur (installs OpenJPEG too)
Basic usage: read and write#
import glymur
jp2 = glymur.Jp2k("in.jp2")
print(jp2.shape) # (rows, cols, components), e.g. (480, 640, 3)
image = jp2[:] # full image as a NumPy array (uint8 or uint16)
# write a lossless JP2 from a NumPy array
glymur.Jp2k("copy.jp2", data=image)
How to read at reduced resolution or a region#
The step of a slice must be a power of 2; each factor of 2 drops one resolution level. Ranges decode only part of the image:
import glymur
jp2 = glymur.Jp2k("big.jp2")
half = jp2[::2, ::2] # half width and height
quarter = jp2[::4, ::4]
smallest = jp2[::-1, ::-1] # lowest resolution level in the file
region = jp2[1000:2000, 500:1500] # rows 1000-1999, columns 500-1499 at full resolution
How to write lossy JPEG 2000#
cratios gives one compression ratio per quality layer; psnr gives target PSNR values in dB. A final value of 1 (cratios) or 0 (psnr) adds a lossless layer:
import glymur
import numpy as np
data = np.random.randint(0, 255, (512, 512, 3), dtype=np.uint8)
glymur.Jp2k("layers.jp2", data=data, cratios=[40, 20, 10], irreversible=True)
glymur.Jp2k("psnr.jp2", data=data, psnr=[30, 40, 50, 0])
glymur.Jp2k("tiled.jp2", data=data, tilesize=(256, 256), numres=6, prog="RPCL", plt=True)
Read only the first quality layer of a layered file with jp2.layer = 0 before slicing.
How to inspect JP2 boxes and metadata#
import glymur
jp2 = glymur.Jp2k("in.jp2")
print(jp2) # all boxes and the main codestream header
for box in jp2.box:
print(box.box_id) # 'jP ', 'ftyp', 'jp2h', 'xml ', 'uuid', 'jp2c' ...
c = jp2.get_codestream() # parsed codestream header
print(c.segment[1]) # SIZ marker: image size, components, bit depths
The ICC profile, if present, is in the colr box inside jp2h; XMP is in a uuid box whose data attribute holds the parsed XML.
How to decode with multiple threads#
import glymur
glymur.set_option("lib.num_threads", 4)
image = glymur.Jp2k("big.jp2")[:]
Tips#
glymur.version.openjpeg_versionshows which OpenJPEG was loaded. If glymur can’t find it, set its path in~/.config/glymur/glymurrc(see the docs).- glymur installs the
jp2dumpcommand, which prints the boxes and codestream of a file (jp2dump in.jp2), andtiff2jp2/jpeg2jp2converters.