Merge branch 'release-0.3.0'
This commit is contained in:
commit
d7635bdfef
22 changed files with 458 additions and 252 deletions
|
|
@ -1,3 +1,5 @@
|
|||
Jul 31, 2013 - v0.3.0 Added support for official 2.0.0.
|
||||
|
||||
Jul 27, 2013 - v0.2.8 Fixed inconsistency regarding configuration
|
||||
file directory on windows (issue91).
|
||||
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ copyright = u'2013, John Evans'
|
|||
# The short X.Y version.
|
||||
version = '0.2'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '0.2.8'
|
||||
release = '0.3.0'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
|
|
|||
|
|
@ -9,13 +9,14 @@ Glymur Configuration
|
|||
The default glymur installation process relies upon OpenJPEG version
|
||||
1.X being properly installed on your system. This will, however, only
|
||||
give you you basic read capabilities, so if you wish to take advantage
|
||||
of more of glymur's features, you should compile OpenJPEG as a shared
|
||||
library (named *openjp2* instead of *openjpeg*) from the developmental
|
||||
source that you can retrieve via subversion. As of this time of writing,
|
||||
svn revision 2345 works. You should also download the test data for
|
||||
the purpose of configuring and running OpenJPEG's test suite, check
|
||||
their instructions for all this. You should set the **OPJ_DATA_ROOT**
|
||||
environment variable for the purpose of running Glymur's test suite. ::
|
||||
of more of glymur's features, you should install version 2.0 or
|
||||
compile OpenJPEG as a shared library (named *openjp2* instead of
|
||||
*openjpeg*) from the developmental source that you can retrieve via
|
||||
subversion. As of this time of writing, svn revision 2345 works.
|
||||
You should also download the test data for the purpose of configuring
|
||||
and running OpenJPEG's test suite, check their instructions for all
|
||||
this. You should set the **OPJ_DATA_ROOT** environment variable
|
||||
for the purpose of running Glymur's test suite. ::
|
||||
|
||||
$ svn co http://openjpeg.googlecode.com/svn/data
|
||||
$ export OPJ_DATA_ROOT=`pwd`/data
|
||||
|
|
@ -40,8 +41,8 @@ but if you have **$XDG_CONFIG_HOME** defined, the path will be ::
|
|||
|
||||
$XDG_CONFIG_HOME/glymur/glymurrc
|
||||
|
||||
On windows, the path to the configuration file can be determined by starting up Python
|
||||
and typing ::
|
||||
On windows, the path to the configuration file can be determined
|
||||
by starting up Python and typing ::
|
||||
|
||||
import os
|
||||
os.path.join(os.path.expanduser('~'), 'glymur', 'glymurrc')
|
||||
|
|
@ -75,9 +76,7 @@ MacPorts. You should install the following set of ports:
|
|||
* py33-matplotlib (optional, for running certain tests)
|
||||
* py33-Pillow (optional, for running certain tests)
|
||||
|
||||
MacPorts supplies both OpenJPEG 1.5.0 and OpenJPEG 2.0.0. As previously
|
||||
mentioned, the 2.0.0 official release is not supported (although the 2.0+
|
||||
development version via SVN *is* supported).
|
||||
MacPorts supplies both OpenJPEG 1.5.0 and OpenJPEG 2.0.0.
|
||||
|
||||
Linux
|
||||
-----
|
||||
|
|
@ -138,12 +137,14 @@ In addition, you must install contextlib2 and Pillow via pip. ::
|
|||
|
||||
Windows
|
||||
-------
|
||||
I would recommend using WinPython, but Python(xy) also seems to work. WinPython 3.3
|
||||
should work with no additional installations required, but 2.7 versions still require
|
||||
contextlib2 and mock to be installed via pip.
|
||||
32-bit WinPython 2.7.5 seems to work with OpenJPEG 1.X, 2.0, and the
|
||||
development version, but still requires contextlib2 and mock to be
|
||||
installed via pip. WinPython 3.3.2, however, seems to have trouble
|
||||
with OpenJPEG 2.0, so I would suggest using the development version
|
||||
there (I'm unwilling to spend ANY more time trying to figure out what
|
||||
the problem is there).
|
||||
|
||||
Glymur has been tested far less extensively on Windows than on the other
|
||||
platforms.
|
||||
64-bit windows is completely untested.
|
||||
|
||||
|
||||
'''''''
|
||||
|
|
|
|||
|
|
@ -122,10 +122,10 @@ and add it after the JP2 header box, but before the codestream box ::
|
|||
Create an image with an alpha layer?
|
||||
====================================
|
||||
|
||||
OpenJPEG can create JP2 files with more than 3 components, but by default, any
|
||||
extra components are not described as such. In order to do so,
|
||||
we need to rewrap such an image in a set of boxes that includes a channel
|
||||
definition box.
|
||||
OpenJPEG can create JP2 files with more than 3 components (requires
|
||||
the development version), but by default, any extra components are
|
||||
not described as such. In order to do so, we need to rewrap such
|
||||
an image in a set of boxes that includes a channel definition box.
|
||||
|
||||
This example is based on SciPy example code found at
|
||||
http://scipy-lectures.github.io/advanced/image_processing/#basic-manipulations .
|
||||
|
|
|
|||
|
|
@ -14,22 +14,21 @@ some very limited support for reading JPX metadata. For instance,
|
|||
**asoc** and **labl** boxes are recognized, so GMLJP2 metadata can
|
||||
be retrieved from such JPX files.
|
||||
|
||||
Glymur works on Python 2.6, 2.7, and 3.3. Python 3.3 is strongly recommended.
|
||||
Glymur works on Python 2.6, 2.7, and 3.3.
|
||||
|
||||
OpenJPEG Installation
|
||||
=====================
|
||||
OpenJPEG should be version 1.4, 1.5, or the trunk/development
|
||||
version of OpenJPEG. The official 2.0.0 release or versions earlier than 1.3.0
|
||||
are not supported. Furthermore, the 1.X versions of OpenJPEG are
|
||||
currently only utilized for read-only purposes. In order to write JPEG 2000
|
||||
images, you must compile the the trunk/development version. For more information
|
||||
about OpenJPEG, please consult http://www.openjpeg.org.
|
||||
Glymur will read JPEG 2000 images with versions 1.3, 1.4, 1.5, 2.0,
|
||||
and the trunk/development version of OpenJPEG. Writing images is
|
||||
only supported with the 2.0 series, however, and the trunk/development
|
||||
version is strongly recommended. For more information about OpenJPEG,
|
||||
please consult http://www.openjpeg.org.
|
||||
|
||||
If you use MacPorts on the mac or if you have a sufficiently recent
|
||||
version of Linux, your package manager should already provide you
|
||||
with a version of OpenJPEG 1.X with which glymur can already use
|
||||
for read-only purposes. If your platform is windows, I suggest
|
||||
using the 1.5.1 windows installer provided to you by the OpenJPEG
|
||||
using the windows installers provided to you by the OpenJPEG
|
||||
folks at https://code.google.com/p/openjpeg/downloads/list .
|
||||
|
||||
Glymur Installation
|
||||
|
|
@ -57,5 +56,5 @@ You can run the tests from within python as follows::
|
|||
>>> glymur.runtests()
|
||||
|
||||
Many tests are currently skipped; in fact most of them are skipped if you
|
||||
are relying on OpenJPEG 1.4 or 1.5. The important thing, though, is whether or
|
||||
are relying on OpenJPEG 1.X. The important thing, though, is whether or
|
||||
not any tests fail.
|
||||
|
|
|
|||
|
|
@ -1,11 +1,17 @@
|
|||
------------
|
||||
Known Issues
|
||||
------------
|
||||
|
||||
* WinPython 3.3.2 and OpenJPEG 2.0 don't seem to want to play well together. If you do not need write support, just use OpenJPEG 1.5 instead. If you do need write support, try the development version of OpenJPEG.
|
||||
|
||||
-------
|
||||
Roadmap
|
||||
-------
|
||||
|
||||
Here's an incomplete list of what I'd like to focus on in the near future.
|
||||
Here's an incomplete list of what I'd like to focus on in the future.
|
||||
|
||||
* continue to monitor upstream changes in the openjp2 library
|
||||
* investigate using CFFI or cython instead of ctypes to wrap openjp2
|
||||
* eventually expose the openjp2 API
|
||||
* investigate JPIP
|
||||
* investigate adding write support for UUID/XMP boxes (potentially a big project)
|
||||
* investigate JPIP (likely to be an even bigger project)
|
||||
|
||||
|
|
|
|||
|
|
@ -636,79 +636,6 @@ class CompositingLayerHeaderBox(Jp2kBox):
|
|||
return box
|
||||
|
||||
|
||||
class JP2HeaderBox(Jp2kBox):
|
||||
"""Container for JP2 header box information.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
box_id : str
|
||||
4-character identifier for the box.
|
||||
length : int
|
||||
length of the box in bytes.
|
||||
offset : int
|
||||
offset of the box from the start of the file.
|
||||
longname : str
|
||||
more verbose description of the box.
|
||||
box : list
|
||||
List of boxes contained in this superbox.
|
||||
"""
|
||||
def __init__(self, length=0, offset=-1):
|
||||
Jp2kBox.__init__(self, box_id='jp2h', longname='JP2 Header')
|
||||
self.length = length
|
||||
self.offset = offset
|
||||
self.box = []
|
||||
|
||||
def __str__(self):
|
||||
msg = Jp2kBox.__str__(self)
|
||||
for box in self.box:
|
||||
boxstr = str(box)
|
||||
|
||||
# Add indentation.
|
||||
strs = [('\n ' + x) for x in boxstr.split('\n')]
|
||||
msg += ''.join(strs)
|
||||
return msg
|
||||
|
||||
def write(self, fptr):
|
||||
"""Write a JP2 Header box to file.
|
||||
"""
|
||||
# Write the contained boxes, then come back and write the length.
|
||||
orig_pos = fptr.tell()
|
||||
fptr.write(struct.pack('>I', 0))
|
||||
fptr.write('jp2h'.encode())
|
||||
for box in self.box:
|
||||
box.write(fptr)
|
||||
|
||||
end_pos = fptr.tell()
|
||||
fptr.seek(orig_pos)
|
||||
fptr.write(struct.pack('>I', end_pos - orig_pos))
|
||||
fptr.seek(end_pos)
|
||||
|
||||
@staticmethod
|
||||
def parse(fptr, offset, length):
|
||||
"""Parse JPEG 2000 header box.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
fptr : file
|
||||
Open file object.
|
||||
offset : int
|
||||
Start position of box in bytes.
|
||||
length : int
|
||||
Length of the box in bytes.
|
||||
|
||||
Returns
|
||||
-------
|
||||
JP2HeaderBox instance
|
||||
"""
|
||||
box = JP2HeaderBox(length=length, offset=offset)
|
||||
|
||||
# The JP2 header box is a superbox, so go ahead and parse its child
|
||||
# boxes.
|
||||
box.box = box.parse_superbox(fptr)
|
||||
|
||||
return box
|
||||
|
||||
|
||||
class ComponentMappingBox(Jp2kBox):
|
||||
"""Container for channel identification information.
|
||||
|
||||
|
|
|
|||
210
glymur/jp2k.py
210
glymur/jp2k.py
|
|
@ -32,6 +32,15 @@ from .jp2box import ImageHeaderBox
|
|||
from .jp2box import ColourSpecificationBox
|
||||
from .lib import _openjpeg as _opj
|
||||
from .lib import _openjp2 as _opj2
|
||||
from .lib import c as _libc
|
||||
|
||||
# Need to known if openjp2 library is the officially release v2.0.0 or not.
|
||||
_OPENJP2_IS_OFFICIAL_V2 = False
|
||||
if _opj2.OPENJP2 is not None:
|
||||
if _opj2.version() == '2.0.0':
|
||||
if not hasattr(_opj2.OPENJP2,
|
||||
'opj_stream_create_default_file_stream_v3'):
|
||||
_OPENJP2_IS_OFFICIAL_V2 = True
|
||||
|
||||
_COLORSPACE_MAP = {'rgb': _opj2.CLRSPC_SRGB,
|
||||
'gray': _opj2.CLRSPC_GRAY,
|
||||
|
|
@ -178,6 +187,94 @@ class Jp2k(Jp2kBox):
|
|||
msg += "profile if the file type box brand is 'jp2 '."
|
||||
warnings.warn(msg)
|
||||
|
||||
def _validate_write_parameters(self, img_array, code_block_size,
|
||||
precinct_sizes, cratios, psnr, colorspace,
|
||||
codec_fmt):
|
||||
"""Check that the input parameters to the write function are valid.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
img_array : ndarray
|
||||
Image data to be written to file.
|
||||
code_block_size : tuple
|
||||
Code block size (DY, DX).
|
||||
precinct_sizes : list
|
||||
List of precinct sizes. Each precinct size tuple is defined in
|
||||
(height x width).
|
||||
cratios : iterable
|
||||
Compression ratios for successive layers.
|
||||
psnr : iterable
|
||||
Different PSNR for successive layers.
|
||||
mct : bool
|
||||
Specifies usage of the multi component transform. If not
|
||||
specified, defaults to True if the colorspace is RGB.
|
||||
colorspace : str, optional
|
||||
Either 'rgb' or 'gray'.
|
||||
codec_fmt : int
|
||||
Are we writing a JP2 file or a J2K file?
|
||||
"""
|
||||
# Validate code block size and precinct sizes.
|
||||
if code_block_size is not None:
|
||||
width = code_block_size[1]
|
||||
height = code_block_size[0]
|
||||
if height * width > 4096 or height < 4 or width < 4:
|
||||
msg = "Code block area cannot exceed 4096. "
|
||||
msg += "Code block height and width must be larger than 4."
|
||||
raise IOError(msg)
|
||||
if ((math.log(height, 2) != math.floor(math.log(height, 2)) or
|
||||
math.log(width, 2) != math.floor(math.log(width, 2)))):
|
||||
msg = "Bad code block size ({0}, {1}), "
|
||||
msg += "must be powers of 2."
|
||||
raise IOError(msg.format(height, width))
|
||||
|
||||
if precinct_sizes is not None:
|
||||
for j, (prch, prcw) in enumerate(precinct_sizes):
|
||||
if j == 0 and code_block_size is not None:
|
||||
cblkh, cblkw = code_block_size
|
||||
if cblkh * 2 > prch or cblkw * 2 > prcw:
|
||||
msg = "Highest Resolution precinct size must be at "
|
||||
msg += "least twice that of the code block dimensions."
|
||||
raise IOError(msg)
|
||||
if ((math.log(prch, 2) != math.floor(math.log(prch, 2)) or
|
||||
math.log(prcw, 2) != math.floor(math.log(prcw, 2)))):
|
||||
msg = "Bad precinct sizes ({0}, {1}), "
|
||||
msg += "must be powers of 2."
|
||||
raise IOError(msg.format(prch, prcw))
|
||||
|
||||
if cratios is not None and psnr is not None:
|
||||
msg = "Cannot specify cratios and psnr together."
|
||||
raise IOError(msg)
|
||||
|
||||
# What would the point of 1D images be?
|
||||
if img_array.ndim == 1 or img_array.ndim > 3:
|
||||
msg = "{0}D imagery is not allowed.".format(img_array.ndim)
|
||||
raise IOError(msg)
|
||||
|
||||
if _OPENJP2_IS_OFFICIAL_V2:
|
||||
if (((img_array.ndim != 2) and
|
||||
(img_array.shape[2] != 1 and img_array.shape[2] != 3))):
|
||||
msg = "Writing images is restricted to single-channel "
|
||||
msg += "greyscale images or three-channel RGB images when "
|
||||
msg += "the OpenJPEG library version is the official 2.0.0 "
|
||||
msg += "release."
|
||||
raise IOError(msg)
|
||||
|
||||
if colorspace is not None:
|
||||
if codec_fmt == _opj2.CODEC_J2K:
|
||||
msg = 'Do not specify a colorspace when writing a raw '
|
||||
msg += 'codestream.'
|
||||
raise IOError(msg)
|
||||
if colorspace.lower() not in ('rgb', 'grey', 'gray'):
|
||||
msg = 'Invalid colorspace "{0}"'.format(colorspace)
|
||||
raise IOError(msg)
|
||||
elif colorspace.lower() == 'rgb' and img_array.shape[2] < 3:
|
||||
msg = 'RGB colorspace requires at least 3 components.'
|
||||
raise IOError(msg)
|
||||
|
||||
if img_array.dtype != np.uint8 and img_array.dtype != np.uint16:
|
||||
msg = "Only uint8 and uint16 images are currently supported."
|
||||
raise RuntimeError(msg)
|
||||
|
||||
# pylint: disable-msg=W0221
|
||||
def write(self, img_array, cratios=None, eph=False, psnr=None, numres=None,
|
||||
cbsize=None, psizes=None, grid_offset=None, sop=False,
|
||||
|
|
@ -202,7 +299,7 @@ class Jp2k(Jp2kBox):
|
|||
Code block size (DY, DX).
|
||||
colorspace : str, optional
|
||||
Either 'rgb' or 'gray'.
|
||||
cratios : sequence, optional
|
||||
cratios : iterable
|
||||
Compression ratios for successive layers.
|
||||
eph : bool, optional
|
||||
If true, write SOP marker after each header packet.
|
||||
|
|
@ -223,7 +320,7 @@ class Jp2k(Jp2kBox):
|
|||
Number of resolutions.
|
||||
prog : str, optional
|
||||
Progression order, one of "LRCP" "RLCP", "RPCL", "PCRL", "CPRL".
|
||||
psnr : list, optional
|
||||
psnr : iterable, optional
|
||||
Different PSNR for successive layers.
|
||||
psizes : list, optional
|
||||
List of precinct sizes. Each precinct size tuple is defined in
|
||||
|
|
@ -255,9 +352,17 @@ class Jp2k(Jp2kBox):
|
|||
If glymur is unable to load the openjp2 library.
|
||||
"""
|
||||
if _opj2.OPENJP2 is None:
|
||||
raise LibraryNotFoundError("You must have the development version "
|
||||
"of OpenJP2 installed before using "
|
||||
"this functionality.")
|
||||
raise LibraryNotFoundError("You must have the openjp2 library "
|
||||
"installed before using this "
|
||||
"functionality.")
|
||||
|
||||
if self.filename[-4:].lower() == '.jp2':
|
||||
codec_fmt = _opj2.CODEC_JP2
|
||||
else:
|
||||
codec_fmt = _opj2.CODEC_J2K
|
||||
|
||||
self._validate_write_parameters(img_array, cbsize, psizes, cratios,
|
||||
psnr, colorspace, codec_fmt)
|
||||
|
||||
cparams = _opj2.set_default_encoder_parameters()
|
||||
|
||||
|
|
@ -266,11 +371,6 @@ class Jp2k(Jp2kBox):
|
|||
outfile += b'0' * num_pad_bytes
|
||||
cparams.outfile = outfile
|
||||
|
||||
if self.filename[-4:].lower() == '.jp2':
|
||||
codec_fmt = _opj2.CODEC_JP2
|
||||
else:
|
||||
codec_fmt = _opj2.CODEC_J2K
|
||||
|
||||
cparams.cod_format = codec_fmt
|
||||
|
||||
# Set defaults to lossless to begin.
|
||||
|
|
@ -281,15 +381,6 @@ class Jp2k(Jp2kBox):
|
|||
if cbsize is not None:
|
||||
width = cbsize[1]
|
||||
height = cbsize[0]
|
||||
if height * width > 4096 or height < 4 or width < 4:
|
||||
msg = "Code block area cannot exceed 4096. "
|
||||
msg += "Code block height and width must be larger than 4."
|
||||
raise RuntimeError(msg)
|
||||
if ((math.log(height, 2) != math.floor(math.log(height, 2)) or
|
||||
math.log(width, 2) != math.floor(math.log(width, 2)))):
|
||||
msg = "Bad code block size ({0}, {1}), "
|
||||
msg += "must be powers of 2."
|
||||
raise IOError(msg.format(height, width))
|
||||
cparams.cblockw_init = width
|
||||
cparams.cblockh_init = height
|
||||
|
||||
|
|
@ -327,18 +418,6 @@ class Jp2k(Jp2kBox):
|
|||
|
||||
if psizes is not None:
|
||||
for j, (prch, prcw) in enumerate(psizes):
|
||||
if j == 0 and cbsize is not None:
|
||||
cblkh, cblkw = cbsize
|
||||
if cblkh * 2 > prch or cblkw * 2 > prcw:
|
||||
msg = "Highest Resolution precinct size must be at "
|
||||
msg += "least twice that of the code block dimensions."
|
||||
raise IOError(msg)
|
||||
if ((math.log(prch, 2) != math.floor(math.log(prch, 2)) or
|
||||
math.log(prcw, 2) != math.floor(math.log(prcw, 2)))):
|
||||
msg = "Bad precinct sizes ({0}, {1}), "
|
||||
msg += "must be powers of 2."
|
||||
raise IOError(msg.format(prch, prcw))
|
||||
|
||||
cparams.prcw_init[j] = prcw
|
||||
cparams.prch_init[j] = prch
|
||||
cparams.csty |= 0x01
|
||||
|
|
@ -356,47 +435,38 @@ class Jp2k(Jp2kBox):
|
|||
cparams.cp_tdy = tilesize[0]
|
||||
cparams.tile_size_on = _opj2.TRUE
|
||||
|
||||
if cratios is not None and psnr is not None:
|
||||
msg = "Cannot specify cratios and psnr together."
|
||||
raise RuntimeError(msg)
|
||||
|
||||
if img_array.ndim == 2:
|
||||
# Force it to be 3D. Just makes things easier later on.
|
||||
numrows, numcols = img_array.shape
|
||||
img_array = img_array.reshape(numrows, numcols, 1)
|
||||
elif img_array.ndim == 3:
|
||||
pass
|
||||
else:
|
||||
msg = "{0}D imagery is not allowed.".format(img_array.ndim)
|
||||
raise IOError(msg)
|
||||
|
||||
numrows, numcols, num_comps = img_array.shape
|
||||
|
||||
if colorspace is None:
|
||||
# Must infer the colorspace from the image dimensions.
|
||||
if img_array.shape[2] == 1 or img_array.shape[2] == 2:
|
||||
# A single channel image or an image with two channels is going
|
||||
# to be greyscale.
|
||||
colorspace = _opj2.CLRSPC_GRAY
|
||||
else:
|
||||
# No YCC unless specifically told to do so.
|
||||
# Anything else must be RGB, right?
|
||||
colorspace = _opj2.CLRSPC_SRGB
|
||||
else:
|
||||
if codec_fmt == _opj2.CODEC_J2K:
|
||||
raise IOError('Do not specify a colorspace with J2K.')
|
||||
colorspace = colorspace.lower()
|
||||
if colorspace not in ('rgb', 'grey', 'gray'):
|
||||
msg = 'Invalid colorspace "{0}"'.format(colorspace)
|
||||
raise IOError(msg)
|
||||
elif colorspace == 'rgb' and img_array.shape[2] < 3:
|
||||
msg = 'RGB colorspace requires at least 3 components.'
|
||||
raise IOError(msg)
|
||||
else:
|
||||
colorspace = _COLORSPACE_MAP[colorspace]
|
||||
# Turn the colorspace from a string to the enumerated value that
|
||||
# the library expects.
|
||||
colorspace = _COLORSPACE_MAP[colorspace.lower()]
|
||||
|
||||
if mct is None:
|
||||
# If the multi component transform was not specified, we infer
|
||||
# that it should be used if the color space is RGB.
|
||||
if colorspace == _opj2.CLRSPC_SRGB:
|
||||
cparams.tcp_mct = 1
|
||||
else:
|
||||
cparams.tcp_mct = 0
|
||||
else:
|
||||
if mct and colorspace == _opj2.CLRSPC_GRAY:
|
||||
# Cannot check for this in the validate routine, as we need
|
||||
# to know what the target colorspace has been determined to be.
|
||||
msg = "Cannot specify usage of the multi component transform "
|
||||
msg += "if the colorspace is gray."
|
||||
raise IOError(msg)
|
||||
|
|
@ -404,10 +474,9 @@ class Jp2k(Jp2kBox):
|
|||
|
||||
if img_array.dtype == np.uint8:
|
||||
comp_prec = 8
|
||||
elif img_array.dtype == np.uint16:
|
||||
comp_prec = 16
|
||||
else:
|
||||
raise RuntimeError("unhandled datatype")
|
||||
# We already know it cannot be anything else than uint16.
|
||||
comp_prec = 16
|
||||
|
||||
comptparms = (_opj2.ImageComptParmType * num_comps)()
|
||||
for j in range(num_comps):
|
||||
|
|
@ -448,14 +517,29 @@ class Jp2k(Jp2kBox):
|
|||
_opj2.set_warning_handler(codec, _WARNING_CALLBACK)
|
||||
_opj2.set_error_handler(codec, _ERROR_CALLBACK)
|
||||
_opj2.setup_encoder(codec, cparams, image)
|
||||
strm = _opj2.stream_create_default_file_stream_v3(self.filename, False)
|
||||
|
||||
if _OPENJP2_IS_OFFICIAL_V2:
|
||||
fptr = _libc.fopen(self.filename, 'wb')
|
||||
strm = _opj2.stream_create_default_file_stream(fptr, False)
|
||||
else:
|
||||
strm = _opj2.stream_create_default_file_stream_v3(self.filename,
|
||||
False)
|
||||
|
||||
# Start to clean up after ourselves.
|
||||
_opj2.start_compress(codec, image, strm)
|
||||
_opj2.encode(codec, strm)
|
||||
_opj2.end_compress(codec, strm)
|
||||
_opj2.stream_destroy_v3(strm)
|
||||
|
||||
if _OPENJP2_IS_OFFICIAL_V2:
|
||||
_opj2.stream_destroy(strm)
|
||||
_libc.fclose(fptr)
|
||||
else:
|
||||
_opj2.stream_destroy_v3(strm)
|
||||
|
||||
_opj2.destroy_codec(codec)
|
||||
_opj2.image_destroy(image)
|
||||
|
||||
# Refresh the metadata.
|
||||
self.parse()
|
||||
|
||||
def wrap(self, filename, boxes=None):
|
||||
|
|
@ -882,9 +966,15 @@ class Jp2k(Jp2kBox):
|
|||
dparam.nb_tile_to_decode = 1
|
||||
|
||||
with ExitStack() as stack:
|
||||
stream = _opj2.stream_create_default_file_stream_v3(self.filename,
|
||||
True)
|
||||
stack.callback(_opj2.stream_destroy_v3, stream)
|
||||
if hasattr(_opj2.OPENJP2, 'opj_stream_create_default_file_stream_v3'):
|
||||
stream = _opj2.stream_create_default_file_stream_v3(self.filename,
|
||||
True)
|
||||
stack.callback(_opj2.stream_destroy_v3, stream)
|
||||
else:
|
||||
fptr = _libc.fopen(self.filename, 'rb')
|
||||
stack.callback(_libc.fclose, fptr)
|
||||
stream = _opj2.stream_create_default_file_stream(fptr, True)
|
||||
stack.callback(_opj2.stream_destroy, stream)
|
||||
codec = _opj2.create_decompress(self._codec_format)
|
||||
stack.callback(_opj2.destroy_codec, codec)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
"""This package organizes individual libraries employed by glymur."""
|
||||
from . import openjp2 as _openjp2
|
||||
from . import openjpeg as _openjpeg
|
||||
from . import c
|
||||
|
|
|
|||
46
glymur/lib/c.py
Normal file
46
glymur/lib/c.py
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
"""
|
||||
Wraps fopen and fclose functions in libc.
|
||||
"""
|
||||
|
||||
import ctypes
|
||||
import ctypes.util
|
||||
|
||||
LIBC_PATH = ctypes.util.find_library('c')
|
||||
C_LIB = ctypes.CDLL(LIBC_PATH)
|
||||
|
||||
|
||||
def fopen(filename, mode):
|
||||
"""Opens the file with the specified mode.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
filename : str
|
||||
Path to filename.
|
||||
mode : str
|
||||
Specifies how the file is to be opened.
|
||||
|
||||
Returns
|
||||
-------
|
||||
fptr : ctypes.c_void_p
|
||||
File pointer.
|
||||
"""
|
||||
C_LIB.fopen.restype = ctypes.c_void_p
|
||||
C_LIB.fopen.argtypes = [ctypes.c_char_p, ctypes.c_char_p]
|
||||
fptr = C_LIB.fopen(ctypes.c_char_p(filename.encode()),
|
||||
ctypes.c_char_p(mode.encode()))
|
||||
return fptr
|
||||
|
||||
|
||||
def fclose(fptr):
|
||||
"""Closes a file stream.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
fptr : ctypes.c_void_p
|
||||
File pointer.
|
||||
"""
|
||||
C_LIB.fclose.argtypes = [ctypes.c_void_p]
|
||||
C_LIB.fclose.restype = ctypes.c_int32
|
||||
status = C_LIB.fclose(fptr)
|
||||
if status != 0:
|
||||
raise IOError("Unable to close file.")
|
||||
|
|
@ -75,8 +75,6 @@ def load_openjpeg(libopenjpeg_path):
|
|||
|
||||
def read_config_file():
|
||||
"""
|
||||
We expect to not find openjp2 on the system path since the only version
|
||||
that we currently care about is still in the svn trunk at openjpeg.org.
|
||||
We must use a configuration file that the user must write.
|
||||
"""
|
||||
lib = {'openjp2': None, 'openjpeg': None}
|
||||
|
|
@ -104,6 +102,17 @@ def load_openjp2(libopenjp2_path):
|
|||
# No help from the config file, try to find it ourselves.
|
||||
libopenjp2_path = find_library('openjp2')
|
||||
|
||||
if libopenjp2_path is None:
|
||||
if platform.system() == 'Darwin':
|
||||
path = '/opt/local/lib/libopenjp2.dylib'
|
||||
if os.path.exists(path):
|
||||
libopenjp2_path = path
|
||||
elif os.name == 'nt':
|
||||
path = os.path.join('C:\\', 'Program files', 'OpenJPEG 2.0',
|
||||
'bin', 'openjp2.dll')
|
||||
if os.path.exists(path):
|
||||
libopenjp2_path = path
|
||||
|
||||
if libopenjp2_path is None:
|
||||
return None
|
||||
|
||||
|
|
@ -145,7 +154,7 @@ def get_configdir():
|
|||
|
||||
if 'HOME' in os.environ and os.name != 'nt':
|
||||
# HOME is set by WinPython to something unusual, so we don't
|
||||
# necessarily want that.
|
||||
# necessarily want that.
|
||||
return os.path.join(os.environ['HOME'], '.config', 'glymur')
|
||||
|
||||
# Last stand. Should handle windows... others?
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ Wraps individual functions in openjp2 library.
|
|||
# pylint: disable=C0302,R0903
|
||||
|
||||
import ctypes
|
||||
import sys
|
||||
|
||||
from .config import glymur_config
|
||||
OPENJP2, OPENJPEG = glymur_config()
|
||||
|
|
@ -639,9 +640,16 @@ if OPENJP2 is not None:
|
|||
ctypes.POINTER(ImageType)]
|
||||
OPENJP2.opj_setup_encoder.argtypes = ARGTYPES
|
||||
|
||||
ARGTYPES = [ctypes.c_char_p, ctypes.c_int32]
|
||||
OPENJP2.opj_stream_create_default_file_stream_v3.argtypes = ARGTYPES
|
||||
OPENJP2.opj_stream_create_default_file_stream_v3.restype = STREAM_TYPE_P
|
||||
if hasattr(OPENJP2, 'opj_stream_create_default_file_stream_v3'):
|
||||
ARGTYPES = [ctypes.c_char_p, ctypes.c_int32]
|
||||
OPENJP2.opj_stream_create_default_file_stream_v3.argtypes = ARGTYPES
|
||||
OPENJP2.opj_stream_create_default_file_stream_v3.restype = STREAM_TYPE_P
|
||||
OPENJP2.opj_stream_destroy_v3.argtypes = [STREAM_TYPE_P]
|
||||
else:
|
||||
ARGTYPES = [ctypes.c_void_p, ctypes.c_int32]
|
||||
OPENJP2.opj_stream_create_default_file_stream.argtypes = ARGTYPES
|
||||
OPENJP2.opj_stream_create_default_file_stream.restype = STREAM_TYPE_P
|
||||
OPENJP2.opj_stream_destroy.argtypes = [STREAM_TYPE_P]
|
||||
|
||||
ARGTYPES = [CODEC_TYPE, ctypes.POINTER(ImageType), STREAM_TYPE_P]
|
||||
OPENJP2.opj_start_compress.argtypes = ARGTYPES
|
||||
|
|
@ -649,7 +657,6 @@ if OPENJP2 is not None:
|
|||
OPENJP2.opj_end_compress.argtypes = [CODEC_TYPE, STREAM_TYPE_P]
|
||||
OPENJP2.opj_end_decompress.argtypes = [CODEC_TYPE, STREAM_TYPE_P]
|
||||
|
||||
OPENJP2.opj_stream_destroy_v3.argtypes = [STREAM_TYPE_P]
|
||||
OPENJP2.opj_destroy_codec.argtypes = [CODEC_TYPE]
|
||||
|
||||
ARGTYPES = [CODEC_TYPE,
|
||||
|
|
@ -1267,16 +1274,17 @@ def start_compress(codec, image, stream):
|
|||
OPENJP2.opj_start_compress(codec, image, stream)
|
||||
|
||||
|
||||
def stream_create_default_file_stream_v3(fname, a_read_stream):
|
||||
"""Wraps openjp2 library function opj_stream_create_default_vile_stream_v3.
|
||||
def stream_create_default_file_stream(fptr, isa_read_stream):
|
||||
"""Wraps openjp2 library function opj_stream_create_default_vile_stream.
|
||||
|
||||
Sets the stream to be a file stream.
|
||||
Sets the stream to be a file stream. This is valid only for version 2.0.0
|
||||
of OpenJPEG.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
fname : str
|
||||
Specifies a file.
|
||||
a_read_stream: bool
|
||||
fptr : ctypes.c_void_p
|
||||
Corresponds to C file pointer. Must be obtained from libc.fopen.
|
||||
isa_read_stream: bool
|
||||
True (read) or False (write)
|
||||
|
||||
Returns
|
||||
|
|
@ -1284,16 +1292,52 @@ def stream_create_default_file_stream_v3(fname, a_read_stream):
|
|||
stream : stream_t
|
||||
An OpenJPEG file stream.
|
||||
"""
|
||||
read_stream = 1 if a_read_stream else 0
|
||||
read_stream = 1 if isa_read_stream else 0
|
||||
stream = OPENJP2.opj_stream_create_default_file_stream(fptr, read_stream)
|
||||
return stream
|
||||
|
||||
|
||||
def stream_create_default_file_stream_v3(fname, isa_read_stream):
|
||||
"""Wraps openjp2 library function opj_stream_create_default_vile_stream_v3.
|
||||
|
||||
Sets the stream to be a file stream. This function is only valid for the
|
||||
trunk/development 2.0+ version of the openjp2 library.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
fname : str
|
||||
Specifies a file.
|
||||
isa_read_stream: bool
|
||||
True (read) or False (write)
|
||||
|
||||
Returns
|
||||
-------
|
||||
stream : stream_t
|
||||
An OpenJPEG file stream.
|
||||
"""
|
||||
read_stream = 1 if isa_read_stream else 0
|
||||
file_argument = ctypes.c_char_p(fname.encode())
|
||||
stream = OPENJP2.opj_stream_create_default_file_stream_v3(file_argument,
|
||||
read_stream)
|
||||
return stream
|
||||
|
||||
|
||||
def stream_destroy_v3(stream):
|
||||
def stream_destroy(stream):
|
||||
"""Wraps openjp2 library function opj_stream_destroy.
|
||||
|
||||
Destroys the stream created by create_stream.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
stream : STREAM_TYPE_P
|
||||
The file stream.
|
||||
"""
|
||||
OPENJP2.opj_stream_destroy(stream)
|
||||
|
||||
|
||||
def stream_destroy_v3(stream):
|
||||
"""Wraps openjp2 library function opj_stream_destroy_v3.
|
||||
|
||||
Destroys the stream created by create_stream_v3.
|
||||
|
||||
Parameters
|
||||
|
|
@ -1339,3 +1383,13 @@ def set_error_message(msg):
|
|||
"""The openjpeg error handler has recorded an error message."""
|
||||
global ERROR_MSG_LST
|
||||
ERROR_MSG_LST.append(msg)
|
||||
|
||||
|
||||
def version():
|
||||
"""Wrapper for opj_version library routine."""
|
||||
OPENJP2.opj_version.restype = ctypes.c_char_p
|
||||
library_version = OPENJP2.opj_version()
|
||||
if sys.hexversion >= 0x03000000:
|
||||
return library_version.decode('utf-8')
|
||||
else:
|
||||
return library_version
|
||||
|
|
|
|||
|
|
@ -28,9 +28,6 @@ else:
|
|||
# Does not really matter. But version should not be called if there is no
|
||||
# OpenJPEG library found.
|
||||
_MINOR = 0
|
||||
# Redefine version so that we can use it.
|
||||
def version():
|
||||
return '0.0.0'
|
||||
|
||||
|
||||
class EventMgrType(ctypes.Structure):
|
||||
|
|
|
|||
|
|
@ -16,10 +16,17 @@ import numpy as np
|
|||
|
||||
import glymur
|
||||
|
||||
OPENJP2_IS_V2_OFFICIAL = False
|
||||
if glymur.lib.openjp2.OPENJP2 is not None:
|
||||
if not hasattr(glymur.lib.openjp2.OPENJP2,
|
||||
'opj_stream_create_default_file_stream_v3'):
|
||||
OPENJP2_IS_V2_OFFICIAL = True
|
||||
|
||||
|
||||
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
|
||||
@unittest.skipIf(glymur.lib._openjp2.OPENJP2 is None,
|
||||
@unittest.skipIf(glymur.lib.openjp2.OPENJP2 is None,
|
||||
"Missing openjp2 library.")
|
||||
@unittest.skipIf(OPENJP2_IS_V2_OFFICIAL, "API followed here specific to V2.0+")
|
||||
class TestOpenJP2(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
|
|
|
|||
|
|
@ -3,6 +3,22 @@ import sys
|
|||
|
||||
import numpy as np
|
||||
|
||||
import glymur
|
||||
|
||||
# Need to know the openjpeg version. If openjpeg is not installed, we use
|
||||
# '0.0.0'
|
||||
OPENJPEG_VERSION = '0.0.0'
|
||||
if glymur.lib.openjpeg.OPENJPEG is not None:
|
||||
OPENJPEG_VERSION = glymur.lib.openjpeg.version()
|
||||
|
||||
# Need to know of the libopenjp2 version is the official 2.0.0 release and NOT
|
||||
# the 2.0+ development version.
|
||||
OPENJP2_IS_V2_OFFICIAL = False
|
||||
if glymur.lib.openjp2.OPENJP2 is not None:
|
||||
if not hasattr(glymur.lib.openjp2.OPENJP2,
|
||||
'opj_stream_create_default_file_stream_v3'):
|
||||
OPENJP2_IS_V2_OFFICIAL = True
|
||||
|
||||
|
||||
def mse(amat, bmat):
|
||||
"""Mean Square Error"""
|
||||
|
|
|
|||
|
|
@ -6,16 +6,18 @@ import sys
|
|||
import tempfile
|
||||
import warnings
|
||||
|
||||
if sys.hexversion < 0x03000000:
|
||||
from StringIO import StringIO
|
||||
else:
|
||||
from io import StringIO
|
||||
|
||||
if sys.hexversion < 0x02070000:
|
||||
import unittest2 as unittest
|
||||
else:
|
||||
import unittest
|
||||
|
||||
if sys.hexversion <= 0x03030000:
|
||||
from mock import patch
|
||||
from StringIO import StringIO
|
||||
else:
|
||||
from unittest.mock import patch
|
||||
from io import StringIO
|
||||
|
||||
import glymur
|
||||
|
||||
|
||||
|
|
@ -24,15 +26,11 @@ import glymur
|
|||
class TestCallbacks(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
# Save sys.stdout.
|
||||
self.stdout = sys.stdout
|
||||
sys.stdout = StringIO()
|
||||
self.jp2file = glymur.data.nemo()
|
||||
self.j2kfile = glymur.data.goodstuff()
|
||||
|
||||
def tearDown(self):
|
||||
# Restore stdout.
|
||||
sys.stdout = self.stdout
|
||||
pass
|
||||
|
||||
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
|
||||
def test_info_callback_on_write(self):
|
||||
|
|
@ -43,8 +41,9 @@ class TestCallbacks(unittest.TestCase):
|
|||
tiledata = j.read(tile=0)
|
||||
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
|
||||
j = glymur.Jp2k(tfile.name, 'wb')
|
||||
j.write(tiledata, verbose=True)
|
||||
actual = sys.stdout.getvalue().strip()
|
||||
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||
j.write(tiledata, verbose=True)
|
||||
actual = fake_out.getvalue().strip()
|
||||
expected = '[INFO] tile number 1 / 1'
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
|
|
@ -52,8 +51,9 @@ class TestCallbacks(unittest.TestCase):
|
|||
# Verify that we get the expected stdio output when our internal info
|
||||
# callback handler is enabled.
|
||||
j = glymur.Jp2k(self.j2kfile)
|
||||
d = j.read(rlevel=1, verbose=True, area=(0, 0, 200, 150))
|
||||
actual = sys.stdout.getvalue().strip()
|
||||
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||
d = j.read(rlevel=1, verbose=True, area=(0, 0, 200, 150))
|
||||
actual = fake_out.getvalue().strip()
|
||||
|
||||
lines = ['[INFO] Start to read j2k main header (0).',
|
||||
'[INFO] Main header has been correctly decoded.',
|
||||
|
|
@ -66,53 +66,41 @@ class TestCallbacks(unittest.TestCase):
|
|||
self.assertEqual(actual, expected)
|
||||
|
||||
|
||||
@unittest.skipIf(glymur.lib.openjp2.OPENJPEG is None,
|
||||
@unittest.skipIf(glymur.lib.openjpeg.OPENJPEG is None,
|
||||
"Missing openjpeg library.")
|
||||
class TestCallbacks15(unittest.TestCase):
|
||||
"""This test suite is for OpenJPEG 1.5.1 properties.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
# Monkey patch the package so as to use OPENJPEG instead of OPENJP2
|
||||
cls.openjp2 = glymur.lib.openjp2.OPENJP2
|
||||
glymur.lib.openjp2.OPENJP2 = None
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
# Restore OPENJP2
|
||||
glymur.lib.openjp2.OPENJP2 = cls.openjp2
|
||||
|
||||
def setUp(self):
|
||||
# Save sys.stdout.
|
||||
self.stdout = sys.stdout
|
||||
sys.stdout = StringIO()
|
||||
self.jp2file = glymur.data.nemo()
|
||||
self.j2kfile = glymur.data.goodstuff()
|
||||
|
||||
def tearDown(self):
|
||||
# Restore stdout.
|
||||
sys.stdout = self.stdout
|
||||
pass
|
||||
|
||||
def test_info_callbacks_on_read(self):
|
||||
# Verify that we get the expected stdio output when our internal info
|
||||
# callback handler is enabled.
|
||||
j = glymur.Jp2k(self.j2kfile)
|
||||
d = j.read(rlevel=1, verbose=True)
|
||||
actual = sys.stdout.getvalue().strip()
|
||||
|
||||
regex = re.compile(r"""\[INFO\]\stile\s1\sof\s1\s+
|
||||
\[INFO\]\s-\stiers-1\stook\s
|
||||
[0-9]+\.[0-9]+\ss\s+
|
||||
\[INFO\]\s-\sdwt\stook\s
|
||||
(-){0,1}[0-9]+\.[0-9]+\ss\s+
|
||||
\[INFO\]\s-\stile\sdecoded\sin\s
|
||||
[0-9]+\.[0-9]+\ss""",
|
||||
re.VERBOSE)
|
||||
if sys.hexversion <= 0x03020000:
|
||||
self.assertRegexpMatches(actual, regex)
|
||||
else:
|
||||
self.assertRegex(actual, regex)
|
||||
with patch('glymur.lib.openjp2.OPENJP2', new=None):
|
||||
# Force to use OPENJPEG instead of OPENJP2.
|
||||
j = glymur.Jp2k(self.j2kfile)
|
||||
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||
d = j.read(rlevel=1, verbose=True)
|
||||
actual = fake_out.getvalue().strip()
|
||||
|
||||
regex = re.compile(r"""\[INFO\]\stile\s1\sof\s1\s+
|
||||
\[INFO\]\s-\stiers-1\stook\s
|
||||
[0-9]+\.[0-9]+\ss\s+
|
||||
\[INFO\]\s-\sdwt\stook\s
|
||||
(-){0,1}[0-9]+\.[0-9]+\ss\s+
|
||||
\[INFO\]\s-\stile\sdecoded\sin\s
|
||||
[0-9]+\.[0-9]+\ss""",
|
||||
re.VERBOSE)
|
||||
if sys.hexversion <= 0x03020000:
|
||||
self.assertRegexpMatches(actual, regex)
|
||||
else:
|
||||
self.assertRegex(actual, regex)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ from glymur.jp2box import *
|
|||
from glymur.core import COLOR, OPACITY
|
||||
from glymur.core import RED, GREEN, BLUE, GREY, WHOLE_IMAGE
|
||||
|
||||
from .fixtures import OPENJP2_IS_V2_OFFICIAL
|
||||
|
||||
try:
|
||||
format_corpus_data_root = os.environ['FORMAT_CORPUS_DATA_ROOT']
|
||||
except KeyError:
|
||||
|
|
@ -34,6 +36,8 @@ def load_tests(loader, tests, ignore):
|
|||
return tests
|
||||
|
||||
|
||||
@unittest.skipIf(OPENJP2_IS_V2_OFFICIAL,
|
||||
"Requires v2.0.0+ in order to run.")
|
||||
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
|
||||
@unittest.skipIf(glymur.lib.openjp2.OPENJP2 is None,
|
||||
"Missing openjp2 library.")
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ import glymur
|
|||
from glymur import Jp2k
|
||||
from glymur.lib import openjp2 as opj2
|
||||
|
||||
from .fixtures import OPENJP2_IS_V2_OFFICIAL
|
||||
|
||||
try:
|
||||
data_root = os.environ['OPJ_DATA_ROOT']
|
||||
except KeyError:
|
||||
|
|
@ -477,6 +479,8 @@ class TestJp2k(unittest.TestCase):
|
|||
self.assertEqual(j.box[2].box[1].colorspace,
|
||||
glymur.core.GREYSCALE)
|
||||
|
||||
@unittest.skipIf(OPENJP2_IS_V2_OFFICIAL,
|
||||
"Does not seem to work on official v2.0.0 release.")
|
||||
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
|
||||
def test_grey_with_extra_component(self):
|
||||
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
|
||||
|
|
@ -501,6 +505,8 @@ class TestJp2k(unittest.TestCase):
|
|||
self.assertEqual(j.box[2].box[1].colorspace,
|
||||
glymur.core.GREYSCALE)
|
||||
|
||||
@unittest.skipIf(OPENJP2_IS_V2_OFFICIAL,
|
||||
"Does not seem to work on official v2.0.0 release.")
|
||||
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
|
||||
def test_rgb_with_extra_component(self):
|
||||
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
|
||||
|
|
@ -512,6 +518,17 @@ class TestJp2k(unittest.TestCase):
|
|||
self.assertEqual(j.box[2].box[0].num_components, 4)
|
||||
self.assertEqual(j.box[2].box[1].colorspace, glymur.core.SRGB)
|
||||
|
||||
@unittest.skipIf(OPENJP2_IS_V2_OFFICIAL is False,
|
||||
"Test is specific for v2.0.0 release")
|
||||
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
|
||||
def test_extra_components_on_v2_official(self):
|
||||
# Extra components seems to require 2.0+. Verify that we error out.
|
||||
with self.assertRaises(IOError):
|
||||
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
|
||||
j = Jp2k(tfile.name, 'wb')
|
||||
data = np.zeros((128, 128, 4), dtype=np.uint8)
|
||||
j.write(data)
|
||||
|
||||
def test_specify_ycc(self):
|
||||
# We don't support writing YCC at the moment.
|
||||
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
|
||||
|
|
@ -655,6 +672,8 @@ class TestJp2k(unittest.TestCase):
|
|||
self.assertEqual(jasoc.box[3].box[1].box_id, 'xml ')
|
||||
|
||||
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
|
||||
@unittest.skipIf(OPENJP2_IS_V2_OFFICIAL,
|
||||
"Segfault on official v2.0.0 release.")
|
||||
def test_openjpeg_library_message(self):
|
||||
# Verify the error message produced by the openjpeg library.
|
||||
# This will confirm that the error callback mechanism is working.
|
||||
|
|
|
|||
|
|
@ -30,6 +30,9 @@ import numpy as np
|
|||
from glymur import Jp2k
|
||||
import glymur
|
||||
|
||||
from .fixtures import OPENJPEG_VERSION
|
||||
from .fixtures import OPENJP2_IS_V2_OFFICIAL
|
||||
|
||||
from .fixtures import *
|
||||
|
||||
try:
|
||||
|
|
@ -986,6 +989,8 @@ class TestSuite(unittest.TestCase):
|
|||
data = Jp2k(jfile).read()
|
||||
self.assertTrue(True)
|
||||
|
||||
@unittest.skipIf(OPENJP2_IS_V2_OFFICIAL,
|
||||
"Test known to fail in v2.0.0 official")
|
||||
def test_NR_DEC_text_GBR_jp2_29_decode(self):
|
||||
jfile = os.path.join(data_root,
|
||||
'input/nonregression/text_GBR.jp2')
|
||||
|
|
@ -1002,12 +1007,16 @@ class TestSuite(unittest.TestCase):
|
|||
data = Jp2k(jfile).read()
|
||||
self.assertTrue(True)
|
||||
|
||||
@unittest.skipIf(OPENJP2_IS_V2_OFFICIAL,
|
||||
"Test known to fail in v2.0.0 official")
|
||||
def test_NR_DEC_kodak_2layers_lrcp_j2c_31_decode(self):
|
||||
jfile = os.path.join(data_root,
|
||||
'input/nonregression/kodak_2layers_lrcp.j2c')
|
||||
data = Jp2k(jfile).read()
|
||||
self.assertTrue(True)
|
||||
|
||||
@unittest.skipIf(OPENJP2_IS_V2_OFFICIAL,
|
||||
"Test known to fail in v2.0.0 official")
|
||||
def test_NR_DEC_kodak_2layers_lrcp_j2c_32_decode(self):
|
||||
jfile = os.path.join(data_root,
|
||||
'input/nonregression/kodak_2layers_lrcp.j2c')
|
||||
|
|
@ -1020,6 +1029,8 @@ class TestSuite(unittest.TestCase):
|
|||
data = Jp2k(jfile).read()
|
||||
self.assertTrue(True)
|
||||
|
||||
@unittest.skipIf(OPENJP2_IS_V2_OFFICIAL,
|
||||
"Test known to fail in v2.0.0 official")
|
||||
def test_NR_DEC_mem_b2ace68c_1381_jp2_34_decode(self):
|
||||
jfile = os.path.join(data_root,
|
||||
'input/nonregression/mem-b2ace68c-1381.jp2')
|
||||
|
|
@ -1030,6 +1041,8 @@ class TestSuite(unittest.TestCase):
|
|||
data = j.read()
|
||||
self.assertTrue(True)
|
||||
|
||||
@unittest.skipIf(OPENJP2_IS_V2_OFFICIAL,
|
||||
"Test known to fail in v2.0.0 official")
|
||||
def test_NR_DEC_mem_b2b86b74_2753_jp2_35_decode(self):
|
||||
jfile = os.path.join(data_root,
|
||||
'input/nonregression/mem-b2b86b74-2753.jp2')
|
||||
|
|
@ -1045,6 +1058,8 @@ class TestSuite(unittest.TestCase):
|
|||
with self.assertRaises(IOError):
|
||||
data = j.read()
|
||||
|
||||
@unittest.skipIf(OPENJP2_IS_V2_OFFICIAL,
|
||||
"Test not in done in v2.0.0 official")
|
||||
def test_NR_DEC_jp2_36_decode(self):
|
||||
lst = ('input',
|
||||
'nonregression',
|
||||
|
|
@ -1056,6 +1071,8 @@ class TestSuite(unittest.TestCase):
|
|||
with self.assertRaises(IOError):
|
||||
data = j.read()
|
||||
|
||||
@unittest.skipIf(OPENJP2_IS_V2_OFFICIAL,
|
||||
"Test not in done in v2.0.0 official")
|
||||
def test_NR_DEC_gdal_fuzzer_check_number_of_tiles_jp2_38_decode(self):
|
||||
relpath = 'input/nonregression/gdal_fuzzer_check_number_of_tiles.jp2'
|
||||
jfile = os.path.join(data_root, relpath)
|
||||
|
|
@ -1065,6 +1082,8 @@ class TestSuite(unittest.TestCase):
|
|||
with self.assertRaises(IOError):
|
||||
data = j.read()
|
||||
|
||||
@unittest.skipIf(OPENJP2_IS_V2_OFFICIAL,
|
||||
"Test not in done in v2.0.0 official")
|
||||
def test_NR_DEC_gdal_fuzzer_check_comp_dx_dy_jp2_39_decode(self):
|
||||
relpath = 'input/nonregression/gdal_fuzzer_check_comp_dx_dy.jp2'
|
||||
jfile = os.path.join(data_root, relpath)
|
||||
|
|
@ -1079,6 +1098,8 @@ class TestSuite(unittest.TestCase):
|
|||
with self.assertRaises(RuntimeError):
|
||||
data = Jp2k(jfile).read()
|
||||
|
||||
@unittest.skipIf(OPENJP2_IS_V2_OFFICIAL,
|
||||
"Test not in done in v2.0.0 official")
|
||||
@unittest.skipIf(sys.hexversion < 0x03020000,
|
||||
"Uses features introduced in 3.2.")
|
||||
def test_NR_DEC_issue188_beach_64bitsbox_jp2_41_decode(self):
|
||||
|
|
@ -1089,6 +1110,8 @@ class TestSuite(unittest.TestCase):
|
|||
with self.assertWarns(UserWarning) as cw:
|
||||
data = Jp2k(jfile).read()
|
||||
|
||||
@unittest.skipIf(OPENJP2_IS_V2_OFFICIAL,
|
||||
"Test not in done in v2.0.0 official")
|
||||
def test_NR_DEC_issue206_image_000_jp2_42_decode(self):
|
||||
jfile = os.path.join(data_root,
|
||||
'input/nonregression/issue206_image-000.jp2')
|
||||
|
|
@ -7779,7 +7802,8 @@ class TestSuite15(unittest.TestCase):
|
|||
jfile = os.path.join(data_root, 'input/conformance/file9.jp2')
|
||||
jp2k = Jp2k(jfile)
|
||||
jpdata = jp2k.read()
|
||||
if glymur.lib.openjpeg.version().startswith('1.3'):
|
||||
if re.match('[01]\.3', OPENJPEG_VERSION):
|
||||
# Version 1.3 reads in the image as the palette indices.
|
||||
self.assertEqual(jpdata.shape, (512, 768))
|
||||
else:
|
||||
self.assertEqual(jpdata.shape, (512, 768, 3))
|
||||
|
|
@ -7818,7 +7842,7 @@ class TestSuite15(unittest.TestCase):
|
|||
data = jp2.read()
|
||||
self.assertTrue(True)
|
||||
|
||||
@unittest.skipIf(int(glymur.lib.openjpeg.version().split('.')[1]) < 5,
|
||||
@unittest.skipIf(re.match('[01]\.[34]', OPENJPEG_VERSION),
|
||||
"Segfaults openjpeg 1.4 and earlier.")
|
||||
def test_NR_DEC_broken2_jp2_5_decode(self):
|
||||
# Null pointer access
|
||||
|
|
@ -7841,7 +7865,7 @@ class TestSuite15(unittest.TestCase):
|
|||
with self.assertRaises(ValueError) as ce:
|
||||
d = j.read()
|
||||
|
||||
@unittest.skipIf(int(glymur.lib.openjpeg.version().split('.')[1]) < 5,
|
||||
@unittest.skipIf(re.match('[01]\.[34]', OPENJPEG_VERSION),
|
||||
"Segfaults openjpeg 1.4 and earlier.")
|
||||
def test_NR_DEC_broken4_jp2_7_decode(self):
|
||||
# Null pointer access
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ class TestSuiteNegative(unittest.TestCase):
|
|||
data = read_image(infile)
|
||||
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
|
||||
j = Jp2k(tfile.name, 'wb')
|
||||
with self.assertRaises(RuntimeError):
|
||||
with self.assertRaises(IOError):
|
||||
j.write(data, psnr=[30, 35, 40], cratios=[2, 3, 4])
|
||||
|
||||
def test_NR_MarkerIsNotCompliant_j2k_dump(self):
|
||||
|
|
@ -109,13 +109,13 @@ class TestSuiteNegative(unittest.TestCase):
|
|||
j = Jp2k(tfile.name, 'wb')
|
||||
|
||||
# opj_compress doesn't allow code block area to exceed 4096.
|
||||
with self.assertRaises(RuntimeError) as cr:
|
||||
with self.assertRaises(IOError) as cr:
|
||||
j.write(data, cbsize=(256, 256))
|
||||
|
||||
# opj_compress doesn't allow either dimension to be less than 4.
|
||||
with self.assertRaises(RuntimeError) as cr:
|
||||
with self.assertRaises(IOError) as cr:
|
||||
j.write(data, cbsize=(2048, 2))
|
||||
with self.assertRaises(RuntimeError) as cr:
|
||||
with self.assertRaises(IOError) as cr:
|
||||
j.write(data, cbsize=(2, 2048))
|
||||
|
||||
@unittest.skipIf(sys.hexversion < 0x03020000,
|
||||
|
|
|
|||
38
release.txt
38
release.txt
|
|
@ -1,35 +1,51 @@
|
|||
| OS | Python | Python | Python | Notes |
|
||||
| | 2.6 | 2.7 | 3.3 | |
|
||||
+-----------+--------+--------+--------+--------------------------------------+
|
||||
| Windows | | X | | Python(xy) with OpenJPEG 1.5.1 and |
|
||||
| | | | | OpenJPEG svn. 285 of 448 tests |
|
||||
| Windows | | X | | WinPython with OpenJPEG 1.5.1 and |
|
||||
| | | | | OpenJPEG 2.0.0. 267 of 455 tests |
|
||||
| | | | | pass. |
|
||||
+-----------+--------+--------+--------+--------------------------------------+
|
||||
| Windows | | | X | WinPython with OpenJPEG 1.5.1 and |
|
||||
| | | | | OpenJPEG svn. 307 of 455 tests |
|
||||
| | | | | pass. |
|
||||
+-----------+--------+--------+--------+--------------------------------------+
|
||||
| Mac | X | | | MacPorts with both OpenJPEG 1.5.1 |
|
||||
| 10.6.8 | | | | and OpenJPEG svn. 354 of 455 tests |
|
||||
| 10.6.8 | | | | and OpenJPEG svn. 354 of 456 tests |
|
||||
| | | | | should pass. |
|
||||
+-----------+--------+--------+--------+--------------------------------------+
|
||||
| Mac | X | | | MacPorts with both OpenJPEG 1.5.1 |
|
||||
| 10.6.8 | | | | and OpenJPEG 2.0. 315 of 456 tests |
|
||||
| | | | | should pass. |
|
||||
+-----------+--------+--------+--------+--------------------------------------+
|
||||
| Mac | | X | | MacPorts with both OpenJPEG 1.5.1 |
|
||||
| 10.6.8 | | | | and OpenJPEG svn. 379 of 460 tests |
|
||||
| 10.6.8 | | | | and OpenJPEG svn. 379 of 461 tests |
|
||||
| | | | | should pass. |
|
||||
+-----------+--------+--------+--------+--------------------------------------+
|
||||
| Mac | | X | | MacPorts with both OpenJPEG 1.5.1 |
|
||||
| 10.6.8 | | | | and OpenJPEG 2.0. 340 of 461 tests |
|
||||
| | | | | should pass. |
|
||||
+-----------+--------+--------+--------+--------------------------------------+
|
||||
| Mac | | | X | MacPorts with both OpenJPEG 1.5.1 |
|
||||
| 10.6.8 | | | | and OpenJPEG svn. 407 of 460 |
|
||||
| 10.6.8 | | | | and OpenJPEG svn. 407 of 461 |
|
||||
| | | | | tests should pass. |
|
||||
+-----------+--------+--------+--------+--------------------------------------+
|
||||
| Mac | | | X | MacPorts with both OpenJPEG 1.5.1 |
|
||||
| 10.6.8 | | | | and OpenJPEG 2.0. 355 of 461 |
|
||||
| | | | | tests should pass. |
|
||||
+-----------+--------+--------+-----------------------------------------------+
|
||||
| Fedora 19 | | | X | Ships with 1.5.1, openjp2 built too. |
|
||||
| | | | | 402 of 455 tests should pass. |
|
||||
| Fedora 19 | | | X | Ships with 1.5.1, openjp2 svn built, |
|
||||
| | | | | too. 407 of 461 tests should pass. |
|
||||
+-----------+--------+--------+--------+--------------------------------------+
|
||||
| Fedora 18 | | | X | Ships with 1.5.1. 169 of 449 tests |
|
||||
| Fedora 18 | | X | | Ships with 1.5.1. 173 of 456 tests |
|
||||
| | | | | should pass. |
|
||||
+-----------+--------+--------+--------+--------------------------------------+
|
||||
| Fedora 17 | | X | | Ships with 1.4.0. 166 of 450 tests |
|
||||
| Fedora 17 | | X | | Ships with 1.4.0. 171 of 456 tests |
|
||||
| | | | | should pass. |
|
||||
+-----------+--------+--------+--------+--------------------------------------+
|
||||
| CentOS | X | | | Ships with 1.3.0. 169 of 455 tests |
|
||||
| CentOS | X | | | Ships with 1.3.0. 169 of 456 tests |
|
||||
| 6.3 | | | | should pass. |
|
||||
+-----------+--------+--------+--------+--------------------------------------+
|
||||
| Raspberry | | X | | Ships with 1.3.0. 171 of 455 tests |
|
||||
| Raspberry | | X | | Ships with 1.3.0. 171 of 456 tests |
|
||||
| Pi | | | | should pass. |
|
||||
| Debian 7 | | | | |
|
||||
+-----------+--------+--------+--------+--------------------------------------+
|
||||
|
|
|
|||
2
setup.py
2
setup.py
|
|
@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
|||
import sys
|
||||
|
||||
kwargs = {'name': 'Glymur',
|
||||
'version': '0.2.8',
|
||||
'version': '0.3.0',
|
||||
'description': 'Tools for accessing JPEG2000 files',
|
||||
'long_description': open('README.md').read(),
|
||||
'author': 'John Evans',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue