diff --git a/MANIFEST.in b/MANIFEST.in index 86279d7..1390b67 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,4 @@ include *.txt *.md prune build exclude readthedocs-pip-requirements.txt +exclude release.txt diff --git a/docs/source/detailed_installation.rst b/docs/source/detailed_installation.rst index 5090cdc..1e6fe4b 100644 --- a/docs/source/detailed_installation.rst +++ b/docs/source/detailed_installation.rst @@ -1,16 +1,54 @@ ------------------------------------------------------ -Detailed Instructions for Package Management, Testing ------------------------------------------------------ +---------------------------------- +Detailed Installation Instructions +---------------------------------- -You only need to read this page if you want detailed +'''''''''''''''''''''' +Glymur Configuration +'''''''''''''''''''''' + +The default glymur installation process relies upon OpenJPEG version +1.5.1 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 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 + +Glymur uses ctypes (for the moment) to access the openjp2 library, and +because ctypes access libraries in a platform-dependent manner, it is +recommended that you create a configuration file to help Glymur properly find +the openjp2 library. You may create the configuration file as follows:: + + $ mkdir -p ~/.config/glymur + $ cd ~/.config/glymur + $ cat > glymurrc << EOF + > [library] + > openjp2: /opt/openjp2-svn/lib/libopenjp2.so + > EOF + +That assumes, of course, that you've installed OpenJPEG into +/opt/openjp2-svn on a linux system. You may also substitute +**$XDG_CONFIG_HOME** for **$HOME/.config**. + +Again, though, the configuration file is not required if you only wish to +read JPEG 2000 files using OpenJPEG version 1.5.1. + + +''''''''''''''''''''''''''''''''''''''''''' +Package Management Suggestions for Testing +''''''''''''''''''''''''''''''''''''''''''' + +You only need to read this section if you want detailed platform-specific instructions on running as many tests as possible or wish to use your system's package manager to install as many required -packages/RPMs/ports/whatever without going through pip. Otherwise go on to -the next page. +packages/RPMs/ports/whatever without going through pip. -'''''''' -Platform -'''''''' Mac OS X -------- @@ -58,8 +96,9 @@ repositories:: Raspbian '''''''' -Yeah, this was the first thing I tried after getting my new Raspberry Pi hooked -up (couldn't help myself :-) Raspbian ships with Python 3.2 and 2.7, so these steps detail working with 2.7. +Yeah, this was the first thing I tried after getting my new Raspberry +Pi hooked up (couldn't help myself :-) Raspbian ships with Python +3.2 and 2.7, so these steps detail working with 2.7. Additional required OS packages include:: @@ -79,8 +118,8 @@ one unexpected failure as of the time of writing. Fedora 17 ''''''''' -Fedora 17 ships with Python 3.2 and 2.7, so these steps detail working with -2.7. +Fedora 17 ships with Python 3.2 and 2.7, but OpenJPEG is only at version 1.4, +so these steps detail working with Python 2.7 and the svn version of OpenJPEG. Required RPMs include:: @@ -111,7 +150,10 @@ it was installable via pip:: Windows ------- -Not currently supported. +The only configuration I've tested is Python(xy), which uses Python 2.7. +Python(xy) already comes with numpy, but you will have to install pip and then +contextlib2 as well. This configuration assumes you've installed OpenJPEG +1.5.1. ''''''' Testing diff --git a/docs/source/introduction.rst b/docs/source/introduction.rst index f8d4c65..5818c1b 100644 --- a/docs/source/introduction.rst +++ b/docs/source/introduction.rst @@ -2,14 +2,9 @@ Glymur: a Python interface for JPEG 2000 ---------------------------------------- -**Glymur** contains a Python interface to the OpenJPEG library -which allows linux and mac users to read and write JPEG 2000 files. For more -information about OpenJPEG, please consult http://www.openjpeg.org. Glymur -currently relies upon a development version of the OpenJPEG library, and so, -while useable, it is totally at the mercy of any upstream changes -made to the development version of OpenJPEG. - -Glymur supports both reading and writing of JPEG 2000 images (part 1). Writing +**Glymur** is an interface to the OpenJPEG library +which allows one to read and write JPEG 2000 files from within Python. +Glymur supports both reading and writing of JPEG 2000 images. Writing JPEG 2000 images is currently limited to images that can fit in memory, however. @@ -23,34 +18,18 @@ Glymur works on Python 2.7 and 3.3. Python 3.3 is strongly recommended. OpenJPEG Installation ===================== -OpenJPEG must be built as a shared library. In addition, you -currently must compile OpenJPEG from the developmental source that -you can retrieve via subversion. As of this time of writing, svn -revision 2345 works. You should 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. :: +The OpenJPEG library version must be either 1.5.1 or the trunk/development +version of OpenJPEG. Version 2.0.0 or versions earlier than 1.5.0 +are not supported. Furthermore, the 1.5.x version of OpenJPEG is +currently only utilized for read-only purposes. For more information +about OpenJPEG, please consult http://www.openjpeg.org. - $ svn co http://openjpeg.googlecode.com/svn/data - $ export OPJ_DATA_ROOT=`pwd`/data - -Earlier versions of OpenJPEG through the 2.0 official release will **NOT** -work and are not supported. - -Glymur uses ctypes (for the moment) to access the openjp2 library, and -because ctypes access libraries in a platform-dependent manner, it is -recommended that you create a configuration file to help Glymur properly find -the openjp2 library. You may create the configuration file as follows:: - - $ mkdir -p ~/.config/glymur - $ cd ~/.config/glymur - $ cat > glymurrc << EOF - > [library] - > openjp2: /opt/openjp2-svn/lib/libopenjp2.so - > EOF - -That assumes, of course, that you've installed OpenJPEG into /opt/openjp2-svn. -You may also substitute **$XDG_CONFIG_HOME** for **$HOME/.config**. +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 at least version +1.5.1 of OpenJPEG, which means that glymur can be installed ready to read JPEG +2000 images. If you use windows, I suggest using the 1.5.1 windows installer provided +to you by the OpenJPEG folks at +https://code.google.com/p/openjpeg/downloads/list . Glymur Installation =================== @@ -76,5 +55,6 @@ You can run the tests from within python as follows:: >>> import glymur >>> glymur.runtests() -Many tests are currently skipped; the important thing is whether or not any +Many tests are currently skipped; in fact most of them are skipped if you +are relying on OpenJPEG 1.5.1. But the important thing is whether or not any tests fail. diff --git a/glymur/core.py b/glymur/core.py index 32b6818..05af436 100644 --- a/glymur/core.py +++ b/glymur/core.py @@ -186,4 +186,3 @@ _reader_requirements_display = { + 'requirements in M.9.2.3', 73: 'YPbPr(1125/60) enumerated colourspace', 74: 'YPbPr(1250/50) enumerated colourspace'} - diff --git a/glymur/data/nemo.jp2 b/glymur/data/nemo.jp2 index 2bb8638..55d199c 100644 Binary files a/glymur/data/nemo.jp2 and b/glymur/data/nemo.jp2 differ diff --git a/glymur/jp2k.py b/glymur/jp2k.py index 96ead49..b0d4157 100644 --- a/glymur/jp2k.py +++ b/glymur/jp2k.py @@ -18,6 +18,7 @@ import numpy as np from .codestream import Codestream from .core import * from .jp2box import * +from .lib import openjpeg as opj from .lib import openjp2 as opj2 _cspace_map = {'rgb': opj2._CLRSPC_SRGB, @@ -202,7 +203,7 @@ class Jp2k(Jp2kBox): >>> import glymur >>> jfile = glymur.data.nemo() >>> jp2 = glymur.Jp2k(jfile) - >>> data = jp2.read(reduce=3) + >>> data = jp2.read(reduce=1) >>> from tempfile import NamedTemporaryFile >>> tfile = NamedTemporaryFile(suffix='.jp2', delete=False) >>> j = Jp2k(tfile.name, mode='wb') @@ -544,16 +545,17 @@ class Jp2k(Jp2kBox): jp2 = Jp2k(filename) return jp2 - def read(self, reduce=0, layer=0, area=None, tile=None, verbose=False): + def read(self, **kwargs): """Read a JPEG 2000 image. Parameters ---------- - layer : int, optional - Number of quality layer to decode. reduce : int, optional Factor by which to reduce output resolution. Use -1 to get the - lowest resolution thumbnail. + lowest resolution thumbnail. This is the only keyword option + available to use when only OpenJPEG version 1.5.1 is present. + layer : int, optional + Number of quality layer to decode. area : tuple, optional Specifies decoding image area, (first_row, first_col, last_row, last_col) @@ -585,7 +587,156 @@ class Jp2k(Jp2kBox): >>> thumbnail = jp.read(reduce=-1) >>> thumbnail.shape - (46, 81, 3) + (728, 1296, 3) + """ + if opj2._OPENJP2 is not None: + img = self._read_openjp2(**kwargs) + else: + img = self._read_openjpeg(**kwargs) + return img + + def _read_openjpeg(self, reduce=0, verbose=False): + """Read a JPEG 2000 image using libopenjpeg. + + Parameters + ---------- + reduce : int, optional + Factor by which to reduce output resolution. Use -1 to get the + lowest resolution thumbnail. + verbose : bool, optional + Print informational messages produced by the OpenJPEG library. + + Returns + ------- + img_array : ndarray + The image data. + + Raises + ------ + RuntimeError + If the image has differing subsample factors. + """ + # Check for differing subsample factors. + codestream = self.get_codestream(header_only=True) + dxs = np.array(codestream.segment[1].XRsiz) + dys = np.array(codestream.segment[1].YRsiz) + if np.any(dxs - dxs[0]) or np.any(dys - dys[0]): + msg = "Components must all have the same subsampling factors " + msg += "to use this method with OpenJPEG 1.5.1. Please consider " + msg += "using OPENJP2 instead." + raise RuntimeError(msg) + + + with ExitStack() as stack: + # Set decoding parameters. + dparameters = opj.dparameters_t() + opj._set_default_decoder_parameters(ctypes.byref(dparameters)) + dparameters.cp_reduce = reduce + dparameters.decod_format = self._codec_format + + infile = self.filename.encode() + nelts = opj._PATH_LEN - len(infile) + infile += b'0' * nelts + dparameters.infile = infile + + dinfo = opj._create_decompress(dparameters.decod_format) + + event_mgr = opj.event_mgr_t() + info_handler = ctypes.cast(_info_callback, ctypes.c_void_p) + event_mgr.info_handler = info_handler if verbose else None + event_mgr.warning_handler = ctypes.cast(_warning_callback, + ctypes.c_void_p) + event_mgr.error_handler = ctypes.cast(_error_callback, + ctypes.c_void_p) + opj._set_event_mgr(dinfo, ctypes.byref(event_mgr)) + + opj._setup_decoder(dinfo, dparameters) + + with open(self.filename, 'rb') as fp: + src = fp.read() + cio = opj._cio_open(dinfo, src) + + image = opj._decode(dinfo, cio) + + stack.callback(opj._image_destroy, image) + stack.callback(opj._destroy_decompress, dinfo) + stack.callback(opj._cio_close, cio) + + ncomps = image.contents.numcomps + component = image.contents.comps[0] + if component.sgnd: + if component.prec <= 8: + dtype = np.int8 + elif component.prec <= 16: + dtype = np.int16 + else: + raise RuntimeError("Unhandled precision, datatype") + else: + if component.prec <= 8: + dtype = np.uint8 + elif component.prec <= 16: + dtype = np.uint16 + else: + raise RuntimeError("Unhandled precision, datatype") + + nrows = image.contents.comps[0].h + ncols = image.contents.comps[0].w + ncomps = image.contents.numcomps + data = np.zeros((nrows, ncols, ncomps), dtype) + + for k in range(image.contents.numcomps): + component = image.contents.comps[k] + nrows = component.h + ncols = component.w + + if nrows == 0 or ncols == 0: + # Letting this situation continue would segfault + # Python. + msg = "Component {0} has dimensions {1} x {2}" + msg = msg.format(k, nrows, ncols) + raise IOError(msg) + + addr = ctypes.addressof(component.data.contents) + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + nelts = nrows * ncols + x = np.ctypeslib.as_array((ctypes.c_int32 * nelts).from_address(addr)) + data[:, :, k] = np.reshape(x.astype(dtype), (nrows, ncols)) + + if data.shape[2] == 1: + data = data.view() + data.shape = data.shape[0:2] + + return data + + def _read_openjp2(self, reduce=0, layer=0, area=None, tile=None, + verbose=False): + """Read a JPEG 2000 image using libopenjp2. + + Parameters + ---------- + layer : int, optional + Number of quality layer to decode. + reduce : int, optional + Factor by which to reduce output resolution. Use -1 to get the + lowest resolution thumbnail. + area : tuple, optional + Specifies decoding image area, + (first_row, first_col, last_row, last_col) + tile : int, optional + Number of tile to decode. + verbose : bool, optional + Print informational messages produced by the OpenJPEG library. + + Returns + ------- + img_array : ndarray + The image data. + + Raises + ------ + RuntimeError + If the image has differing subsample factors. """ # Check for differing subsample factors. codestream = self.get_codestream(header_only=True) @@ -593,7 +744,7 @@ class Jp2k(Jp2kBox): dys = np.array(codestream.segment[1].YRsiz) if np.any(dxs - dxs[0]) or np.any(dys - dys[0]): msg = "Components must all have the same subsampling factors." - raise IOError(msg) + raise RuntimeError(msg) img_array = self._read_common(reduce=reduce, layer=layer, @@ -780,7 +931,16 @@ class Jp2k(Jp2kBox): >>> jfile = glymur.data.nemo() >>> jp = glymur.Jp2k(jfile) >>> components_lst = jp.read_bands(reduce=1) + + Raises + ------ + NotImplementedError + If the openjp2 library is not available. """ + if opj2._OPENJP2 is None: + msg = "Requires openjp2 library." + raise NotImplementedError(msg) + lst = self._read_common(reduce=reduce, layer=layer, area=area, @@ -807,14 +967,14 @@ class Jp2k(Jp2kBox): -------- >>> import glymur >>> jfile = glymur.data.nemo() - >>> jp = glymur.Jp2k(jfile) - >>> codestream = jp.get_codestream() + >>> jp2 = glymur.Jp2k(jfile) + >>> codestream = jp2.get_codestream() >>> print(codestream.segment[1]) SIZ marker segment @ (3137, 47) Profile: 2 Reference Grid Height, Width: (1456 x 2592) Vertical, Horizontal Reference Grid Offset: (0 x 0) - Reference Tile Height, Width: (512 x 512) + Reference Tile Height, Width: (1456 x 2592) Vertical, Horizontal Reference Tile Offset: (0 x 0) Bitdepth: (8, 8, 8) Signed: (False, False, False) diff --git a/glymur/lib/__init__.py b/glymur/lib/__init__.py index ca7460c..8484e19 100644 --- a/glymur/lib/__init__.py +++ b/glymur/lib/__init__.py @@ -1,3 +1,4 @@ """This package organizes individual libraries employed by glymur.""" from . import openjp2 -from . import test +from . import openjpeg +#from . import test diff --git a/glymur/lib/openjp2.py b/glymur/lib/openjp2.py index d7578d0..6e41e66 100644 --- a/glymur/lib/openjp2.py +++ b/glymur/lib/openjp2.py @@ -1,6 +1,9 @@ """ Wraps individual functions in openjp2 library. """ +import ctypes +import platform +from ctypes.util import find_library def _glymurrc_fname(): @@ -23,19 +26,46 @@ def _glymurrc_fname(): fname = os.path.join(confdir, 'glymurrc') if os.path.exists(fname): return fname - else: - msg = "Configuration file '{0}' does not exist.".format(confdir) - warnings.warn(msg, UserWarning) # didn't find a configuration file. return None -def _config(): - """Read configuration file. +def _get_openjpeg_config(): + libopenjpeg_path = find_library('openjpeg') + + # If we could not find it, then look in some likely locations. + if libopenjpeg_path is None: + if platform.system() == 'Darwin': + path = '/opt/local/lib/libopenjpeg.dylib' + if os.path.exists(path): + libopenjpeg_path = path + elif os.name == 'nt': + path = os.path.join('C:\\', 'Program files', 'OpenJPEG 1.5', + 'bin', 'openjpeg.dll') + if os.path.exists(path): + libopenjpeg_path = path - Based on matplotlib. - """ + try: + if os.name == "nt": + _OPENJPEG = ctypes.windll.LoadLibrary(libopenjpeg_path) + else: + _OPENJPEG = ctypes.CDLL(libopenjpeg_path) + except OSError: + _OPENJPEG = None + + if _OPENJPEG is not None: + # Must be at least 1.5.0 + _OPENJPEG.opj_version.restype = ctypes.c_char_p + v = _OPENJPEG.opj_version() + v = v.decode('utf-8') + major, minor, patch = v.split('.') + if minor != '5': + _OPENJPEG = None + return _OPENJPEG + + +def _get_openjp2_config(): filename = _glymurrc_fname() if filename is not None: # Read the configuration file for the library location. @@ -44,14 +74,16 @@ def _config(): libopenjp2_path = parser.get('library', 'openjp2') else: # No help from the config file, try to find it ourselves. - from ctypes.util import find_library libopenjp2_path = find_library('openjp2') if libopenjp2_path is None: return None try: - _OPENJP2 = ctypes.CDLL(libopenjp2_path) + if os.name == "nt": + _OPENJP2 = ctypes.windll.LoadLibrary(libopenjp2_path) + else: + _OPENJP2 = ctypes.CDLL(libopenjp2_path) except OSError: msg = '"Library {0}" could not be loaded. Operating in degraded mode.' msg = msg.format(libopenjp2_path) @@ -60,6 +92,13 @@ def _config(): return _OPENJP2 +def _config(): + """Read configuration file. + """ + _OPENJP2 = _get_openjp2_config() + _OPENJPEG = _get_openjpeg_config() + return _OPENJP2, _OPENJPEG + def _get_configdir(): """Return string representing the configuration directory. @@ -73,7 +112,9 @@ def _get_configdir(): if 'HOME' in os.environ: return os.path.join(os.environ['HOME'], '.config', 'glymur') -import ctypes + if 'USERPROFILE' in os.environ: + return os.path.join(os.environ['USERPROFILE'], 'Application Data', 'glymur') + import os import warnings @@ -85,7 +126,12 @@ else: from configparser import ConfigParser from configparser import NoOptionError -_OPENJP2 = _config() +_OPENJP2, _OPENJPEG = _config() +if _OPENJP2 is None and _OPENJPEG is None: + msg = "Neither the glymur configuration file could not be located " + msg += "nor could openjpeg 1.5.1 be located. Glymur can only " + msg += "operate under extremely degraded conditions." + warnings.warn(msg, UserWarning) import numpy as np @@ -616,8 +662,8 @@ class _codestream_info_v2_t(ctypes.Structure): # Restrict the input and output argument types for each function used in the # API. if _OPENJP2 is not None: - _OPENJP2.opj_create_compress.argtypes = [_codec_format_t] _OPENJP2.opj_create_compress.restype = _codec_t_p + _OPENJP2.opj_create_compress.argtypes = [_codec_format_t] _OPENJP2.opj_create_decompress.argtypes = [_codec_format_t] _OPENJP2.opj_create_decompress.restype = _codec_t_p diff --git a/glymur/lib/openjpeg.py b/glymur/lib/openjpeg.py new file mode 100644 index 0000000..f818e1a --- /dev/null +++ b/glymur/lib/openjpeg.py @@ -0,0 +1,189 @@ +"""Wraps library calls to openjpeg. +""" + +import ctypes +from ctypes.util import find_library +import platform +import os + +if os.name == "nt": + path = os.path.join('C:\\', 'Program files', 'OpenJPEG 1.5', + 'bin', 'openjpeg.dll') + _OPENJPEG = ctypes.windll.LoadLibrary(path) +else: + if platform.system() == 'Darwin': + _OPENJPEG = ctypes.CDLL('/opt/local/lib/libopenjpeg.dylib') + elif platform.system() == 'Linux': + _OPENJPEG = ctypes.CDLL(find_library('openjpeg')) + +_PATH_LEN = 4096 # maximum allowed size for filenames + + +class event_mgr_t(ctypes.Structure): + """Message handler object. + """ + _fields_ = [("error_handler", ctypes.c_void_p), + ("warning_handler", ctypes.c_void_p), + ("info_handler", ctypes.c_void_p)] + +class common_struct_t(ctypes.Structure): + """Common fields between JPEG 2000 compression and decompression contextx. + """ + _fields_ = [("event_mgr", ctypes.POINTER(event_mgr_t)), + ("client_data", ctypes.c_void_p), + ("is_decompressor", ctypes.c_bool), + ("codec_format", ctypes.c_int), + ("j2k_handle", ctypes.c_void_p), + ("jp2_handle", ctypes.c_void_p), + ("mj2_handle", ctypes.c_void_p)] + + +class dinfo_t(ctypes.Structure): + """Common fields between JPEG 2000 compression and decompression contextx. + This is for decompression contexts. + """ + pass + + +class cio_t(ctypes.Structure): + _fields_ = [# codec context + ("cinfo", ctypes.POINTER(common_struct_t)), + # STREAM_READ or STREAM_WRITE + ("openmode", ctypes.c_int), + # pointer to start of buffer + ("buffer", ctypes.POINTER(ctypes.c_char)), + # buffer size in bytes + ("length", ctypes.c_int), + # pointer to start of stream + ("start", ctypes.c_char_p), + # pointer to end of stream + ("end", ctypes.c_char_p), + # pointer to current position + ("bp", ctypes.c_char_p)] + + +class dparameters_t(ctypes.Structure): + # cp_reduce: the number of highest resolution levels to be discarded + _fields_ = [("cp_reduce", ctypes.c_int), + # cp_layer: the maximum number of quality layers to decode + ("cp_layer", ctypes.c_int), + # infile: input file name + ("infile", ctypes.c_char * _PATH_LEN), + # outfile: output file name + ("outfile", ctypes.c_char * _PATH_LEN), + # decod_format: input file format 0: J2K, 1: JP2, 2: JPT + ("decod_format", ctypes.c_int), + # cod_format: output file format 0: PGX, 1: PxM, 2: BMP + ("cod_format", ctypes.c_int), + # jpwl_correct: activates the JPWL correction capabilities + ("jpwl_correct", ctypes.c_bool), + # jpwl_exp_comps: expected number of components + ("jpwl_exp_comps", ctypes.c_int), + # jpwl_max_tiles: maximum number of tiles + ("jpwl_max_tiles", ctypes.c_int), + # cp_limit_decoding: whether decoding should be done on the + # entire codestream or be limited to the main header + ("cp_limit_decoding", ctypes.c_int), + ("flags", ctypes.c_uint)] + + +class image_comp_t(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 image_t(ctypes.Structure): + """Defines image data and characteristics.""" + _fields_ = [("x0", ctypes.c_int), + ("y0", ctypes.c_int), + ("x1", ctypes.c_int), + ("y1", ctypes.c_int), + ("numcomps", ctypes.c_int), + ("color_space", ctypes.c_int), + ("comps", ctypes.POINTER(image_comp_t)), + ("icc_profile_buf", ctypes.c_char_p), + ("icc_profile_len", ctypes.c_int)] + +def _cio_open(cinfo, src): + """Wrapper for openjpeg library function opj_cio_open.""" + argtypes = [ctypes.POINTER(common_struct_t), ctypes.c_char_p, ctypes.c_int] + _OPENJPEG.opj_cio_open.argtypes = argtypes + _OPENJPEG.opj_cio_open.restype = ctypes.POINTER(cio_t) + + cio = _OPENJPEG.opj_cio_open(ctypes.cast(cinfo, ctypes.POINTER(common_struct_t)), + src, len(src)) + return cio + +def _cio_close(cio): + """Wraps openjpeg library function cio_close. + """ + _OPENJPEG.opj_cio_close.argtypes = [ctypes.POINTER(cio_t)] + _OPENJPEG.opj_cio_close(cio) + +def _create_decompress(fmt): + """Wraps openjpeg library function opj_create_decompress. + """ + _OPENJPEG.opj_create_decompress.argtypes = [ctypes.c_int] + _OPENJPEG.opj_create_decompress.restype = ctypes.POINTER(dinfo_t) + dinfo = _OPENJPEG.opj_create_decompress(fmt) + return dinfo + +def _decode(dinfo, cio): + """Wrapper for opj_decode. + """ + argtypes = [ctypes.POINTER(dinfo_t), ctypes.POINTER(cio_t)] + _OPENJPEG.opj_decode.argtypes = argtypes + _OPENJPEG.opj_decode.restype = ctypes.POINTER(image_t) + image = _OPENJPEG.opj_decode(dinfo, cio) + return image + +def _destroy_decompress(dinfo): + """Wraps openjpeg library function opj_destroy_decompress.""" + _OPENJPEG.opj_destroy_decompress.argtypes = [ctypes.POINTER(dinfo_t)] + _OPENJPEG.opj_destroy_decompress(dinfo) + +def _image_destroy(image): + """Wraps openjpeg library function opj_image_destroy.""" + _OPENJPEG.opj_image_destroy.argtypes = [ctypes.POINTER(image_t)] + _OPENJPEG.opj_image_destroy(image) + +def _set_default_decoder_parameters(dparams_p): + """Wrapper for opj_set_default_decoder_parameters. + """ + argtypes = [ctypes.POINTER(dparameters_t)] + _OPENJPEG.opj_set_default_decoder_parameters.argtypes = argtypes + _OPENJPEG.opj_set_default_decoder_parameters(dparams_p) + + +def _set_event_mgr(dinfo, event_mgr, context=None): + """Wrapper for openjpeg library function opj_set_event_mgr. + """ + argtypes = [ctypes.POINTER(common_struct_t), + ctypes.POINTER(event_mgr_t), + ctypes.c_void_p] + _OPENJPEG.opj_set_event_mgr(ctypes.cast(dinfo, + ctypes.POINTER(common_struct_t)), + event_mgr, context) + +def _setup_decoder(dinfo, dparams): + """Wrapper for openjpeg library function opj_setup_decoder.""" + argtypes = [ctypes.POINTER(dinfo_t), ctypes.POINTER(dparameters_t)] + _OPENJPEG.opj_setup_decoder.argtypes = argtypes + _OPENJPEG.opj_setup_decoder(dinfo, dparams) + +def _version(): + """Wrapper for opj_version library routine.""" + _OPENJPEG.opj_version.restype = ctypes.c_char_p + v = _OPENJPEG.opj_version() + return v.decode('utf-8') diff --git a/glymur/lib/test/test_openjpeg.py b/glymur/lib/test/test_openjpeg.py new file mode 100644 index 0000000..9307191 --- /dev/null +++ b/glymur/lib/test/test_openjpeg.py @@ -0,0 +1,38 @@ +import ctypes +import unittest + +import glymur + +@unittest.skipIf(glymur.lib.openjpeg._OPENJPEG is None, + "Missing openjpeg library.") +class TestOpenJPEG(unittest.TestCase): + + def setUp(self): + pass + + def tearDown(self): + pass + + def test_version(self): + v = glymur.lib.openjpeg._version() + parts = v.split('.') + self.assertEqual(parts[0], '1') + self.assertEqual(parts[1], '5') + + def test_set_default_decoder_parameters(self): + # Verify that we properly set the default decode parameters. + dp = glymur.lib.openjpeg.dparameters_t() + glymur.lib.openjpeg._set_default_decoder_parameters(ctypes.byref(dp)) + + self.assertEqual(dp.cp_reduce, 0) + self.assertEqual(dp.cp_layer, 0) + self.assertEqual(dp.infile, b'') + self.assertEqual(dp.outfile, b'') + self.assertEqual(dp.decod_format, -1) + self.assertEqual(dp.cod_format, -1) + self.assertEqual(dp.jpwl_correct, 0) + self.assertEqual(dp.jpwl_exp_comps, 0) + self.assertEqual(dp.jpwl_max_tiles, 0) + self.assertEqual(dp.cp_limit_decoding, 0) + self.assertEqual(dp.flags, 0) + diff --git a/glymur/test/__init__.py b/glymur/test/__init__.py index aec2d72..644bc56 100644 --- a/glymur/test/__init__.py +++ b/glymur/test/__init__.py @@ -1,5 +1,6 @@ from .test_callbacks import TestCallbacks as callbacks from .test_codestream import TestCodestream as codestream +from .test_config import TestSuite as config from .test_jp2k import TestJp2k as jp2k from .test_icc import TestICC as icc from .test_printing import TestPrinting as printing diff --git a/glymur/test/fixtures.py b/glymur/test/fixtures.py new file mode 100644 index 0000000..ce52fae --- /dev/null +++ b/glymur/test/fixtures.py @@ -0,0 +1,93 @@ +import re +import sys + +import numpy as np + +def mse(A, B): + """Mean Square Error""" + diff = A.astype(np.double) - B.astype(np.double) + #e = np.sqrt(np.mean(diff**2)) + e = np.mean(diff**2) + return e + + +def peak_tolerance(A, B): + """Peak Tolerance""" + diff = np.abs(A.astype(np.double) - B.astype(np.double)) + p = diff.max() + return p + + +def read_pgx(pgx_file): + """Helper function for reading the PGX comparison files. + + Open the file in ascii mode and read the header line. + Will look something like + + PG ML + 8 128 128 + PG%[ \t]%c%c%[ \t+-]%d%[ \t]%d%[ \t]%d" + """ + header = '' + with open(pgx_file, 'rb') as fp: + while True: + x = fp.read(1) + if x[0] == 10 or x == '\n': + pos = fp.tell() + break + else: + if sys.hexversion < 0x03000000: + header += x + else: + header += chr(x[0]) + + header = header.rstrip() + n = re.split('\s', header) + + if (n[1][0] == 'M') and (sys.byteorder == 'little'): + swapbytes = True + elif (n[1][0] == 'L') and (sys.byteorder == 'big'): + swapbytes = True + else: + swapbytes = False + + if (len(n) == 6): + bitdepth = int(n[3]) + signed = bitdepth < 0 + if signed: + bitdepth = -1 * bitdepth + nrows = int(n[5]) + ncols = int(n[4]) + else: + bitdepth = int(n[2]) + signed = bitdepth < 0 + if signed: + bitdepth = -1 * bitdepth + nrows = int(n[4]) + ncols = int(n[3]) + + if signed: + if bitdepth <= 8: + dtype = np.int8 + elif bitdepth <= 16: + dtype = np.int16 + else: + raise RuntimeError("unhandled bitdepth") + else: + if bitdepth <= 8: + dtype = np.uint8 + elif bitdepth <= 16: + dtype = np.uint16 + else: + raise RuntimeError("unhandled bitdepth") + + shape = [nrows, ncols] + + # Reopen the file in binary mode and seek to the start of the binary + # data + with open(pgx_file, 'rb') as fp: + fp.seek(pos) + data = np.fromfile(file=fp, dtype=dtype).reshape(shape) + + return(data.byteswap(swapbytes)) + + diff --git a/glymur/test/test_callbacks.py b/glymur/test/test_callbacks.py index 9a1250b..fd2f58c 100644 --- a/glymur/test/test_callbacks.py +++ b/glymur/test/test_callbacks.py @@ -1,5 +1,6 @@ import os import pkg_resources +import re import sys import tempfile import unittest @@ -21,8 +22,8 @@ class TestCallbacks(unittest.TestCase): # Save sys.stdout. self.stdout = sys.stdout sys.stdout = StringIO() - self.jp2file = pkg_resources.resource_filename(glymur.__name__, - "data/nemo.jp2") + self.jp2file = glymur.data.nemo() + self.j2kfile = glymur.data.goodstuff() def tearDown(self): # Restore stdout. @@ -44,24 +45,67 @@ class TestCallbacks(unittest.TestCase): def test_info_warning_callbacks_on_read(self): # Verify that we get the expected stdio output when our internal info # callback handler is enabled. - j = glymur.Jp2k(self.jp2file) - d = j.read(reduce=3, verbose=True, area=(0, 0, 512, 1024)) + j = glymur.Jp2k(self.j2kfile) + d = j.read(reduce=1, verbose=True, area=(0, 0, 200, 150)) actual = sys.stdout.getvalue().strip() - lines = ['[INFO] Start to read j2k main header (3135).', + lines = ['[INFO] Start to read j2k main header (0).', '[INFO] Main header has been correctly decoded.', - '[INFO] Setting decoding area to 0,0,1024,512', - '[INFO] Header of tile 0 / 17 has been read.', - '[INFO] Tile 1/18 has been decoded.', - '[INFO] Image data has been updated with tile 1.', - '[INFO] Header of tile 1 / 17 has been read.', - '[INFO] Tile 2/18 has been decoded.', - '[INFO] Image data has been updated with tile 2.', - '[INFO] Stream reached its end !'] + '[INFO] Setting decoding area to 0,0,150,200', + '[INFO] Header of tile 0 / 0 has been read.', + '[INFO] Tile 1/1 has been decoded.', + '[INFO] Image data has been updated with tile 1.'] expected = '\n'.join(lines) self.assertEqual(actual, expected) +@unittest.skipIf(glymur.lib.openjp2._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 + + 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(reduce=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-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__": unittest.main() diff --git a/glymur/test/test_config.py b/glymur/test/test_config.py index 1abf02f..9e5374a 100644 --- a/glymur/test/test_config.py +++ b/glymur/test/test_config.py @@ -1,3 +1,7 @@ +"""These tests are for edge cases where OPENJPEG does not exist, but +OPENJP2 may be present in some form or other. +""" + import imp import os import sys @@ -16,14 +20,28 @@ from glymur import Jp2k from glymur.lib import openjp2 as opj2 +@unittest.skip("Cannot work when both OPENJPEG and OPENJP2 are both present.") +@unittest.skipIf(glymur.lib.openjp2._OPENJP2 is None, + "Needs openjp2 library first before these tests make sense.") @unittest.skipIf(sys.hexversion < 0x03020000, "Uses features introduced in 3.2.") -class TestJp2k(unittest.TestCase): +class TestSuite(unittest.TestCase): + + @classmethod + def setUpClass(cls): + # Monkey patch the package so as to ignore OPENJPEG if it exists. + cls.openjpeg = glymur.lib.openjpeg._OPENJPEG + glymur.lib.openjp2._OPENJPEG = None + + @classmethod + def tearDownClass(cls): + # Restore OPENJPEG + glymur.lib.openjpeg._OPENJPEG = cls.openjpeg + def setUp(self): - self.jp2file = pkg_resources.resource_filename(glymur.__name__, - "data/nemo.jp2") imp.reload(glymur) imp.reload(glymur.lib.openjp2) + self.jp2file = glymur.data.nemo() def tearDown(self): imp.reload(glymur) diff --git a/glymur/test/test_jp2box.py b/glymur/test/test_jp2box.py index e0500f5..1fe5330 100644 --- a/glymur/test/test_jp2box.py +++ b/glymur/test/test_jp2box.py @@ -1,4 +1,5 @@ import doctest +import os import tempfile import xml.etree.cElementTree as ET import unittest @@ -17,6 +18,8 @@ def load_tests(loader, tests, ignore): return tests +@unittest.skipIf(glymur.lib.openjp2._OPENJP2 is None, + "Missing openjp2 library.") class TestChannelDefinition(unittest.TestCase): @classmethod @@ -288,6 +291,8 @@ class TestXML(unittest.TestCase): with self.assertRaises((IOError, OSError)) as ce: xmlb = glymur.jp2box.XMLBox(filename=self.xmlfile, xml=xml_object) + @unittest.skipIf(os.name == "nt", + "Problems using NamedTemporaryFile on windows.") def test_basic_xml(self): # Should be able to write an XMLBox. j2k = Jp2k(self.j2kfile) @@ -308,6 +313,8 @@ class TestXML(unittest.TestCase): self.assertEqual(ET.tostring(jp2.box[3].xml), b'0') + @unittest.skipIf(os.name == "nt", + "Problems using NamedTemporaryFile on windows.") def test_xml_from_file(self): j2k = Jp2k(self.j2kfile) @@ -352,6 +359,8 @@ class TestColourSpecificationBox(unittest.TestCase): def tearDown(self): pass + @unittest.skipIf(os.name == "nt", + "Problems using NamedTemporaryFile on windows.") def test_color_specification_box_with_out_enumerated_colorspace(self): j2k = Jp2k(self.j2kfile) diff --git a/glymur/test/test_jp2k.py b/glymur/test/test_jp2k.py index d9549c5..d664820 100644 --- a/glymur/test/test_jp2k.py +++ b/glymur/test/test_jp2k.py @@ -74,10 +74,8 @@ class TestJp2k(unittest.TestCase): os.unlink(cls._bad_xml_file) def setUp(self): - self.jp2file = pkg_resources.resource_filename(glymur.__name__, - "data/nemo.jp2") - self.j2kfile = pkg_resources.resource_filename(glymur.__name__, - "data/goodstuff.j2k") + self.jp2file = glymur.data.nemo() + self.j2kfile = glymur.data.goodstuff() def tearDown(self): pass @@ -92,11 +90,11 @@ class TestJp2k(unittest.TestCase): def test_reduce_max(self): # Verify that reduce=-1 gets us the lowest resolution image - j = Jp2k(self.jp2file) + j = Jp2k(self.j2kfile) thumbnail1 = j.read(reduce=-1) thumbnail2 = j.read(reduce=5) np.testing.assert_array_equal(thumbnail1, thumbnail2) - self.assertEqual(thumbnail1.shape, (46, 81, 3)) + self.assertEqual(thumbnail1.shape, (25, 15, 3)) def test_invalid_xml_box(self): # Should be able to recover from xml box with bad xml. @@ -145,15 +143,6 @@ class TestJp2k(unittest.TestCase): filename = 'this file does not actually exist on the file system.' jp2k = Jp2k(filename) - def test_nemo_tile(self): - # Issue 134, trouble reading first nemo tile. - j = Jp2k(self.jp2file) - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - tiledata = j.read(tile=0) - subsetdata = j.read(area=(0, 0, 512, 512)) - np.testing.assert_array_equal(tiledata, subsetdata) - def test_write_srgb_without_mct(self): j2k = Jp2k(self.j2kfile) expdata = j2k.read() @@ -178,7 +167,7 @@ class TestJp2k(unittest.TestCase): def test_write_cprl(self): # Issue 17 j = Jp2k(self.jp2file) - expdata = j.read(reduce=2) + expdata = j.read(reduce=1) with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: ofile = Jp2k(tfile.name, 'wb') ofile.write(expdata, prog='CPRL') @@ -220,7 +209,7 @@ class TestJp2k(unittest.TestCase): self.assertEqual(jp2k.box[5].id, 'jp2c') self.assertEqual(jp2k.box[5].offset, 3127) - self.assertEqual(jp2k.box[5].length, 1133427) + self.assertEqual(jp2k.box[5].length, 1132296) # jp2h super box self.assertEqual(len(jp2k.box[2].box), 2) @@ -330,7 +319,7 @@ class TestJp2k(unittest.TestCase): # Issue 86. filename = os.path.join(data_root, 'input/conformance/p0_05.j2k') j = Jp2k(filename) - with self.assertRaises(IOError): + with self.assertRaises(RuntimeError): j.read() @unittest.skipIf(data_root is None, @@ -540,7 +529,7 @@ class TestJp2k(unittest.TestCase): def test_asoc_label_box(self): # Construct a fake file with an asoc and a label box, as # OpenJPEG doesn't have such a file. - data = Jp2k(self.jp2file).read(reduce=3) + data = Jp2k(self.jp2file).read(reduce=1) with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: j = Jp2k(tfile.name, 'wb') j.write(data) @@ -659,5 +648,66 @@ class TestJp2k(unittest.TestCase): self.assertFalse('Make' in exif['Image'].keys()) +@unittest.skipIf(glymur.lib.openjpeg._OPENJPEG is None, + "Missing openjpeg library.") +class TestJp2k15(unittest.TestCase): + + @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): + self.jp2file = glymur.data.nemo() + self.j2kfile = glymur.data.goodstuff() + + def tearDown(self): + pass + + def test_bands(self): + # Reading individual bands is an advanced maneuver. + jp2k = Jp2k(self.j2kfile) + with self.assertRaises(NotImplementedError) as ce: + jpdata = jp2k.read_bands() + + def test_area(self): + # Area option not allowed for 1.5.1. + j2k = Jp2k(self.j2kfile) + with self.assertRaises(TypeError) as ce: + d = j2k.read(area=(0, 0, 100, 100)) + + def test_tile(self): + # tile option not allowed for 1.5.1. + j2k = Jp2k(self.j2kfile) + with self.assertRaises(TypeError) as ce: + d = j2k.read(tile=0) + + def test_layer(self): + # layer option not allowed for 1.5.1. + j2k = Jp2k(self.j2kfile) + with self.assertRaises(TypeError) as ce: + d = j2k.read(layer=1) + + def test_basic_jp2(self): + # This test is only useful when openjp2 is not available + # and OPJ_DATA_ROOT is not set. We need at least one + # working JP2 test. + j2k = Jp2k(self.jp2file) + d = j2k.read(reduce=1) + + def test_basic_j2k(self): + # This test is only useful when openjp2 is not available + # and OPJ_DATA_ROOT is not set. We need at least one + # working J2K test. + j2k = Jp2k(self.j2kfile) + d = j2k.read() + + if __name__ == "__main__": unittest.main() diff --git a/glymur/test/test_opj_suite.py b/glymur/test/test_opj_suite.py index d64810e..a6f59dc 100644 --- a/glymur/test/test_opj_suite.py +++ b/glymur/test/test_opj_suite.py @@ -24,6 +24,8 @@ import numpy as np from glymur import Jp2k import glymur +from .fixtures import * + try: data_root = os.environ['OPJ_DATA_ROOT'] except KeyError: @@ -32,94 +34,6 @@ except: raise -def mse(A, B): - """Mean Square Error""" - diff = A.astype(np.double) - B.astype(np.double) - #e = np.sqrt(np.mean(diff**2)) - e = np.mean(diff**2) - return e - - -def peak_tolerance(A, B): - """Peak Tolerance""" - diff = np.abs(A.astype(np.double) - B.astype(np.double)) - p = diff.max() - return p - - -def read_pgx(pgx_file): - """Helper function for reading the PGX comparison files. - - Open the file in ascii mode and read the header line. - Will look something like - - PG ML + 8 128 128 - PG%[ \t]%c%c%[ \t+-]%d%[ \t]%d%[ \t]%d" - """ - header = '' - with open(pgx_file, 'rb') as fp: - while True: - x = fp.read(1) - if x[0] == 10 or x == '\n': - pos = fp.tell() - break - else: - if sys.hexversion < 0x03000000: - header += x - else: - header += chr(x[0]) - - header = header.rstrip() - n = re.split('\s', header) - - if (n[1][0] == 'M') and (sys.byteorder == 'little'): - swapbytes = True - elif (n[1][0] == 'L') and (sys.byteorder == 'big'): - swapbytes = True - else: - swapbytes = False - - if (len(n) == 6): - bitdepth = int(n[3]) - signed = bitdepth < 0 - if signed: - bitdepth = -1 * bitdepth - nrows = int(n[5]) - ncols = int(n[4]) - else: - bitdepth = int(n[2]) - signed = bitdepth < 0 - if signed: - bitdepth = -1 * bitdepth - nrows = int(n[4]) - ncols = int(n[3]) - - if signed: - if bitdepth <= 8: - dtype = np.int8 - elif bitdepth <= 16: - dtype = np.int16 - else: - raise RuntimeError("unhandled bitdepth") - else: - if bitdepth <= 8: - dtype = np.uint8 - elif bitdepth <= 16: - dtype = np.uint16 - else: - raise RuntimeError("unhandled bitdepth") - - shape = [nrows, ncols] - - # Reopen the file in binary mode and seek to the start of the binary - # data - with open(pgx_file, 'rb') as fp: - fp.seek(pos) - data = np.fromfile(file=fp, dtype=dtype).reshape(shape) - - return(data.byteswap(swapbytes)) - - @unittest.skipIf(glymur.lib.openjp2._OPENJP2 is None, "Missing openjp2 library.") @unittest.skipIf(data_root is None, @@ -1155,7 +1069,7 @@ class TestSuite(unittest.TestCase): def test_NR_DEC_file_409752_jp2_40_decode(self): jfile = os.path.join(data_root, 'input/nonregression/file409752.jp2') - with self.assertRaises(IOError): + with self.assertRaises(RuntimeError): data = Jp2k(jfile).read() @unittest.skipIf(sys.hexversion < 0x03020000, @@ -7418,5 +7332,615 @@ class TestSuiteDump(unittest.TestCase): self.assertEqual(c.segment[3]._exponent, [8, 9, 9, 10, 9, 9, 10, 9, 9, 10, 9, 9, 10, 9, 9, 10]) + +@unittest.skipIf(glymur.lib.openjpeg._OPENJPEG is None, + "Missing openjpeg library.") +@unittest.skipIf(data_root is None, + "OPJ_DATA_ROOT environment variable not set") +class TestSuite15(unittest.TestCase): + """Suite of tests for libopenjpeg 1.5.1""" + + @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): + pass + + def tearDown(self): + pass + + def test_ETS_C0P0_p0_01_j2k(self): + jfile = os.path.join(data_root, 'input/conformance/p0_01.j2k') + jp2k = Jp2k(jfile) + jpdata = jp2k.read(reduce=0) + + pgxfile = os.path.join(data_root, 'baseline/conformance/c0p0_01.pgx') + pgxdata = read_pgx(pgxfile) + np.testing.assert_array_equal(jpdata, pgxdata) + + def test_ETS_C0P0_p0_02_j2k(self): + jfile = os.path.join(data_root, 'input/conformance/p0_02.j2k') + with warnings.catch_warnings(): + # There's a 0xff30 marker segment. Not illegal, but we don't + # really know what to do with it. Just ignore. + warnings.simplefilter("ignore") + jp2k = Jp2k(jfile) + jpdata = jp2k.read(reduce=0) + + pgxfile = os.path.join(data_root, 'baseline/conformance/c0p0_02.pgx') + pgxdata = read_pgx(pgxfile) + np.testing.assert_array_equal(jpdata, pgxdata) + + def test_ETS_C0P0_p0_09_j2k(self): + jfile = os.path.join(data_root, 'input/conformance/p0_09.j2k') + jp2k = Jp2k(jfile) + jpdata = jp2k.read(reduce=2) + + pgxfile = os.path.join(data_root, + 'baseline/conformance/c0p0_09.pgx') + pgxdata = read_pgx(pgxfile) + + self.assertTrue(peak_tolerance(jpdata, pgxdata) < 4) + self.assertTrue(mse(jpdata, pgxdata) < 1.47) + + def test_ETS_C0P0_p0_11_j2k(self): + jfile = os.path.join(data_root, 'input/conformance/p0_11.j2k') + jp2k = Jp2k(jfile) + jpdata = jp2k.read(reduce=0) + + pgxfile = os.path.join(data_root, + 'baseline/conformance/c0p0_11.pgx') + pgxdata = read_pgx(pgxfile) + + np.testing.assert_array_equal(jpdata, pgxdata) + + @unittest.skip("fprintf stderr output in r2343.") + def test_ETS_C0P0_p0_12_j2k(self): + jfile = os.path.join(data_root, 'input/conformance/p0_12.j2k') + jp2k = Jp2k(jfile) + jpdata = jp2k.read(reduce=0) + + pgxfile = os.path.join(data_root, + 'baseline/conformance/c0p0_12.pgx') + pgxdata = read_pgx(pgxfile) + + np.testing.assert_array_equal(jpdata, pgxdata) + + def test_ETS_C0P0_p0_16_j2k(self): + jfile = os.path.join(data_root, 'input/conformance/p0_16.j2k') + jp2k = Jp2k(jfile) + jpdata = jp2k.read(reduce=0) + + pgxfile = os.path.join(data_root, + 'baseline/conformance/c0p0_16.pgx') + pgxdata = read_pgx(pgxfile) + + np.testing.assert_array_equal(jpdata, pgxdata) + + def test_ETS_C0P1_p1_01_j2k(self): + jfile = os.path.join(data_root, 'input/conformance/p1_01.j2k') + jp2k = Jp2k(jfile) + jpdata = jp2k.read(reduce=0) + + pgxfile = os.path.join(data_root, + 'baseline/conformance/c0p1_01.pgx') + pgxdata = read_pgx(pgxfile) + + np.testing.assert_array_equal(jpdata, pgxdata) + + def test_ETS_C1P0_p0_01_j2k(self): + jfile = os.path.join(data_root, 'input/conformance/p0_01.j2k') + jp2k = Jp2k(jfile) + jpdata = jp2k.read(reduce=0) + + pgxfile = os.path.join(data_root, 'baseline/conformance/c1p0_01_0.pgx') + pgxdata = read_pgx(pgxfile) + + np.testing.assert_array_equal(jpdata, pgxdata) + + def test_ETS_C1P0_p0_02_j2k(self): + jfile = os.path.join(data_root, 'input/conformance/p0_02.j2k') + with warnings.catch_warnings(): + # There's a 0xff30 marker segment. Not illegal, but we don't + # really know what to do with it. Just ignore. + warnings.simplefilter("ignore") + jp2k = Jp2k(jfile) + jpdata = jp2k.read(reduce=0) + + pgxfile = os.path.join(data_root, 'baseline/conformance/c1p0_02_0.pgx') + pgxdata = read_pgx(pgxfile) + + np.testing.assert_array_equal(jpdata, pgxdata) + + def test_ETS_C1P0_p0_03_j2k(self): + jfile = os.path.join(data_root, 'input/conformance/p0_03.j2k') + jp2k = Jp2k(jfile) + jpdata = jp2k.read(reduce=0) + + pgxfile = os.path.join(data_root, 'baseline/conformance/c1p0_03_0.pgx') + pgxdata = read_pgx(pgxfile) + + np.testing.assert_array_equal(jpdata, pgxdata) + + def test_ETS_C1P0_p0_04_j2k(self): + jfile = os.path.join(data_root, 'input/conformance/p0_04.j2k') + jp2k = Jp2k(jfile) + jpdata = jp2k.read(reduce=0) + + pgxfile = os.path.join(data_root, 'baseline/conformance/c1p0_04_0.pgx') + pgxdata = read_pgx(pgxfile) + self.assertTrue(peak_tolerance(jpdata[:, :, 0], pgxdata) < 5) + self.assertTrue(mse(jpdata[:, :, 0], pgxdata) < 0.776) + + pgxfile = os.path.join(data_root, 'baseline/conformance/c1p0_04_1.pgx') + pgxdata = read_pgx(pgxfile) + self.assertTrue(peak_tolerance(jpdata[:, :, 1], pgxdata) < 4) + self.assertTrue(mse(jpdata[:, :, 1], pgxdata) < 0.626) + + pgxfile = os.path.join(data_root, 'baseline/conformance/c1p0_04_2.pgx') + pgxdata = read_pgx(pgxfile) + self.assertTrue(peak_tolerance(jpdata[:, :, 2], pgxdata) < 6) + self.assertTrue(mse(jpdata[:, :, 2], pgxdata) < 1.07) + + def test_ETS_C1P0_p0_08_j2k(self): + jfile = os.path.join(data_root, 'input/conformance/p0_08.j2k') + jp2k = Jp2k(jfile) + jpdata = jp2k.read(reduce=1) + + pgxfile = os.path.join(data_root, 'baseline/conformance/c1p0_08_0.pgx') + pgxdata = read_pgx(pgxfile) + np.testing.assert_array_equal(jpdata[:, :, 0], pgxdata) + + pgxfile = os.path.join(data_root, 'baseline/conformance/c1p0_08_1.pgx') + pgxdata = read_pgx(pgxfile) + np.testing.assert_array_equal(jpdata[:, :, 1], pgxdata) + + pgxfile = os.path.join(data_root, 'baseline/conformance/c1p0_08_2.pgx') + pgxdata = read_pgx(pgxfile) + np.testing.assert_array_equal(jpdata[:, :, 2], pgxdata) + + def test_ETS_C1P0_p0_09_j2k(self): + jfile = os.path.join(data_root, 'input/conformance/p0_09.j2k') + jp2k = Jp2k(jfile) + jpdata = jp2k.read(reduce=0) + + pgxfile = os.path.join(data_root, 'baseline/conformance/c1p0_09_0.pgx') + pgxdata = read_pgx(pgxfile) + np.testing.assert_array_equal(jpdata, pgxdata) + + def test_ETS_C1P0_p0_10_j2k(self): + jfile = os.path.join(data_root, 'input/conformance/p0_10.j2k') + jp2k = Jp2k(jfile) + with warnings.catch_warnings(): + # This file has an invalid ICC profile + warnings.simplefilter("ignore") + jpdata = jp2k.read(reduce=0) + + pgxfile = os.path.join(data_root, 'baseline/conformance/c1p0_10_0.pgx') + pgxdata = read_pgx(pgxfile) + np.testing.assert_array_equal(jpdata[:, :, 0], pgxdata) + + pgxfile = os.path.join(data_root, 'baseline/conformance/c1p0_10_1.pgx') + pgxdata = read_pgx(pgxfile) + np.testing.assert_array_equal(jpdata[:, :, 1], pgxdata) + + pgxfile = os.path.join(data_root, 'baseline/conformance/c1p0_10_2.pgx') + pgxdata = read_pgx(pgxfile) + np.testing.assert_array_equal(jpdata[:, :, 2], pgxdata) + + def test_ETS_C1P0_p0_11_j2k(self): + jfile = os.path.join(data_root, 'input/conformance/p0_11.j2k') + jp2k = Jp2k(jfile) + jpdata = jp2k.read(reduce=0) + + pgxfile = os.path.join(data_root, 'baseline/conformance/c1p0_11_0.pgx') + pgxdata = read_pgx(pgxfile) + np.testing.assert_array_equal(jpdata, pgxdata) + + @unittest.skip("fprintf stderr output in r2343.") + def test_ETS_C1P0_p0_12_j2k(self): + jfile = os.path.join(data_root, 'input/conformance/p0_12.j2k') + jp2k = Jp2k(jfile) + jpdata = jp2k.read(reduce=0) + + pgxfile = os.path.join(data_root, 'baseline/conformance/c1p0_12_0.pgx') + pgxdata = read_pgx(pgxfile) + np.testing.assert_array_equal(jpdata, pgxdata) + + @unittest.skip("fprintf stderr output in r2343.") + def test_ETS_C1P0_p0_13_j2k(self): + jfile = os.path.join(data_root, 'input/conformance/p0_13.j2k') + jp2k = Jp2k(jfile) + jpdata = jp2k.read(reduce=0) + + pgxfile = os.path.join(data_root, 'baseline/conformance/c1p0_13_0.pgx') + pgxdata = read_pgx(pgxfile) + np.testing.assert_array_equal(jpdata[:, :, 0], pgxdata) + + pgxfile = os.path.join(data_root, 'baseline/conformance/c1p0_13_1.pgx') + pgxdata = read_pgx(pgxfile) + np.testing.assert_array_equal(jpdata[:, :, 1], pgxdata) + + pgxfile = os.path.join(data_root, 'baseline/conformance/c1p0_13_2.pgx') + pgxdata = read_pgx(pgxfile) + np.testing.assert_array_equal(jpdata[:, :, 2], pgxdata) + + pgxfile = os.path.join(data_root, 'baseline/conformance/c1p0_13_3.pgx') + pgxdata = read_pgx(pgxfile) + np.testing.assert_array_equal(jpdata[:, :, 3], pgxdata) + + def test_ETS_C1P0_p0_14_j2k(self): + jfile = os.path.join(data_root, 'input/conformance/p0_14.j2k') + jp2k = Jp2k(jfile) + jpdata = jp2k.read(reduce=0) + + pgxfile = os.path.join(data_root, 'baseline/conformance/c1p0_14_0.pgx') + pgxdata = read_pgx(pgxfile) + np.testing.assert_array_equal(jpdata[:, :, 0], pgxdata) + + pgxfile = os.path.join(data_root, 'baseline/conformance/c1p0_14_1.pgx') + pgxdata = read_pgx(pgxfile) + np.testing.assert_array_equal(jpdata[:, :, 1], pgxdata) + + pgxfile = os.path.join(data_root, 'baseline/conformance/c1p0_14_2.pgx') + pgxdata = read_pgx(pgxfile) + np.testing.assert_array_equal(jpdata[:, :, 2], pgxdata) + + def test_ETS_C1P0_p0_15_j2k(self): + jfile = os.path.join(data_root, 'input/conformance/p0_15.j2k') + jp2k = Jp2k(jfile) + jpdata = jp2k.read(reduce=0) + + pgxfile = os.path.join(data_root, 'baseline/conformance/c1p0_15_0.pgx') + pgxdata = read_pgx(pgxfile) + np.testing.assert_array_equal(jpdata, pgxdata) + + def test_ETS_C1P0_p0_16_j2k(self): + jfile = os.path.join(data_root, 'input/conformance/p0_16.j2k') + jp2k = Jp2k(jfile) + jpdata = jp2k.read(reduce=0) + + pgxfile = os.path.join(data_root, 'baseline/conformance/c1p0_16_0.pgx') + pgxdata = read_pgx(pgxfile) + np.testing.assert_array_equal(jpdata, pgxdata) + + def test_ETS_C1P1_p1_01_j2k(self): + jfile = os.path.join(data_root, 'input/conformance/p1_01.j2k') + jp2k = Jp2k(jfile) + jpdata = jp2k.read(reduce=0) + + pgxfile = os.path.join(data_root, 'baseline/conformance/c1p1_01_0.pgx') + pgxdata = read_pgx(pgxfile) + np.testing.assert_array_equal(jpdata, pgxdata) + + def test_ETS_C1P1_p1_02_j2k(self): + jfile = os.path.join(data_root, 'input/conformance/p1_02.j2k') + jp2k = Jp2k(jfile) + jpdata = jp2k.read(reduce=0) + + pgxfile = os.path.join(data_root, 'baseline/conformance/c1p1_02_0.pgx') + pgxdata = read_pgx(pgxfile) + self.assertTrue(peak_tolerance(jpdata[:, :, 0], pgxdata) < 5) + self.assertTrue(mse(jpdata[:, :, 0], pgxdata) < 0.765) + + pgxfile = os.path.join(data_root, 'baseline/conformance/c1p1_02_1.pgx') + pgxdata = read_pgx(pgxfile) + self.assertTrue(peak_tolerance(jpdata[:, :, 1], pgxdata) < 4) + self.assertTrue(mse(jpdata[:, :, 1], pgxdata) < 0.616) + + pgxfile = os.path.join(data_root, 'baseline/conformance/c1p1_02_2.pgx') + pgxdata = read_pgx(pgxfile) + self.assertTrue(peak_tolerance(jpdata[:, :, 2], pgxdata) < 6) + self.assertTrue(mse(jpdata[:, :, 2], pgxdata) < 1.051) + + def test_ETS_C1P1_p1_04_j2k(self): + jfile = os.path.join(data_root, 'input/conformance/p1_04.j2k') + jp2k = Jp2k(jfile) + jpdata = jp2k.read() + + pgxfile = os.path.join(data_root, 'baseline/conformance/c1p1_04_0.pgx') + pgxdata = read_pgx(pgxfile) + self.assertTrue(peak_tolerance(jpdata, pgxdata) < 624) + self.assertTrue(mse(jpdata, pgxdata) < 3080) + + @unittest.skip("fprintf stderr output in r2343.") + def test_ETS_C1P1_p1_05_j2k(self): + jfile = os.path.join(data_root, 'input/conformance/p1_05.j2k') + jp2k = Jp2k(jfile) + jpdata = jp2k.read() + + pgxfile = os.path.join(data_root, 'baseline/conformance/c1p1_05_0.pgx') + pgxdata = read_pgx(pgxfile) + self.assertTrue(peak_tolerance(jpdata[:, :, 0], pgxdata) < 40) + self.assertTrue(mse(jpdata[:, :, 0], pgxdata) < 8.458) + + pgxfile = os.path.join(data_root, 'baseline/conformance/c1p1_05_1.pgx') + pgxdata = read_pgx(pgxfile) + self.assertTrue(peak_tolerance(jpdata[:, :, 1], pgxdata) < 40) + self.assertTrue(mse(jpdata[:, :, 1], pgxdata) < 9.816) + + pgxfile = os.path.join(data_root, 'baseline/conformance/c1p1_05_2.pgx') + pgxdata = read_pgx(pgxfile) + self.assertTrue(peak_tolerance(jpdata[:, :, 2], pgxdata) < 40) + self.assertTrue(mse(jpdata[:, :, 2], pgxdata) < 10.154) + + @unittest.skip("fprintf stderr output in r2343.") + def test_ETS_C1P1_p1_06_j2k(self): + jfile = os.path.join(data_root, 'input/conformance/p1_06.j2k') + jp2k = Jp2k(jfile) + jpdata = jp2k.read() + + pgxfile = os.path.join(data_root, 'baseline/conformance/c1p1_06_0.pgx') + pgxdata = read_pgx(pgxfile) + self.assertTrue(peak_tolerance(jpdata[:, :, 0], pgxdata) < 2) + self.assertTrue(mse(jpdata[:, :, 0], pgxdata) < 0.6) + + pgxfile = os.path.join(data_root, 'baseline/conformance/c1p1_06_1.pgx') + pgxdata = read_pgx(pgxfile) + self.assertTrue(peak_tolerance(jpdata[:, :, 1], pgxdata) < 2) + self.assertTrue(mse(jpdata[:, :, 1], pgxdata) < 0.6) + + pgxfile = os.path.join(data_root, 'baseline/conformance/c1p1_06_2.pgx') + pgxdata = read_pgx(pgxfile) + self.assertTrue(peak_tolerance(jpdata[:, :, 2], pgxdata) < 2) + self.assertTrue(mse(jpdata[:, :, 2], pgxdata) < 0.6) + + def test_ETS_JP2_file1(self): + jfile = os.path.join(data_root, 'input/conformance/file1.jp2') + jp2k = Jp2k(jfile) + jpdata = jp2k.read() + self.assertEqual(jpdata.shape, (512, 768, 3)) + + def test_ETS_JP2_file2(self): + jfile = os.path.join(data_root, 'input/conformance/file2.jp2') + jp2k = Jp2k(jfile) + jpdata = jp2k.read() + self.assertEqual(jpdata.shape, (640, 480, 3)) + + def test_ETS_JP2_file4(self): + jfile = os.path.join(data_root, 'input/conformance/file4.jp2') + jp2k = Jp2k(jfile) + jpdata = jp2k.read() + self.assertEqual(jpdata.shape, (512, 768)) + + def test_ETS_JP2_file5(self): + jfile = os.path.join(data_root, 'input/conformance/file5.jp2') + jp2k = Jp2k(jfile) + jpdata = jp2k.read() + self.assertEqual(jpdata.shape, (512, 768, 3)) + + def test_ETS_JP2_file6(self): + jfile = os.path.join(data_root, 'input/conformance/file6.jp2') + jp2k = Jp2k(jfile) + jpdata = jp2k.read() + self.assertEqual(jpdata.shape, (512, 768)) + + def test_ETS_JP2_file7(self): + jfile = os.path.join(data_root, 'input/conformance/file7.jp2') + jp2k = Jp2k(jfile) + jpdata = jp2k.read() + self.assertEqual(jpdata.shape, (640, 480, 3)) + + def test_ETS_JP2_file8(self): + jfile = os.path.join(data_root, 'input/conformance/file8.jp2') + jp2k = Jp2k(jfile) + jpdata = jp2k.read() + self.assertEqual(jpdata.shape, (400, 700)) + + def test_ETS_JP2_file9(self): + jfile = os.path.join(data_root, 'input/conformance/file9.jp2') + jp2k = Jp2k(jfile) + jpdata = jp2k.read() + self.assertEqual(jpdata.shape, (512, 768, 3)) + + def test_NR_DEC_Bretagne2_j2k_1_decode(self): + jfile = os.path.join(data_root, + 'input/nonregression/Bretagne2.j2k') + jp2 = Jp2k(jfile) + data = jp2.read() + self.assertTrue(True) + + def test_NR_DEC__00042_j2k_2_decode(self): + jfile = os.path.join(data_root, + 'input/nonregression/_00042.j2k') + jp2 = Jp2k(jfile) + data = jp2.read() + self.assertTrue(True) + + @unittest.skip("fprintf stderr output in r2343.") + def test_NR_DEC_123_j2c_3_decode(self): + jfile = os.path.join(data_root, + 'input/nonregression/123.j2c') + jp2 = Jp2k(jfile) + data = jp2.read() + self.assertTrue(True) + + @unittest.skipIf(sys.hexversion < 0x03020000, + "Uses features introduced in 3.2.") + def test_NR_DEC_broken_jp2_4_decode(self): + jfile = os.path.join(data_root, + 'input/nonregression/broken.jp2') + with self.assertWarns(UserWarning) as cw: + # colr box has bad length. + jp2 = Jp2k(jfile) + with self.assertRaises(ValueError): + data = jp2.read() + self.assertTrue(True) + + def test_NR_DEC_broken2_jp2_5_decode(self): + jfile = os.path.join(data_root, + 'input/nonregression/broken2.jp2') + with self.assertRaises(IOError): + data = Jp2k(jfile).read() + self.assertTrue(True) + + @unittest.skipIf(sys.hexversion < 0x03020000, + "Uses features introduced in 3.2.") + def test_NR_DEC_broken3_jp2_6_decode(self): + jfile = os.path.join(data_root, + 'input/nonregression/broken3.jp2') + with self.assertWarns(UserWarning) as cw: + # colr box has bad length. + j = Jp2k(jfile) + + with self.assertRaises(ValueError) as ce: + d = j.read() + + def test_NR_DEC_broken4_jp2_7_decode(self): + jfile = os.path.join(data_root, + 'input/nonregression/broken4.jp2') + with self.assertRaises(IOError): + data = Jp2k(jfile).read() + self.assertTrue(True) + + @unittest.skip("fprintf stderr output in r2343.") + def test_NR_DEC_bug_j2c_8_decode(self): + jfile = os.path.join(data_root, + 'input/nonregression/bug.j2c') + data = Jp2k(jfile).read() + self.assertTrue(True) + + def test_NR_DEC_buxI_j2k_9_decode(self): + jfile = os.path.join(data_root, + 'input/nonregression/buxI.j2k') + data = Jp2k(jfile).read() + self.assertTrue(True) + + def test_NR_DEC_buxR_j2k_10_decode(self): + jfile = os.path.join(data_root, + 'input/nonregression/buxR.j2k') + data = Jp2k(jfile).read() + self.assertTrue(True) + + def test_NR_DEC_Cannotreaddatawithnosizeknown_j2k_11_decode(self): + relpath = 'input/nonregression/Cannotreaddatawithnosizeknown.j2k' + jfile = os.path.join(data_root, relpath) + data = Jp2k(jfile).read() + self.assertTrue(True) + + def test_NR_DEC_cthead1_j2k_12_decode(self): + jfile = os.path.join(data_root, + 'input/nonregression/cthead1.j2k') + data = Jp2k(jfile).read() + self.assertTrue(True) + + def test_NR_DEC_CT_Phillips_JPEG2K_Decompr_Problem_j2k_13_decode(self): + relpath = 'input/nonregression/CT_Phillips_JPEG2K_Decompr_Problem.j2k' + jfile = os.path.join(data_root, relpath) + data = Jp2k(jfile).read() + self.assertTrue(True) + + @unittest.skip("fprintf stderr output in r2343.") + def test_NR_DEC_illegalcolortransform_j2k_14_decode(self): + # Stream too short, expected SOT. + jfile = os.path.join(data_root, + 'input/nonregression/illegalcolortransform.j2k') + data = Jp2k(jfile).read() + self.assertTrue(True) + + def test_NR_DEC_j2k32_j2k_15_decode(self): + jfile = os.path.join(data_root, + 'input/nonregression/j2k32.j2k') + data = Jp2k(jfile).read() + self.assertTrue(True) + + def test_NR_DEC_kakadu_v4_4_openjpegv2_broken_j2k_16_decode(self): + relpath = 'input/nonregression/kakadu_v4-4_openjpegv2_broken.j2k' + jfile = os.path.join(data_root, relpath) + with warnings.catch_warnings(): + # This file has an invalid ICC profile + warnings.simplefilter("ignore") + data = Jp2k(jfile).read() + self.assertTrue(True) + + def test_NR_DEC_MarkerIsNotCompliant_j2k_17_decode(self): + jfile = os.path.join(data_root, + 'input/nonregression/MarkerIsNotCompliant.j2k') + data = Jp2k(jfile).read() + self.assertTrue(True) + + def test_NR_DEC_Marrin_jp2_18_decode(self): + jfile = os.path.join(data_root, + 'input/nonregression/Marrin.jp2') + data = Jp2k(jfile).read() + self.assertTrue(True) + + def test_NR_DEC_movie_00000_j2k_20_decode(self): + jfile = os.path.join(data_root, + 'input/nonregression/movie_00000.j2k') + data = Jp2k(jfile).read() + self.assertTrue(True) + + def test_NR_DEC_movie_00001_j2k_21_decode(self): + jfile = os.path.join(data_root, + 'input/nonregression/movie_00001.j2k') + data = Jp2k(jfile).read() + self.assertTrue(True) + + def test_NR_DEC_movie_00002_j2k_22_decode(self): + jfile = os.path.join(data_root, + 'input/nonregression/movie_00002.j2k') + data = Jp2k(jfile).read() + self.assertTrue(True) + + def test_NR_DEC_orb_blue_lin_j2k_j2k_23_decode(self): + jfile = os.path.join(data_root, + 'input/nonregression/orb-blue10-lin-j2k.j2k') + data = Jp2k(jfile).read() + self.assertTrue(True) + + def test_NR_DEC_orb_blue_win_j2k_j2k_24_decode(self): + jfile = os.path.join(data_root, + 'input/nonregression/orb-blue10-win-j2k.j2k') + data = Jp2k(jfile).read() + self.assertTrue(True) + + def test_NR_DEC_orb_blue_lin_jp2_25_decode(self): + jfile = os.path.join(data_root, + 'input/nonregression/orb-blue10-lin-jp2.jp2') + with warnings.catch_warnings(): + # This file has an invalid ICC profile + warnings.simplefilter("ignore") + data = Jp2k(jfile).read() + self.assertTrue(True) + + def test_NR_DEC_orb_blue_win_jp2_26_decode(self): + jfile = os.path.join(data_root, + 'input/nonregression/orb-blue10-win-jp2.jp2') + data = Jp2k(jfile).read() + self.assertTrue(True) + + def test_NR_DEC_relax_jp2_27_decode(self): + jfile = os.path.join(data_root, + 'input/nonregression/relax.jp2') + data = Jp2k(jfile).read() + self.assertTrue(True) + + def test_NR_DEC_test_lossless_j2k_28_decode(self): + jfile = os.path.join(data_root, + 'input/nonregression/test_lossless.j2k') + data = Jp2k(jfile).read() + self.assertTrue(True) + + def test_NR_DEC_issue104_jpxstream_jp2_33_decode(self): + jfile = os.path.join(data_root, + 'input/nonregression/issue104_jpxstream.jp2') + data = Jp2k(jfile).read() + self.assertTrue(True) + + def test_NR_DEC_file_409752_jp2_40_decode(self): + jfile = os.path.join(data_root, 'input/nonregression/file409752.jp2') + j = Jp2k(jfile) + with self.assertRaises(RuntimeError) as ce: + data = j.read() + if __name__ == "__main__": unittest.main() diff --git a/glymur/test/test_opj_suite_neg.py b/glymur/test/test_opj_suite_neg.py index 5754e16..78d6154 100644 --- a/glymur/test/test_opj_suite_neg.py +++ b/glymur/test/test_opj_suite_neg.py @@ -65,8 +65,8 @@ def read_image(infile): class TestSuiteNegative(unittest.TestCase): def setUp(self): - self.jp2file = pkg_resources.resource_filename(glymur.__name__, - "data/nemo.jp2") + self.jp2file = glymur.data.nemo() + self.j2kfile = glymur.data.goodstuff() def tearDown(self): pass @@ -156,8 +156,8 @@ class TestSuiteNegative(unittest.TestCase): def test_precinct_size_not_multiple_of_two(self): # Seems like precinct sizes should be powers of two. - ifile = Jp2k(self.jp2file) - data = ifile.read(reduce=3) + ifile = Jp2k(self.j2kfile) + data = ifile.read(reduce=2) with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: ofile = Jp2k(tfile.name, 'wb') with self.assertRaises(IOError) as ce: @@ -165,8 +165,8 @@ class TestSuiteNegative(unittest.TestCase): def test_codeblock_size_not_multiple_of_two(self): # Seems like code block sizes should be powers of two. - ifile = Jp2k(self.jp2file) - data = ifile.read(reduce=3) + ifile = Jp2k(self.j2kfile) + data = ifile.read(reduce=2) with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: ofile = Jp2k(tfile.name, 'wb') with self.assertRaises(IOError) as ce: @@ -175,8 +175,8 @@ class TestSuiteNegative(unittest.TestCase): def test_codeblock_size_with_precinct_size(self): # Seems like code block sizes should never exceed half that of # precinct size. - ifile = Jp2k(self.jp2file) - data = ifile.read(reduce=3) + ifile = Jp2k(self.j2kfile) + data = ifile.read(reduce=2) with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: ofile = Jp2k(tfile.name, 'wb') with self.assertRaises(IOError) as ce: diff --git a/glymur/test/test_printing.py b/glymur/test/test_printing.py index 4090a2d..8b2e033 100644 --- a/glymur/test/test_printing.py +++ b/glymur/test/test_printing.py @@ -34,7 +34,7 @@ class TestPrintingNeedsLib(unittest.TestCase): with tempfile.NamedTemporaryFile(suffix='.jp2', delete=False) as tfile: cls._plain_nemo_file = tfile.name ijfile = Jp2k(jp2file) - data = ijfile.read(reduce=3) + data = ijfile.read(reduce=1) ojfile = Jp2k(cls._plain_nemo_file, 'wb') ojfile.write(data) @@ -43,8 +43,9 @@ class TestPrintingNeedsLib(unittest.TestCase): os.unlink(cls._plain_nemo_file) def setUp(self): - self.jp2file = pkg_resources.resource_filename(glymur.__name__, - "data/nemo.jp2") + self.jp2file = glymur.data.nemo() + self.j2kfile = glymur.data.goodstuff() + # Save sys.stdout. self.stdout = sys.stdout sys.stdout = StringIO() @@ -57,7 +58,7 @@ class TestPrintingNeedsLib(unittest.TestCase): " Compatibility: ['jp2 ']", 'JP2 Header Box (jp2h) @ (32, 45)', ' Image Header Box (ihdr) @ (40, 22)', - ' Size: [182 324 3]', + ' Size: [728 1296 3]', ' Bitdepth: 8', ' Signed: False', ' Compression: wavelet', @@ -66,15 +67,15 @@ class TestPrintingNeedsLib(unittest.TestCase): ' Method: enumerated colorspace', ' Precedence: 0', ' Colorspace: sRGB', - 'Contiguous Codestream Box (jp2c) @ (77, 112814)', + 'Contiguous Codestream Box (jp2c) @ (77, 1632355)', ' Main header:', ' SOC marker segment @ (85, 0)', ' SIZ marker segment @ (87, 47)', ' Profile: 2', - ' Reference Grid Height, Width: (182 x 324)', + ' Reference Grid Height, Width: (728 x 1296)', ' Vertical, Horizontal Reference Grid Offset: ' + '(0 x 0)', - ' Reference Tile Height, Width: (182 x 324)', + ' Reference Tile Height, Width: (728 x 1296)', ' Vertical, Horizontal Reference Tile Offset: ' + '(0 x 0)', ' Bitdepth: (8, 8, 8)', @@ -121,7 +122,7 @@ class TestPrintingNeedsLib(unittest.TestCase): def test_asoc_label_box(self): # Construct a fake file with an asoc and a label box, as # OpenJPEG doesn't have such a file. - data = glymur.Jp2k(self.jp2file).read(reduce=3) + data = glymur.Jp2k(self.jp2file).read(reduce=1) with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: j = glymur.Jp2k(tfile.name, 'wb') j.write(data) @@ -177,7 +178,7 @@ class TestPrintingNeedsLib(unittest.TestCase): lst = actual.split('\n') lst = lst[1:] actual = '\n'.join(lst) - + self.maxDiff = None self.assertEqual(actual, self.expectedPlain) def test_entire_file(self): @@ -209,15 +210,15 @@ class TestPrinting(unittest.TestCase): def test_COC_segment(self): j = glymur.Jp2k(self.jp2file) codestream = j.get_codestream(header_only=False) - print(codestream.segment[5]) + print(codestream.segment[6]) actual = sys.stdout.getvalue().strip() - lines = ['COC marker segment @ (3233, 9)', + lines = ['COC marker segment @ (3260, 9)', ' Associated component: 1', ' Coding style for this component: ' + 'Entropy coder, PARTITION = 0', ' Coding style parameters:', - ' Number of resolutions: 6', + ' Number of resolutions: 2', ' Code block height, width: (64 x 64)', ' Wavelet transform: 5-3 reversible', ' Code block context:', @@ -230,6 +231,7 @@ class TestPrinting(unittest.TestCase): ' Segmentation symbols: False'] expected = '\n'.join(lines) + self.maxDiff = None self.assertEqual(actual, expected) def test_COD_segment(self): @@ -245,10 +247,10 @@ class TestPrinting(unittest.TestCase): ' EPH marker segments: False', ' Coding style parameters:', ' Progression order: LRCP', - ' Number of layers: 3', + ' Number of layers: 2', ' Multiple component transformation usage: ' + 'reversible', - ' Number of resolutions: 6', + ' Number of resolutions: 2', ' Code block height, width: (64 x 64)', ' Wavelet transform: 5-3 reversible', ' Precinct size: default, 2^15 x 2^15', @@ -392,7 +394,7 @@ class TestPrinting(unittest.TestCase): print(codestream.segment[-1]) actual = sys.stdout.getvalue().strip() - lines = ['EOC marker segment @ (1136552, 0)'] + lines = ['EOC marker segment @ (1135421, 0)'] expected = '\n'.join(lines) self.assertEqual(actual, expected) @@ -476,15 +478,13 @@ class TestPrinting(unittest.TestCase): def test_QCC_segment(self): j = glymur.Jp2k(self.jp2file) codestream = j.get_codestream(header_only=False) - print(codestream.segment[6]) + print(codestream.segment[7]) actual = sys.stdout.getvalue().strip() - lines = ['QCC marker segment @ (3244, 20)', + lines = ['QCC marker segment @ (3271, 8)', ' Associated Component: 1', ' Quantization style: no quantization, 2 guard bits', - ' Step size: [(0, 8), (0, 9), (0, 9), (0, 10), (0, 9), ' - + '(0, 9), (0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), ' - + '(0, 10), (0, 9), (0, 9), (0, 10)]'] + ' Step size: [(0, 8), (0, 9), (0, 9), (0, 10)]'] expected = '\n'.join(lines) self.assertEqual(actual, expected) @@ -495,11 +495,9 @@ class TestPrinting(unittest.TestCase): print(codestream.segment[3]) actual = sys.stdout.getvalue().strip() - lines = ['QCD marker segment @ (3200, 19)', + lines = ['QCD marker segment @ (3200, 7)', ' Quantization style: no quantization, 2 guard bits', - ' Step size: [(0, 8), (0, 9), (0, 9), (0, 10), (0, 9), ' - + '(0, 9), (0, 10), (0, 9), (0, 9), (0, 10), (0, 9), ' - + '(0, 9), (0, 10), (0, 9), (0, 9), (0, 10)]'] + ' Step size: [(0, 8), (0, 9), (0, 9), (0, 10)]'] expected = '\n'.join(lines) self.assertEqual(actual, expected) @@ -514,7 +512,7 @@ class TestPrinting(unittest.TestCase): ' Profile: 2', ' Reference Grid Height, Width: (1456 x 2592)', ' Vertical, Horizontal Reference Grid Offset: (0 x 0)', - ' Reference Tile Height, Width: (512 x 512)', + ' Reference Tile Height, Width: (1456 x 2592)', ' Vertical, Horizontal Reference Tile Offset: (0 x 0)', ' Bitdepth: (8, 8, 8)', ' Signed: (False, False, False)', @@ -537,26 +535,27 @@ class TestPrinting(unittest.TestCase): def test_SOD_segment(self): j = glymur.Jp2k(self.jp2file) codestream = j.get_codestream(header_only=False) - print(codestream.segment[9]) + print(codestream.segment[10]) actual = sys.stdout.getvalue().strip() - lines = ['SOD marker segment @ (3299, 0)'] + lines = ['SOD marker segment @ (3302, 0)'] expected = '\n'.join(lines) self.assertEqual(actual, expected) def test_SOT_segment(self): j = glymur.Jp2k(self.jp2file) codestream = j.get_codestream(header_only=False) - print(codestream.segment[4]) + print(codestream.segment[5]) actual = sys.stdout.getvalue().strip() - lines = ['SOT marker segment @ (3221, 10)', + lines = ['SOT marker segment @ (3248, 10)', ' Tile part index: 0', - ' Tile part length: 78629', + ' Tile part length: 1132173', ' Tile part instance: 0', ' Number of tile parts: 1'] expected = '\n'.join(lines) + self.maxDiff = None self.assertEqual(actual, expected) @unittest.skipIf(data_root is None, @@ -606,7 +605,7 @@ class TestPrinting(unittest.TestCase): ' Profile: 2', ' Reference Grid Height, Width: (1456 x 2592)', ' Vertical, Horizontal Reference Grid Offset: (0 x 0)', - ' Reference Tile Height, Width: (512 x 512)', + ' Reference Tile Height, Width: (1456 x 2592)', ' Vertical, Horizontal Reference Tile Offset: (0 x 0)', ' Bitdepth: (8, 8, 8)', ' Signed: (False, False, False)', @@ -619,10 +618,10 @@ class TestPrinting(unittest.TestCase): ' EPH marker segments: False', ' Coding style parameters:', ' Progression order: LRCP', - ' Number of layers: 3', + ' Number of layers: 2', ' Multiple component transformation usage: ' + 'reversible', - ' Number of resolutions: 6', + ' Number of resolutions: 2', ' Code block height, width: (64 x 64)', ' Wavelet transform: 5-3 reversible', ' Precinct size: default, 2^15 x 2^15', @@ -634,13 +633,12 @@ class TestPrinting(unittest.TestCase): ' Vertically stripe causal context: False', ' Predictable termination: False', ' Segmentation symbols: False', - ' QCD marker segment @ (3200, 19)', + ' QCD marker segment @ (3200, 7)', ' Quantization style: no quantization, ' + '2 guard bits', - ' Step size: [(0, 8), (0, 9), (0, 9), ' - + '(0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), ' - + '(0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), ' - + '(0, 10)]'] + ' Step size: [(0, 8), (0, 9), (0, 9), (0, 10)]', + ' CME marker segment @ (3209, 37)', + ' "Created by OpenJPEG version 2.0.0"'] expected = '\n'.join(lst) self.assertEqual(actual, expected) @@ -783,6 +781,8 @@ class TestPrinting(unittest.TestCase): expected = '\n'.join(lines) self.assertEqual(actual, expected) + @unittest.skipIf(os.name == "nt", + "Problems using NamedTemporaryFile on windows.") def test_less_common_boxes(self): with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: with open(self.jp2file, 'rb') as ifile: diff --git a/release.txt b/release.txt new file mode 100644 index 0000000..c553960 --- /dev/null +++ b/release.txt @@ -0,0 +1,22 @@ +| OS | Python 2.7 | Python 3.3 | Notes | ++------------+------------+------------+----------------------------------+ +| Windows | X | | Python(xy) with OpenJPEG 1.5.1. | +| | | | At least 25 tests should pass | ++------------+------------+------------+----------------------------------+ +| Mac | X | | MacPorts with both OpenJPEG 1.5.1| +| | | | and OpenJPEG svn. Maximum number| +| | | | of tests should pass. | ++------------+------------+------------+----------------------------------+ +| Mac | | X | MacPorts with both OpenJPEG 1.5.1| +| | | | and OpenJPEG svn. Maximum number| +| | | | of tests should pass. | ++------------+------------+------------+----------------------------------+ +| Raspberry | X | | Ships with only 1.3. Should | +| | | | error out gracefully. | ++------------+------------+------------+----------------------------------+ +| Fedora 17 | X | | Ships with 1.4. Should error out| +| | | | gracefully. | ++------------+------------+------------+----------------------------------+ +| Fedora 18 | | X | Ships with 1.5.1. Some tests | +| | | | should pass. | ++------------+------------+------------+----------------------------------+