Merge branch 'issue111' into devel
This commit is contained in:
commit
c051d8d291
5 changed files with 610 additions and 77 deletions
|
|
@ -80,60 +80,42 @@ MacPorts supplies both OpenJPEG 1.5.0 and OpenJPEG 2.0.0.
|
||||||
|
|
||||||
Linux
|
Linux
|
||||||
-----
|
-----
|
||||||
|
For the most part, you only need python and numpy to run glymur. In order to
|
||||||
|
run as many tests as possible, however, the following Python packages may also
|
||||||
|
need to be installed.
|
||||||
|
|
||||||
|
* setuptools
|
||||||
|
* matplotlib
|
||||||
|
* pillow
|
||||||
|
* contextlib2 (python 2.7 only)
|
||||||
|
* mock (python 2.7 only)
|
||||||
|
|
||||||
|
OpenSUSE 12.3
|
||||||
|
'''''''''''''
|
||||||
|
Ships with Python 3.3 and 2.7. You should use pip to install Pillow.
|
||||||
|
|
||||||
Fedora 19
|
Fedora 19
|
||||||
'''''''''
|
'''''''''
|
||||||
Fedora 18 ships with Python 3.3 and all the necessary RPMs are available to
|
Ships with Python 3.3 and 2.7. All packages available as RPMs.
|
||||||
run the maximum number of tests.
|
|
||||||
|
|
||||||
* python3
|
|
||||||
* python3-numpy
|
|
||||||
* python3-setuptools
|
|
||||||
* python3-matplotlib (for running tests)
|
|
||||||
* python3-matplotlib-tk (or whichever matplotlib backend you prefer)
|
|
||||||
* python3-pillow (for running tests)
|
|
||||||
|
|
||||||
Fedora 18
|
Fedora 18
|
||||||
'''''''''
|
'''''''''
|
||||||
Fedora 18 ships with Python 3.3 and the following RPMs are available to
|
Fedora 18 ships with Python 3.3 and 2.7. Most packages are available as
|
||||||
meet the minimal set of requirements for running glymur.
|
standard RPMS, but you should use pip to install Pillow as it is not available
|
||||||
|
in the Fedora 18 default repositories::
|
||||||
* python3
|
|
||||||
* python3-numpy
|
|
||||||
* python3-setuptools
|
|
||||||
|
|
||||||
For running the maximal number of tests, you also need
|
|
||||||
|
|
||||||
* python3-matplotlib
|
|
||||||
* python3-matplotlib-tk (or whichever matplotlib backend you prefer)
|
|
||||||
|
|
||||||
Pillow is also needed in order to run the maximum number of tests, so
|
|
||||||
go ahead and install Pillow via pip since Pillow is not available
|
|
||||||
in Fedora 18 default repositories::
|
|
||||||
|
|
||||||
$ yum install python3-devel # pip needs this in order to compile Pillow
|
$ yum install python3-devel # pip needs this in order to compile Pillow
|
||||||
$ yum install python3-pip
|
$ yum install python3-pip
|
||||||
$ pip-python3 install Pillow --user
|
$ pip-python3 install Pillow --user
|
||||||
$ export PYTHONPATH=$HOME/.local/lib/python3.3/site-packages:$PYTHONPATH
|
|
||||||
|
|
||||||
Fedora 17
|
Fedora 17
|
||||||
'''''''''
|
'''''''''
|
||||||
Fedora 17 ships with Python 2.7 and OpenJPEG 1.4. You should have the
|
Fedora 17 ships with Python 2.7 and OpenJPEG 1.4. You must install contextlib2
|
||||||
following RPMs installed.
|
and Pillow via pip. ::
|
||||||
|
|
||||||
* python
|
|
||||||
* python-mock
|
|
||||||
* python-pip
|
|
||||||
* python-setuptools
|
|
||||||
* numpy
|
|
||||||
* matplotlib (optional)
|
|
||||||
|
|
||||||
In addition, you must install contextlib2 and Pillow via pip. ::
|
|
||||||
|
|
||||||
$ yum install python-devel # pip needs this in order to compile Pillow
|
$ yum install python-devel # pip needs this in order to compile Pillow
|
||||||
$ pip-python install Pillow --user
|
$ pip-python install Pillow --user
|
||||||
$ pip-python install contextlib2 --user
|
$ pip-python install contextlib2 --user
|
||||||
$ export PYTHONPATH=$HOME/.local/lib/python2.7/site-packages:$PYTHONPATH
|
|
||||||
|
|
||||||
Windows
|
Windows
|
||||||
-------
|
-------
|
||||||
|
|
|
||||||
146
glymur/jp2k.py
146
glymur/jp2k.py
|
|
@ -15,14 +15,14 @@ else:
|
||||||
import ctypes
|
import ctypes
|
||||||
import math
|
import math
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import struct
|
import struct
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from .codestream import Codestream
|
from .codestream import Codestream
|
||||||
from .core import SRGB
|
from .core import SRGB, GREYSCALE
|
||||||
from .core import GREYSCALE
|
|
||||||
from .core import PROGRESSION_ORDER
|
from .core import PROGRESSION_ORDER
|
||||||
from .core import ENUMERATED_COLORSPACE, RESTRICTED_ICC_PROFILE
|
from .core import ENUMERATED_COLORSPACE, RESTRICTED_ICC_PROFILE
|
||||||
from .jp2box import Jp2kBox
|
from .jp2box import Jp2kBox
|
||||||
|
|
@ -33,6 +33,13 @@ from .lib import openjpeg as opj
|
||||||
from .lib import openjp2 as opj2
|
from .lib import openjp2 as opj2
|
||||||
from .lib import c as libc
|
from .lib import c as libc
|
||||||
|
|
||||||
|
if opj.OPENJPEG is None and opj2.OPENJP2 is None:
|
||||||
|
OPENJPEG_VERSION = '0.0.0'
|
||||||
|
elif opj2.OPENJP2 is None:
|
||||||
|
OPENJPEG_VERSION = opj.version()
|
||||||
|
else:
|
||||||
|
OPENJPEG_VERSION = opj2.version()
|
||||||
|
|
||||||
|
|
||||||
class Jp2k(Jp2kBox):
|
class Jp2k(Jp2kBox):
|
||||||
"""JPEG 2000 file.
|
"""JPEG 2000 file.
|
||||||
|
|
@ -185,7 +192,10 @@ class Jp2k(Jp2kBox):
|
||||||
cparams : CompressionParametersType(ctypes.Structure)
|
cparams : CompressionParametersType(ctypes.Structure)
|
||||||
Corresponds to cparameters_t type in openjp2 headers.
|
Corresponds to cparameters_t type in openjp2 headers.
|
||||||
"""
|
"""
|
||||||
cparams = opj2.set_default_encoder_parameters()
|
if re.match(r"""1\.\d\.\d""", OPENJPEG_VERSION):
|
||||||
|
cparams = opj.set_default_encoder_parameters()
|
||||||
|
else:
|
||||||
|
cparams = opj2.set_default_encoder_parameters()
|
||||||
|
|
||||||
outfile = self.filename.encode()
|
outfile = self.filename.encode()
|
||||||
num_pad_bytes = opj2.PATH_LEN - len(outfile)
|
num_pad_bytes = opj2.PATH_LEN - len(outfile)
|
||||||
|
|
@ -280,9 +290,6 @@ class Jp2k(Jp2kBox):
|
||||||
Corresponds to cparameters_t type in openjp2 headers.
|
Corresponds to cparameters_t type in openjp2 headers.
|
||||||
colorspace : int
|
colorspace : int
|
||||||
Either CLRSPC_SRGB or CLRSPC_GRAY
|
Either CLRSPC_SRGB or CLRSPC_GRAY
|
||||||
mct : bool, optional
|
|
||||||
Specifies usage of the multi component transform. If not
|
|
||||||
specified, defaults to True if the colorspace is RGB.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if 'cratios' in kwargs and 'psnr' in kwargs:
|
if 'cratios' in kwargs and 'psnr' in kwargs:
|
||||||
|
|
@ -380,11 +387,127 @@ class Jp2k(Jp2kBox):
|
||||||
glymur.LibraryNotFoundError
|
glymur.LibraryNotFoundError
|
||||||
If glymur is unable to load the openjp2 library.
|
If glymur is unable to load the openjp2 library.
|
||||||
"""
|
"""
|
||||||
if opj2.OPENJP2 is None:
|
if opj2.OPENJP2 is not None:
|
||||||
raise LibraryNotFoundError("You must have the openjp2 library "
|
img = self._write_openjp2(img_array, verbose=verbose, **kwargs)
|
||||||
"installed before using this "
|
elif opj.OPENJPEG is not None:
|
||||||
|
img = self._write_openjpeg(img_array, verbose=verbose, **kwargs)
|
||||||
|
else:
|
||||||
|
raise LibraryNotFoundError("You must have version 1.5 of OpenJPEG "
|
||||||
|
"or more recent before using this "
|
||||||
"functionality.")
|
"functionality.")
|
||||||
|
|
||||||
|
def _write_openjpeg(self, img_array, verbose=False, **kwargs):
|
||||||
|
"""
|
||||||
|
"""
|
||||||
|
cparams, colorspace = self._process_write_inputs(img_array, **kwargs)
|
||||||
|
|
||||||
|
if img_array.ndim == 2:
|
||||||
|
# Force the image to be 3D. Just makes things easier later on.
|
||||||
|
numrows, numcols = img_array.shape
|
||||||
|
img_array = img_array.reshape(numrows, numcols, 1)
|
||||||
|
|
||||||
|
comptparms = _populate_comptparms(img_array, cparams)
|
||||||
|
|
||||||
|
image = opj.image_create(comptparms, colorspace)
|
||||||
|
|
||||||
|
numrows, numcols, numlayers = img_array.shape
|
||||||
|
|
||||||
|
# set image offset and reference grid
|
||||||
|
image.contents.x0 = cparams.image_offset_x0
|
||||||
|
image.contents.y0 = cparams.image_offset_y0
|
||||||
|
image.contents.x1 = image.contents.x0 + (numcols - 1) * cparams.subsampling_dx + 1
|
||||||
|
image.contents.y1 = image.contents.y0 + (numrows - 1) * cparams.subsampling_dy + 1
|
||||||
|
|
||||||
|
# Stage the image data to the openjpeg data structure.
|
||||||
|
for k in range(0,numlayers):
|
||||||
|
layer = np.ascontiguousarray(img_array[:,:,k], dtype=np.int32)
|
||||||
|
dest = image.contents.comps[k].data
|
||||||
|
src = layer.ctypes.data
|
||||||
|
ctypes.memmove(dest, src, layer.nbytes)
|
||||||
|
|
||||||
|
# set encode format
|
||||||
|
cinfo = opj.create_compress(cparams.codec_fmt)
|
||||||
|
|
||||||
|
event_mgr = opj.EventMgrType(None, None, None)
|
||||||
|
#opj.set_event_mgr(cparams, ctypes.byref(event_mgr), None)
|
||||||
|
|
||||||
|
opj.setup_encoder(cinfo, ctypes.byref(cparams), image)
|
||||||
|
|
||||||
|
# open a byte stream for writing
|
||||||
|
# allocate memory for all tiles
|
||||||
|
cio = opj.cio_open(cinfo)
|
||||||
|
|
||||||
|
opj.encode(cinfo, cio, image)
|
||||||
|
pos = opj.cio_tell(cio)
|
||||||
|
|
||||||
|
ss = ctypes.string_at(cio.contents.buffer, pos)
|
||||||
|
f = open(self.filename,'wb')
|
||||||
|
f.write(ss)
|
||||||
|
f.close()
|
||||||
|
opj.cio_close(cio);
|
||||||
|
|
||||||
|
opj.destroy_compress(cinfo);
|
||||||
|
opj.image_destroy(image);
|
||||||
|
|
||||||
|
self.parse()
|
||||||
|
|
||||||
|
|
||||||
|
def _write_openjp2(self, img_array, verbose=False, **kwargs):
|
||||||
|
"""Write image data to a JP2/JPX/J2k file. Intended usage of the
|
||||||
|
various parameters follows that of OpenJPEG's opj_compress utility.
|
||||||
|
|
||||||
|
This method can only be used to create JPEG 2000 images that can fit
|
||||||
|
in memory.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
img_array : ndarray
|
||||||
|
Image data to be written to file.
|
||||||
|
cbsize : tuple, optional
|
||||||
|
Code block size (DY, DX).
|
||||||
|
colorspace : str, optional
|
||||||
|
Either 'rgb' or 'gray'.
|
||||||
|
cratios : iterable
|
||||||
|
Compression ratios for successive layers.
|
||||||
|
eph : bool, optional
|
||||||
|
If true, write SOP marker after each header packet.
|
||||||
|
grid_offset : tuple, optional
|
||||||
|
Offset (DY, DX) of the origin of the image in the reference grid.
|
||||||
|
mct : bool, optional
|
||||||
|
Specifies usage of the multi component transform. If not
|
||||||
|
specified, defaults to True if the colorspace is RGB.
|
||||||
|
modesw : int, optional
|
||||||
|
Mode switch.
|
||||||
|
1 = BYPASS(LAZY)
|
||||||
|
2 = RESET
|
||||||
|
4 = RESTART(TERMALL)
|
||||||
|
8 = VSC
|
||||||
|
16 = ERTERM(SEGTERM)
|
||||||
|
32 = SEGMARK(SEGSYM)
|
||||||
|
numres : int, optional
|
||||||
|
Number of resolutions.
|
||||||
|
prog : str, optional
|
||||||
|
Progression order, one of "LRCP" "RLCP", "RPCL", "PCRL", "CPRL".
|
||||||
|
psnr : iterable, optional
|
||||||
|
Different PSNR for successive layers.
|
||||||
|
psizes : list, optional
|
||||||
|
List of precinct sizes. Each precinct size tuple is defined in
|
||||||
|
(height x width).
|
||||||
|
sop : bool, optional
|
||||||
|
If true, write SOP marker before each packet.
|
||||||
|
subsam : tuple, optional
|
||||||
|
Subsampling factors (dy, dx).
|
||||||
|
tilesize : tuple, optional
|
||||||
|
Numeric tuple specifying tile size in terms of (numrows, numcols),
|
||||||
|
not (X, Y).
|
||||||
|
verbose : bool, optional
|
||||||
|
Print informational messages produced by the OpenJPEG library.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
glymur.LibraryNotFoundError
|
||||||
|
If glymur is unable to load the openjp2 library.
|
||||||
|
"""
|
||||||
cparams, colorspace = self._process_write_inputs(img_array, **kwargs)
|
cparams, colorspace = self._process_write_inputs(img_array, **kwargs)
|
||||||
|
|
||||||
if img_array.ndim == 2:
|
if img_array.ndim == 2:
|
||||||
|
|
@ -1207,7 +1330,10 @@ def _populate_comptparms(img_array, cparams):
|
||||||
comp_prec = 16
|
comp_prec = 16
|
||||||
|
|
||||||
numrows, numcols, num_comps = img_array.shape
|
numrows, numcols, num_comps = img_array.shape
|
||||||
comptparms = (opj2.ImageComptParmType * num_comps)()
|
if re.match(r"""1\.\d\.\d""", OPENJPEG_VERSION):
|
||||||
|
comptparms = (opj.ImageComptParmType * num_comps)()
|
||||||
|
else:
|
||||||
|
comptparms = (opj2.ImageComptParmType * num_comps)()
|
||||||
for j in range(num_comps):
|
for j in range(num_comps):
|
||||||
comptparms[j].dx = cparams.subsampling_dx
|
comptparms[j].dx = cparams.subsampling_dx
|
||||||
comptparms[j].dy = cparams.subsampling_dy
|
comptparms[j].dy = cparams.subsampling_dy
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,16 @@
|
||||||
import ctypes
|
import ctypes
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
from .config import glymur_config
|
from .config import glymur_config
|
||||||
_, OPENJPEG = glymur_config()
|
_, OPENJPEG = glymur_config()
|
||||||
|
|
||||||
PATH_LEN = 4096 # maximum allowed size for filenames
|
# Maximum number of tile parts expected by JPWL: increase at your will
|
||||||
|
JPWL_MAX_NO_TILESPECS = 16
|
||||||
|
|
||||||
|
J2K_MAXRLVLS = 33 # Number of maximum resolution level authorized
|
||||||
|
PATH_LEN = 4096 # maximum allowed size for filenames
|
||||||
|
|
||||||
|
|
||||||
def version():
|
def version():
|
||||||
|
|
@ -52,14 +58,8 @@ class CommonStructType(ctypes.Structure):
|
||||||
("mj2_handle", ctypes.c_void_p)]
|
("mj2_handle", ctypes.c_void_p)]
|
||||||
|
|
||||||
|
|
||||||
class DecompressionInfoType(ctypes.Structure):
|
STREAM_READ = 0x0001 # The stream was opened for reading.
|
||||||
"""This is for decompression contexts.
|
STREAM_WRITE = 0x0002 # The stream was opened for writing.
|
||||||
|
|
||||||
Corresponds to dinfo_t type in openjpeg headers.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class CioType(ctypes.Structure):
|
class CioType(ctypes.Structure):
|
||||||
"""Byte input-output stream (CIO)
|
"""Byte input-output stream (CIO)
|
||||||
|
|
||||||
|
|
@ -80,6 +80,266 @@ class CioType(ctypes.Structure):
|
||||||
("bp", ctypes.c_char_p)]
|
("bp", ctypes.c_char_p)]
|
||||||
|
|
||||||
|
|
||||||
|
class CompressionInfoType(CommonStructType):
|
||||||
|
"""Common fields between JPEG-2000 compression and decompression contexts.
|
||||||
|
This is for compression contexts. Corresponds to common_struct_t.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class PocType(ctypes.Structure):
|
||||||
|
"""Progression order changes."""
|
||||||
|
_fields_ = [("resno", ctypes.c_int),
|
||||||
|
# Resolution num start, Component num start, given by POC
|
||||||
|
("compno0", ctypes.c_int),
|
||||||
|
|
||||||
|
# Layer num end,Resolution num end, Component num end, given by POC
|
||||||
|
("layno1", ctypes.c_int),
|
||||||
|
("resno1", ctypes.c_int),
|
||||||
|
("compno1", ctypes.c_int),
|
||||||
|
|
||||||
|
# Layer num start,Precinct num start, Precinct num end
|
||||||
|
("layno0", ctypes.c_int),
|
||||||
|
("precno0", ctypes.c_int),
|
||||||
|
("precno1", ctypes.c_int),
|
||||||
|
|
||||||
|
# Progression order enum
|
||||||
|
# OPJ_PROG_ORDER prg1,prg;
|
||||||
|
("prg1", ctypes.c_int),
|
||||||
|
("prg", ctypes.c_int),
|
||||||
|
|
||||||
|
# Progression order string
|
||||||
|
# char progorder[5];
|
||||||
|
("progorder", ctypes.c_char * 5),
|
||||||
|
|
||||||
|
# Tile number
|
||||||
|
# int tile;
|
||||||
|
("tile", ctypes.c_int),
|
||||||
|
|
||||||
|
# /** Start and end values for Tile width and height*/
|
||||||
|
# int tx0,tx1,ty0,ty1;
|
||||||
|
("tx0", ctypes.c_int),
|
||||||
|
("tx1", ctypes.c_int),
|
||||||
|
("ty0", ctypes.c_int),
|
||||||
|
("ty1", ctypes.c_int),
|
||||||
|
|
||||||
|
# /** Start value, initialised in pi_initialise_encode*/
|
||||||
|
# int layS, resS, compS, prcS;
|
||||||
|
("layS", ctypes.c_int),
|
||||||
|
("resS", ctypes.c_int),
|
||||||
|
("compS", ctypes.c_int),
|
||||||
|
("prcS", ctypes.c_int),
|
||||||
|
|
||||||
|
# /** End value, initialised in pi_initialise_encode */
|
||||||
|
# int layE, resE, compE, prcE;
|
||||||
|
("layE", ctypes.c_int),
|
||||||
|
("resE", ctypes.c_int),
|
||||||
|
("compE", ctypes.c_int),
|
||||||
|
("prcE", ctypes.c_int),
|
||||||
|
|
||||||
|
# Start and end values of Tile width and height, initialised in
|
||||||
|
# pi_initialise_encode int txS,txE,tyS,tyE,dx,dy;
|
||||||
|
("txS", ctypes.c_int),
|
||||||
|
("txE", ctypes.c_int),
|
||||||
|
("tyS", ctypes.c_int),
|
||||||
|
("tyE", ctypes.c_int),
|
||||||
|
("dx", ctypes.c_int),
|
||||||
|
("dy", ctypes.c_int),
|
||||||
|
|
||||||
|
# Temporary values for Tile parts, initialised in pi_create_encode
|
||||||
|
# int lay_t, res_t, comp_t, prc_t,tx0_t,ty0_t;
|
||||||
|
("lay_t", ctypes.c_int),
|
||||||
|
("res_t", ctypes.c_int),
|
||||||
|
("comp_t", ctypes.c_int),
|
||||||
|
("prc_t", ctypes.c_int),
|
||||||
|
("tx0_t", ctypes.c_int),
|
||||||
|
("ty0_t", ctypes.c_int)]
|
||||||
|
|
||||||
|
|
||||||
|
class CompressionParametersType(ctypes.Structure):
|
||||||
|
"""Compression parameters.
|
||||||
|
|
||||||
|
Corresponds to cparameters_t type in openjp2 headers.
|
||||||
|
"""
|
||||||
|
_fields_ = [
|
||||||
|
# size of tile:
|
||||||
|
# tile_size_on = false (not in argument) or
|
||||||
|
# = true (in argument)
|
||||||
|
("tile_size_on", ctypes.c_int),
|
||||||
|
|
||||||
|
# XTOsiz, YTOsiz
|
||||||
|
("cp_tx0", ctypes.c_int),
|
||||||
|
("cp_ty0", ctypes.c_int),
|
||||||
|
|
||||||
|
# XTsiz, YTsiz
|
||||||
|
("cp_tdx", ctypes.c_int),
|
||||||
|
("cp_tdy", ctypes.c_int),
|
||||||
|
|
||||||
|
# allocation by rate/distortion
|
||||||
|
("cp_disto_alloc", ctypes.c_int),
|
||||||
|
|
||||||
|
# allocation by fixed layer
|
||||||
|
("cp_fixed_alloc", ctypes.c_int),
|
||||||
|
|
||||||
|
# add fixed_quality
|
||||||
|
("cp_fixed_quality", ctypes.c_int),
|
||||||
|
|
||||||
|
# fixed layer
|
||||||
|
("cp_matrice", ctypes.c_void_p),
|
||||||
|
|
||||||
|
# comment for coding
|
||||||
|
("cp_comment", ctypes.c_char_p),
|
||||||
|
|
||||||
|
# csty : coding style
|
||||||
|
("csty", ctypes.c_int),
|
||||||
|
|
||||||
|
# progression order (default OPJ_LRCP)
|
||||||
|
("prog_order", ctypes.c_int),
|
||||||
|
|
||||||
|
# progression order changes
|
||||||
|
("poc", PocType * 32),
|
||||||
|
|
||||||
|
# number of progression order changes (POC), default to 0
|
||||||
|
("numpocs", ctypes.c_uint),
|
||||||
|
|
||||||
|
# number of layers
|
||||||
|
("tcp_numlayers", ctypes.c_int),
|
||||||
|
|
||||||
|
# rates of layers
|
||||||
|
("tcp_rates", ctypes.c_float * 100),
|
||||||
|
|
||||||
|
# different psnr for successive layers
|
||||||
|
("tcp_distoratio", ctypes.c_float * 100),
|
||||||
|
|
||||||
|
# number of resolutions
|
||||||
|
("numresolution", ctypes.c_int),
|
||||||
|
|
||||||
|
# initial code block width, default to 64
|
||||||
|
("cblockw_init", ctypes.c_int),
|
||||||
|
|
||||||
|
# initial code block height, default to 64
|
||||||
|
("cblockh_init", ctypes.c_int),
|
||||||
|
|
||||||
|
# mode switch (cblk_style)
|
||||||
|
("mode", ctypes.c_int),
|
||||||
|
|
||||||
|
# 1 : use the irreversible DWT 9-7
|
||||||
|
# 0 : use lossless compression (default)
|
||||||
|
("irreversible", ctypes.c_int),
|
||||||
|
|
||||||
|
# region of interest: affected component in [0..3], -1 means no ROI
|
||||||
|
("roi_compno", ctypes.c_int),
|
||||||
|
|
||||||
|
# region of interest: upshift value
|
||||||
|
("roi_shift", ctypes.c_int),
|
||||||
|
|
||||||
|
# number of precinct size specifications
|
||||||
|
("res_spec", ctypes.c_int),
|
||||||
|
|
||||||
|
# initial precinct width
|
||||||
|
("prcw_init", ctypes.c_int * J2K_MAXRLVLS),
|
||||||
|
|
||||||
|
# initial precinct height
|
||||||
|
("prch_init", ctypes.c_int * J2K_MAXRLVLS),
|
||||||
|
|
||||||
|
# input file name
|
||||||
|
("infile", ctypes.c_char * PATH_LEN),
|
||||||
|
|
||||||
|
# output file name
|
||||||
|
("outfile", ctypes.c_char * PATH_LEN),
|
||||||
|
|
||||||
|
# DEPRECATED.
|
||||||
|
("index_on", ctypes.c_int),
|
||||||
|
|
||||||
|
# DEPRECATED.
|
||||||
|
("index", ctypes.c_char * PATH_LEN),
|
||||||
|
|
||||||
|
# subimage encoding: origin image offset in x direction
|
||||||
|
# subimage encoding: origin image offset in y direction
|
||||||
|
("image_offset_x0", ctypes.c_int),
|
||||||
|
("image_offset_y0", ctypes.c_int),
|
||||||
|
|
||||||
|
# subsampling value for dx
|
||||||
|
# subsampling value for dy
|
||||||
|
("subsampling_dx", ctypes.c_int),
|
||||||
|
("subsampling_dy", ctypes.c_int),
|
||||||
|
|
||||||
|
# input file format 0: PGX, 1: PxM, 2: BMP 3:TIF
|
||||||
|
# output file format 0: J2K, 1: JP2, 2: JPT
|
||||||
|
("decod_format", ctypes.c_int),
|
||||||
|
("cod_format", ctypes.c_int),
|
||||||
|
|
||||||
|
# JPWL encoding parameters
|
||||||
|
# enables writing of EPC in MH, thus activating JPWL
|
||||||
|
("jpwl_epc_on", ctypes.c_int),
|
||||||
|
|
||||||
|
# error protection method for MH (0,1,16,32,37-128)
|
||||||
|
("jpwl_hprot_mh", ctypes.c_int),
|
||||||
|
|
||||||
|
# tile number of header protection specification (>=0)
|
||||||
|
("jpwl_hprot_tph_tileno", ctypes.c_int * JPWL_MAX_NO_TILESPECS),
|
||||||
|
|
||||||
|
# error protection methods for TPHs (0,1,16,32,37-128)
|
||||||
|
("jpwl_hprot_tph", ctypes.c_int * JPWL_MAX_NO_TILESPECS),
|
||||||
|
|
||||||
|
# tile number of packet protection specification (>=0)
|
||||||
|
("jpwl_pprot_tileno", ctypes.c_int * JPWL_MAX_NO_TILESPECS),
|
||||||
|
|
||||||
|
# packet number of packet protection specification (>=0)
|
||||||
|
("jpwl_pprot_packno", ctypes.c_int * JPWL_MAX_NO_TILESPECS),
|
||||||
|
|
||||||
|
# error protection methods for packets (0,1,16,32,37-128)
|
||||||
|
("jpwl_pprot", ctypes.c_int * JPWL_MAX_NO_TILESPECS),
|
||||||
|
|
||||||
|
# enables writing of ESD, (0=no/1/2 bytes)
|
||||||
|
("jpwl_sens_size", ctypes.c_int),
|
||||||
|
|
||||||
|
# sensitivity addressing size (0=auto/2/4 bytes)
|
||||||
|
("jpwl_sens_addr", ctypes.c_int),
|
||||||
|
|
||||||
|
# sensitivity range (0-3)
|
||||||
|
("jpwl_sens_range", ctypes.c_int),
|
||||||
|
|
||||||
|
# sensitivity method for MH (-1=no,0-7)
|
||||||
|
("jpwl_sens_mh", ctypes.c_int),
|
||||||
|
|
||||||
|
# tile number of sensitivity specification (>=0)
|
||||||
|
("jpwl_sens_tph_tileno", ctypes.c_int * JPWL_MAX_NO_TILESPECS),
|
||||||
|
|
||||||
|
# sensitivity methods for TPHs (-1=no,0-7)
|
||||||
|
("jpwl_sens_tph", ctypes.c_int * JPWL_MAX_NO_TILESPECS),
|
||||||
|
|
||||||
|
# Digital Cinema compliance 0-not compliant, 1-compliant
|
||||||
|
("cp_cinema", ctypes.c_int),
|
||||||
|
|
||||||
|
# Maximum rate for each component.
|
||||||
|
# If == 0, component size limitation is not considered
|
||||||
|
("max_comp_size", ctypes.c_int),
|
||||||
|
|
||||||
|
# Profile name
|
||||||
|
("cp_rsiz", ctypes.c_int),
|
||||||
|
|
||||||
|
# Tile part generation
|
||||||
|
("tp_on", ctypes.c_uint8),
|
||||||
|
|
||||||
|
# Flag for Tile part generation
|
||||||
|
("tp_flag", ctypes.c_uint8),
|
||||||
|
|
||||||
|
# MCT (multiple component transform)
|
||||||
|
("tcp_mct", ctypes.c_uint8),
|
||||||
|
|
||||||
|
# Enable JPIP indexing
|
||||||
|
("jpip_on", ctypes.c_int)]
|
||||||
|
|
||||||
|
|
||||||
|
class DecompressionInfoType(ctypes.Structure):
|
||||||
|
"""This is for decompression contexts.
|
||||||
|
|
||||||
|
Corresponds to dinfo_t type in openjpeg headers.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class DecompressionParametersType(ctypes.Structure):
|
class DecompressionParametersType(ctypes.Structure):
|
||||||
"""Decompression parameters.
|
"""Decompression parameters.
|
||||||
|
|
||||||
|
|
@ -111,23 +371,51 @@ class DecompressionParametersType(ctypes.Structure):
|
||||||
_fields_.append(("flags", ctypes.c_uint))
|
_fields_.append(("flags", ctypes.c_uint))
|
||||||
|
|
||||||
|
|
||||||
class ImageCompType(ctypes.Structure):
|
class ImageComptParmType(ctypes.Structure):
|
||||||
"""Defines a single image component.
|
"""Component parameters structure used by the opj_image_create function.
|
||||||
|
|
||||||
Corresponds to image_comp_t type in openjpeg.
|
|
||||||
"""
|
"""
|
||||||
_fields_ = [("dx", ctypes.c_int),
|
_fields_ = [
|
||||||
("dy", ctypes.c_int),
|
# XRsiz: horizontal separation of a sample of ith component with
|
||||||
("w", ctypes.c_int),
|
# respect to the reference grid
|
||||||
("h", ctypes.c_int),
|
("dx", ctypes.c_int),
|
||||||
("x0", ctypes.c_int),
|
|
||||||
("y0", ctypes.c_int),
|
# YRsiz: vertical separation of a sample of ith component with
|
||||||
("prec", ctypes.c_int),
|
# respect to the reference grid */
|
||||||
("bpp", ctypes.c_int),
|
("dy", ctypes.c_int),
|
||||||
("sgnd", ctypes.c_int),
|
|
||||||
("resno_decoded", ctypes.c_int),
|
# data width, height
|
||||||
("factor", ctypes.c_int),
|
("w", ctypes.c_int),
|
||||||
("data", ctypes.POINTER(ctypes.c_int))]
|
("h", ctypes.c_int),
|
||||||
|
|
||||||
|
# x component offset compared to the whole image
|
||||||
|
# y component offset compared to the whole image
|
||||||
|
("x0", ctypes.c_int),
|
||||||
|
("y0", ctypes.c_int),
|
||||||
|
|
||||||
|
# precision
|
||||||
|
('prec', ctypes.c_int),
|
||||||
|
|
||||||
|
# image depth in bits
|
||||||
|
('bpp', ctypes.c_int),
|
||||||
|
|
||||||
|
# signed (1) / unsigned (0)
|
||||||
|
('sgnd', ctypes.c_int)]
|
||||||
|
|
||||||
|
|
||||||
|
class ImageCompType(ctypes.Structure):
|
||||||
|
"""Defines a single image component. """
|
||||||
|
_fields_ = [("dx", ctypes.c_int),
|
||||||
|
("dy", ctypes.c_int),
|
||||||
|
("w", ctypes.c_int),
|
||||||
|
("h", ctypes.c_int),
|
||||||
|
("x0", ctypes.c_int),
|
||||||
|
("y0", ctypes.c_int),
|
||||||
|
("prec", ctypes.c_int),
|
||||||
|
("bpp", ctypes.c_int),
|
||||||
|
("sgnd", ctypes.c_int),
|
||||||
|
("resno_decoded", ctypes.c_int),
|
||||||
|
("factor", ctypes.c_int),
|
||||||
|
("data", ctypes.POINTER(ctypes.c_int))]
|
||||||
|
|
||||||
|
|
||||||
class ImageType(ctypes.Structure):
|
class ImageType(ctypes.Structure):
|
||||||
|
|
@ -146,16 +434,22 @@ class ImageType(ctypes.Structure):
|
||||||
("icc_profile_len", ctypes.c_int)]
|
("icc_profile_len", ctypes.c_int)]
|
||||||
|
|
||||||
|
|
||||||
def cio_open(cinfo, src):
|
def cio_open(cinfo, src=None):
|
||||||
"""Wrapper for openjpeg library function opj_cio_open."""
|
"""Wrapper for openjpeg library function opj_cio_open."""
|
||||||
argtypes = [ctypes.POINTER(CommonStructType), ctypes.c_char_p,
|
argtypes = [ctypes.POINTER(CommonStructType), ctypes.c_char_p,
|
||||||
ctypes.c_int]
|
ctypes.c_int]
|
||||||
OPENJPEG.opj_cio_open.argtypes = argtypes
|
OPENJPEG.opj_cio_open.argtypes = argtypes
|
||||||
OPENJPEG.opj_cio_open.restype = ctypes.POINTER(CioType)
|
OPENJPEG.opj_cio_open.restype = ctypes.POINTER(CioType)
|
||||||
|
|
||||||
|
if src is None:
|
||||||
|
length = 0
|
||||||
|
else:
|
||||||
|
length = len(src)
|
||||||
|
|
||||||
cio = OPENJPEG.opj_cio_open(ctypes.cast(cinfo,
|
cio = OPENJPEG.opj_cio_open(ctypes.cast(cinfo,
|
||||||
ctypes.POINTER(CommonStructType)),
|
ctypes.POINTER(CommonStructType)),
|
||||||
src, len(src))
|
src,
|
||||||
|
length)
|
||||||
return cio
|
return cio
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -166,6 +460,24 @@ def cio_close(cio):
|
||||||
OPENJPEG.opj_cio_close(cio)
|
OPENJPEG.opj_cio_close(cio)
|
||||||
|
|
||||||
|
|
||||||
|
def cio_tell(cio):
|
||||||
|
"""Get position in byte stream."""
|
||||||
|
OPENJPEG.cio_tell.argtypes = [ctypes.POINTER(CioType)]
|
||||||
|
OPENJPEG.cio_tell.restype = ctypes.c_int
|
||||||
|
pos = OPENJPEG.cio_tell(cio)
|
||||||
|
return pos
|
||||||
|
|
||||||
|
def create_compress(fmt):
|
||||||
|
"""Wrapper for openjpeg library function opj_create_compress.
|
||||||
|
|
||||||
|
Creates a J2K/JPT/JP2 compression structure.
|
||||||
|
"""
|
||||||
|
OPENJPEG.opj_create_compress.argtypes = [ctypes.c_int]
|
||||||
|
OPENJPEG.opj_create_compress.restype = ctypes.POINTER(CompressionInfoType)
|
||||||
|
cinfo = OPENJPEG.opj_create_compress(fmt)
|
||||||
|
return cinfo
|
||||||
|
|
||||||
|
|
||||||
def create_decompress(fmt):
|
def create_decompress(fmt):
|
||||||
"""Wraps openjpeg library function opj_create_decompress.
|
"""Wraps openjpeg library function opj_create_decompress.
|
||||||
"""
|
"""
|
||||||
|
|
@ -186,6 +498,39 @@ def decode(dinfo, cio):
|
||||||
return image
|
return image
|
||||||
|
|
||||||
|
|
||||||
|
def destroy_compress(cinfo):
|
||||||
|
"""Wrapper for openjpeg library function opj_destroy_compress.
|
||||||
|
|
||||||
|
Release resources for a compressor handle.
|
||||||
|
"""
|
||||||
|
argtypes = [ctypes.POINTER(CompressionInfoType)]
|
||||||
|
OPENJPEG.opj_destroy_compress.argtypes = argtypes
|
||||||
|
OPENJPEG.opj_destroy_compress(cinfo)
|
||||||
|
|
||||||
|
|
||||||
|
def encode(cinfo, cio, image):
|
||||||
|
"""Wrapper for openjpeg library function opj_encode.
|
||||||
|
|
||||||
|
Encodes an image into a JPEG-2000 codestream.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
cinfo : compression handle
|
||||||
|
|
||||||
|
cio : output buffer stream
|
||||||
|
|
||||||
|
image : image to encode
|
||||||
|
"""
|
||||||
|
argtypes = [ctypes.POINTER(CompressionInfoType),
|
||||||
|
ctypes.POINTER(CioType),
|
||||||
|
ctypes.POINTER(ImageType)]
|
||||||
|
OPENJPEG.opj_encode.argtypes = argtypes
|
||||||
|
OPENJPEG.opj_encode.restype = ctypes.c_int
|
||||||
|
status = OPENJPEG.opj_encode(cinfo, cio, image)
|
||||||
|
if not status:
|
||||||
|
raise RuntimeError("opj_encode failed")
|
||||||
|
|
||||||
|
|
||||||
def destroy_decompress(dinfo):
|
def destroy_decompress(dinfo):
|
||||||
"""Wraps openjpeg library function opj_destroy_decompress."""
|
"""Wraps openjpeg library function opj_destroy_decompress."""
|
||||||
argtypes = [ctypes.POINTER(DecompressionInfoType)]
|
argtypes = [ctypes.POINTER(DecompressionInfoType)]
|
||||||
|
|
@ -193,12 +538,78 @@ def destroy_decompress(dinfo):
|
||||||
OPENJPEG.opj_destroy_decompress(dinfo)
|
OPENJPEG.opj_destroy_decompress(dinfo)
|
||||||
|
|
||||||
|
|
||||||
|
def image_cmptparm_t_from_np(np_image):
|
||||||
|
"""Return appropriate image_cmptparm_t based on given numpy array.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
num_comps = np_image.shape[2]
|
||||||
|
except IndexError:
|
||||||
|
num_comps = 1
|
||||||
|
|
||||||
|
cmpt_parm_array_t = ImageCmptparmType * num_comps
|
||||||
|
tarr = cmpt_parm_array_t()
|
||||||
|
|
||||||
|
if np_image.dtype == np.uint8:
|
||||||
|
prec = 8
|
||||||
|
bpp = 8
|
||||||
|
sgnd = 0
|
||||||
|
elif np_image.dtype == np.int8:
|
||||||
|
prec = 8
|
||||||
|
bpp = 8
|
||||||
|
sgnd = 1
|
||||||
|
elif np_image.dtype == np.uint16:
|
||||||
|
prec = 16
|
||||||
|
bpp = 16
|
||||||
|
sgnd = 0
|
||||||
|
elif np_image.dtype == np.int16:
|
||||||
|
prec = 16
|
||||||
|
bpp = 16
|
||||||
|
sgnd = 1
|
||||||
|
else:
|
||||||
|
raise(TypeError("unhandled"))
|
||||||
|
|
||||||
|
for j in range(0, num_comps):
|
||||||
|
tarr[j].dx = 1
|
||||||
|
tarr[j].dy = 1
|
||||||
|
tarr[j].w = np_image.shape[1]
|
||||||
|
tarr[j].h = np_image.shape[0]
|
||||||
|
tarr[j].x0 = 0
|
||||||
|
tarr[j].y0 = 0
|
||||||
|
tarr[j].prec = prec
|
||||||
|
tarr[j].bpp = bpp
|
||||||
|
tarr[j].sgnd = sgnd
|
||||||
|
|
||||||
|
return(tarr)
|
||||||
|
|
||||||
|
|
||||||
|
def image_create(cmptparms, cspace):
|
||||||
|
"""Wrapper for openjpeg library function opj_image_create.
|
||||||
|
"""
|
||||||
|
OPENJPEG.opj_image_create.argtypes = [ctypes.c_int,
|
||||||
|
ctypes.POINTER(ImageComptParmType),
|
||||||
|
ctypes.c_int]
|
||||||
|
OPENJPEG.opj_image_create.restype = ctypes.POINTER(ImageType)
|
||||||
|
|
||||||
|
image = OPENJPEG.opj_image_create(len(cmptparms), cmptparms, cspace)
|
||||||
|
return(image)
|
||||||
|
|
||||||
|
|
||||||
def image_destroy(image):
|
def image_destroy(image):
|
||||||
"""Wraps openjpeg library function opj_image_destroy."""
|
"""Wraps openjpeg library function opj_image_destroy."""
|
||||||
OPENJPEG.opj_image_destroy.argtypes = [ctypes.POINTER(ImageType)]
|
OPENJPEG.opj_image_destroy.argtypes = [ctypes.POINTER(ImageType)]
|
||||||
OPENJPEG.opj_image_destroy(image)
|
OPENJPEG.opj_image_destroy(image)
|
||||||
|
|
||||||
|
|
||||||
|
def set_default_encoder_parameters():
|
||||||
|
"""Wrapper for openjpeg library function opj_set_default_encoder_parameters.
|
||||||
|
"""
|
||||||
|
cparams = CompressionParametersType()
|
||||||
|
argtypes = [ctypes.POINTER(CompressionParametersType)]
|
||||||
|
OPENJPEG.opj_set_default_encoder_parameters.argtypes = argtypes
|
||||||
|
OPENJPEG.opj_set_default_encoder_parameters(ctypes.byref(cparams))
|
||||||
|
return cparams
|
||||||
|
|
||||||
|
|
||||||
def set_default_decoder_parameters(dparams_p):
|
def set_default_decoder_parameters(dparams_p):
|
||||||
"""Wrapper for opj_set_default_decoder_parameters.
|
"""Wrapper for opj_set_default_decoder_parameters.
|
||||||
"""
|
"""
|
||||||
|
|
@ -219,6 +630,15 @@ def set_event_mgr(dinfo, event_mgr, context=None):
|
||||||
event_mgr, context)
|
event_mgr, context)
|
||||||
|
|
||||||
|
|
||||||
|
def setup_encoder(cinfo, cparameters, image):
|
||||||
|
"""Wrapper for openjpeg library function opj_setup_decoder."""
|
||||||
|
argtypes = [ctypes.POINTER(CompressionInfoType),
|
||||||
|
ctypes.POINTER(CompressionParametersType),
|
||||||
|
ctypes.POINTER(ImageType)]
|
||||||
|
OPENJPEG.opj_setup_encoder.argtypes = argtypes
|
||||||
|
OPENJPEG.opj_setup_encoder(cinfo, cparameters, image)
|
||||||
|
|
||||||
|
|
||||||
def setup_decoder(dinfo, dparams):
|
def setup_decoder(dinfo, dparams):
|
||||||
"""Wrapper for openjpeg library function opj_setup_decoder."""
|
"""Wrapper for openjpeg library function opj_setup_decoder."""
|
||||||
argtypes = [ctypes.POINTER(DecompressionInfoType),
|
argtypes = [ctypes.POINTER(DecompressionInfoType),
|
||||||
|
|
|
||||||
|
|
@ -679,6 +679,9 @@ class TestJp2k15(unittest.TestCase):
|
||||||
j2k.read(layer=1)
|
j2k.read(layer=1)
|
||||||
|
|
||||||
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
|
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
|
||||||
|
@unittest.skipIf(re.match(r"""1\.[01234]\.\d""",
|
||||||
|
OPENJPEG_VERSION) is not None,
|
||||||
|
"Writing only supported with openjpeg version 1.5+.")
|
||||||
def test_2d_rgb(self):
|
def test_2d_rgb(self):
|
||||||
"""RGB must have at least 3 components."""
|
"""RGB must have at least 3 components."""
|
||||||
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
|
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ suite.
|
||||||
# pylint: disable=F0401
|
# pylint: disable=F0401
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
|
|
@ -19,15 +20,16 @@ else:
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from .fixtures import read_image, NO_READ_BACKEND, NO_READ_BACKEND_MSG
|
from .fixtures import read_image, NO_READ_BACKEND, NO_READ_BACKEND_MSG
|
||||||
from .fixtures import OPJ_DATA_ROOT, opj_data_file
|
from .fixtures import OPJ_DATA_ROOT, OPENJPEG_VERSION, opj_data_file
|
||||||
|
|
||||||
from glymur import Jp2k
|
from glymur import Jp2k
|
||||||
import glymur
|
import glymur
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(os.name == "nt", "no write support on windows, period")
|
@unittest.skipIf(os.name == "nt", "no write support on windows, period")
|
||||||
@unittest.skipIf(glymur.lib.openjp2.OPENJP2 is None,
|
@unittest.skipIf(re.match(r"""1\.[01234]\.\d""",
|
||||||
"Missing openjp2 library.")
|
OPENJPEG_VERSION) is not None,
|
||||||
|
"Writing only supported with openjpeg version 1.5+.")
|
||||||
@unittest.skipIf(NO_READ_BACKEND, NO_READ_BACKEND_MSG)
|
@unittest.skipIf(NO_READ_BACKEND, NO_READ_BACKEND_MSG)
|
||||||
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
||||||
"OPJ_DATA_ROOT environment variable not set")
|
"OPJ_DATA_ROOT environment variable not set")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue