Merge branch 'release-0.2.0'

This commit is contained in:
jevans 2013-07-11 19:03:54 -04:00
commit fc07117faa
32 changed files with 6351 additions and 4883 deletions

View file

@ -1,3 +1,5 @@
Jul 11, 2013 - v0.2.0 Support for Python 2.7 on windows, OpenJPEG 1.5.1.
Jun 27, 2013 - v0.1.10 Can wrap codestreams in custom JP2 jackets. Exposing Jun 27, 2013 - v0.1.10 Can wrap codestreams in custom JP2 jackets. Exposing
parameter to specify multi component transform. Added a raw codestream parameter to specify multi component transform. Added a raw codestream
file. file.

View file

@ -1,3 +1,4 @@
include *.txt *.md include *.txt *.md
prune build prune build
exclude readthedocs-pip-requirements.txt exclude readthedocs-pip-requirements.txt
exclude release.txt

View file

@ -78,7 +78,7 @@ copyright = u'2013, John Evans'
# The short X.Y version. # The short X.Y version.
version = '0.1' version = '0.1'
# The full version, including alpha/beta/rc tags. # The full version, including alpha/beta/rc tags.
release = '0.1.10' release = '0.2.0'
# The language for content autogenerated by Sphinx. Refer to documentation # The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages. # for a list of supported languages.

View file

@ -1,16 +1,57 @@
----------------------------------------------------- ----------------------------------
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 (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
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
This 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**.
You may also include a line for the version 1.5.1 library if you have it installed
in a non-standard place, i.e. ::
[library]
openjp2: /opt/openjp2-svn/lib/libopenjp2.so
openjpeg: /not/the/usual/location/lib/libopenjpeg.so
'''''''''''''''''''''''''''''''''''''''''''
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 platform-specific instructions on running as many tests as possible or wish to
use your system's package manager to install as many required use your system's package manager to install as many required
packages/RPMs/ports/whatever without going through pip. Otherwise go on to packages/RPMs/ports/whatever without going through pip.
the next page.
''''''''
Platform
''''''''
Mac OS X Mac OS X
-------- --------
@ -27,13 +68,29 @@ additionally be installed:
* py33-scikit-image and either py33-Pillow or freeimage * py33-scikit-image and either py33-Pillow or freeimage
* py33-matplotlib and py33-Pillow * py33-matplotlib and py33-Pillow
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).
Linux Linux
----- -----
Fedora 19
'''''''''
Fedora 18 ships with Python 3.3 and all the necessary RPMs are available to
run the maximum number of tests.
* python3
* python3-numpy
* python3-setuptools
* python3-matplotlib (for running tests)
* python3-matplotlib-tk (or whichever matplotlib backend you prefer)
* python3-pillow (for running tests)
Fedora 18 Fedora 18
''''''''' '''''''''
Fedora 18 ships with Python 3.3, so all the necessary RPMs are available to Fedora 18 ships with Python 3.3 and the following RPMs are available to
meet the minimal set of requirements. meet the minimal set of requirements for running glymur.
* python3 * python3
* python3-numpy * python3-numpy
@ -56,31 +113,10 @@ repositories::
$ pip-python3 install Pillow --user $ pip-python3 install Pillow --user
$ export PYTHONPATH=$HOME/.local/lib/python3.3/site-packages:$PYTHONPATH $ export PYTHONPATH=$HOME/.local/lib/python3.3/site-packages:$PYTHONPATH
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.
Additional required OS packages include::
* python-pip
* python-pkg-resources
* python-mock
You must install contextlib2 via pip, and then you can run at least
a minimal number of tests. To attempt to run more of the tests,
install the following debs::
* python-dev
* python-matplotlib
and then install Pillow via pip. The tests take about 30 minutes to run, with
one unexpected failure as of the time of writing.
Fedora 17 Fedora 17
''''''''' '''''''''
Fedora 17 ships with Python 3.2 and 2.7, so these steps detail working with Fedora 17 ships with Python 3.2 and 2.7, but OpenJPEG is only at version 1.4,
2.7. so these steps detail working with Python 2.7 and the svn version of OpenJPEG.
Required RPMs include:: Required RPMs include::
@ -111,19 +147,23 @@ it was installable via pip::
Windows 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 and mock as well. Both 1.5.1 and the svn development versions of
openjpeg work.
''''''' '''''''
Testing Testing
''''''' '''''''
If you wish to run the tests (strongly suggested :-), you can either run them If you wish to run the tests (strongly recommended :-), you can either run them
from within python as follows ... :: from within python as follows ... ::
>>> import glymur >>> import glymur
>>> glymur.runtests() >>> glymur.runtests()
or from the unix command line. :: or from the command line. ::
$ cd /to/where/you/unpacked/glymur $ cd /to/where/you/unpacked/glymur
$ python -m unittest discover $ python -m unittest discover

View file

@ -6,13 +6,13 @@ How do I...?
Read the lowest resolution thumbnail? Read the lowest resolution thumbnail?
===================================== =====================================
Printing the Jp2k object should reveal the number of resolutions (look in the Printing the Jp2k object should reveal the number of resolutions (look in the
COD segment section), but you can take a shortcut by supplying -1 as the reduce COD segment section), but you can take a shortcut by supplying -1 as the
level. :: resolution level. ::
>>> import glymur >>> import glymur
>>> file = glymur.data.nemo() >>> file = glymur.data.nemo()
>>> j = glymur.Jp2k(file) >>> j = glymur.Jp2k(file)
>>> thumbnail = j.read(reduce=-1) >>> thumbnail = j.read(rlevel=-1)
Display metadata? Display metadata?
================= =================

View file

@ -2,14 +2,9 @@
Glymur: a Python interface for JPEG 2000 Glymur: a Python interface for JPEG 2000
---------------------------------------- ----------------------------------------
**Glymur** contains a Python interface to the OpenJPEG library **Glymur** is an interface to the OpenJPEG library
which allows linux and mac users to read and write JPEG 2000 files. For more which allows one to read and write JPEG 2000 files from within Python.
information about OpenJPEG, please consult http://www.openjpeg.org. Glymur Glymur supports both reading and writing of JPEG 2000 images. Writing
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
JPEG 2000 images is currently limited to images that can fit in memory, JPEG 2000 images is currently limited to images that can fit in memory,
however. however.
@ -23,34 +18,18 @@ Glymur works on Python 2.7 and 3.3. Python 3.3 is strongly recommended.
OpenJPEG Installation OpenJPEG Installation
===================== =====================
OpenJPEG must be built as a shared library. In addition, you The OpenJPEG library version must be either 1.5.1 or the trunk/development
currently must compile OpenJPEG from the developmental source that version of OpenJPEG. Version 2.0.0 or versions earlier than 1.5.0
you can retrieve via subversion. As of this time of writing, svn are not supported. Furthermore, the 1.5.x version of OpenJPEG is
revision 2345 works. You should download the test data for the purpose currently only utilized for read-only purposes. For more information
of configuring and running OpenJPEG's test suite, check their instructions for about OpenJPEG, please consult http://www.openjpeg.org.
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 If you use MacPorts on the mac or if you have a sufficiently recent version of
$ export OPJ_DATA_ROOT=`pwd`/data 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
Earlier versions of OpenJPEG through the 2.0 official release will **NOT** 2000 images. If you use windows, I suggest using the 1.5.1 windows installer
work and are not supported. provided to you by the OpenJPEG folks at
https://code.google.com/p/openjpeg/downloads/list .
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**.
Glymur Installation Glymur Installation
=================== ===================
@ -76,5 +55,6 @@ You can run the tests from within python as follows::
>>> import glymur >>> import glymur
>>> glymur.runtests() >>> 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
tests fail. are relying on OpenJPEG 1.5.1. But the important thing, though, is whether or
not any tests fail.

File diff suppressed because it is too large Load diff

View file

@ -1,3 +1,5 @@
"""Core definitions to be shared amongst the modules.
"""
# Progression order # Progression order
LRCP = 0 LRCP = 0
RLCP = 1 RLCP = 1
@ -5,44 +7,21 @@ RPCL = 2
PCRL = 3 PCRL = 3
CPRL = 4 CPRL = 4
_progression_order_display = { PROGRESSION_ORDER = {
LRCP: 'LRCP',
RLCP: 'RLCP',
RPCL: 'RPCL',
PCRL: 'PCRL',
CPRL: 'CPRL'}
progression_order = {
'LRCP': LRCP, 'LRCP': LRCP,
'RLCP': RLCP, 'RLCP': RLCP,
'RPCL': RPCL, 'RPCL': RPCL,
'PCRL': PCRL, 'PCRL': PCRL,
'CPRL': CPRL} 'CPRL': CPRL}
WAVELET_TRANSFORM_9x7_IRREVERSIBLE = 0 WAVELET_XFORM_9X7_IRREVERSIBLE = 0
WAVELET_TRANSFORM_5x3_REVERSIBLE = 1 WAVELET_XFORM_5X3_REVERSIBLE = 1
_wavelet_transform_display = {
WAVELET_TRANSFORM_9x7_IRREVERSIBLE: '9-7 irreversible',
WAVELET_TRANSFORM_5x3_REVERSIBLE: '5-3 reversible'}
ENUMERATED_COLORSPACE = 1 ENUMERATED_COLORSPACE = 1
RESTRICTED_ICC_PROFILE = 2 RESTRICTED_ICC_PROFILE = 2
ANY_ICC_PROFILE = 3 ANY_ICC_PROFILE = 3
VENDOR_COLOR_METHOD = 4 VENDOR_COLOR_METHOD = 4
_method_display = {
ENUMERATED_COLORSPACE: 'enumerated colorspace',
RESTRICTED_ICC_PROFILE: 'restricted ICC profile',
ANY_ICC_PROFILE: 'any ICC profile',
VENDOR_COLOR_METHOD: 'vendor color method'}
_ = {1: 'accurately represents correct colorspace definition',
2: 'approximates correct colorspace definition, exceptional quality',
3: 'approximates correct colorspace definition, reasonable quality',
4: 'approximates correct colorspace definition, poor quality'}
_approximation_display = _
# Registration values for comment markers. # Registration values for comment markers.
RCME_BINARY = 0 # binary value comments RCME_BINARY = 0 # binary value comments
RCME_ISO_8859_1 = 1 # comments in latin-1 codec RCME_ISO_8859_1 = 1 # comments in latin-1 codec
@ -55,7 +34,7 @@ YCC = 18
E_SRGB = 20 E_SRGB = 20
ROMM_RGB = 21 ROMM_RGB = 21
_colorspace_map_display = { _COLORSPACE_MAP_DISPLAY = {
CMYK: 'CMYK', CMYK: 'CMYK',
SRGB: 'sRGB', SRGB: 'sRGB',
GREYSCALE: 'greyscale', GREYSCALE: 'greyscale',
@ -68,7 +47,7 @@ _COLOR = 0
_OPACITY = 1 _OPACITY = 1
_PRE_MULTIPLIED_OPACITY = 2 _PRE_MULTIPLIED_OPACITY = 2
_UNSPECIFIED = 65535 _UNSPECIFIED = 65535
_color_type_map_display = { _COLOR_TYPE_MAP_DISPLAY = {
_COLOR: 'color', _COLOR: 'color',
_OPACITY: 'opacity', _OPACITY: 'opacity',
_PRE_MULTIPLIED_OPACITY: 'pre-multiplied opacity', _PRE_MULTIPLIED_OPACITY: 'pre-multiplied opacity',
@ -81,49 +60,15 @@ BLUE = 3
GREY = 1 GREY = 1
# enumerated color channel associations # enumerated color channel associations
_rgb_colorspace = {"R": 1, "G": 2, "B": 3} _COLORSPACE = {SRGB: {"R": 1, "G": 2, "B": 3},
_greyscale_colorspace = {"Y": 1} GREYSCALE: {"Y": 1},
_ycbcr_colorspace = {"Y": 1, "Cb": 2, "Cr": 3} YCC: {"Y": 1, "Cb": 2, "Cr": 3},
_colorspace = {SRGB: _rgb_colorspace, E_SRGB: {"R": 1, "G": 2, "B": 3},
GREYSCALE: _greyscale_colorspace, ROMM_RGB: {"R": 1, "G": 2, "B": 3}}
YCC: _ycbcr_colorspace,
E_SRGB: _rgb_colorspace,
ROMM_RGB: _rgb_colorspace}
# How to display the codestream profile. # How to display the codestream profile.
_capabilities_display = { _CAPABILITIES_DISPLAY = {
0: '2', 0: '2',
1: '0', 1: '0',
2: '1', 2: '1',
3: '3'} 3: '3'}
# Reader requirements
RREQ_UNRESTRICTED_JPEG2000_PART_1 = 5
RREQ_UNRESTRICTED_JPEG2000_PART_2 = 6
RREQ_CMYK_ENUMERATED_COLORSPACE = 55
RREQ_CMYK_ENUMERATED_COLORSPACE = 55
RREQ_E_SRGB_ENUMERATED_COLORSPACE = 60
RREQ_ROMM_RGB_ENUMERATED_COLORSPACE = 61
_reader_requirements_display = {
0: 'File not completely understood',
1: 'Deprecated',
2: 'Contains multiple composition layers',
3: 'Deprecated',
4: 'JPEG 2000 Part 1 Profile 1 codestream',
RREQ_UNRESTRICTED_JPEG2000_PART_1:
'Unrestricted JPEG 2000 Part 1 codestream, ITU-T Rec. T.800 '
+ '| ISO/IEC 15444-1',
RREQ_UNRESTRICTED_JPEG2000_PART_2:
'Unrestricted JPEG 2000 Part 2 codestream',
7: 'JPEG codestream as defined in ISO/IEC 10918-1',
8: 'Deprecated',
9: 'Non-premultiplied opacity channel',
10: 'Premultiplied opacity channel',
12: 'Deprecated',
18: 'Deprecated',
43: '(Deprecated) compositing layer uses restricted ICC profile',
44: 'Compositing layer uses Any ICC profile',
45: 'Deprecated',
RREQ_CMYK_ENUMERATED_COLORSPACE: 'CMYK enumerated colorspace',
RREQ_E_SRGB_ENUMERATED_COLORSPACE: 'e-sRGB enumerated colorspace',
RREQ_ROMM_RGB_ENUMERATED_COLORSPACE: 'ROMM_RGB enumerated colorspace'}

View file

@ -17,8 +17,8 @@ def nemo():
file : str file : str
Platform-independent path to nemo.jp2. Platform-independent path to nemo.jp2.
""" """
file = pkg_resources.resource_filename(__name__, "nemo.jp2") filename = pkg_resources.resource_filename(__name__, "nemo.jp2")
return file return filename
def goodstuff(): def goodstuff():
@ -29,5 +29,5 @@ def goodstuff():
file : str file : str
Platform-independent path to goodstuff.j2k. Platform-independent path to goodstuff.j2k.
""" """
file = pkg_resources.resource_filename(__name__, "goodstuff.j2k") filename = pkg_resources.resource_filename(__name__, "goodstuff.j2k")
return file return filename

Binary file not shown.

File diff suppressed because it is too large Load diff

View file

@ -2,6 +2,9 @@
License: MIT License: MIT
""" """
# pylint: disable=C0302
import sys import sys
if sys.hexversion >= 0x03030000: if sys.hexversion >= 0x03030000:
from contextlib import ExitStack from contextlib import ExitStack
@ -16,14 +19,23 @@ import warnings
import numpy as np import numpy as np
from .codestream import Codestream from .codestream import Codestream
from .core import * from .core import SRGB
from .jp2box import * from .core import GREYSCALE
from .lib import openjp2 as opj2 from .core import PROGRESSION_ORDER
from .jp2box import Jp2kBox
from .jp2box import JPEG2000SignatureBox
from .jp2box import FileTypeBox
from .jp2box import JP2HeaderBox
from .jp2box import ContiguousCodestreamBox
from .jp2box import ImageHeaderBox
from .jp2box import ColourSpecificationBox
from .lib import _openjpeg as _opj
from .lib import _openjp2 as _opj2
_cspace_map = {'rgb': opj2._CLRSPC_SRGB, _COLORSPACE_MAP = {'rgb': _opj2.CLRSPC_SRGB,
'gray': opj2._CLRSPC_GRAY, 'gray': _opj2.CLRSPC_GRAY,
'grey': opj2._CLRSPC_GRAY, 'grey': _opj2.CLRSPC_GRAY,
'ycc': opj2._CLRSPC_YCC} 'ycc': _opj2.CLRSPC_YCC}
# Setup the default callback handlers. See the callback functions subsection # Setup the default callback handlers. See the callback functions subsection
# in the ctypes section of the Python documentation for a solid explanation of # in the ctypes section of the Python documentation for a solid explanation of
@ -31,23 +43,26 @@ _cspace_map = {'rgb': opj2._CLRSPC_SRGB,
_CMPFUNC = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_char_p, ctypes.c_void_p) _CMPFUNC = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_char_p, ctypes.c_void_p)
def _default_error_handler(msg, client_data): def _default_error_handler(msg, _):
"""Default error handler callback for openjpeg library."""
msg = "OpenJPEG library error: {0}".format(msg.decode('utf-8').rstrip()) msg = "OpenJPEG library error: {0}".format(msg.decode('utf-8').rstrip())
opj2._set_error_message(msg) _opj2.set_error_message(msg)
def _default_info_handler(msg, client_data): def _default_info_handler(msg, _):
"""Default info handler callback for openjpeg library."""
print("[INFO] {0}".format(msg.decode('utf-8').rstrip())) print("[INFO] {0}".format(msg.decode('utf-8').rstrip()))
def _default_warning_handler(library_msg, client_data): def _default_warning_handler(library_msg, _):
"""Default warning handler callback for openjpeg library."""
library_msg = library_msg.decode('utf-8').rstrip() library_msg = library_msg.decode('utf-8').rstrip()
msg = "OpenJPEG library warning: {0}".format(library_msg) msg = "OpenJPEG library warning: {0}".format(library_msg)
warnings.warn(msg) warnings.warn(msg)
_error_callback = _CMPFUNC(_default_error_handler) _ERROR_CALLBACK = _CMPFUNC(_default_error_handler)
_info_callback = _CMPFUNC(_default_info_handler) _INFO_CALLBACK = _CMPFUNC(_default_info_handler)
_warning_callback = _CMPFUNC(_default_warning_handler) _WARNING_CALLBACK = _CMPFUNC(_default_warning_handler)
class Jp2k(Jp2kBox): class Jp2k(Jp2kBox):
@ -74,14 +89,16 @@ class Jp2k(Jp2kBox):
mode : str, optional mode : str, optional
The mode used to open the file. The mode used to open the file.
""" """
Jp2kBox.__init__(self)
self.filename = filename self.filename = filename
self.mode = mode self.mode = mode
self.box = [] self.box = []
self.offset = 0 self._codec_format = None
self._file_size = 0
# Parse the file for JP2/JPX contents only if we are reading it. # Parse the file for JP2/JPX contents only if we are reading it.
if mode == 'rb': if mode == 'rb':
self._parse() self.parse()
def __str__(self): def __str__(self):
metadata = ['File: ' + os.path.basename(self.filename)] metadata = ['File: ' + os.path.basename(self.filename)]
@ -89,11 +106,11 @@ class Jp2k(Jp2kBox):
for box in self.box: for box in self.box:
metadata.append(str(box)) metadata.append(str(box))
else: else:
c = self.get_codestream() codestream = self.get_codestream()
metadata.append(str(c)) metadata.append(str(codestream))
return '\n'.join(metadata) return '\n'.join(metadata)
def _parse(self): def parse(self):
"""Parses the JPEG 2000 file. """Parses the JPEG 2000 file.
Raises Raises
@ -105,39 +122,41 @@ class Jp2k(Jp2kBox):
self.length = stat.st_size self.length = stat.st_size
self._file_size = stat.st_size self._file_size = stat.st_size
with open(self.filename, 'rb') as f: with open(self.filename, 'rb') as fptr:
# Make sure we have a JPEG2000 file. It could be either JP2 or # Make sure we have a JPEG2000 file. It could be either JP2 or
# J2C. Check for J2C first, single box in that case. # J2C. Check for J2C first, single box in that case.
buffer = f.read(2) read_buffer = fptr.read(2)
signature, = struct.unpack('>H', buffer) signature, = struct.unpack('>H', read_buffer)
if signature == 0xff4f: if signature == 0xff4f:
self._codec_format = opj2._CODEC_J2K self._codec_format = _opj2.CODEC_J2K
# That's it, we're done. The codestream object is only # That's it, we're done. The codestream object is only
# produced upon explicit request. # produced upon explicit request.
return return
self._codec_format = opj2._CODEC_JP2 self._codec_format = _opj2.CODEC_JP2
# Should be JP2. # Should be JP2.
# First 4 bytes should be 12, the length of the 'jP ' box. # First 4 bytes should be 12, the length of the 'jP ' box.
# 2nd 4 bytes should be the box ID ('jP '). # 2nd 4 bytes should be the box ID ('jP ').
# 3rd 4 bytes should be the box signature (13, 10, 135, 10). # 3rd 4 bytes should be the box signature (13, 10, 135, 10).
f.seek(0) fptr.seek(0)
buffer = f.read(12) read_buffer = fptr.read(12)
values = struct.unpack('>I4s4B', buffer) values = struct.unpack('>I4s4B', read_buffer)
L = values[0] box_length = values[0]
T = values[1] box_id = values[1]
signature = values[2:] signature = values[2:]
if L != 12 or T != b'jP ' or signature != (13, 10, 135, 10): if (((box_length != 12) or (box_id != b'jP ') or
(signature != (13, 10, 135, 10)))):
msg = '{0} is not a JPEG 2000 file.'.format(self.filename) msg = '{0} is not a JPEG 2000 file.'.format(self.filename)
raise IOError(msg) raise IOError(msg)
# Back up and start again, we know we have a superbox (box of # Back up and start again, we know we have a superbox (box of
# boxes) here. # boxes) here.
f.seek(0) fptr.seek(0)
self.box = self._parse_superbox(f) self.box = self.parse_superbox(fptr)
# pylint: disable-msg=W0221
def write(self, img_array, cratios=None, eph=False, psnr=None, numres=None, def write(self, img_array, cratios=None, eph=False, psnr=None, numres=None,
cbsize=None, psizes=None, grid_offset=None, sop=False, cbsize=None, psizes=None, grid_offset=None, sop=False,
subsam=None, tilesize=None, prog=None, modesw=None, subsam=None, tilesize=None, prog=None, modesw=None,
@ -202,24 +221,24 @@ class Jp2k(Jp2kBox):
>>> import glymur >>> import glymur
>>> jfile = glymur.data.nemo() >>> jfile = glymur.data.nemo()
>>> jp2 = glymur.Jp2k(jfile) >>> jp2 = glymur.Jp2k(jfile)
>>> data = jp2.read(reduce=3) >>> data = jp2.read(rlevel=1)
>>> from tempfile import NamedTemporaryFile >>> from tempfile import NamedTemporaryFile
>>> tfile = NamedTemporaryFile(suffix='.jp2', delete=False) >>> tfile = NamedTemporaryFile(suffix='.jp2', delete=False)
>>> j = Jp2k(tfile.name, mode='wb') >>> j = Jp2k(tfile.name, mode='wb')
>>> j.write(data.astype(np.uint8)) >>> j.write(data.astype(np.uint8))
""" """
cparams = opj2._set_default_encoder_parameters() cparams = _opj2.set_default_encoder_parameters()
outfile = self.filename.encode() outfile = self.filename.encode()
n = opj2._PATH_LEN - len(outfile) num_pad_bytes = _opj2.PATH_LEN - len(outfile)
outfile += b'0' * n outfile += b'0' * num_pad_bytes
cparams.outfile = outfile cparams.outfile = outfile
if self.filename[-4:].lower() == '.jp2': if self.filename[-4:].lower() == '.jp2':
codec_fmt = opj2._CODEC_JP2 codec_fmt = _opj2.CODEC_JP2
else: else:
codec_fmt = opj2._CODEC_J2K codec_fmt = _opj2.CODEC_J2K
cparams.cod_format = codec_fmt cparams.cod_format = codec_fmt
@ -229,19 +248,19 @@ class Jp2k(Jp2kBox):
cparams.cp_disto_alloc = 1 cparams.cp_disto_alloc = 1
if cbsize is not None: if cbsize is not None:
w = cbsize[1] width = cbsize[1]
h = cbsize[0] height = cbsize[0]
if h * w > 4096 or h < 4 or w < 4: if height * width > 4096 or height < 4 or width < 4:
msg = "Code block area cannot exceed 4096. " msg = "Code block area cannot exceed 4096. "
msg += "Code block height and width must be larger than 4." msg += "Code block height and width must be larger than 4."
raise RuntimeError(msg) raise RuntimeError(msg)
if ((math.log(h, 2) != math.floor(math.log(h, 2)) or if ((math.log(height, 2) != math.floor(math.log(height, 2)) or
math.log(w, 2) != math.floor(math.log(w, 2)))): math.log(width, 2) != math.floor(math.log(width, 2)))):
msg = "Bad code block size ({0}, {1}), " msg = "Bad code block size ({0}, {1}), "
msg += "must be powers of 2." msg += "must be powers of 2."
raise IOError(msg.format(h, w)) raise IOError(msg.format(height, width))
cparams.cblockw_init = w cparams.cblockw_init = width
cparams.cblockh_init = h cparams.cblockh_init = height
if cratios is not None: if cratios is not None:
cparams.tcp_numlayers = len(cratios) cparams.tcp_numlayers = len(cratios)
@ -257,16 +276,17 @@ class Jp2k(Jp2kBox):
cparams.image_offset_y0 = grid_offset[0] cparams.image_offset_y0 = grid_offset[0]
if modesw is not None: if modesw is not None:
for x in range(6): for shift in range(6):
if modesw & (1 << x): power_of_two = 1 << shift
cparams.mode |= (1 << x) if modesw & power_of_two:
cparams.mode |= power_of_two
if numres is not None: if numres is not None:
cparams.numresolution = numres cparams.numresolution = numres
if prog is not None: if prog is not None:
prog = prog.upper() prog = prog.upper()
cparams.prog_order = progression_order[prog] cparams.prog_order = PROGRESSION_ORDER[prog]
if psnr is not None: if psnr is not None:
cparams.tcp_numlayers = len(psnr) cparams.tcp_numlayers = len(psnr)
@ -303,7 +323,7 @@ class Jp2k(Jp2kBox):
if tilesize is not None: if tilesize is not None:
cparams.cp_tdx = tilesize[1] cparams.cp_tdx = tilesize[1]
cparams.cp_tdy = tilesize[0] cparams.cp_tdy = tilesize[0]
cparams.tile_size_on = opj2._TRUE cparams.tile_size_on = _opj2.TRUE
if cratios is not None and psnr is not None: if cratios is not None and psnr is not None:
msg = "Cannot specify cratios and psnr together." msg = "Cannot specify cratios and psnr together."
@ -322,12 +342,12 @@ class Jp2k(Jp2kBox):
if colorspace is None: if colorspace is None:
if img_array.shape[2] == 1 or img_array.shape[2] == 2: if img_array.shape[2] == 1 or img_array.shape[2] == 2:
colorspace = opj2._CLRSPC_GRAY colorspace = _opj2.CLRSPC_GRAY
else: else:
# No YCC unless specifically told to do so. # No YCC unless specifically told to do so.
colorspace = opj2._CLRSPC_SRGB colorspace = _opj2.CLRSPC_SRGB
else: else:
if codec_fmt == opj2._CODEC_J2K: if codec_fmt == _opj2.CODEC_J2K:
raise IOError('Do not specify a colorspace with J2K.') raise IOError('Do not specify a colorspace with J2K.')
colorspace = colorspace.lower() colorspace = colorspace.lower()
if colorspace not in ('rgb', 'grey', 'gray'): if colorspace not in ('rgb', 'grey', 'gray'):
@ -337,15 +357,15 @@ class Jp2k(Jp2kBox):
msg = 'RGB colorspace requires at least 3 components.' msg = 'RGB colorspace requires at least 3 components.'
raise IOError(msg) raise IOError(msg)
else: else:
colorspace = _cspace_map[colorspace] colorspace = _COLORSPACE_MAP[colorspace]
if mct is None: if mct is None:
if colorspace == opj2._CLRSPC_SRGB: if colorspace == _opj2.CLRSPC_SRGB:
cparams.tcp_mct = 1 cparams.tcp_mct = 1
else: else:
cparams.tcp_mct = 0 cparams.tcp_mct = 0
else: else:
if mct and colorspace == opj2._CLRSPC_GRAY: if mct and colorspace == _opj2.CLRSPC_GRAY:
msg = "Cannot specify usage of the multi component transform " msg = "Cannot specify usage of the multi component transform "
msg += "if the colorspace is gray." msg += "if the colorspace is gray."
raise IOError(msg) raise IOError(msg)
@ -358,7 +378,7 @@ class Jp2k(Jp2kBox):
else: else:
raise RuntimeError("unhandled datatype") raise RuntimeError("unhandled datatype")
comptparms = (opj2._image_comptparm_t * num_comps)() comptparms = (_opj2.ImageComptParmType * num_comps)()
for j in range(num_comps): for j in range(num_comps):
comptparms[j].dx = cparams.subsampling_dx comptparms[j].dx = cparams.subsampling_dx
comptparms[j].dy = cparams.subsampling_dy comptparms[j].dy = cparams.subsampling_dy
@ -370,7 +390,7 @@ class Jp2k(Jp2kBox):
comptparms[j].bpp = comp_prec comptparms[j].bpp = comp_prec
comptparms[j].sgnd = 0 comptparms[j].sgnd = 0
image = opj2._image_create(comptparms, colorspace) image = _opj2.image_create(comptparms, colorspace)
# set image offset and reference grid # set image offset and reference grid
image.contents.x0 = cparams.image_offset_x0 image.contents.x0 = cparams.image_offset_x0
@ -387,25 +407,25 @@ class Jp2k(Jp2kBox):
src = layer.ctypes.data src = layer.ctypes.data
ctypes.memmove(dest, src, layer.nbytes) ctypes.memmove(dest, src, layer.nbytes)
codec = opj2._create_compress(codec_fmt) codec = _opj2.create_compress(codec_fmt)
if verbose: if verbose:
opj2._set_info_handler(codec, _info_callback) _opj2.set_info_handler(codec, _INFO_CALLBACK)
else: else:
opj2._set_info_handler(codec, None) _opj2.set_info_handler(codec, None)
opj2._set_warning_handler(codec, _warning_callback) _opj2.set_warning_handler(codec, _WARNING_CALLBACK)
opj2._set_error_handler(codec, _error_callback) _opj2.set_error_handler(codec, _ERROR_CALLBACK)
opj2._setup_encoder(codec, cparams, image) _opj2.setup_encoder(codec, cparams, image)
strm = opj2._stream_create_default_file_stream_v3(self.filename, False) strm = _opj2.stream_create_default_file_stream_v3(self.filename, False)
opj2._start_compress(codec, image, strm) _opj2.start_compress(codec, image, strm)
opj2._encode(codec, strm) _opj2.encode(codec, strm)
opj2._end_compress(codec, strm) _opj2.end_compress(codec, strm)
opj2._stream_destroy_v3(strm) _opj2.stream_destroy_v3(strm)
opj2._destroy_codec(codec) _opj2.destroy_codec(codec)
opj2._image_destroy(image) _opj2.image_destroy(image)
self._parse() self.parse()
def wrap(self, filename, boxes=None): def wrap(self, filename, boxes=None):
"""Write the codestream back out to file, wrapped in new JP2 jacket. """Write the codestream back out to file, wrapped in new JP2 jacket.
@ -438,10 +458,10 @@ class Jp2k(Jp2kBox):
FileTypeBox(), FileTypeBox(),
JP2HeaderBox(), JP2HeaderBox(),
ContiguousCodestreamBox()] ContiguousCodestreamBox()]
c = self.get_codestream() codestream = self.get_codestream()
height = c.segment[1].Ysiz height = codestream.segment[1].ysiz
width = c.segment[1].Xsiz width = codestream.segment[1].xsiz
num_components = len(c.segment[1].XRsiz) num_components = len(codestream.segment[1].xrsiz)
boxes[2].box = [ImageHeaderBox(height=height, boxes[2].box = [ImageHeaderBox(height=height,
width=width, width=width,
num_components=num_components), num_components=num_components),
@ -449,15 +469,17 @@ class Jp2k(Jp2kBox):
# Check for a bad sequence of boxes. # Check for a bad sequence of boxes.
# 1st two boxes must be 'jP ' and 'ftyp' # 1st two boxes must be 'jP ' and 'ftyp'
if boxes[0].id != 'jP ' or boxes[1].id != 'ftyp': if boxes[0].box_id != 'jP ' or boxes[1].box_id != 'ftyp':
msg = "The first box must be the signature box and the second " msg = "The first box must be the signature box and the second "
msg += "must be the file type box." msg += "must be the file type box."
raise IOError(msg) raise IOError(msg)
# jp2c must be preceeded by jp2h # jp2c must be preceeded by jp2h
jp2h_lst = [idx for (idx, box) in enumerate(boxes) if box.id == 'jp2h'] jp2h_lst = [idx for (idx, box) in enumerate(boxes)
if box.box_id == 'jp2h']
jp2h_idx = jp2h_lst[0] jp2h_idx = jp2h_lst[0]
jp2c_lst = [idx for (idx, box) in enumerate(boxes) if box.id == 'jp2c'] jp2c_lst = [idx for (idx, box) in enumerate(boxes)
if box.box_id == 'jp2c']
if len(jp2c_lst) == 0: if len(jp2c_lst) == 0:
msg = "A codestream box must be defined in the outermost " msg = "A codestream box must be defined in the outermost "
msg += "list of boxes." msg += "list of boxes."
@ -470,27 +492,28 @@ class Jp2k(Jp2kBox):
# 1st jp2 header box must be ihdr # 1st jp2 header box must be ihdr
jp2h = boxes[jp2h_idx] jp2h = boxes[jp2h_idx]
if jp2h.box[0].id != 'ihdr': if jp2h.box[0].box_id != 'ihdr':
msg = "The first box in the jp2 header box must be the image " msg = "The first box in the jp2 header box must be the image "
msg += "header box." msg += "header box."
raise IOError(msg) raise IOError(msg)
# colr must be present in jp2 header box. # colr must be present in jp2 header box.
jp2hb = jp2h.box colr_lst = [j for (j, box) in enumerate(jp2h.box)
colr_lst = [j for (j, box) in enumerate(jp2h.box) if box.id == 'colr'] if box.box_id == 'colr']
if len(colr_lst) == 0: if len(colr_lst) == 0:
msg = "The jp2 header box must contain a color definition box." msg = "The jp2 header box must contain a color definition box."
raise IOError(msg) raise IOError(msg)
colr = jp2h.box[colr_lst[0]] colr = jp2h.box[colr_lst[0]]
# Any cdef box must be in the jp2 header following the image header. # Any cdef box must be in the jp2 header following the image header.
cdef_lst = [j for (j, box) in enumerate(boxes) if box.id == 'cdef'] cdef_lst = [j for (j, box) in enumerate(boxes) if box.box_id == 'cdef']
if len(cdef_lst) != 0: if len(cdef_lst) != 0:
msg = "Any channel defintion box must be in the JP2 header " msg = "Any channel defintion box must be in the JP2 header "
msg += "following the image header." msg += "following the image header."
raise IOError(msg) raise IOError(msg)
cdef_lst = [j for (j, box) in enumerate(jp2h.box) if box.id == 'cdef'] cdef_lst = [j for (j, box) in enumerate(jp2h.box)
if box.box_id == 'cdef']
if len(cdef_lst) > 1: if len(cdef_lst) > 1:
msg = "Only one channel definition box is allowed in the " msg = "Only one channel definition box is allowed in the "
msg += "JP2 header." msg += "JP2 header."
@ -499,7 +522,6 @@ class Jp2k(Jp2kBox):
cdef = jp2h.box[cdef_lst[0]] cdef = jp2h.box[cdef_lst[0]]
assn = cdef.association assn = cdef.association
typ = cdef.channel_type typ = cdef.channel_type
index = cdef.index
if colr.colorspace == SRGB: if colr.colorspace == SRGB:
if any([chan + 1 not in assn or typ[chan] != 0 if any([chan + 1 not in assn or typ[chan] != 0
for chan in [0, 1, 2]]): for chan in [0, 1, 2]]):
@ -514,8 +536,8 @@ class Jp2k(Jp2kBox):
with open(filename, 'wb') as ofile: with open(filename, 'wb') as ofile:
for box in boxes: for box in boxes:
if box.id != 'jp2c': if box.box_id != 'jp2c':
box._write(ofile) box.write(ofile)
else: else:
# The codestream gets written last. # The codestream gets written last.
if len(self.box) == 0: if len(self.box) == 0:
@ -529,7 +551,8 @@ class Jp2k(Jp2kBox):
else: else:
# OK, I'm a jp2 file. Need to find out where the # OK, I'm a jp2 file. Need to find out where the
# raw codestream actually starts. # raw codestream actually starts.
jp2c = [box for box in self.box if box.id == 'jp2c'] jp2c = [box for box in self.box
if box.box_id == 'jp2c']
jp2c = jp2c[0] jp2c = jp2c[0]
ofile.write(struct.pack('>I', jp2c.length + 8)) ofile.write(struct.pack('>I', jp2c.length + 8))
ofile.write('jp2c'.encode()) ofile.write('jp2c'.encode())
@ -544,16 +567,17 @@ class Jp2k(Jp2kBox):
jp2 = Jp2k(filename) jp2 = Jp2k(filename)
return jp2 return jp2
def read(self, reduce=0, layer=0, area=None, tile=None, verbose=False): def read(self, **kwargs):
"""Read a JPEG 2000 image. """Read a JPEG 2000 image.
Parameters Parameters
---------- ----------
rlevel : int, optional
Factor by which to rlevel output resolution. Use -1 to get the
lowest resolution thumbnail. This is the only keyword option
available to use when only OpenJPEG version 1.5.1 is present.
layer : int, optional layer : int, optional
Number of quality layer to decode. 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 area : tuple, optional
Specifies decoding image area, Specifies decoding image area,
(first_row, first_col, last_row, last_col) (first_row, first_col, last_row, last_col)
@ -583,19 +607,169 @@ class Jp2k(Jp2kBox):
Read the lowest resolution thumbnail. Read the lowest resolution thumbnail.
>>> thumbnail = jp.read(reduce=-1) >>> thumbnail = jp.read(rlevel=-1)
>>> thumbnail.shape >>> 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, rlevel=0, verbose=False):
"""Read a JPEG 2000 image using libopenjpeg.
Parameters
----------
rlevel : int, optional
Factor by which to rlevel 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. # Check for differing subsample factors.
codestream = self.get_codestream(header_only=True) codestream = self.get_codestream(header_only=True)
dxs = np.array(codestream.segment[1].XRsiz) dxs = np.array(codestream.segment[1].xrsiz)
dys = np.array(codestream.segment[1].YRsiz) 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.DecompressionParametersType()
_opj.set_default_decoder_parameters(ctypes.byref(dparameters))
dparameters.cp_reduce = rlevel
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.EventMgrType()
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 fptr:
src = fptr.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
band = np.ctypeslib.as_array(
(ctypes.c_int32 * nelts).from_address(addr))
data[:, :, k] = np.reshape(band.astype(dtype),
(nrows, ncols))
if data.shape[2] == 1:
data = data.view()
data.shape = data.shape[0:2]
return data
def _read_openjp2(self, rlevel=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.
rlevel : int, optional
Factor by which to rlevel 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)
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]): if np.any(dxs - dxs[0]) or np.any(dys - dys[0]):
msg = "Components must all have the same subsampling factors." msg = "Components must all have the same subsampling factors."
raise IOError(msg) raise RuntimeError(msg)
img_array = self._read_common(reduce=reduce, img_array = self._read_common(rlevel=rlevel,
layer=layer, layer=layer,
area=area, area=area,
tile=tile, tile=tile,
@ -608,7 +782,7 @@ class Jp2k(Jp2kBox):
return img_array return img_array
def _read_common(self, reduce=0, layer=0, area=None, tile=None, def _read_common(self, rlevel=0, layer=0, area=None, tile=None,
verbose=False, as_bands=False): verbose=False, as_bands=False):
"""Read a JPEG 2000 image. """Read a JPEG 2000 image.
@ -616,8 +790,8 @@ class Jp2k(Jp2kBox):
---------- ----------
layer : int, optional layer : int, optional
Number of quality layer to decode. Number of quality layer to decode.
reduce : int, optional rlevel : int, optional
Factor by which to reduce output resolution. Factor by which to rlevel output resolution.
area : tuple, optional area : tuple, optional
Specifies decoding image area, Specifies decoding image area,
(first_row, first_col, last_row, last_col) (first_row, first_col, last_row, last_col)
@ -633,10 +807,10 @@ class Jp2k(Jp2kBox):
img_array : ndarray img_array : ndarray
The individual image components or a single array. The individual image components or a single array.
""" """
dparam = opj2._set_default_decoder_parameters() dparam = _opj2.set_default_decoder_parameters()
infile = self.filename.encode() infile = self.filename.encode()
nelts = opj2._PATH_LEN - len(infile) nelts = _opj2.PATH_LEN - len(infile)
infile += b'0' * nelts infile += b'0' * nelts
dparam.infile = infile dparam.infile = infile
@ -644,12 +818,12 @@ class Jp2k(Jp2kBox):
dparam.cp_layer = layer dparam.cp_layer = layer
if reduce == -1: if rlevel == -1:
# Get the lowest resolution thumbnail. # Get the lowest resolution thumbnail.
codestream = self.get_codestream() codestream = self.get_codestream()
reduce = codestream.segment[2].SPcod[4] rlevel = codestream.segment[2].spcod[4]
dparam.cp_reduce = reduce dparam.cp_reduce = rlevel
if area is not None: if area is not None:
if area[0] < 0 or area[1] < 0: if area[0] < 0 or area[1] < 0:
msg = "Upper left corner coordinates must be nonnegative: {0}" msg = "Upper left corner coordinates must be nonnegative: {0}"
@ -669,31 +843,31 @@ class Jp2k(Jp2kBox):
dparam.nb_tile_to_decode = 1 dparam.nb_tile_to_decode = 1
with ExitStack() as stack: with ExitStack() as stack:
stream = opj2._stream_create_default_file_stream_v3(self.filename, stream = _opj2.stream_create_default_file_stream_v3(self.filename,
True) True)
stack.callback(opj2._stream_destroy_v3, stream) stack.callback(_opj2.stream_destroy_v3, stream)
codec = opj2._create_decompress(self._codec_format) codec = _opj2.create_decompress(self._codec_format)
stack.callback(opj2._destroy_codec, codec) stack.callback(_opj2.destroy_codec, codec)
opj2._set_error_handler(codec, _error_callback) _opj2.set_error_handler(codec, _ERROR_CALLBACK)
opj2._set_warning_handler(codec, _warning_callback) _opj2.set_warning_handler(codec, _WARNING_CALLBACK)
if verbose: if verbose:
opj2._set_info_handler(codec, _info_callback) _opj2.set_info_handler(codec, _INFO_CALLBACK)
else: else:
opj2._set_info_handler(codec, None) _opj2.set_info_handler(codec, None)
opj2._setup_decoder(codec, dparam) _opj2.setup_decoder(codec, dparam)
image = opj2._read_header(stream, codec) image = _opj2.read_header(stream, codec)
stack.callback(opj2._image_destroy, image) stack.callback(_opj2.image_destroy, image)
if dparam.nb_tile_to_decode: if dparam.nb_tile_to_decode:
opj2._get_decoded_tile(codec, stream, image, dparam.tile_index) _opj2.get_decoded_tile(codec, stream, image, dparam.tile_index)
else: else:
opj2._set_decode_area(codec, image, _opj2.set_decode_area(codec, image,
dparam.DA_x0, dparam.DA_y0, dparam.DA_x0, dparam.DA_y0,
dparam.DA_x1, dparam.DA_y1) dparam.DA_x1, dparam.DA_y1)
opj2._decode(codec, stream, image) _opj2.decode(codec, stream, image)
opj2._end_decompress(codec, stream) _opj2.end_decompress(codec, stream)
component = image.contents.comps[0] component = image.contents.comps[0]
if component.sgnd: if component.sgnd:
@ -734,16 +908,17 @@ class Jp2k(Jp2kBox):
addr = ctypes.addressof(component.data.contents) addr = ctypes.addressof(component.data.contents)
with warnings.catch_warnings(): with warnings.catch_warnings():
warnings.simplefilter("ignore") warnings.simplefilter("ignore")
x = np.ctypeslib.as_array( band = np.ctypeslib.as_array(
(ctypes.c_int32 * nrows * ncols).from_address(addr)) (ctypes.c_int32 * nrows * ncols).from_address(addr))
if as_bands: if as_bands:
data.append(np.reshape(x.astype(dtype), (nrows, ncols))) data.append(np.reshape(band.astype(dtype), (nrows, ncols)))
else: else:
data[:, :, k] = np.reshape(x.astype(dtype), (nrows, ncols)) data[:, :, k] = np.reshape(band.astype(dtype),
(nrows, ncols))
return data return data
def read_bands(self, reduce=0, layer=0, area=None, tile=None, def read_bands(self, rlevel=0, layer=0, area=None, tile=None,
verbose=False): verbose=False):
"""Read a JPEG 2000 image. """Read a JPEG 2000 image.
@ -755,8 +930,8 @@ class Jp2k(Jp2kBox):
---------- ----------
layer : int, optional layer : int, optional
Number of quality layer to decode. Number of quality layer to decode.
reduce : int, optional rlevel : int, optional
Factor by which to reduce output resolution. Factor by which to rlevel output resolution.
area : tuple, optional area : tuple, optional
Specifies decoding image area, Specifies decoding image area,
(first_row, first_col, last_row, last_col) (first_row, first_col, last_row, last_col)
@ -779,9 +954,18 @@ class Jp2k(Jp2kBox):
>>> import glymur >>> import glymur
>>> jfile = glymur.data.nemo() >>> jfile = glymur.data.nemo()
>>> jp = glymur.Jp2k(jfile) >>> jp = glymur.Jp2k(jfile)
>>> components_lst = jp.read_bands(reduce=1) >>> components_lst = jp.read_bands(rlevel=1)
Raises
------
NotImplementedError
If the openjp2 library is not available.
""" """
lst = self._read_common(reduce=reduce, if _opj2.OPENJP2 is None:
msg = "Requires openjp2 library."
raise NotImplementedError(msg)
lst = self._read_common(rlevel=rlevel,
layer=layer, layer=layer,
area=area, area=area,
tile=tile, tile=tile,
@ -807,14 +991,14 @@ class Jp2k(Jp2kBox):
-------- --------
>>> import glymur >>> import glymur
>>> jfile = glymur.data.nemo() >>> jfile = glymur.data.nemo()
>>> jp = glymur.Jp2k(jfile) >>> jp2 = glymur.Jp2k(jfile)
>>> codestream = jp.get_codestream() >>> codestream = jp2.get_codestream()
>>> print(codestream.segment[1]) >>> print(codestream.segment[1])
SIZ marker segment @ (3137, 47) SIZ marker segment @ (3137, 47)
Profile: 2 Profile: 2
Reference Grid Height, Width: (1456 x 2592) Reference Grid Height, Width: (1456 x 2592)
Vertical, Horizontal Reference Grid Offset: (0 x 0) 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) Vertical, Horizontal Reference Tile Offset: (0 x 0)
Bitdepth: (8, 8, 8) Bitdepth: (8, 8, 8)
Signed: (False, False, False) Signed: (False, False, False)
@ -825,20 +1009,20 @@ class Jp2k(Jp2kBox):
IOError IOError
If the file is JPX with more than one codestream. If the file is JPX with more than one codestream.
""" """
with open(self.filename, 'rb') as fp: with open(self.filename, 'rb') as fptr:
if self._codec_format == opj2._CODEC_J2K: if self._codec_format == _opj2.CODEC_J2K:
codestream = Codestream(fp, header_only=header_only) codestream = Codestream(fptr, header_only=header_only)
else: else:
box = [x for x in self.box if x.id == 'jp2c'] box = [x for x in self.box if x.box_id == 'jp2c']
if len(box) != 1: if len(box) != 1:
msg = "JP2 files must have a single codestream." msg = "JP2 files must have a single codestream."
raise RuntimeError(msg) raise RuntimeError(msg)
fp.seek(box[0].offset) fptr.seek(box[0].offset)
buffer = fp.read(8) read_buffer = fptr.read(8)
(L, T) = struct.unpack('>I4s', buffer) (box_length, _) = struct.unpack('>I4s', read_buffer)
if L == 1: if box_length == 1:
# Seek past the XL field. # Seek past the XL field.
buffer = fp.read(8) read_buffer = fptr.read(8)
codestream = Codestream(fp, header_only=header_only) codestream = Codestream(fptr, header_only=header_only)
return codestream return codestream

View file

@ -1,3 +1,4 @@
"""This package organizes individual libraries employed by glymur.""" """This package organizes individual libraries employed by glymur."""
from . import openjp2 from . import openjp2 as _openjp2
from . import test from . import openjpeg as _openjpeg
#from . import test

153
glymur/lib/config.py Normal file
View file

@ -0,0 +1,153 @@
"""
Configure glymur to use installed libraries if possible.
"""
import ctypes
from ctypes.util import find_library
import os
import platform
import warnings
import sys
if sys.hexversion <= 0x03000000:
from ConfigParser import SafeConfigParser as ConfigParser
from ConfigParser import NoOptionError
else:
from configparser import ConfigParser
from configparser import NoOptionError
def glymurrc_fname():
"""Return the path to the configuration file.
Search order:
1) current working directory
2) environ var XDG_CONFIG_HOME
3) $HOME/.config/glymur/glymurrc
"""
# Current directory.
fname = os.path.join(os.getcwd(), 'glymurrc')
if os.path.exists(fname):
return fname
confdir = get_configdir()
if confdir is not None:
fname = os.path.join(confdir, 'glymurrc')
if os.path.exists(fname):
return fname
# didn't find a configuration file.
return None
def load_openjpeg(libopenjpeg_path):
"""Load the openjpeg library, falling back on defaults if necessary.
"""
if libopenjpeg_path is None:
# Let ctypes try to find it.
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
try:
if os.name == "nt":
openjpeg_lib = ctypes.windll.LoadLibrary(libopenjpeg_path)
else:
openjpeg_lib = ctypes.CDLL(libopenjpeg_path)
except OSError:
openjpeg_lib = None
if openjpeg_lib is not None:
# Must be at least 1.5.0
openjpeg_lib.opj_version.restype = ctypes.c_char_p
version = openjpeg_lib.opj_version().decode('utf-8')
_, minor, _ = version.split('.')
if minor != '5':
openjpeg_lib = None
return openjpeg_lib
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}
filename = glymurrc_fname()
if filename is not None:
# Read the configuration file for the library location.
parser = ConfigParser()
parser.read(filename)
try:
lib['openjp2'] = parser.get('library', 'openjp2')
except NoOptionError:
pass
try:
lib['openjpeg'] = parser.get('library', 'openjpeg')
except NoOptionError:
pass
return lib
def load_openjp2(libopenjp2_path):
"""Load the openjp2 library, falling back on defaults if necessary.
"""
if libopenjp2_path is None:
# No help from the config file, try to find it ourselves.
libopenjp2_path = find_library('openjp2')
if libopenjp2_path is None:
return None
try:
if os.name == "nt":
openjp2_lib = ctypes.windll.LoadLibrary(libopenjp2_path)
else:
openjp2_lib = ctypes.CDLL(libopenjp2_path)
except (TypeError, OSError):
msg = '"Library {0}" could not be loaded. Operating in degraded mode.'
msg = msg.format(libopenjp2_path)
warnings.warn(msg, UserWarning)
openjp2_lib = None
return openjp2_lib
def glymur_config():
"""Try to ascertain locations of openjp2, openjpeg libraries.
"""
libs = read_config_file()
libopenjp2_handle = load_openjp2(libs['openjp2'])
libopenjpeg_handle = load_openjpeg(libs['openjpeg'])
return libopenjp2_handle, libopenjpeg_handle
def get_configdir():
"""Return string representing the configuration directory.
Default is $HOME/.config/glymur. You can override this with the
XDG_CONFIG_HOME environment variable.
"""
if 'XDG_CONFIG_HOME' in os.environ:
return os.path.join(os.environ['XDG_CONFIG_HOME'], 'glymur')
if 'HOME' in os.environ:
return os.path.join(os.environ['HOME'], '.config', 'glymur')
if 'USERPROFILE' in os.environ:
# Windows?
return os.path.join(os.environ['USERPROFILE'], 'Application Data',
'glymur')

File diff suppressed because it is too large Load diff

213
glymur/lib/openjpeg.py Normal file
View file

@ -0,0 +1,213 @@
"""Wraps library calls to openjpeg.
"""
# pylint: disable=R0903
import ctypes
from .config import glymur_config
_, OPENJPEG = glymur_config()
PATH_LEN = 4096 # maximum allowed size for filenames
class EventMgrType(ctypes.Structure):
"""Message handler object.
Corresponds to event_mgr_t type in openjpeg headers.
"""
_fields_ = [("error_handler", ctypes.c_void_p),
("warning_handler", ctypes.c_void_p),
("info_handler", ctypes.c_void_p)]
class CommonStructType(ctypes.Structure):
"""Common fields between JPEG 2000 compression and decompression contextx.
"""
_fields_ = [("event_mgr", ctypes.POINTER(EventMgrType)),
("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 DecompressionInfoType(ctypes.Structure):
"""This is for decompression contexts.
Corresponds to dinfo_t type in openjpeg headers.
"""
pass
class CioType(ctypes.Structure):
"""Byte input-output stream (CIO)
Corresponds to cio_t in openjpeg headers.
"""
_fields_ = [("cinfo", ctypes.POINTER(CommonStructType)), # codec context
# 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 DecompressionParametersType(ctypes.Structure):
"""Decompression parameters.
Corresponds to dparameters_t type in openjpeg headers.
"""
# 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 ImageCompType(ctypes.Structure):
"""Defines a single image component.
Corresponds to image_comp_t type in openjpeg.
"""
_fields_ = [("dx", ctypes.c_int),
("dy", ctypes.c_int),
("w", ctypes.c_int),
("h", ctypes.c_int),
("x0", ctypes.c_int),
("y0", ctypes.c_int),
("prec", ctypes.c_int),
("bpp", ctypes.c_int),
("sgnd", ctypes.c_int),
("resno_decoded", ctypes.c_int),
("factor", ctypes.c_int),
("data", ctypes.POINTER(ctypes.c_int))]
class ImageType(ctypes.Structure):
"""Defines image data and characteristics.
Corresponds to image_t type in openjpeg headers.
"""
_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(ImageCompType)),
("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(CommonStructType), ctypes.c_char_p,
ctypes.c_int]
OPENJPEG.opj_cio_open.argtypes = argtypes
OPENJPEG.opj_cio_open.restype = ctypes.POINTER(CioType)
cio = OPENJPEG.opj_cio_open(ctypes.cast(cinfo,
ctypes.POINTER(CommonStructType)),
src, len(src))
return cio
def cio_close(cio):
"""Wraps openjpeg library function cio_close.
"""
OPENJPEG.opj_cio_close.argtypes = [ctypes.POINTER(CioType)]
OPENJPEG.opj_cio_close(cio)
def create_decompress(fmt):
"""Wraps openjpeg library function opj_create_decompress.
"""
OPENJPEG.opj_create_decompress.argtypes = [ctypes.c_int]
restype = ctypes.POINTER(DecompressionInfoType)
OPENJPEG.opj_create_decompress.restype = restype
dinfo = OPENJPEG.opj_create_decompress(fmt)
return dinfo
def decode(dinfo, cio):
"""Wrapper for opj_decode.
"""
argtypes = [ctypes.POINTER(DecompressionInfoType), ctypes.POINTER(CioType)]
OPENJPEG.opj_decode.argtypes = argtypes
OPENJPEG.opj_decode.restype = ctypes.POINTER(ImageType)
image = OPENJPEG.opj_decode(dinfo, cio)
return image
def destroy_decompress(dinfo):
"""Wraps openjpeg library function opj_destroy_decompress."""
argtypes = [ctypes.POINTER(DecompressionInfoType)]
OPENJPEG.opj_destroy_decompress.argtypes = argtypes
OPENJPEG.opj_destroy_decompress(dinfo)
def image_destroy(image):
"""Wraps openjpeg library function opj_image_destroy."""
OPENJPEG.opj_image_destroy.argtypes = [ctypes.POINTER(ImageType)]
OPENJPEG.opj_image_destroy(image)
def set_default_decoder_parameters(dparams_p):
"""Wrapper for opj_set_default_decoder_parameters.
"""
argtypes = [ctypes.POINTER(DecompressionParametersType)]
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(CommonStructType),
ctypes.POINTER(EventMgrType),
ctypes.c_void_p]
OPENJPEG.opj_set_event_mgr.argtypes = argtypes
OPENJPEG.opj_set_event_mgr(ctypes.cast(dinfo,
ctypes.POINTER(CommonStructType)),
event_mgr, context)
def setup_decoder(dinfo, dparams):
"""Wrapper for openjpeg library function opj_setup_decoder."""
argtypes = [ctypes.POINTER(DecompressionInfoType),
ctypes.POINTER(DecompressionParametersType)]
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
library_version = OPENJPEG.opj_version()
return library_version.decode('utf-8')

View file

@ -1,3 +1,4 @@
#pylint: disable-all
import doctest import doctest
import os import os
import pkg_resources import pkg_resources
@ -12,13 +13,8 @@ import numpy as np
import glymur import glymur
def load_tests(loader, tests, ignore): @unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
if glymur.lib.openjp2._OPENJP2 is not None: @unittest.skipIf(glymur.lib._openjp2.OPENJP2 is None,
tests.addTests(doctest.DocTestSuite('glymur.lib.openjp2'))
return tests
@unittest.skipIf(glymur.lib.openjp2._OPENJP2 is None,
"Missing openjp2 library.") "Missing openjp2 library.")
class TestOpenJP2(unittest.TestCase): class TestOpenJP2(unittest.TestCase):
@ -29,7 +25,7 @@ class TestOpenJP2(unittest.TestCase):
pass pass
def test_set_default_encoder_parameters(self): def test_set_default_encoder_parameters(self):
cparams = glymur.lib.openjp2._set_default_encoder_parameters() cparams = glymur.lib._openjp2.set_default_encoder_parameters()
self.assertEqual(cparams.res_spec, 0) self.assertEqual(cparams.res_spec, 0)
self.assertEqual(cparams.cblockw_init, 64) self.assertEqual(cparams.cblockw_init, 64)
@ -38,7 +34,7 @@ class TestOpenJP2(unittest.TestCase):
self.assertEqual(cparams.subsampling_dx, 1) self.assertEqual(cparams.subsampling_dx, 1)
self.assertEqual(cparams.subsampling_dy, 1) self.assertEqual(cparams.subsampling_dy, 1)
self.assertEqual(cparams.mode, 0) self.assertEqual(cparams.mode, 0)
self.assertEqual(cparams.prog_order, glymur.lib.openjp2.LRCP) self.assertEqual(cparams.prog_order, glymur.core.LRCP)
self.assertEqual(cparams.roi_shift, 0) self.assertEqual(cparams.roi_shift, 0)
self.assertEqual(cparams.cp_tx0, 0) self.assertEqual(cparams.cp_tx0, 0)
self.assertEqual(cparams.cp_ty0, 0) self.assertEqual(cparams.cp_ty0, 0)
@ -46,7 +42,7 @@ class TestOpenJP2(unittest.TestCase):
self.assertEqual(cparams.irreversible, 0) self.assertEqual(cparams.irreversible, 0)
def test_set_default_decoder_parameters(self): def test_set_default_decoder_parameters(self):
dparams = glymur.lib.openjp2._set_default_decoder_parameters() dparams = glymur.lib._openjp2.set_default_decoder_parameters()
self.assertEqual(dparams.DA_x0, 0) self.assertEqual(dparams.DA_x0, 0)
self.assertEqual(dparams.DA_y0, 0) self.assertEqual(dparams.DA_y0, 0)
@ -55,34 +51,34 @@ class TestOpenJP2(unittest.TestCase):
def tile_macro(self, codec, stream, imagep, tidx): def tile_macro(self, codec, stream, imagep, tidx):
# called only by j2k_random_tile_access # called only by j2k_random_tile_access
glymur.lib.openjp2._get_decoded_tile(codec, stream, imagep, tidx) glymur.lib._openjp2.get_decoded_tile(codec, stream, imagep, tidx)
for j in range(imagep.contents.numcomps): for j in range(imagep.contents.numcomps):
self.assertIsNotNone(imagep.contents.comps[j].data) self.assertIsNotNone(imagep.contents.comps[j].data)
def j2k_random_tile_access(self, filename, codec_format=None): def j2k_random_tile_access(self, filename, codec_format=None):
# called by the test_rtaX methods # called by the test_rtaX methods
dparam = glymur.lib.openjp2._set_default_decoder_parameters() dparam = glymur.lib._openjp2.set_default_decoder_parameters()
infile = filename.encode() infile = filename.encode()
nelts = glymur.lib.openjp2._PATH_LEN - len(infile) nelts = glymur.lib._openjp2.PATH_LEN - len(infile)
infile += b'0' * nelts infile += b'0' * nelts
dparam.infile = infile dparam.infile = infile
dparam.decod_format = codec_format dparam.decod_format = codec_format
codec = glymur.lib.openjp2._create_decompress(codec_format) codec = glymur.lib._openjp2.create_decompress(codec_format)
glymur.lib.openjp2._set_info_handler(codec, None) glymur.lib._openjp2.set_info_handler(codec, None)
glymur.lib.openjp2._set_warning_handler(codec, None) glymur.lib._openjp2.set_warning_handler(codec, None)
glymur.lib.openjp2._set_error_handler(codec, None) glymur.lib._openjp2.set_error_handler(codec, None)
x = (filename, True) x = (filename, True)
stream = glymur.lib.openjp2._stream_create_default_file_stream_v3(*x) stream = glymur.lib._openjp2.stream_create_default_file_stream_v3(*x)
glymur.lib.openjp2._setup_decoder(codec, dparam) glymur.lib._openjp2.setup_decoder(codec, dparam)
image = glymur.lib.openjp2._read_header(stream, codec) image = glymur.lib._openjp2.read_header(stream, codec)
cstr_info = glymur.lib.openjp2._get_cstr_info(codec) cstr_info = glymur.lib._openjp2.get_cstr_info(codec)
tile_ul = 0 tile_ul = 0
tile_ur = cstr_info.contents.tw - 1 tile_ur = cstr_info.contents.tw - 1
@ -94,18 +90,18 @@ class TestOpenJP2(unittest.TestCase):
self.tile_macro(codec, stream, image, tile_lr) self.tile_macro(codec, stream, image, tile_lr)
self.tile_macro(codec, stream, image, tile_ll) self.tile_macro(codec, stream, image, tile_ll)
glymur.lib.openjp2._destroy_cstr_info(cstr_info) glymur.lib._openjp2.destroy_cstr_info(cstr_info)
glymur.lib.openjp2._end_decompress(codec, stream) glymur.lib._openjp2.end_decompress(codec, stream)
glymur.lib.openjp2._destroy_codec(codec) glymur.lib._openjp2.destroy_codec(codec)
glymur.lib.openjp2._stream_destroy_v3(stream) glymur.lib._openjp2.stream_destroy_v3(stream)
glymur.lib.openjp2._image_destroy(image) glymur.lib._openjp2.image_destroy(image)
def tile_decoder(self, x0=None, y0=None, x1=None, y1=None, filename=None, def tile_decoder(self, x0=None, y0=None, x1=None, y1=None, filename=None,
codec_format=None): codec_format=None):
x = (filename, True) x = (filename, True)
stream = glymur.lib.openjp2._stream_create_default_file_stream_v3(*x) stream = glymur.lib._openjp2.stream_create_default_file_stream_v3(*x)
dparam = glymur.lib.openjp2._set_default_decoder_parameters() dparam = glymur.lib._openjp2.set_default_decoder_parameters()
dparam.decod_format = codec_format dparam.decod_format = codec_format
@ -115,30 +111,30 @@ class TestOpenJP2(unittest.TestCase):
# do not use resolution reductions. # do not use resolution reductions.
dparam.cp_reduce = 0 dparam.cp_reduce = 0
codec = glymur.lib.openjp2._create_decompress(codec_format) codec = glymur.lib._openjp2.create_decompress(codec_format)
glymur.lib.openjp2._set_info_handler(codec, None) glymur.lib._openjp2.set_info_handler(codec, None)
glymur.lib.openjp2._set_warning_handler(codec, None) glymur.lib._openjp2.set_warning_handler(codec, None)
glymur.lib.openjp2._set_error_handler(codec, None) glymur.lib._openjp2.set_error_handler(codec, None)
glymur.lib.openjp2._setup_decoder(codec, dparam) glymur.lib._openjp2.setup_decoder(codec, dparam)
image = glymur.lib.openjp2._read_header(stream, codec) image = glymur.lib._openjp2.read_header(stream, codec)
glymur.lib.openjp2._set_decode_area(codec, image, x0, y0, x1, y1) glymur.lib._openjp2.set_decode_area(codec, image, x0, y0, x1, y1)
data = np.zeros((1150, 2048, 3), dtype=np.uint8) data = np.zeros((1150, 2048, 3), dtype=np.uint8)
while True: while True:
rargs = glymur.lib.openjp2._read_tile_header(codec, stream) rargs = glymur.lib._openjp2.read_tile_header(codec, stream)
tidx = rargs[0] tidx = rargs[0]
sz = rargs[1] sz = rargs[1]
go_on = rargs[-1] go_on = rargs[-1]
if not go_on: if not go_on:
break break
glymur.lib.openjp2._decode_tile_data(codec, tidx, data, sz, stream) glymur.lib._openjp2.decode_tile_data(codec, tidx, data, sz, stream)
glymur.lib.openjp2._end_decompress(codec, stream) glymur.lib._openjp2.end_decompress(codec, stream)
glymur.lib.openjp2._destroy_codec(codec) glymur.lib._openjp2.destroy_codec(codec)
glymur.lib.openjp2._stream_destroy_v3(stream) glymur.lib._openjp2.stream_destroy_v3(stream)
glymur.lib.openjp2._image_destroy(image) glymur.lib._openjp2.image_destroy(image)
def tile_encoder(self, num_comps=None, tile_width=None, tile_height=None, def tile_encoder(self, num_comps=None, tile_width=None, tile_height=None,
filename=None, codec=None, comp_prec=None, filename=None, codec=None, comp_prec=None,
@ -150,7 +146,7 @@ class TestOpenJP2(unittest.TestCase):
data = np.random.random((tile_height, tile_width, num_comps)) data = np.random.random((tile_height, tile_width, num_comps))
data = (data * 255).astype(np.uint8) data = (data * 255).astype(np.uint8)
l_param = glymur.lib.openjp2._set_default_encoder_parameters() l_param = glymur.lib._openjp2.set_default_encoder_parameters()
l_param.tcp_numlayers = 1 l_param.tcp_numlayers = 1
l_param.cp_fixed_quality = 1 l_param.cp_fixed_quality = 1
@ -170,9 +166,9 @@ class TestOpenJP2(unittest.TestCase):
l_param.numresolution = 6 l_param.numresolution = 6
l_param.prog_order = glymur.lib.openjp2.LRCP l_param.prog_order = glymur.core.LRCP
l_params = (glymur.lib.openjp2._image_comptparm_t * num_comps)() l_params = (glymur.lib._openjp2.ImageComptParmType * num_comps)()
for j in range(num_comps): for j in range(num_comps):
l_params[j].dx = 1 l_params[j].dx = 1
l_params[j].dy = 1 l_params[j].dy = 1
@ -183,38 +179,38 @@ class TestOpenJP2(unittest.TestCase):
l_params[j].x0 = 0 l_params[j].x0 = 0
l_params[j].y0 = 0 l_params[j].y0 = 0
codec = glymur.lib.openjp2._create_compress(codec) codec = glymur.lib._openjp2.create_compress(codec)
glymur.lib.openjp2._set_info_handler(codec, None) glymur.lib._openjp2.set_info_handler(codec, None)
glymur.lib.openjp2._set_warning_handler(codec, None) glymur.lib._openjp2.set_warning_handler(codec, None)
glymur.lib.openjp2._set_error_handler(codec, None) glymur.lib._openjp2.set_error_handler(codec, None)
cspace = glymur.lib.openjp2._CLRSPC_SRGB cspace = glymur.lib._openjp2.CLRSPC_SRGB
l_image = glymur.lib.openjp2._image_tile_create(l_params, cspace) l_image = glymur.lib._openjp2.image_tile_create(l_params, cspace)
l_image.contents.x0 = 0 l_image.contents.x0 = 0
l_image.contents.y0 = 0 l_image.contents.y0 = 0
l_image.contents.x1 = image_width l_image.contents.x1 = image_width
l_image.contents.y1 = image_height l_image.contents.y1 = image_height
l_image.contents.color_space = glymur.lib.openjp2._CLRSPC_SRGB l_image.contents.color_space = glymur.lib._openjp2.CLRSPC_SRGB
glymur.lib.openjp2._setup_encoder(codec, l_param, l_image) glymur.lib._openjp2.setup_encoder(codec, l_param, l_image)
x = (filename, False) x = (filename, False)
stream = glymur.lib.openjp2._stream_create_default_file_stream_v3(*x) stream = glymur.lib._openjp2.stream_create_default_file_stream_v3(*x)
glymur.lib.openjp2._start_compress(codec, l_image, stream) glymur.lib._openjp2.start_compress(codec, l_image, stream)
for j in np.arange(num_tiles): for j in np.arange(num_tiles):
glymur.lib.openjp2._write_tile(codec, j, data, tile_size, stream) glymur.lib._openjp2.write_tile(codec, j, data, tile_size, stream)
glymur.lib.openjp2._end_compress(codec, stream) glymur.lib._openjp2.end_compress(codec, stream)
glymur.lib.openjp2._stream_destroy_v3(stream) glymur.lib._openjp2.stream_destroy_v3(stream)
glymur.lib.openjp2._destroy_codec(codec) glymur.lib._openjp2.destroy_codec(codec)
glymur.lib.openjp2._image_destroy(l_image) glymur.lib._openjp2.image_destroy(l_image)
def tte0_setup(self, filename): def tte0_setup(self, filename):
kwargs = {'filename': filename, kwargs = {'filename': filename,
'codec': glymur.lib.openjp2._CODEC_J2K, 'codec': glymur.lib._openjp2.CODEC_J2K,
'comp_prec': 8, 'comp_prec': 8,
'irreversible': 1, 'irreversible': 1,
'num_comps': 3, 'num_comps': 3,
@ -241,12 +237,12 @@ class TestOpenJP2(unittest.TestCase):
'x1': 1000, 'x1': 1000,
'y1': 1000, 'y1': 1000,
'filename': tfile.name, 'filename': tfile.name,
'codec_format': glymur.lib.openjp2._CODEC_J2K} 'codec_format': glymur.lib._openjp2.CODEC_J2K}
self.tile_decoder(**kwargs) self.tile_decoder(**kwargs)
def tte1_setup(self, filename): def tte1_setup(self, filename):
kwargs = {'filename': filename, kwargs = {'filename': filename,
'codec': glymur.lib.openjp2._CODEC_J2K, 'codec': glymur.lib._openjp2.CODEC_J2K,
'comp_prec': 8, 'comp_prec': 8,
'irreversible': 1, 'irreversible': 1,
'num_comps': 3, 'num_comps': 3,
@ -273,7 +269,7 @@ class TestOpenJP2(unittest.TestCase):
'x1': 128, 'x1': 128,
'y1': 128, 'y1': 128,
'filename': tfile.name, 'filename': tfile.name,
'codec_format': glymur.lib.openjp2._CODEC_J2K} 'codec_format': glymur.lib._openjp2.CODEC_J2K}
self.tile_decoder(**kwargs) self.tile_decoder(**kwargs)
def test_rta1(self): def test_rta1(self):
@ -281,12 +277,12 @@ class TestOpenJP2(unittest.TestCase):
# Runs test designated rta1 in OpenJPEG test suite. # Runs test designated rta1 in OpenJPEG test suite.
self.tte1_setup(tfile.name) self.tte1_setup(tfile.name)
kwargs = {'codec_format': glymur.lib.openjp2._CODEC_J2K} kwargs = {'codec_format': glymur.lib._openjp2.CODEC_J2K}
self.j2k_random_tile_access(tfile.name, **kwargs) self.j2k_random_tile_access(tfile.name, **kwargs)
def tte2_setup(self, filename): def tte2_setup(self, filename):
kwargs = {'filename': filename, kwargs = {'filename': filename,
'codec': glymur.lib.openjp2._CODEC_JP2, 'codec': glymur.lib._openjp2.CODEC_JP2,
'comp_prec': 8, 'comp_prec': 8,
'irreversible': 1, 'irreversible': 1,
'num_comps': 3, 'num_comps': 3,
@ -312,7 +308,7 @@ class TestOpenJP2(unittest.TestCase):
'x1': 128, 'x1': 128,
'y1': 128, 'y1': 128,
'filename': tfile.name, 'filename': tfile.name,
'codec_format': glymur.lib.openjp2._CODEC_JP2} 'codec_format': glymur.lib._openjp2.CODEC_JP2}
self.tile_decoder(**kwargs) self.tile_decoder(**kwargs)
def test_rta2(self): def test_rta2(self):
@ -320,12 +316,12 @@ class TestOpenJP2(unittest.TestCase):
# Runs test designated rta2 in OpenJPEG test suite. # Runs test designated rta2 in OpenJPEG test suite.
self.tte2_setup(tfile.name) self.tte2_setup(tfile.name)
kwargs = {'codec_format': glymur.lib.openjp2._CODEC_JP2} kwargs = {'codec_format': glymur.lib._openjp2.CODEC_JP2}
self.j2k_random_tile_access(tfile.name, **kwargs) self.j2k_random_tile_access(tfile.name, **kwargs)
def tte3_setup(self, filename): def tte3_setup(self, filename):
kwargs = {'filename': filename, kwargs = {'filename': filename,
'codec': glymur.lib.openjp2._CODEC_J2K, 'codec': glymur.lib._openjp2.CODEC_J2K,
'comp_prec': 8, 'comp_prec': 8,
'irreversible': 1, 'irreversible': 1,
'num_comps': 1, 'num_comps': 1,
@ -345,12 +341,12 @@ class TestOpenJP2(unittest.TestCase):
with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile: with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile:
self.tte3_setup(tfile.name) self.tte3_setup(tfile.name)
kwargs = {'codec_format': glymur.lib.openjp2._CODEC_J2K} kwargs = {'codec_format': glymur.lib._openjp2.CODEC_J2K}
self.j2k_random_tile_access(tfile.name, **kwargs) self.j2k_random_tile_access(tfile.name, **kwargs)
def tte4_setup(self, filename): def tte4_setup(self, filename):
kwargs = {'filename': filename, kwargs = {'filename': filename,
'codec': glymur.lib.openjp2._CODEC_J2K, 'codec': glymur.lib._openjp2.CODEC_J2K,
'comp_prec': 8, 'comp_prec': 8,
'irreversible': 0, 'irreversible': 0,
'num_comps': 1, 'num_comps': 1,
@ -370,12 +366,12 @@ class TestOpenJP2(unittest.TestCase):
with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile: with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile:
self.tte4_setup(tfile.name) self.tte4_setup(tfile.name)
kwargs = {'codec_format': glymur.lib.openjp2._CODEC_J2K} kwargs = {'codec_format': glymur.lib._openjp2.CODEC_J2K}
self.j2k_random_tile_access(tfile.name, **kwargs) self.j2k_random_tile_access(tfile.name, **kwargs)
def tte5_setup(self, filename): def tte5_setup(self, filename):
kwargs = {'filename': filename, kwargs = {'filename': filename,
'codec': glymur.lib.openjp2._CODEC_J2K, 'codec': glymur.lib._openjp2.CODEC_J2K,
'comp_prec': 8, 'comp_prec': 8,
'irreversible': 0, 'irreversible': 0,
'num_comps': 1, 'num_comps': 1,
@ -395,7 +391,7 @@ class TestOpenJP2(unittest.TestCase):
with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile: with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile:
self.tte5_setup(tfile.name) self.tte5_setup(tfile.name)
kwargs = {'codec_format': glymur.lib.openjp2._CODEC_J2K} kwargs = {'codec_format': glymur.lib._openjp2.CODEC_J2K}
self.j2k_random_tile_access(tfile.name, **kwargs) self.j2k_random_tile_access(tfile.name, **kwargs)
if __name__ == "__main__": if __name__ == "__main__":

View file

@ -0,0 +1,39 @@
#pylint: disable-all
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.DecompressionParametersType()
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)

View file

@ -1,9 +1,11 @@
from .test_callbacks import TestCallbacks as callbacks from .test_callbacks import TestCallbacks as callbacks
from .test_codestream import TestCodestream as codestream from .test_codestream import TestCodestream as codestream
from .test_config import TestSuite as config
from .test_jp2k import TestJp2k as jp2k from .test_jp2k import TestJp2k as jp2k
from .test_icc import TestICC as icc from .test_icc import TestICC as icc
from .test_printing import TestPrinting as printing from .test_printing import TestPrinting as printing
from .test_opj_suite import TestSuite as suite from .test_opj_suite import TestSuite as suite
from .test_opj_suite import TestSuiteDump as suitedump
from .test_opj_suite_write import TestSuiteWrite as suitew from .test_opj_suite_write import TestSuiteWrite as suitew
from .test_opj_suite_neg import TestSuiteNegative as suiteneg from .test_opj_suite_neg import TestSuiteNegative as suiteneg
from .test_jp2box import TestJp2Boxes as box from .test_jp2box import TestJp2Boxes as box

91
glymur/test/fixtures.py Normal file
View file

@ -0,0 +1,91 @@
import re
import sys
import numpy as np
def mse(amat, bmat):
"""Mean Square Error"""
diff = amat.astype(np.double) - bmat.astype(np.double)
err = np.mean(diff**2)
return err
def peak_tolerance(amat, bmat):
"""Peak Tolerance"""
diff = np.abs(amat.astype(np.double) - bmat.astype(np.double))
ptol = diff.max()
return ptol
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 fptr:
while True:
char = fptr.read(1)
if char[0] == 10 or char == '\n':
pos = fptr.tell()
break
else:
if sys.hexversion < 0x03000000:
header += char
else:
header += chr(char[0])
header = header.rstrip()
tokens = re.split('\s', header)
if (tokens[1][0] == 'M') and (sys.byteorder == 'little'):
swapbytes = True
elif (tokens[1][0] == 'L') and (sys.byteorder == 'big'):
swapbytes = True
else:
swapbytes = False
if (len(tokens) == 6):
bitdepth = int(tokens[3])
signed = bitdepth < 0
if signed:
bitdepth = -1 * bitdepth
nrows = int(tokens[5])
ncols = int(tokens[4])
else:
bitdepth = int(tokens[2])
signed = bitdepth < 0
if signed:
bitdepth = -1 * bitdepth
nrows = int(tokens[4])
ncols = int(tokens[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 fptr:
fptr.seek(pos)
data = np.fromfile(file=fptr, dtype=dtype).reshape(shape)
return(data.byteswap(swapbytes))

View file

@ -1,5 +1,7 @@
#pylint: disable-all
import os import os
import pkg_resources import pkg_resources
import re
import sys import sys
import tempfile import tempfile
import unittest import unittest
@ -13,7 +15,7 @@ else:
import glymur import glymur
@unittest.skipIf(glymur.lib.openjp2._OPENJP2 is None, @unittest.skipIf(glymur.lib.openjp2.OPENJP2 is None,
"Missing openjp2 library.") "Missing openjp2 library.")
class TestCallbacks(unittest.TestCase): class TestCallbacks(unittest.TestCase):
@ -21,13 +23,14 @@ class TestCallbacks(unittest.TestCase):
# Save sys.stdout. # Save sys.stdout.
self.stdout = sys.stdout self.stdout = sys.stdout
sys.stdout = StringIO() sys.stdout = StringIO()
self.jp2file = pkg_resources.resource_filename(glymur.__name__, self.jp2file = glymur.data.nemo()
"data/nemo.jp2") self.j2kfile = glymur.data.goodstuff()
def tearDown(self): def tearDown(self):
# Restore stdout. # Restore stdout.
sys.stdout = self.stdout sys.stdout = self.stdout
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
def test_info_callback_on_write(self): def test_info_callback_on_write(self):
# Verify the messages printed when writing an image in verbose mode. # Verify the messages printed when writing an image in verbose mode.
j = glymur.Jp2k(self.jp2file) j = glymur.Jp2k(self.jp2file)
@ -44,24 +47,69 @@ class TestCallbacks(unittest.TestCase):
def test_info_warning_callbacks_on_read(self): def test_info_warning_callbacks_on_read(self):
# Verify that we get the expected stdio output when our internal info # Verify that we get the expected stdio output when our internal info
# callback handler is enabled. # callback handler is enabled.
j = glymur.Jp2k(self.jp2file) j = glymur.Jp2k(self.j2kfile)
d = j.read(reduce=3, verbose=True, area=(0, 0, 512, 1024)) d = j.read(rlevel=1, verbose=True, area=(0, 0, 200, 150))
actual = sys.stdout.getvalue().strip() 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] Main header has been correctly decoded.',
'[INFO] Setting decoding area to 0,0,1024,512', '[INFO] Setting decoding area to 0,0,150,200',
'[INFO] Header of tile 0 / 17 has been read.', '[INFO] Header of tile 0 / 0 has been read.',
'[INFO] Tile 1/18 has been decoded.', '[INFO] Tile 1/1 has been decoded.',
'[INFO] Image data has been updated with tile 1.', '[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 !']
expected = '\n'.join(lines) expected = '\n'.join(lines)
self.assertEqual(actual, expected) 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(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)
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

View file

@ -1,3 +1,4 @@
#pylint: disable-all
import os import os
import struct import struct
import sys import sys
@ -29,6 +30,7 @@ class TestCodestream(unittest.TestCase):
def tearDown(self): def tearDown(self):
pass pass
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
def test_reserved_marker_segment(self): def test_reserved_marker_segment(self):
# Some marker segments were reserved in FCD15444-1. Since that # Some marker segments were reserved in FCD15444-1. Since that
# standard is old, some of them may have come into use. # standard is old, some of them may have come into use.
@ -54,10 +56,11 @@ class TestCodestream(unittest.TestCase):
j = Jp2k(tfile.name) j = Jp2k(tfile.name)
c = j.get_codestream() c = j.get_codestream()
self.assertEqual(c.segment[2].id, '0xff6f') self.assertEqual(c.segment[2].marker_id, '0xff6f')
self.assertEqual(c.segment[2].length, 3) self.assertEqual(c.segment[2].length, 3)
self.assertEqual(c.segment[2].data, b'\x00') self.assertEqual(c.segment[2]._data, b'\x00')
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
@unittest.skipIf(sys.hexversion < 0x03020000, @unittest.skipIf(sys.hexversion < 0x03020000,
"Uses features introduced in 3.2.") "Uses features introduced in 3.2.")
def test_unknown_marker_segment(self): def test_unknown_marker_segment(self):
@ -84,9 +87,9 @@ class TestCodestream(unittest.TestCase):
j = Jp2k(tfile.name) j = Jp2k(tfile.name)
c = j.get_codestream() c = j.get_codestream()
self.assertEqual(c.segment[2].id, '0xff79') self.assertEqual(c.segment[2].marker_id, '0xff79')
self.assertEqual(c.segment[2].length, 3) self.assertEqual(c.segment[2].length, 3)
self.assertEqual(c.segment[2].data, b'\x00') self.assertEqual(c.segment[2]._data, b'\x00')
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

View file

@ -1,3 +1,8 @@
"""These tests are for edge cases where OPENJPEG does not exist, but
OPENJP2 may be present in some form or other.
"""
#pylint: disable-all
import imp import imp
import os import os
import sys import sys
@ -16,14 +21,28 @@ from glymur import Jp2k
from glymur.lib import openjp2 as opj2 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, @unittest.skipIf(sys.hexversion < 0x03020000,
"Uses features introduced in 3.2.") "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): def setUp(self):
self.jp2file = pkg_resources.resource_filename(glymur.__name__,
"data/nemo.jp2")
imp.reload(glymur) imp.reload(glymur)
imp.reload(glymur.lib.openjp2) imp.reload(glymur.lib.openjp2)
self.jp2file = glymur.data.nemo()
def tearDown(self): def tearDown(self):
imp.reload(glymur) imp.reload(glymur)
@ -37,7 +56,7 @@ class TestJp2k(unittest.TestCase):
filename = os.path.join(configdir, 'glymurrc') filename = os.path.join(configdir, 'glymurrc')
with open(filename, 'wb') as tfile: with open(filename, 'wb') as tfile:
tfile.write('[library]\n'.encode()) tfile.write('[library]\n'.encode())
libloc = glymur.lib.openjp2._OPENJP2._name libloc = glymur.lib.openjp2.OPENJP2._name
line = 'openjp2: {0}\n'.format(libloc) line = 'openjp2: {0}\n'.format(libloc)
tfile.write(line.encode()) tfile.write(line.encode())
tfile.flush() tfile.flush()

View file

@ -1,3 +1,4 @@
#pylint: disable-all
import datetime import datetime
import os import os
import struct import struct

View file

@ -1,4 +1,6 @@
#pylint: disable-all
import doctest import doctest
import os
import tempfile import tempfile
import xml.etree.cElementTree as ET import xml.etree.cElementTree as ET
import unittest import unittest
@ -13,10 +15,16 @@ from glymur.jp2box import *
# Doc tests should be run as well. # Doc tests should be run as well.
def load_tests(loader, tests, ignore): def load_tests(loader, tests, ignore):
if os.name == "nt":
# Can't do it on windows, temporary file issue.
return tests
tests.addTests(doctest.DocTestSuite('glymur.jp2box')) tests.addTests(doctest.DocTestSuite('glymur.jp2box'))
return tests return tests
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
@unittest.skipIf(glymur.lib.openjp2.OPENJP2 is None,
"Missing openjp2 library.")
class TestChannelDefinition(unittest.TestCase): class TestChannelDefinition(unittest.TestCase):
@classmethod @classmethod
@ -55,9 +63,9 @@ class TestChannelDefinition(unittest.TestCase):
j2k = Jp2k(self.j2kfile) j2k = Jp2k(self.j2kfile)
c = j2k.get_codestream() c = j2k.get_codestream()
height = c.segment[1].Ysiz height = c.segment[1].ysiz
width = c.segment[1].Xsiz width = c.segment[1].xsiz
num_components = len(c.segment[1].XRsiz) num_components = len(c.segment[1].xrsiz)
self.jP = JPEG2000SignatureBox() self.jP = JPEG2000SignatureBox()
self.ftyp = FileTypeBox() self.ftyp = FileTypeBox()
@ -85,7 +93,7 @@ class TestChannelDefinition(unittest.TestCase):
jp2 = Jp2k(tfile.name) jp2 = Jp2k(tfile.name)
jp2h = jp2.box[2] jp2h = jp2.box[2]
boxes = [box.id for box in jp2h.box] boxes = [box.box_id for box in jp2h.box]
self.assertEqual(boxes, ['ihdr', 'colr', 'cdef']) self.assertEqual(boxes, ['ihdr', 'colr', 'cdef'])
self.assertEqual(jp2h.box[2].index, (0, 1, 2)) self.assertEqual(jp2h.box[2].index, (0, 1, 2))
self.assertEqual(jp2h.box[2].channel_type, (0, 0, 0)) self.assertEqual(jp2h.box[2].channel_type, (0, 0, 0))
@ -105,7 +113,7 @@ class TestChannelDefinition(unittest.TestCase):
jp2 = Jp2k(tfile.name) jp2 = Jp2k(tfile.name)
jp2h = jp2.box[2] jp2h = jp2.box[2]
boxes = [box.id for box in jp2h.box] boxes = [box.box_id for box in jp2h.box]
self.assertEqual(boxes, ['ihdr', 'colr', 'cdef']) self.assertEqual(boxes, ['ihdr', 'colr', 'cdef'])
self.assertEqual(jp2h.box[2].index, (0, 1, 2, 3)) self.assertEqual(jp2h.box[2].index, (0, 1, 2, 3))
self.assertEqual(jp2h.box[2].channel_type, (0, 0, 0, 1)) self.assertEqual(jp2h.box[2].channel_type, (0, 0, 0, 1))
@ -138,7 +146,7 @@ class TestChannelDefinition(unittest.TestCase):
jp2 = Jp2k(tfile.name) jp2 = Jp2k(tfile.name)
jp2h = jp2.box[2] jp2h = jp2.box[2]
boxes = [box.id for box in jp2h.box] boxes = [box.box_id for box in jp2h.box]
self.assertEqual(boxes, ['ihdr', 'colr', 'cdef']) self.assertEqual(boxes, ['ihdr', 'colr', 'cdef'])
self.assertEqual(jp2h.box[2].index, (0,)) self.assertEqual(jp2h.box[2].index, (0,))
self.assertEqual(jp2h.box[2].channel_type, (0,)) self.assertEqual(jp2h.box[2].channel_type, (0,))
@ -158,7 +166,7 @@ class TestChannelDefinition(unittest.TestCase):
jp2 = Jp2k(tfile.name) jp2 = Jp2k(tfile.name)
jp2h = jp2.box[2] jp2h = jp2.box[2]
boxes = [box.id for box in jp2h.box] boxes = [box.box_id for box in jp2h.box]
self.assertEqual(boxes, ['ihdr', 'colr', 'cdef']) self.assertEqual(boxes, ['ihdr', 'colr', 'cdef'])
self.assertEqual(jp2h.box[2].index, (0, 1)) self.assertEqual(jp2h.box[2].index, (0, 1))
self.assertEqual(jp2h.box[2].channel_type, (0, 1)) self.assertEqual(jp2h.box[2].channel_type, (0, 1))
@ -229,6 +237,7 @@ class TestChannelDefinition(unittest.TestCase):
association=[1, 2, 3]) association=[1, 2, 3])
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
class TestXML(unittest.TestCase): class TestXML(unittest.TestCase):
def setUp(self): def setUp(self):
@ -265,9 +274,9 @@ class TestXML(unittest.TestCase):
j2k = Jp2k(self.j2kfile) j2k = Jp2k(self.j2kfile)
c = j2k.get_codestream() c = j2k.get_codestream()
height = c.segment[1].Ysiz height = c.segment[1].ysiz
width = c.segment[1].Xsiz width = c.segment[1].xsiz
num_components = len(c.segment[1].XRsiz) num_components = len(c.segment[1].xrsiz)
self.jP = JPEG2000SignatureBox() self.jP = JPEG2000SignatureBox()
self.ftyp = FileTypeBox() self.ftyp = FileTypeBox()
@ -288,6 +297,8 @@ class TestXML(unittest.TestCase):
with self.assertRaises((IOError, OSError)) as ce: with self.assertRaises((IOError, OSError)) as ce:
xmlb = glymur.jp2box.XMLBox(filename=self.xmlfile, xml=xml_object) 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): def test_basic_xml(self):
# Should be able to write an XMLBox. # Should be able to write an XMLBox.
j2k = Jp2k(self.j2kfile) j2k = Jp2k(self.j2kfile)
@ -304,10 +315,12 @@ class TestXML(unittest.TestCase):
with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile: with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile:
j2k.wrap(tfile.name, boxes=boxes) j2k.wrap(tfile.name, boxes=boxes)
jp2 = Jp2k(tfile.name) jp2 = Jp2k(tfile.name)
self.assertEqual(jp2.box[3].id, 'xml ') self.assertEqual(jp2.box[3].box_id, 'xml ')
self.assertEqual(ET.tostring(jp2.box[3].xml), self.assertEqual(ET.tostring(jp2.box[3].xml),
b'<data>0</data>') b'<data>0</data>')
@unittest.skipIf(os.name == "nt",
"Problems using NamedTemporaryFile on windows.")
def test_xml_from_file(self): def test_xml_from_file(self):
j2k = Jp2k(self.j2kfile) j2k = Jp2k(self.j2kfile)
@ -319,7 +332,7 @@ class TestXML(unittest.TestCase):
j2k.wrap(tfile.name, boxes=boxes) j2k.wrap(tfile.name, boxes=boxes)
jp2 = Jp2k(tfile.name) jp2 = Jp2k(tfile.name)
output_boxes = [box.id for box in jp2.box] output_boxes = [box.box_id for box in jp2.box]
self.assertEqual(output_boxes, ['jP ', 'ftyp', 'jp2h', 'xml ', self.assertEqual(output_boxes, ['jP ', 'ftyp', 'jp2h', 'xml ',
'jp2c']) 'jp2c'])
@ -338,9 +351,9 @@ class TestColourSpecificationBox(unittest.TestCase):
j2k = Jp2k(self.j2kfile) j2k = Jp2k(self.j2kfile)
c = j2k.get_codestream() c = j2k.get_codestream()
height = c.segment[1].Ysiz height = c.segment[1].ysiz
width = c.segment[1].Xsiz width = c.segment[1].xsiz
num_components = len(c.segment[1].XRsiz) num_components = len(c.segment[1].xrsiz)
self.jP = JPEG2000SignatureBox() self.jP = JPEG2000SignatureBox()
self.ftyp = FileTypeBox() self.ftyp = FileTypeBox()
@ -352,6 +365,8 @@ class TestColourSpecificationBox(unittest.TestCase):
def tearDown(self): def tearDown(self):
pass pass
@unittest.skipIf(os.name == "nt",
"Problems using NamedTemporaryFile on windows.")
def test_color_specification_box_with_out_enumerated_colorspace(self): def test_color_specification_box_with_out_enumerated_colorspace(self):
j2k = Jp2k(self.j2kfile) j2k = Jp2k(self.j2kfile)
@ -361,6 +376,7 @@ class TestColourSpecificationBox(unittest.TestCase):
with self.assertRaises(NotImplementedError): with self.assertRaises(NotImplementedError):
j2k.wrap(tfile.name, boxes=boxes) j2k.wrap(tfile.name, boxes=boxes)
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
def test_missing_colr_box(self): def test_missing_colr_box(self):
j2k = Jp2k(self.j2kfile) j2k = Jp2k(self.j2kfile)
boxes = [self.jP, self.ftyp, self.jp2h, self.jp2c] boxes = [self.jP, self.ftyp, self.jp2h, self.jp2c]
@ -400,7 +416,7 @@ class TestColourSpecificationBox(unittest.TestCase):
approximation=approx) approximation=approx)
@unittest.skipIf(glymur.lib.openjp2._OPENJP2 is None, @unittest.skipIf(glymur.lib.openjp2.OPENJP2 is None,
"Missing openjp2 library.") "Missing openjp2 library.")
class TestJp2Boxes(unittest.TestCase): class TestJp2Boxes(unittest.TestCase):
@ -440,37 +456,37 @@ class TestJp2Boxes(unittest.TestCase):
def test_default_ContiguousCodestreamBox(self): def test_default_ContiguousCodestreamBox(self):
b = ContiguousCodestreamBox() b = ContiguousCodestreamBox()
self.assertEqual(b.id, 'jp2c') self.assertEqual(b.box_id, 'jp2c')
self.assertEqual(b.main_header, []) self.assertIsNone(b.main_header)
def verify_wrapped_raw(self, jp2file): def verify_wrapped_raw(self, jp2file):
# Shared method by at least two tests. # Shared method by at least two tests.
jp2 = Jp2k(jp2file) jp2 = Jp2k(jp2file)
self.assertEqual(len(jp2.box), 4) self.assertEqual(len(jp2.box), 4)
self.assertEqual(jp2.box[0].id, 'jP ') self.assertEqual(jp2.box[0].box_id, 'jP ')
self.assertEqual(jp2.box[0].offset, 0) self.assertEqual(jp2.box[0].offset, 0)
self.assertEqual(jp2.box[0].length, 12) self.assertEqual(jp2.box[0].length, 12)
self.assertEqual(jp2.box[0].longname, 'JPEG 2000 Signature') self.assertEqual(jp2.box[0].longname, 'JPEG 2000 Signature')
self.assertEqual(jp2.box[1].id, 'ftyp') self.assertEqual(jp2.box[1].box_id, 'ftyp')
self.assertEqual(jp2.box[1].offset, 12) self.assertEqual(jp2.box[1].offset, 12)
self.assertEqual(jp2.box[1].length, 20) self.assertEqual(jp2.box[1].length, 20)
self.assertEqual(jp2.box[1].longname, 'File Type') self.assertEqual(jp2.box[1].longname, 'File Type')
self.assertEqual(jp2.box[2].id, 'jp2h') self.assertEqual(jp2.box[2].box_id, 'jp2h')
self.assertEqual(jp2.box[2].offset, 32) self.assertEqual(jp2.box[2].offset, 32)
self.assertEqual(jp2.box[2].length, 45) self.assertEqual(jp2.box[2].length, 45)
self.assertEqual(jp2.box[2].longname, 'JP2 Header') self.assertEqual(jp2.box[2].longname, 'JP2 Header')
self.assertEqual(jp2.box[3].id, 'jp2c') self.assertEqual(jp2.box[3].box_id, 'jp2c')
self.assertEqual(jp2.box[3].offset, 77) self.assertEqual(jp2.box[3].offset, 77)
self.assertEqual(jp2.box[3].length, 115228) self.assertEqual(jp2.box[3].length, 115228)
# jp2h super box # jp2h super box
self.assertEqual(len(jp2.box[2].box), 2) self.assertEqual(len(jp2.box[2].box), 2)
self.assertEqual(jp2.box[2].box[0].id, 'ihdr') self.assertEqual(jp2.box[2].box[0].box_id, 'ihdr')
self.assertEqual(jp2.box[2].box[0].offset, 40) self.assertEqual(jp2.box[2].box[0].offset, 40)
self.assertEqual(jp2.box[2].box[0].length, 22) self.assertEqual(jp2.box[2].box[0].length, 22)
self.assertEqual(jp2.box[2].box[0].longname, 'Image Header') self.assertEqual(jp2.box[2].box[0].longname, 'Image Header')
@ -483,7 +499,7 @@ class TestJp2Boxes(unittest.TestCase):
self.assertEqual(jp2.box[2].box[0].colorspace_unknown, False) self.assertEqual(jp2.box[2].box[0].colorspace_unknown, False)
self.assertEqual(jp2.box[2].box[0].ip_provided, False) self.assertEqual(jp2.box[2].box[0].ip_provided, False)
self.assertEqual(jp2.box[2].box[1].id, 'colr') self.assertEqual(jp2.box[2].box[1].box_id, 'colr')
self.assertEqual(jp2.box[2].box[1].offset, 62) self.assertEqual(jp2.box[2].box[1].offset, 62)
self.assertEqual(jp2.box[2].box[1].length, 15) self.assertEqual(jp2.box[2].box[1].length, 15)
self.assertEqual(jp2.box[2].box[1].longname, 'Colour Specification') self.assertEqual(jp2.box[2].box[1].longname, 'Colour Specification')
@ -492,19 +508,22 @@ class TestJp2Boxes(unittest.TestCase):
self.assertEqual(jp2.box[2].box[1].colorspace, glymur.core.SRGB) self.assertEqual(jp2.box[2].box[1].colorspace, glymur.core.SRGB)
self.assertIsNone(jp2.box[2].box[1].icc_profile) self.assertIsNone(jp2.box[2].box[1].icc_profile)
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
def test_wrap(self): def test_wrap(self):
j2k = Jp2k(self.j2kfile) j2k = Jp2k(self.j2kfile)
with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile: with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile:
j2k.wrap(tfile.name) j2k.wrap(tfile.name)
self.verify_wrapped_raw(tfile.name) self.verify_wrapped_raw(tfile.name)
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
def test_wrap_jp2(self): def test_wrap_jp2(self):
j2k = Jp2k(self.j2kfile) j2k = Jp2k(self.j2kfile)
with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile: with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile:
jp2 = j2k.wrap(tfile.name) jp2 = j2k.wrap(tfile.name)
boxes = [box.id for box in jp2.box] boxes = [box.box_id for box in jp2.box]
self.assertEqual(boxes, ['jP ', 'ftyp', 'jp2h', 'jp2c']) self.assertEqual(boxes, ['jP ', 'ftyp', 'jp2h', 'jp2c'])
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
def test_default_layout_but_with_specified_boxes(self): def test_default_layout_but_with_specified_boxes(self):
j2k = Jp2k(self.j2kfile) j2k = Jp2k(self.j2kfile)
boxes = [JPEG2000SignatureBox(), boxes = [JPEG2000SignatureBox(),
@ -512,9 +531,9 @@ class TestJp2Boxes(unittest.TestCase):
JP2HeaderBox(), JP2HeaderBox(),
ContiguousCodestreamBox()] ContiguousCodestreamBox()]
c = j2k.get_codestream() c = j2k.get_codestream()
height = c.segment[1].Ysiz height = c.segment[1].ysiz
width = c.segment[1].Xsiz width = c.segment[1].xsiz
num_components = len(c.segment[1].XRsiz) num_components = len(c.segment[1].xrsiz)
boxes[2].box = [ImageHeaderBox(height=height, boxes[2].box = [ImageHeaderBox(height=height,
width=width, width=width,
num_components=num_components), num_components=num_components),
@ -523,6 +542,7 @@ class TestJp2Boxes(unittest.TestCase):
j2k.wrap(tfile.name, boxes=boxes) j2k.wrap(tfile.name, boxes=boxes)
self.verify_wrapped_raw(tfile.name) self.verify_wrapped_raw(tfile.name)
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
def test_image_header_box_not_first_in_jp2_header(self): def test_image_header_box_not_first_in_jp2_header(self):
# The specification says that ihdr must be the first box in jp2h. # The specification says that ihdr must be the first box in jp2h.
j2k = Jp2k(self.j2kfile) j2k = Jp2k(self.j2kfile)
@ -531,9 +551,9 @@ class TestJp2Boxes(unittest.TestCase):
JP2HeaderBox(), JP2HeaderBox(),
ContiguousCodestreamBox()] ContiguousCodestreamBox()]
c = j2k.get_codestream() c = j2k.get_codestream()
height = c.segment[1].Ysiz height = c.segment[1].ysiz
width = c.segment[1].Xsiz width = c.segment[1].xsiz
num_components = len(c.segment[1].XRsiz) num_components = len(c.segment[1].xrsiz)
boxes[2].box = [ColourSpecificationBox(colorspace=glymur.core.SRGB), boxes[2].box = [ColourSpecificationBox(colorspace=glymur.core.SRGB),
ImageHeaderBox(height=height, ImageHeaderBox(height=height,
width=width, width=width,
@ -542,12 +562,13 @@ class TestJp2Boxes(unittest.TestCase):
with self.assertRaises(IOError): with self.assertRaises(IOError):
j2k.wrap(tfile.name, boxes=boxes) j2k.wrap(tfile.name, boxes=boxes)
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
def test_first_2_boxes_not_jP_and_ftyp(self): def test_first_2_boxes_not_jP_and_ftyp(self):
j2k = Jp2k(self.j2kfile) j2k = Jp2k(self.j2kfile)
c = j2k.get_codestream() c = j2k.get_codestream()
height = c.segment[1].Ysiz height = c.segment[1].ysiz
width = c.segment[1].Xsiz width = c.segment[1].xsiz
num_components = len(c.segment[1].XRsiz) num_components = len(c.segment[1].xrsiz)
jP = JPEG2000SignatureBox() jP = JPEG2000SignatureBox()
ftyp = FileTypeBox() ftyp = FileTypeBox()
@ -562,12 +583,13 @@ class TestJp2Boxes(unittest.TestCase):
with self.assertRaises(IOError): with self.assertRaises(IOError):
j2k.wrap(tfile.name, boxes=boxes) j2k.wrap(tfile.name, boxes=boxes)
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
def test_jp2h_not_preceeding_jp2c(self): def test_jp2h_not_preceeding_jp2c(self):
j2k = Jp2k(self.j2kfile) j2k = Jp2k(self.j2kfile)
c = j2k.get_codestream() c = j2k.get_codestream()
height = c.segment[1].Ysiz height = c.segment[1].ysiz
width = c.segment[1].Xsiz width = c.segment[1].xsiz
num_components = len(c.segment[1].XRsiz) num_components = len(c.segment[1].xrsiz)
jP = JPEG2000SignatureBox() jP = JPEG2000SignatureBox()
ftyp = FileTypeBox() ftyp = FileTypeBox()
@ -582,12 +604,13 @@ class TestJp2Boxes(unittest.TestCase):
with self.assertRaises(IOError): with self.assertRaises(IOError):
j2k.wrap(tfile.name, boxes=boxes) j2k.wrap(tfile.name, boxes=boxes)
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
def test_missing_codestream(self): def test_missing_codestream(self):
j2k = Jp2k(self.j2kfile) j2k = Jp2k(self.j2kfile)
c = j2k.get_codestream() c = j2k.get_codestream()
height = c.segment[1].Ysiz height = c.segment[1].ysiz
width = c.segment[1].Xsiz width = c.segment[1].xsiz
num_components = len(c.segment[1].XRsiz) num_components = len(c.segment[1].xrsiz)
jP = JPEG2000SignatureBox() jP = JPEG2000SignatureBox()
ftyp = FileTypeBox() ftyp = FileTypeBox()

View file

@ -1,7 +1,5 @@
import contextlib # pylint: disable-all
import ctypes
import doctest import doctest
import imp
import os import os
import re import re
import shutil import shutil
@ -33,14 +31,18 @@ except:
# Doc tests should be run as well. # Doc tests should be run as well.
def load_tests(loader, tests, ignore): def load_tests(loader, tests, ignore):
if glymur.lib.openjp2._OPENJP2 is not None: if os.name == "nt":
# Can't do it on windows, temporary file issue.
return tests
if glymur.lib.openjp2.OPENJP2 is not None:
tests.addTests(doctest.DocTestSuite('glymur.jp2k')) tests.addTests(doctest.DocTestSuite('glymur.jp2k'))
return tests return tests
@unittest.skipIf(glymur.lib.openjp2._OPENJP2 is None, @unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
@unittest.skipIf(glymur.lib.openjp2.OPENJP2 is None,
"Missing openjp2 library.") "Missing openjp2 library.")
class TestJp2k(unittest.TestCase): class TestJp2kBadXmlFile(unittest.TestCase):
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
@ -74,10 +76,8 @@ class TestJp2k(unittest.TestCase):
os.unlink(cls._bad_xml_file) os.unlink(cls._bad_xml_file)
def setUp(self): def setUp(self):
self.jp2file = pkg_resources.resource_filename(glymur.__name__, self.jp2file = glymur.data.nemo()
"data/nemo.jp2") self.j2kfile = glymur.data.goodstuff()
self.j2kfile = pkg_resources.resource_filename(glymur.__name__,
"data/goodstuff.j2k")
def tearDown(self): def tearDown(self):
pass pass
@ -90,25 +90,37 @@ class TestJp2k(unittest.TestCase):
with self.assertWarns(UserWarning) as cw: with self.assertWarns(UserWarning) as cw:
jp2k = Jp2k(self._bad_xml_file) jp2k = Jp2k(self._bad_xml_file)
def test_reduce_max(self):
# Verify that reduce=-1 gets us the lowest resolution image
j = Jp2k(self.jp2file)
thumbnail1 = j.read(reduce=-1)
thumbnail2 = j.read(reduce=5)
np.testing.assert_array_equal(thumbnail1, thumbnail2)
self.assertEqual(thumbnail1.shape, (46, 81, 3))
def test_invalid_xml_box(self): def test_invalid_xml_box(self):
# Should be able to recover from xml box with bad xml. # Should be able to recover from xml box with bad xml.
with warnings.catch_warnings(): with warnings.catch_warnings():
warnings.simplefilter("ignore") warnings.simplefilter("ignore")
jp2k = Jp2k(self._bad_xml_file) jp2k = Jp2k(self._bad_xml_file)
self.assertEqual(jp2k.box[3].id, 'xml ') self.assertEqual(jp2k.box[3].box_id, 'xml ')
self.assertEqual(jp2k.box[3].offset, 77) self.assertEqual(jp2k.box[3].offset, 77)
self.assertEqual(jp2k.box[3].length, 28) self.assertEqual(jp2k.box[3].length, 28)
self.assertIsNone(jp2k.box[3].xml) self.assertIsNone(jp2k.box[3].xml)
@unittest.skipIf(glymur.lib.openjp2.OPENJP2 is None,
"Missing openjp2 library.")
class TestJp2k(unittest.TestCase):
def setUp(self):
self.jp2file = glymur.data.nemo()
self.j2kfile = glymur.data.goodstuff()
def tearDown(self):
pass
def test_rlevel_max(self):
# Verify that rlevel=-1 gets us the lowest resolution image
j = Jp2k(self.j2kfile)
thumbnail1 = j.read(rlevel=-1)
thumbnail2 = j.read(rlevel=5)
np.testing.assert_array_equal(thumbnail1, thumbnail2)
self.assertEqual(thumbnail1.shape, (25, 15, 3))
def test_bad_area_parameter(self): def test_bad_area_parameter(self):
# Verify that we error out appropriately if given a bad area parameter. # Verify that we error out appropriately if given a bad area parameter.
j = Jp2k(self.jp2file) j = Jp2k(self.jp2file)
@ -122,11 +134,11 @@ class TestJp2k(unittest.TestCase):
# End corner must be >= start corner # End corner must be >= start corner
d = j.read(area=(10, 10, 8, 8)) d = j.read(area=(10, 10, 8, 8))
def test_reduce_too_high(self): def test_rlevel_too_high(self):
# Verify that we error out appropriately if not given a JPEG 2000 file. # Verify that we error out appropriately if not given a JPEG 2000 file.
j = Jp2k(self.jp2file) j = Jp2k(self.jp2file)
with self.assertRaises(IOError): with self.assertRaises(IOError):
d = j.read(reduce=6) d = j.read(rlevel=6)
def test_not_JPEG2000(self): def test_not_JPEG2000(self):
# Verify that we error out appropriately if not given a JPEG 2000 file. # Verify that we error out appropriately if not given a JPEG 2000 file.
@ -145,15 +157,7 @@ class TestJp2k(unittest.TestCase):
filename = 'this file does not actually exist on the file system.' filename = 'this file does not actually exist on the file system.'
jp2k = Jp2k(filename) jp2k = Jp2k(filename)
def test_nemo_tile(self): @unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
# 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): def test_write_srgb_without_mct(self):
j2k = Jp2k(self.j2kfile) j2k = Jp2k(self.j2kfile)
expdata = j2k.read() expdata = j2k.read()
@ -164,8 +168,9 @@ class TestJp2k(unittest.TestCase):
np.testing.assert_array_equal(actdata, expdata) np.testing.assert_array_equal(actdata, expdata)
c = ofile.get_codestream() c = ofile.get_codestream()
self.assertEqual(c.segment[2].SPcod[3], 0) # no mct self.assertEqual(c.segment[2].spcod[3], 0) # no mct
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
def test_write_grayscale_with_mct(self): def test_write_grayscale_with_mct(self):
# MCT usage makes no sense for grayscale images. # MCT usage makes no sense for grayscale images.
j2k = Jp2k(self.j2kfile) j2k = Jp2k(self.j2kfile)
@ -175,10 +180,11 @@ class TestJp2k(unittest.TestCase):
with self.assertRaises(IOError): with self.assertRaises(IOError):
ofile.write(expdata[:, :, 0], mct=True) ofile.write(expdata[:, :, 0], mct=True)
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
def test_write_cprl(self): def test_write_cprl(self):
# Issue 17 # Issue 17
j = Jp2k(self.jp2file) j = Jp2k(self.jp2file)
expdata = j.read(reduce=2) expdata = j.read(rlevel=1)
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
ofile = Jp2k(tfile.name, 'wb') ofile = Jp2k(tfile.name, 'wb')
ofile.write(expdata, prog='CPRL') ofile.write(expdata, prog='CPRL')
@ -186,7 +192,7 @@ class TestJp2k(unittest.TestCase):
np.testing.assert_array_equal(actdata, expdata) np.testing.assert_array_equal(actdata, expdata)
c = ofile.get_codestream() c = ofile.get_codestream()
self.assertEqual(c.segment[2].SPcod[0], glymur.core.CPRL) self.assertEqual(c.segment[2].spcod[0], glymur.core.CPRL)
def test_jp2_boxes(self): def test_jp2_boxes(self):
# Verify the boxes of a JP2 file. # Verify the boxes of a JP2 file.
@ -195,37 +201,37 @@ class TestJp2k(unittest.TestCase):
# top-level boxes # top-level boxes
self.assertEqual(len(jp2k.box), 6) self.assertEqual(len(jp2k.box), 6)
self.assertEqual(jp2k.box[0].id, 'jP ') self.assertEqual(jp2k.box[0].box_id, 'jP ')
self.assertEqual(jp2k.box[0].offset, 0) self.assertEqual(jp2k.box[0].offset, 0)
self.assertEqual(jp2k.box[0].length, 12) self.assertEqual(jp2k.box[0].length, 12)
self.assertEqual(jp2k.box[0].longname, 'JPEG 2000 Signature') self.assertEqual(jp2k.box[0].longname, 'JPEG 2000 Signature')
self.assertEqual(jp2k.box[1].id, 'ftyp') self.assertEqual(jp2k.box[1].box_id, 'ftyp')
self.assertEqual(jp2k.box[1].offset, 12) self.assertEqual(jp2k.box[1].offset, 12)
self.assertEqual(jp2k.box[1].length, 20) self.assertEqual(jp2k.box[1].length, 20)
self.assertEqual(jp2k.box[1].longname, 'File Type') self.assertEqual(jp2k.box[1].longname, 'File Type')
self.assertEqual(jp2k.box[2].id, 'jp2h') self.assertEqual(jp2k.box[2].box_id, 'jp2h')
self.assertEqual(jp2k.box[2].offset, 32) self.assertEqual(jp2k.box[2].offset, 32)
self.assertEqual(jp2k.box[2].length, 45) self.assertEqual(jp2k.box[2].length, 45)
self.assertEqual(jp2k.box[2].longname, 'JP2 Header') self.assertEqual(jp2k.box[2].longname, 'JP2 Header')
self.assertEqual(jp2k.box[3].id, 'uuid') self.assertEqual(jp2k.box[3].box_id, 'uuid')
self.assertEqual(jp2k.box[3].offset, 77) self.assertEqual(jp2k.box[3].offset, 77)
self.assertEqual(jp2k.box[3].length, 638) self.assertEqual(jp2k.box[3].length, 638)
self.assertEqual(jp2k.box[4].id, 'uuid') self.assertEqual(jp2k.box[4].box_id, 'uuid')
self.assertEqual(jp2k.box[4].offset, 715) self.assertEqual(jp2k.box[4].offset, 715)
self.assertEqual(jp2k.box[4].length, 2412) self.assertEqual(jp2k.box[4].length, 2412)
self.assertEqual(jp2k.box[5].id, 'jp2c') self.assertEqual(jp2k.box[5].box_id, 'jp2c')
self.assertEqual(jp2k.box[5].offset, 3127) self.assertEqual(jp2k.box[5].offset, 3127)
self.assertEqual(jp2k.box[5].length, 1133427) self.assertEqual(jp2k.box[5].length, 1132296)
# jp2h super box # jp2h super box
self.assertEqual(len(jp2k.box[2].box), 2) self.assertEqual(len(jp2k.box[2].box), 2)
self.assertEqual(jp2k.box[2].box[0].id, 'ihdr') self.assertEqual(jp2k.box[2].box[0].box_id, 'ihdr')
self.assertEqual(jp2k.box[2].box[0].offset, 40) self.assertEqual(jp2k.box[2].box[0].offset, 40)
self.assertEqual(jp2k.box[2].box[0].length, 22) self.assertEqual(jp2k.box[2].box[0].length, 22)
self.assertEqual(jp2k.box[2].box[0].longname, 'Image Header') self.assertEqual(jp2k.box[2].box[0].longname, 'Image Header')
@ -238,7 +244,7 @@ class TestJp2k(unittest.TestCase):
self.assertEqual(jp2k.box[2].box[0].colorspace_unknown, False) self.assertEqual(jp2k.box[2].box[0].colorspace_unknown, False)
self.assertEqual(jp2k.box[2].box[0].ip_provided, False) self.assertEqual(jp2k.box[2].box[0].ip_provided, False)
self.assertEqual(jp2k.box[2].box[1].id, 'colr') self.assertEqual(jp2k.box[2].box[1].box_id, 'colr')
self.assertEqual(jp2k.box[2].box[1].offset, 62) self.assertEqual(jp2k.box[2].box[1].offset, 62)
self.assertEqual(jp2k.box[2].box[1].length, 15) self.assertEqual(jp2k.box[2].box[1].length, 15)
self.assertEqual(jp2k.box[2].box[1].longname, 'Colour Specification') self.assertEqual(jp2k.box[2].box[1].longname, 'Colour Specification')
@ -255,6 +261,7 @@ class TestJp2k(unittest.TestCase):
jp2k = Jp2k(filename) jp2k = Jp2k(filename)
self.assertEqual(len(jp2k.box), 0) self.assertEqual(len(jp2k.box), 0)
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
def test_64bit_XL_field(self): def test_64bit_XL_field(self):
# Verify that boxes with the XL field are properly read. # Verify that boxes with the XL field are properly read.
# Don't have such a file on hand, so we create one. Copy our example # Don't have such a file on hand, so we create one. Copy our example
@ -283,10 +290,11 @@ class TestJp2k(unittest.TestCase):
jp2k = Jp2k(tfile.name) jp2k = Jp2k(tfile.name)
self.assertEqual(jp2k.box[5].id, 'jp2c') self.assertEqual(jp2k.box[5].box_id, 'jp2c')
self.assertEqual(jp2k.box[5].offset, 3127) self.assertEqual(jp2k.box[5].offset, 3127)
self.assertEqual(jp2k.box[5].length, 1133427 + 8) self.assertEqual(jp2k.box[5].length, 1133427 + 8)
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
def test_L_is_zero(self): def test_L_is_zero(self):
# Verify that boxes with the L field as zero are correctly read. # Verify that boxes with the L field as zero are correctly read.
# This should only happen in the last box of a JPEG 2000 file. # This should only happen in the last box of a JPEG 2000 file.
@ -314,8 +322,8 @@ class TestJp2k(unittest.TestCase):
# The top level boxes in each file should match. # The top level boxes in each file should match.
for j in range(len(baseline_jp2.box)): for j in range(len(baseline_jp2.box)):
self.assertEqual(new_jp2.box[j].id, self.assertEqual(new_jp2.box[j].box_id,
baseline_jp2.box[j].id) baseline_jp2.box[j].box_id)
self.assertEqual(new_jp2.box[j].offset, self.assertEqual(new_jp2.box[j].offset,
baseline_jp2.box[j].offset) baseline_jp2.box[j].offset)
self.assertEqual(new_jp2.box[j].length, self.assertEqual(new_jp2.box[j].length,
@ -330,7 +338,7 @@ class TestJp2k(unittest.TestCase):
# Issue 86. # Issue 86.
filename = os.path.join(data_root, 'input/conformance/p0_05.j2k') filename = os.path.join(data_root, 'input/conformance/p0_05.j2k')
j = Jp2k(filename) j = Jp2k(filename)
with self.assertRaises(IOError): with self.assertRaises(RuntimeError):
j.read() j.read()
@unittest.skipIf(data_root is None, @unittest.skipIf(data_root is None,
@ -342,6 +350,7 @@ class TestJp2k(unittest.TestCase):
j = Jp2k(filename) j = Jp2k(filename)
self.assertEqual(j.box, []) self.assertEqual(j.box, [])
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
def test_code_block_height_different_than_width(self): def test_code_block_height_different_than_width(self):
# Verify that we can set a code block size where height does not equal # Verify that we can set a code block size where height does not equal
# width. # width.
@ -355,8 +364,9 @@ class TestJp2k(unittest.TestCase):
c = j.get_codestream() c = j.get_codestream()
# Code block size is reported as XY in the codestream. # Code block size is reported as XY in the codestream.
self.assertEqual(tuple(c.segment[2].SPcod[5:7]), (3, 2)) self.assertEqual(tuple(c.segment[2].spcod[5:7]), (3, 2))
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
def test_negative_too_many_dimensions(self): def test_negative_too_many_dimensions(self):
# OpenJP2 only allows 2D or 3D images. # OpenJP2 only allows 2D or 3D images.
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
@ -365,6 +375,7 @@ class TestJp2k(unittest.TestCase):
data = np.zeros((128, 128, 2, 2), dtype=np.uint8) data = np.zeros((128, 128, 2, 2), dtype=np.uint8)
j.write(data) j.write(data)
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
def test_unrecognized_jp2_colorspace(self): def test_unrecognized_jp2_colorspace(self):
# We only allow RGB and GRAYSCALE. # We only allow RGB and GRAYSCALE.
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
@ -373,6 +384,7 @@ class TestJp2k(unittest.TestCase):
data = np.zeros((128, 128, 3), dtype=np.uint8) data = np.zeros((128, 128, 3), dtype=np.uint8)
j.write(data, colorspace='cmyk') j.write(data, colorspace='cmyk')
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
def test_2D_rgb(self): def test_2D_rgb(self):
# RGB must have at least 3 components. # RGB must have at least 3 components.
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
@ -381,6 +393,7 @@ class TestJp2k(unittest.TestCase):
data = np.zeros((128, 128, 2), dtype=np.uint8) data = np.zeros((128, 128, 2), dtype=np.uint8)
j.write(data, colorspace='rgb') j.write(data, colorspace='rgb')
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
def test_colorspace_with_j2k(self): def test_colorspace_with_j2k(self):
# Specifying a colorspace with J2K does not make sense. # Specifying a colorspace with J2K does not make sense.
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
@ -389,6 +402,7 @@ class TestJp2k(unittest.TestCase):
data = np.zeros((128, 128, 3), dtype=np.uint8) data = np.zeros((128, 128, 3), dtype=np.uint8)
j.write(data, colorspace='rgb') j.write(data, colorspace='rgb')
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
def test_specify_rgb(self): def test_specify_rgb(self):
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
j = Jp2k(tfile.name, 'wb') j = Jp2k(tfile.name, 'wb')
@ -396,6 +410,7 @@ class TestJp2k(unittest.TestCase):
j.write(data, colorspace='rgb') j.write(data, colorspace='rgb')
self.assertEqual(j.box[2].box[1].colorspace, glymur.core.SRGB) self.assertEqual(j.box[2].box[1].colorspace, glymur.core.SRGB)
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
def test_specify_gray(self): def test_specify_gray(self):
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
j = Jp2k(tfile.name, 'wb') j = Jp2k(tfile.name, 'wb')
@ -404,6 +419,7 @@ class TestJp2k(unittest.TestCase):
self.assertEqual(j.box[2].box[1].colorspace, self.assertEqual(j.box[2].box[1].colorspace,
glymur.core.GREYSCALE) glymur.core.GREYSCALE)
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
def test_specify_grey(self): def test_specify_grey(self):
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
j = Jp2k(tfile.name, 'wb') j = Jp2k(tfile.name, 'wb')
@ -412,6 +428,7 @@ class TestJp2k(unittest.TestCase):
self.assertEqual(j.box[2].box[1].colorspace, self.assertEqual(j.box[2].box[1].colorspace,
glymur.core.GREYSCALE) glymur.core.GREYSCALE)
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
def test_grey_with_extra_component(self): def test_grey_with_extra_component(self):
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
j = Jp2k(tfile.name, 'wb') j = Jp2k(tfile.name, 'wb')
@ -423,6 +440,7 @@ class TestJp2k(unittest.TestCase):
self.assertEqual(j.box[2].box[1].colorspace, self.assertEqual(j.box[2].box[1].colorspace,
glymur.core.GREYSCALE) glymur.core.GREYSCALE)
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
def test_grey_with_two_extra_components(self): def test_grey_with_two_extra_components(self):
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
j = Jp2k(tfile.name, 'wb') j = Jp2k(tfile.name, 'wb')
@ -434,6 +452,7 @@ class TestJp2k(unittest.TestCase):
self.assertEqual(j.box[2].box[1].colorspace, self.assertEqual(j.box[2].box[1].colorspace,
glymur.core.GREYSCALE) glymur.core.GREYSCALE)
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
def test_rgb_with_extra_component(self): def test_rgb_with_extra_component(self):
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
j = Jp2k(tfile.name, 'wb') j = Jp2k(tfile.name, 'wb')
@ -452,6 +471,7 @@ class TestJp2k(unittest.TestCase):
data = np.zeros((128, 128, 3), dtype=np.uint8) data = np.zeros((128, 128, 3), dtype=np.uint8)
j.write(data, colorspace='ycc') j.write(data, colorspace='ycc')
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
def test_uinf_ulst_url_boxes(self): def test_uinf_ulst_url_boxes(self):
# Verify that we can read UINF, ULST, and URL boxes. I don't have # Verify that we can read UINF, ULST, and URL boxes. I don't have
# easy access to such a file, and there's no such file in the # easy access to such a file, and there's no such file in the
@ -490,24 +510,25 @@ class TestJp2k(unittest.TestCase):
jp2k = Jp2k(tfile.name) jp2k = Jp2k(tfile.name)
self.assertEqual(jp2k.box[3].id, 'uinf') self.assertEqual(jp2k.box[3].box_id, 'uinf')
self.assertEqual(jp2k.box[3].offset, 77) self.assertEqual(jp2k.box[3].offset, 77)
self.assertEqual(jp2k.box[3].length, 50) self.assertEqual(jp2k.box[3].length, 50)
self.assertEqual(jp2k.box[3].box[0].id, 'ulst') self.assertEqual(jp2k.box[3].box[0].box_id, 'ulst')
self.assertEqual(jp2k.box[3].box[0].offset, 85) self.assertEqual(jp2k.box[3].box[0].offset, 85)
self.assertEqual(jp2k.box[3].box[0].length, 26) self.assertEqual(jp2k.box[3].box[0].length, 26)
ulst = [] ulst = []
ulst.append(uuid.UUID('00000000-0000-0000-0000-000000000000')) ulst.append(uuid.UUID('00000000-0000-0000-0000-000000000000'))
self.assertEqual(jp2k.box[3].box[0].ulst, ulst) self.assertEqual(jp2k.box[3].box[0].ulst, ulst)
self.assertEqual(jp2k.box[3].box[1].id, 'url ') self.assertEqual(jp2k.box[3].box[1].box_id, 'url ')
self.assertEqual(jp2k.box[3].box[1].offset, 111) self.assertEqual(jp2k.box[3].box[1].offset, 111)
self.assertEqual(jp2k.box[3].box[1].length, 16) self.assertEqual(jp2k.box[3].box[1].length, 16)
self.assertEqual(jp2k.box[3].box[1].version, 0) self.assertEqual(jp2k.box[3].box[1].version, 0)
self.assertEqual(jp2k.box[3].box[1].flag, (0, 0, 0)) self.assertEqual(jp2k.box[3].box[1].flag, (0, 0, 0))
self.assertEqual(jp2k.box[3].box[1].URL, 'abcd') self.assertEqual(jp2k.box[3].box[1].url, 'abcd')
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
def test_xml_box_with_trailing_nulls(self): def test_xml_box_with_trailing_nulls(self):
# ElementTree does not like trailing null chars after valid XML # ElementTree does not like trailing null chars after valid XML
# text. # text.
@ -533,14 +554,15 @@ class TestJp2k(unittest.TestCase):
jp2k = Jp2k(tfile.name) jp2k = Jp2k(tfile.name)
self.assertEqual(jp2k.box[3].id, 'xml ') self.assertEqual(jp2k.box[3].box_id, 'xml ')
self.assertEqual(jp2k.box[3].offset, 77) self.assertEqual(jp2k.box[3].offset, 77)
self.assertEqual(jp2k.box[3].length, 36) self.assertEqual(jp2k.box[3].length, 36)
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
def test_asoc_label_box(self): def test_asoc_label_box(self):
# Construct a fake file with an asoc and a label box, as # Construct a fake file with an asoc and a label box, as
# OpenJPEG doesn't have such a file. # OpenJPEG doesn't have such a file.
data = Jp2k(self.jp2file).read(reduce=3) data = Jp2k(self.jp2file).read(rlevel=1)
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
j = Jp2k(tfile.name, 'wb') j = Jp2k(tfile.name, 'wb')
j.write(data) j.write(data)
@ -578,11 +600,12 @@ class TestJp2k(unittest.TestCase):
tfile2.flush() tfile2.flush()
jasoc = Jp2k(tfile2.name) jasoc = Jp2k(tfile2.name)
self.assertEqual(jasoc.box[3].id, 'asoc') self.assertEqual(jasoc.box[3].box_id, 'asoc')
self.assertEqual(jasoc.box[3].box[0].id, 'lbl ') self.assertEqual(jasoc.box[3].box[0].box_id, 'lbl ')
self.assertEqual(jasoc.box[3].box[0].label, 'label') self.assertEqual(jasoc.box[3].box[0].label, 'label')
self.assertEqual(jasoc.box[3].box[1].id, 'xml ') self.assertEqual(jasoc.box[3].box[1].box_id, 'xml ')
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
def test_openjpeg_library_message(self): def test_openjpeg_library_message(self):
# Verify the error message produced by the openjpeg library. # Verify the error message produced by the openjpeg library.
# This will confirm that the error callback mechanism is working. # This will confirm that the error callback mechanism is working.
@ -610,11 +633,11 @@ class TestJp2k(unittest.TestCase):
Invalid\svalues\sfor\scomp\s=\s0\s+ Invalid\svalues\sfor\scomp\s=\s0\s+
:\sdx=1\sdy=0''', re.VERBOSE) :\sdx=1\sdy=0''', re.VERBOSE)
if sys.hexversion < 0x03020000: if sys.hexversion < 0x03020000:
with self.assertRaisesRegexp(IOError, regexp) as ce: with self.assertRaisesRegexp((IOError, OSError), regexp):
d = j.read(reduce=3) d = j.read(rlevel=1)
else: else:
with self.assertRaisesRegex(IOError, regexp) as ce: with self.assertRaisesRegex((IOError, OSError), regexp):
d = j.read(reduce=3) d = j.read(rlevel=1)
def test_xmp_attribute(self): def test_xmp_attribute(self):
# Verify that we can read the XMP packet in our shipping example file. # Verify that we can read the XMP packet in our shipping example file.
@ -627,6 +650,7 @@ class TestJp2k(unittest.TestCase):
attr_value = elt.attrib['{0}CreatorTool'.format(ns1)] attr_value = elt.attrib['{0}CreatorTool'.format(ns1)]
self.assertEqual(attr_value, 'glymur') self.assertEqual(attr_value, 'glymur')
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
def test_unrecognized_exif_tag(self): def test_unrecognized_exif_tag(self):
# An unrecognized exif tag should be handled gracefully. # An unrecognized exif tag should be handled gracefully.
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
@ -659,5 +683,72 @@ class TestJp2k(unittest.TestCase):
self.assertFalse('Make' in exif['Image'].keys()) 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):
jp2k.read_bands()
def test_area(self):
"""Area option not allowed for 1.5.1.
"""
j2k = Jp2k(self.j2kfile)
with self.assertRaises(TypeError):
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):
j2k.read(tile=0)
def test_layer(self):
"""layer option not allowed for 1.5.1.
"""
j2k = Jp2k(self.j2kfile)
with self.assertRaises(TypeError):
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)
j2k.read(rlevel=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)
j2k.read()
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

File diff suppressed because it is too large Load diff

View file

@ -2,6 +2,7 @@
The tests here do not correspond directly to the OpenJPEG test suite, but The tests here do not correspond directly to the OpenJPEG test suite, but
seem like logical negative tests to add. seem like logical negative tests to add.
""" """
#pylint: disable-all
import os import os
import sys import sys
import tempfile import tempfile
@ -57,7 +58,7 @@ def read_image(infile):
return data return data
@unittest.skipIf(glymur.lib.openjp2._OPENJP2 is None, @unittest.skipIf(glymur.lib.openjp2.OPENJP2 is None,
"Missing openjp2 library.") "Missing openjp2 library.")
@unittest.skipIf(no_read_backend, no_read_backend_msg) @unittest.skipIf(no_read_backend, no_read_backend_msg)
@unittest.skipIf(data_root is None, @unittest.skipIf(data_root is None,
@ -65,12 +66,13 @@ def read_image(infile):
class TestSuiteNegative(unittest.TestCase): class TestSuiteNegative(unittest.TestCase):
def setUp(self): def setUp(self):
self.jp2file = pkg_resources.resource_filename(glymur.__name__, self.jp2file = glymur.data.nemo()
"data/nemo.jp2") self.j2kfile = glymur.data.goodstuff()
def tearDown(self): def tearDown(self):
pass pass
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
def test_negative_psnr_with_cratios(self): def test_negative_psnr_with_cratios(self):
# Using psnr with cratios options is not allowed. # Using psnr with cratios options is not allowed.
# Not an OpenJPEG test, but close. # Not an OpenJPEG test, but close.
@ -94,7 +96,7 @@ class TestSuiteNegative(unittest.TestCase):
# Verify that the last segment returned in the codestream is SOD, # Verify that the last segment returned in the codestream is SOD,
# not EOC. Codestream parsing should stop when we try to jump to # not EOC. Codestream parsing should stop when we try to jump to
# the end of SOT. # the end of SOT.
self.assertEqual(c.segment[-1].id, 'SOD') self.assertEqual(c.segment[-1].marker_id, 'SOD')
@unittest.skipIf(sys.hexversion < 0x03020000, @unittest.skipIf(sys.hexversion < 0x03020000,
"Uses features introduced in 3.2.") "Uses features introduced in 3.2.")
@ -109,7 +111,7 @@ class TestSuiteNegative(unittest.TestCase):
# Verify that the last segment returned in the codestream is SOD, # Verify that the last segment returned in the codestream is SOD,
# not EOC. Codestream parsing should stop when we try to jump to # not EOC. Codestream parsing should stop when we try to jump to
# the end of SOT. # the end of SOT.
self.assertEqual(c.segment[-1].id, 'SOD') self.assertEqual(c.segment[-1].marker_id, 'SOD')
@unittest.skipIf(sys.hexversion < 0x03020000, @unittest.skipIf(sys.hexversion < 0x03020000,
"Uses features introduced in 3.2.") "Uses features introduced in 3.2.")
@ -124,8 +126,9 @@ class TestSuiteNegative(unittest.TestCase):
# Verify that the last segment returned in the codestream is SOD, # Verify that the last segment returned in the codestream is SOD,
# not EOC. Codestream parsing should stop when we try to jump to # not EOC. Codestream parsing should stop when we try to jump to
# the end of SOT. # the end of SOT.
self.assertEqual(c.segment[-1].id, 'SOD') self.assertEqual(c.segment[-1].marker_id, 'SOD')
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
def test_code_block_dimensions(self): def test_code_block_dimensions(self):
# opj_compress doesn't allow the dimensions of a codeblock # opj_compress doesn't allow the dimensions of a codeblock
# to be too small or too big, so neither will we. # to be too small or too big, so neither will we.
@ -154,29 +157,32 @@ class TestSuiteNegative(unittest.TestCase):
with self.assertWarns(UserWarning) as cw: with self.assertWarns(UserWarning) as cw:
j = Jp2k(infile) j = Jp2k(infile)
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
def test_precinct_size_not_multiple_of_two(self): def test_precinct_size_not_multiple_of_two(self):
# Seems like precinct sizes should be powers of two. # Seems like precinct sizes should be powers of two.
ifile = Jp2k(self.jp2file) ifile = Jp2k(self.j2kfile)
data = ifile.read(reduce=3) data = ifile.read(rlevel=2)
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
ofile = Jp2k(tfile.name, 'wb') ofile = Jp2k(tfile.name, 'wb')
with self.assertRaises(IOError) as ce: with self.assertRaises(IOError) as ce:
ofile.write(data, psizes=[(13, 13)]) ofile.write(data, psizes=[(13, 13)])
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
def test_codeblock_size_not_multiple_of_two(self): def test_codeblock_size_not_multiple_of_two(self):
# Seems like code block sizes should be powers of two. # Seems like code block sizes should be powers of two.
ifile = Jp2k(self.jp2file) ifile = Jp2k(self.j2kfile)
data = ifile.read(reduce=3) data = ifile.read(rlevel=2)
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
ofile = Jp2k(tfile.name, 'wb') ofile = Jp2k(tfile.name, 'wb')
with self.assertRaises(IOError) as ce: with self.assertRaises(IOError) as ce:
ofile.write(data, cbsize=(13, 12)) ofile.write(data, cbsize=(13, 12))
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
def test_codeblock_size_with_precinct_size(self): def test_codeblock_size_with_precinct_size(self):
# Seems like code block sizes should never exceed half that of # Seems like code block sizes should never exceed half that of
# precinct size. # precinct size.
ifile = Jp2k(self.jp2file) ifile = Jp2k(self.j2kfile)
data = ifile.read(reduce=3) data = ifile.read(rlevel=2)
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
ofile = Jp2k(tfile.name, 'wb') ofile = Jp2k(tfile.name, 'wb')
with self.assertRaises(IOError) as ce: with self.assertRaises(IOError) as ce:

View file

@ -2,6 +2,7 @@
The tests defined here roughly correspond to what is in the OpenJPEG test The tests defined here roughly correspond to what is in the OpenJPEG test
suite. suite.
""" """
#pylint: disable-all
import os import os
import platform import platform
import sys import sys
@ -57,7 +58,8 @@ def read_image(infile):
return data return data
@unittest.skipIf(glymur.lib.openjp2._OPENJP2 is None, @unittest.skipIf(os.name == "nt", "no write support on windows, period")
@unittest.skipIf(glymur.lib.openjp2.OPENJP2 is None,
"Missing openjp2 library.") "Missing openjp2 library.")
@unittest.skipIf(no_read_backend, no_read_backend_msg) @unittest.skipIf(no_read_backend, no_read_backend_msg)
@unittest.skipIf(data_root is None, @unittest.skipIf(data_root is None,
@ -83,50 +85,50 @@ class TestSuiteWrite(unittest.TestCase):
# SIZ: Image and tile size # SIZ: Image and tile size
# Profile: "0" means profile 2 # Profile: "0" means profile 2
self.assertEqual(c.segment[1].Rsiz, 0) self.assertEqual(c.segment[1].rsiz, 0)
# Reference grid size # Reference grid size
self.assertEqual((c.segment[1].Xsiz, c.segment[1].Ysiz), self.assertEqual((c.segment[1].xsiz, c.segment[1].ysiz),
(640, 480)) (640, 480))
# Reference grid offset # Reference grid offset
self.assertEqual((c.segment[1].XOsiz, c.segment[1].YOsiz), (0, 0)) self.assertEqual((c.segment[1].xosiz, c.segment[1].yosiz), (0, 0))
# Tile size # Tile size
self.assertEqual((c.segment[1].XTsiz, c.segment[1].YTsiz), self.assertEqual((c.segment[1].xtsiz, c.segment[1].ytsiz),
(640, 480)) (640, 480))
# Tile offset # Tile offset
self.assertEqual((c.segment[1].XTOsiz, c.segment[1].YTOsiz), self.assertEqual((c.segment[1].xtosiz, c.segment[1].ytosiz),
(0, 0)) (0, 0))
# bitdepth # bitdepth
self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8)) self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8))
# signed # signed
self.assertEqual(c.segment[1]._signed, (False, False, False)) self.assertEqual(c.segment[1]._signed, (False, False, False))
# subsampling # subsampling
self.assertEqual(list(zip(c.segment[1].XRsiz, c.segment[1].YRsiz)), self.assertEqual(list(zip(c.segment[1].xrsiz, c.segment[1].yrsiz)),
[(1, 1)] * 3) [(1, 1)] * 3)
# COD: Coding style default # COD: Coding style default
self.assertFalse(c.segment[2].Scod & 2) # no sop self.assertFalse(c.segment[2].scod & 2) # no sop
self.assertFalse(c.segment[2].Scod & 4) # no eph self.assertFalse(c.segment[2].scod & 4) # no eph
self.assertEqual(c.segment[2].SPcod[0], glymur.core.LRCP) self.assertEqual(c.segment[2].spcod[0], glymur.core.LRCP)
self.assertEqual(c.segment[2]._layers, 3) # layers = 3 self.assertEqual(c.segment[2]._layers, 3) # layers = 3
self.assertEqual(c.segment[2].SPcod[3], 1) # mct self.assertEqual(c.segment[2].spcod[3], 1) # mct
self.assertEqual(c.segment[2].SPcod[4], 5) # levels self.assertEqual(c.segment[2].spcod[4], 5) # levels
self.assertEqual(tuple(c.segment[2]._code_block_size), self.assertEqual(tuple(c.segment[2]._code_block_size),
(64, 64)) # cblksz (64, 64)) # cblksz
# Selective arithmetic coding bypass # Selective arithmetic coding bypass
self.assertFalse(c.segment[2].SPcod[7] & 0x01) self.assertFalse(c.segment[2].spcod[7] & 0x01)
# Reset context probabilities # Reset context probabilities
self.assertFalse(c.segment[2].SPcod[7] & 0x02) self.assertFalse(c.segment[2].spcod[7] & 0x02)
# Termination on each coding pass # Termination on each coding pass
self.assertFalse(c.segment[2].SPcod[7] & 0x04) self.assertFalse(c.segment[2].spcod[7] & 0x04)
# Vertically causal context # Vertically causal context
self.assertFalse(c.segment[2].SPcod[7] & 0x08) self.assertFalse(c.segment[2].spcod[7] & 0x08)
# Predictable termination # Predictable termination
self.assertFalse(c.segment[2].SPcod[7] & 0x0010) self.assertFalse(c.segment[2].spcod[7] & 0x0010)
# Segmentation symbols # Segmentation symbols
self.assertFalse(c.segment[2].SPcod[7] & 0x0020) self.assertFalse(c.segment[2].spcod[7] & 0x0020)
self.assertEqual(c.segment[2].SPcod[8], self.assertEqual(c.segment[2].spcod[8],
glymur.core.WAVELET_TRANSFORM_5x3_REVERSIBLE) glymur.core.WAVELET_XFORM_5X3_REVERSIBLE)
self.assertEqual(len(c.segment[2].SPcod), 9) self.assertEqual(len(c.segment[2].spcod), 9)
def test_NR_ENC_Bretagne1_ppm_2_encode(self): def test_NR_ENC_Bretagne1_ppm_2_encode(self):
# NR-ENC-Bretagne1.ppm-2-encode # NR-ENC-Bretagne1.ppm-2-encode
@ -141,50 +143,50 @@ class TestSuiteWrite(unittest.TestCase):
# SIZ: Image and tile size # SIZ: Image and tile size
# Profile: "0" means profile 2 # Profile: "0" means profile 2
self.assertEqual(c.segment[1].Rsiz, 0) self.assertEqual(c.segment[1].rsiz, 0)
# Reference grid size # Reference grid size
self.assertEqual((c.segment[1].Xsiz, c.segment[1].Ysiz), self.assertEqual((c.segment[1].xsiz, c.segment[1].ysiz),
(640, 480)) (640, 480))
# Reference grid offset # Reference grid offset
self.assertEqual((c.segment[1].XOsiz, c.segment[1].YOsiz), (0, 0)) self.assertEqual((c.segment[1].xosiz, c.segment[1].yosiz), (0, 0))
# Tile size # Tile size
self.assertEqual((c.segment[1].XTsiz, c.segment[1].YTsiz), self.assertEqual((c.segment[1].xtsiz, c.segment[1].ytsiz),
(640, 480)) (640, 480))
# Tile offset # Tile offset
self.assertEqual((c.segment[1].XTOsiz, c.segment[1].YTOsiz), self.assertEqual((c.segment[1].xtosiz, c.segment[1].ytosiz),
(0, 0)) (0, 0))
# bitdepth # bitdepth
self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8)) self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8))
# signed # signed
self.assertEqual(c.segment[1]._signed, (False, False, False)) self.assertEqual(c.segment[1]._signed, (False, False, False))
# subsampling # subsampling
self.assertEqual(list(zip(c.segment[1].XRsiz, c.segment[1].YRsiz)), self.assertEqual(list(zip(c.segment[1].xrsiz, c.segment[1].yrsiz)),
[(1, 1)] * 3) [(1, 1)] * 3)
# COD: Coding style default # COD: Coding style default
self.assertFalse(c.segment[2].Scod & 2) # no sop self.assertFalse(c.segment[2].scod & 2) # no sop
self.assertFalse(c.segment[2].Scod & 4) # no eph self.assertFalse(c.segment[2].scod & 4) # no eph
self.assertEqual(c.segment[2].SPcod[0], glymur.core.LRCP) self.assertEqual(c.segment[2].spcod[0], glymur.core.LRCP)
self.assertEqual(c.segment[2]._layers, 3) # layers = 3 self.assertEqual(c.segment[2]._layers, 3) # layers = 3
self.assertEqual(c.segment[2].SPcod[3], 1) # mct self.assertEqual(c.segment[2].spcod[3], 1) # mct
self.assertEqual(c.segment[2].SPcod[4], 1) # levels self.assertEqual(c.segment[2].spcod[4], 1) # levels
self.assertEqual(tuple(c.segment[2]._code_block_size), self.assertEqual(tuple(c.segment[2]._code_block_size),
(64, 64)) # cblksz (64, 64)) # cblksz
# Selective arithmetic coding bypass # Selective arithmetic coding bypass
self.assertFalse(c.segment[2].SPcod[7] & 0x01) self.assertFalse(c.segment[2].spcod[7] & 0x01)
# Reset context probabilities # Reset context probabilities
self.assertFalse(c.segment[2].SPcod[7] & 0x02) self.assertFalse(c.segment[2].spcod[7] & 0x02)
# Termination on each coding pass # Termination on each coding pass
self.assertFalse(c.segment[2].SPcod[7] & 0x04) self.assertFalse(c.segment[2].spcod[7] & 0x04)
# Vertically causal context # Vertically causal context
self.assertFalse(c.segment[2].SPcod[7] & 0x08) self.assertFalse(c.segment[2].spcod[7] & 0x08)
# Predictable termination # Predictable termination
self.assertFalse(c.segment[2].SPcod[7] & 0x0010) self.assertFalse(c.segment[2].spcod[7] & 0x0010)
# Segmentation symbols # Segmentation symbols
self.assertFalse(c.segment[2].SPcod[7] & 0x0020) self.assertFalse(c.segment[2].spcod[7] & 0x0020)
self.assertEqual(c.segment[2].SPcod[8], self.assertEqual(c.segment[2].spcod[8],
glymur.core.WAVELET_TRANSFORM_5x3_REVERSIBLE) glymur.core.WAVELET_XFORM_5X3_REVERSIBLE)
self.assertEqual(len(c.segment[2].SPcod), 9) self.assertEqual(len(c.segment[2].spcod), 9)
def test_NR_ENC_Bretagne1_ppm_3_encode(self): def test_NR_ENC_Bretagne1_ppm_3_encode(self):
# NR-ENC-Bretagne1.ppm-3-encode # NR-ENC-Bretagne1.ppm-3-encode
@ -200,49 +202,49 @@ class TestSuiteWrite(unittest.TestCase):
# SIZ: Image and tile size # SIZ: Image and tile size
# Profile: "0" means profile 2 # Profile: "0" means profile 2
self.assertEqual(c.segment[1].Rsiz, 0) self.assertEqual(c.segment[1].rsiz, 0)
# Reference grid size # Reference grid size
self.assertEqual((c.segment[1].Xsiz, c.segment[1].Ysiz), self.assertEqual((c.segment[1].xsiz, c.segment[1].ysiz),
(640, 480)) (640, 480))
# Reference grid offset # Reference grid offset
self.assertEqual((c.segment[1].XOsiz, c.segment[1].YOsiz), (0, 0)) self.assertEqual((c.segment[1].xosiz, c.segment[1].yosiz), (0, 0))
# Tile size # Tile size
self.assertEqual((c.segment[1].XTsiz, c.segment[1].YTsiz), self.assertEqual((c.segment[1].xtsiz, c.segment[1].ytsiz),
(640, 480)) (640, 480))
# Tile offset # Tile offset
self.assertEqual((c.segment[1].XTOsiz, c.segment[1].YTOsiz), self.assertEqual((c.segment[1].xtosiz, c.segment[1].ytosiz),
(0, 0)) (0, 0))
# bitdepth # bitdepth
self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8)) self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8))
# signed # signed
self.assertEqual(c.segment[1]._signed, (False, False, False)) self.assertEqual(c.segment[1]._signed, (False, False, False))
# subsampling # subsampling
self.assertEqual(list(zip(c.segment[1].XRsiz, c.segment[1].YRsiz)), self.assertEqual(list(zip(c.segment[1].xrsiz, c.segment[1].yrsiz)),
[(1, 1)] * 3) [(1, 1)] * 3)
# COD: Coding style default # COD: Coding style default
self.assertFalse(c.segment[2].Scod & 2) # no sop self.assertFalse(c.segment[2].scod & 2) # no sop
self.assertFalse(c.segment[2].Scod & 4) # no eph self.assertFalse(c.segment[2].scod & 4) # no eph
self.assertEqual(c.segment[2].SPcod[0], glymur.core.LRCP) self.assertEqual(c.segment[2].spcod[0], glymur.core.LRCP)
self.assertEqual(c.segment[2]._layers, 3) # layers = 3 self.assertEqual(c.segment[2]._layers, 3) # layers = 3
self.assertEqual(c.segment[2].SPcod[3], 1) # mct self.assertEqual(c.segment[2].spcod[3], 1) # mct
self.assertEqual(c.segment[2].SPcod[4], 5) # levels self.assertEqual(c.segment[2].spcod[4], 5) # levels
self.assertEqual(tuple(c.segment[2]._code_block_size), self.assertEqual(tuple(c.segment[2]._code_block_size),
(16, 16)) # cblksz (16, 16)) # cblksz
# Selective arithmetic coding bypass # Selective arithmetic coding bypass
self.assertFalse(c.segment[2].SPcod[7] & 0x01) self.assertFalse(c.segment[2].spcod[7] & 0x01)
# Reset context probabilities # Reset context probabilities
self.assertFalse(c.segment[2].SPcod[7] & 0x02) self.assertFalse(c.segment[2].spcod[7] & 0x02)
# Termination on each coding pass # Termination on each coding pass
self.assertFalse(c.segment[2].SPcod[7] & 0x04) self.assertFalse(c.segment[2].spcod[7] & 0x04)
# Vertically causal context # Vertically causal context
self.assertFalse(c.segment[2].SPcod[7] & 0x08) self.assertFalse(c.segment[2].spcod[7] & 0x08)
# Predictable termination # Predictable termination
self.assertFalse(c.segment[2].SPcod[7] & 0x0010) self.assertFalse(c.segment[2].spcod[7] & 0x0010)
# Segmentation symbols # Segmentation symbols
self.assertFalse(c.segment[2].SPcod[7] & 0x0020) self.assertFalse(c.segment[2].spcod[7] & 0x0020)
self.assertEqual(c.segment[2].SPcod[8], self.assertEqual(c.segment[2].spcod[8],
glymur.core.WAVELET_TRANSFORM_5x3_REVERSIBLE) glymur.core.WAVELET_XFORM_5X3_REVERSIBLE)
self.assertEqual(c.segment[2]._precinct_size, self.assertEqual(c.segment[2]._precinct_size,
[(2, 2), (4, 4), (8, 8), (16, 16), (32, 32), [(2, 2), (4, 4), (8, 8), (16, 16), (32, 32),
(64, 64)]) (64, 64)])
@ -263,49 +265,49 @@ class TestSuiteWrite(unittest.TestCase):
# SIZ: Image and tile size # SIZ: Image and tile size
# Profile: "0" means profile 2 # Profile: "0" means profile 2
self.assertEqual(c.segment[1].Rsiz, 0) self.assertEqual(c.segment[1].rsiz, 0)
# Reference grid size # Reference grid size
self.assertEqual((c.segment[1].Xsiz, c.segment[1].Ysiz), self.assertEqual((c.segment[1].xsiz, c.segment[1].ysiz),
(data.shape[1], data.shape[0])) (data.shape[1], data.shape[0]))
# Reference grid offset # Reference grid offset
self.assertEqual((c.segment[1].XOsiz, c.segment[1].YOsiz), (0, 0)) self.assertEqual((c.segment[1].xosiz, c.segment[1].yosiz), (0, 0))
# Tile size. Reported as XY, not RC. # Tile size. Reported as XY, not RC.
self.assertEqual((c.segment[1].XTsiz, c.segment[1].YTsiz), self.assertEqual((c.segment[1].xtsiz, c.segment[1].ytsiz),
(640, 480)) (640, 480))
# Tile offset # Tile offset
self.assertEqual((c.segment[1].XTOsiz, c.segment[1].YTOsiz), self.assertEqual((c.segment[1].xtosiz, c.segment[1].ytosiz),
(0, 0)) (0, 0))
# bitdepth # bitdepth
self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8)) self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8))
# signed # signed
self.assertEqual(c.segment[1]._signed, (False, False, False)) self.assertEqual(c.segment[1]._signed, (False, False, False))
# subsampling # subsampling
self.assertEqual(list(zip(c.segment[1].XRsiz, c.segment[1].YRsiz)), self.assertEqual(list(zip(c.segment[1].xrsiz, c.segment[1].yrsiz)),
[(1, 1)] * 3) [(1, 1)] * 3)
# COD: Coding style default # COD: Coding style default
self.assertFalse(c.segment[2].Scod & 2) # no sop self.assertFalse(c.segment[2].scod & 2) # no sop
self.assertFalse(c.segment[2].Scod & 4) # no eph self.assertFalse(c.segment[2].scod & 4) # no eph
self.assertEqual(c.segment[2].SPcod[0], glymur.core.LRCP) self.assertEqual(c.segment[2].spcod[0], glymur.core.LRCP)
self.assertEqual(c.segment[2]._layers, 3) # layers = 3 self.assertEqual(c.segment[2]._layers, 3) # layers = 3
self.assertEqual(c.segment[2].SPcod[3], 1) # mct self.assertEqual(c.segment[2].spcod[3], 1) # mct
self.assertEqual(c.segment[2].SPcod[4], 5) # levels self.assertEqual(c.segment[2].spcod[4], 5) # levels
self.assertEqual(tuple(c.segment[2]._code_block_size), self.assertEqual(tuple(c.segment[2]._code_block_size),
(32, 32)) # cblksz (32, 32)) # cblksz
# Selective arithmetic coding bypass # Selective arithmetic coding bypass
self.assertFalse(c.segment[2].SPcod[7] & 0x01) self.assertFalse(c.segment[2].spcod[7] & 0x01)
# Reset context probabilities # Reset context probabilities
self.assertFalse(c.segment[2].SPcod[7] & 0x02) self.assertFalse(c.segment[2].spcod[7] & 0x02)
# Termination on each coding pass # Termination on each coding pass
self.assertFalse(c.segment[2].SPcod[7] & 0x04) self.assertFalse(c.segment[2].spcod[7] & 0x04)
# Vertically causal context # Vertically causal context
self.assertFalse(c.segment[2].SPcod[7] & 0x08) self.assertFalse(c.segment[2].spcod[7] & 0x08)
# Predictable termination # Predictable termination
self.assertFalse(c.segment[2].SPcod[7] & 0x0010) self.assertFalse(c.segment[2].spcod[7] & 0x0010)
# Segmentation symbols # Segmentation symbols
self.assertFalse(c.segment[2].SPcod[7] & 0x0020) self.assertFalse(c.segment[2].spcod[7] & 0x0020)
self.assertEqual(c.segment[2].SPcod[8], self.assertEqual(c.segment[2].spcod[8],
glymur.core.WAVELET_TRANSFORM_5x3_REVERSIBLE) glymur.core.WAVELET_XFORM_5X3_REVERSIBLE)
self.assertEqual(c.segment[2]._precinct_size, self.assertEqual(c.segment[2]._precinct_size,
[(16, 16), (32, 32), (64, 64)] + [(128, 128)] * 3) [(16, 16), (32, 32), (64, 64)] + [(128, 128)] * 3)
@ -321,50 +323,50 @@ class TestSuiteWrite(unittest.TestCase):
# SIZ: Image and tile size # SIZ: Image and tile size
# Profile: "0" means profile 2 # Profile: "0" means profile 2
self.assertEqual(c.segment[1].Rsiz, 0) self.assertEqual(c.segment[1].rsiz, 0)
# Reference grid size # Reference grid size
self.assertEqual((c.segment[1].Xsiz, c.segment[1].Ysiz), self.assertEqual((c.segment[1].xsiz, c.segment[1].ysiz),
(data.shape[1], data.shape[0])) (data.shape[1], data.shape[0]))
# Reference grid offset # Reference grid offset
self.assertEqual((c.segment[1].XOsiz, c.segment[1].YOsiz), (0, 0)) self.assertEqual((c.segment[1].xosiz, c.segment[1].yosiz), (0, 0))
# Tile size # Tile size
self.assertEqual((c.segment[1].XTsiz, c.segment[1].YTsiz), self.assertEqual((c.segment[1].xtsiz, c.segment[1].ytsiz),
(127, 127)) (127, 127))
# Tile offset # Tile offset
self.assertEqual((c.segment[1].XTOsiz, c.segment[1].YTOsiz), self.assertEqual((c.segment[1].xtosiz, c.segment[1].ytosiz),
(0, 0)) (0, 0))
# bitdepth # bitdepth
self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8)) self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8))
# signed # signed
self.assertEqual(c.segment[1]._signed, (False, False, False)) self.assertEqual(c.segment[1]._signed, (False, False, False))
# subsampling # subsampling
self.assertEqual(list(zip(c.segment[1].XRsiz, c.segment[1].YRsiz)), self.assertEqual(list(zip(c.segment[1].xrsiz, c.segment[1].yrsiz)),
[(1, 1)] * 3) [(1, 1)] * 3)
# COD: Coding style default # COD: Coding style default
self.assertFalse(c.segment[2].Scod & 2) # no sop self.assertFalse(c.segment[2].scod & 2) # no sop
self.assertFalse(c.segment[2].Scod & 4) # no eph self.assertFalse(c.segment[2].scod & 4) # no eph
self.assertEqual(c.segment[2].SPcod[0], glymur.core.PCRL) self.assertEqual(c.segment[2].spcod[0], glymur.core.PCRL)
self.assertEqual(c.segment[2]._layers, 1) # layers = 1 self.assertEqual(c.segment[2]._layers, 1) # layers = 1
self.assertEqual(c.segment[2].SPcod[3], 1) # mct self.assertEqual(c.segment[2].spcod[3], 1) # mct
self.assertEqual(c.segment[2].SPcod[4], 5) # levels self.assertEqual(c.segment[2].spcod[4], 5) # levels
self.assertEqual(tuple(c.segment[2]._code_block_size), self.assertEqual(tuple(c.segment[2]._code_block_size),
(64, 64)) # cblksz (64, 64)) # cblksz
# Selective arithmetic coding bypass # Selective arithmetic coding bypass
self.assertFalse(c.segment[2].SPcod[7] & 0x01) self.assertFalse(c.segment[2].spcod[7] & 0x01)
# Reset context probabilities # Reset context probabilities
self.assertFalse(c.segment[2].SPcod[7] & 0x02) self.assertFalse(c.segment[2].spcod[7] & 0x02)
# Termination on each coding pass # Termination on each coding pass
self.assertFalse(c.segment[2].SPcod[7] & 0x04) self.assertFalse(c.segment[2].spcod[7] & 0x04)
# Vertically causal context # Vertically causal context
self.assertFalse(c.segment[2].SPcod[7] & 0x08) self.assertFalse(c.segment[2].spcod[7] & 0x08)
# Predictable termination # Predictable termination
self.assertFalse(c.segment[2].SPcod[7] & 0x0010) self.assertFalse(c.segment[2].spcod[7] & 0x0010)
# Segmentation symbols # Segmentation symbols
self.assertFalse(c.segment[2].SPcod[7] & 0x0020) self.assertFalse(c.segment[2].spcod[7] & 0x0020)
self.assertEqual(c.segment[2].SPcod[8], self.assertEqual(c.segment[2].spcod[8],
glymur.core.WAVELET_TRANSFORM_5x3_REVERSIBLE) glymur.core.WAVELET_XFORM_5X3_REVERSIBLE)
self.assertEqual(len(c.segment[2].SPcod), 9) self.assertEqual(len(c.segment[2].spcod), 9)
def test_NR_ENC_Bretagne2_ppm_6_encode(self): def test_NR_ENC_Bretagne2_ppm_6_encode(self):
# NR-ENC-Bretagne2.ppm-6-encode # NR-ENC-Bretagne2.ppm-6-encode
@ -378,53 +380,53 @@ class TestSuiteWrite(unittest.TestCase):
# SIZ: Image and tile size # SIZ: Image and tile size
# Profile: "0" means profile 2 # Profile: "0" means profile 2
self.assertEqual(c.segment[1].Rsiz, 0) self.assertEqual(c.segment[1].rsiz, 0)
# Reference grid size # Reference grid size
self.assertEqual((c.segment[1].Xsiz, c.segment[1].Ysiz), self.assertEqual((c.segment[1].xsiz, c.segment[1].ysiz),
(5183, 3887)) (5183, 3887))
# Reference grid offset # Reference grid offset
self.assertEqual((c.segment[1].XOsiz, c.segment[1].YOsiz), (0, 0)) self.assertEqual((c.segment[1].xosiz, c.segment[1].yosiz), (0, 0))
# Tile size # Tile size
self.assertEqual((c.segment[1].XTsiz, c.segment[1].YTsiz), self.assertEqual((c.segment[1].xtsiz, c.segment[1].ytsiz),
(5183, 3887)) (5183, 3887))
# Tile offset # Tile offset
self.assertEqual((c.segment[1].XTOsiz, c.segment[1].YTOsiz), self.assertEqual((c.segment[1].xtosiz, c.segment[1].ytosiz),
(0, 0)) (0, 0))
# bitdepth # bitdepth
self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8)) self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8))
# signed # signed
self.assertEqual(c.segment[1]._signed, (False, False, False)) self.assertEqual(c.segment[1]._signed, (False, False, False))
# subsampling # subsampling
self.assertEqual(list(zip(c.segment[1].XRsiz, c.segment[1].YRsiz)), self.assertEqual(list(zip(c.segment[1].xrsiz, c.segment[1].yrsiz)),
[(2, 2)] * 3) [(2, 2)] * 3)
# COD: Coding style default # COD: Coding style default
self.assertTrue(c.segment[2].Scod & 2) # sop self.assertTrue(c.segment[2].scod & 2) # sop
self.assertFalse(c.segment[2].Scod & 4) # no eph self.assertFalse(c.segment[2].scod & 4) # no eph
self.assertEqual(c.segment[2].SPcod[0], glymur.core.LRCP) self.assertEqual(c.segment[2].spcod[0], glymur.core.LRCP)
self.assertEqual(c.segment[2]._layers, 1) # layers = 1 self.assertEqual(c.segment[2]._layers, 1) # layers = 1
self.assertEqual(c.segment[2].SPcod[3], 1) # mct self.assertEqual(c.segment[2].spcod[3], 1) # mct
self.assertEqual(c.segment[2].SPcod[4], 5) # levels self.assertEqual(c.segment[2].spcod[4], 5) # levels
self.assertEqual(tuple(c.segment[2]._code_block_size), self.assertEqual(tuple(c.segment[2]._code_block_size),
(64, 64)) # cblksz (64, 64)) # cblksz
# Selective arithmetic coding bypass # Selective arithmetic coding bypass
self.assertFalse(c.segment[2].SPcod[7] & 0x01) self.assertFalse(c.segment[2].spcod[7] & 0x01)
# Reset context probabilities # Reset context probabilities
self.assertFalse(c.segment[2].SPcod[7] & 0x02) self.assertFalse(c.segment[2].spcod[7] & 0x02)
# Termination on each coding pass # Termination on each coding pass
self.assertFalse(c.segment[2].SPcod[7] & 0x04) self.assertFalse(c.segment[2].spcod[7] & 0x04)
# Vertically causal context # Vertically causal context
self.assertFalse(c.segment[2].SPcod[7] & 0x08) self.assertFalse(c.segment[2].spcod[7] & 0x08)
# Predictable termination # Predictable termination
self.assertFalse(c.segment[2].SPcod[7] & 0x0010) self.assertFalse(c.segment[2].spcod[7] & 0x0010)
# Segmentation symbols # Segmentation symbols
self.assertFalse(c.segment[2].SPcod[7] & 0x0020) self.assertFalse(c.segment[2].spcod[7] & 0x0020)
self.assertEqual(c.segment[2].SPcod[8], self.assertEqual(c.segment[2].spcod[8],
glymur.core.WAVELET_TRANSFORM_5x3_REVERSIBLE) glymur.core.WAVELET_XFORM_5X3_REVERSIBLE)
self.assertEqual(len(c.segment[2].SPcod), 9) self.assertEqual(len(c.segment[2].spcod), 9)
# 18 SOP segments. # 18 SOP segments.
nsops = [x.Nsop for x in c.segment if x.id == 'SOP'] nsops = [x.nsop for x in c.segment if x.marker_id == 'SOP']
self.assertEqual(nsops, list(range(18))) self.assertEqual(nsops, list(range(18)))
def test_NR_ENC_Bretagne2_ppm_7_encode(self): def test_NR_ENC_Bretagne2_ppm_7_encode(self):
@ -438,53 +440,53 @@ class TestSuiteWrite(unittest.TestCase):
# SIZ: Image and tile size # SIZ: Image and tile size
# Profile: "0" means profile 2 # Profile: "0" means profile 2
self.assertEqual(c.segment[1].Rsiz, 0) self.assertEqual(c.segment[1].rsiz, 0)
# Reference grid size # Reference grid size
self.assertEqual((c.segment[1].Xsiz, c.segment[1].Ysiz), self.assertEqual((c.segment[1].xsiz, c.segment[1].ysiz),
(2592, 1944)) (2592, 1944))
# Reference grid offset # Reference grid offset
self.assertEqual((c.segment[1].XOsiz, c.segment[1].YOsiz), (0, 0)) self.assertEqual((c.segment[1].xosiz, c.segment[1].yosiz), (0, 0))
# Tile size # Tile size
self.assertEqual((c.segment[1].XTsiz, c.segment[1].YTsiz), self.assertEqual((c.segment[1].xtsiz, c.segment[1].ytsiz),
(2592, 1944)) (2592, 1944))
# Tile offset # Tile offset
self.assertEqual((c.segment[1].XTOsiz, c.segment[1].YTOsiz), self.assertEqual((c.segment[1].xtosiz, c.segment[1].ytosiz),
(0, 0)) (0, 0))
# bitdepth # bitdepth
self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8)) self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8))
# signed # signed
self.assertEqual(c.segment[1]._signed, (False, False, False)) self.assertEqual(c.segment[1]._signed, (False, False, False))
# subsampling # subsampling
self.assertEqual(list(zip(c.segment[1].XRsiz, c.segment[1].YRsiz)), self.assertEqual(list(zip(c.segment[1].xrsiz, c.segment[1].yrsiz)),
[(1, 1)] * 3) [(1, 1)] * 3)
# COD: Coding style default # COD: Coding style default
self.assertFalse(c.segment[2].Scod & 2) # no sop self.assertFalse(c.segment[2].scod & 2) # no sop
self.assertTrue(c.segment[2].Scod & 4) # eph self.assertTrue(c.segment[2].scod & 4) # eph
self.assertEqual(c.segment[2].SPcod[0], glymur.core.LRCP) self.assertEqual(c.segment[2].spcod[0], glymur.core.LRCP)
self.assertEqual(c.segment[2]._layers, 1) # layers = 1 self.assertEqual(c.segment[2]._layers, 1) # layers = 1
self.assertEqual(c.segment[2].SPcod[3], 1) # mct self.assertEqual(c.segment[2].spcod[3], 1) # mct
self.assertEqual(c.segment[2].SPcod[4], 5) # levels self.assertEqual(c.segment[2].spcod[4], 5) # levels
self.assertEqual(tuple(c.segment[2]._code_block_size), self.assertEqual(tuple(c.segment[2]._code_block_size),
(64, 64)) # cblksz (64, 64)) # cblksz
# Selective arithmetic coding BYPASS # Selective arithmetic coding BYPASS
self.assertFalse(c.segment[2].SPcod[7] & 0x01) self.assertFalse(c.segment[2].spcod[7] & 0x01)
# RESET context probabilities (RESET) # RESET context probabilities (RESET)
self.assertTrue(c.segment[2].SPcod[7] & 0x02) self.assertTrue(c.segment[2].spcod[7] & 0x02)
# Termination on each coding pass, RESTART(TERMALL) # Termination on each coding pass, RESTART(TERMALL)
self.assertTrue(c.segment[2].SPcod[7] & 0x04) self.assertTrue(c.segment[2].spcod[7] & 0x04)
# Vertically causal context (VSC) # Vertically causal context (VSC)
self.assertFalse(c.segment[2].SPcod[7] & 0x08) self.assertFalse(c.segment[2].spcod[7] & 0x08)
# Predictable termination, ERTERM(SEGTERM) # Predictable termination, ERTERM(SEGTERM)
self.assertFalse(c.segment[2].SPcod[7] & 0x0010) self.assertFalse(c.segment[2].spcod[7] & 0x0010)
# Segmentation symbols, SEGMARK(SEGSYSM) # Segmentation symbols, SEGMARK(SEGSYSM)
self.assertTrue(c.segment[2].SPcod[7] & 0x0020) self.assertTrue(c.segment[2].spcod[7] & 0x0020)
self.assertEqual(c.segment[2].SPcod[8], self.assertEqual(c.segment[2].spcod[8],
glymur.core.WAVELET_TRANSFORM_5x3_REVERSIBLE) glymur.core.WAVELET_XFORM_5X3_REVERSIBLE)
self.assertEqual(len(c.segment[2].SPcod), 9) self.assertEqual(len(c.segment[2].spcod), 9)
# 18 EPH segments. # 18 EPH segments.
ephs = [x for x in c.segment if x.id == 'EPH'] ephs = [x for x in c.segment if x.marker_id == 'EPH']
self.assertEqual(len(ephs), 18) self.assertEqual(len(ephs), 18)
def test_NR_ENC_Bretagne2_ppm_8_encode(self): def test_NR_ENC_Bretagne2_ppm_8_encode(self):
@ -498,51 +500,51 @@ class TestSuiteWrite(unittest.TestCase):
# SIZ: Image and tile size # SIZ: Image and tile size
# Profile: "0" means profile 2 # Profile: "0" means profile 2
self.assertEqual(c.segment[1].Rsiz, 0) self.assertEqual(c.segment[1].rsiz, 0)
# Reference grid size # Reference grid size
self.assertEqual((c.segment[1].Xsiz, c.segment[1].Ysiz), self.assertEqual((c.segment[1].xsiz, c.segment[1].ysiz),
(2742, 2244)) (2742, 2244))
# Reference grid offset # Reference grid offset
self.assertEqual((c.segment[1].XOsiz, c.segment[1].YOsiz), self.assertEqual((c.segment[1].xosiz, c.segment[1].yosiz),
(150, 300)) (150, 300))
# Tile size # Tile size
self.assertEqual((c.segment[1].XTsiz, c.segment[1].YTsiz), self.assertEqual((c.segment[1].xtsiz, c.segment[1].ytsiz),
(2742, 2244)) (2742, 2244))
# Tile offset # Tile offset
self.assertEqual((c.segment[1].XTOsiz, c.segment[1].YTOsiz), self.assertEqual((c.segment[1].xtosiz, c.segment[1].ytosiz),
(0, 0)) (0, 0))
# bitdepth # bitdepth
self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8)) self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8))
# signed # signed
self.assertEqual(c.segment[1]._signed, (False, False, False)) self.assertEqual(c.segment[1]._signed, (False, False, False))
# subsampling # subsampling
self.assertEqual(list(zip(c.segment[1].XRsiz, c.segment[1].YRsiz)), self.assertEqual(list(zip(c.segment[1].xrsiz, c.segment[1].yrsiz)),
[(1, 1)] * 3) [(1, 1)] * 3)
# COD: Coding style default # COD: Coding style default
self.assertFalse(c.segment[2].Scod & 2) # no sop self.assertFalse(c.segment[2].scod & 2) # no sop
self.assertFalse(c.segment[2].Scod & 4) # no eph self.assertFalse(c.segment[2].scod & 4) # no eph
self.assertEqual(c.segment[2].SPcod[0], glymur.core.LRCP) self.assertEqual(c.segment[2].spcod[0], glymur.core.LRCP)
self.assertEqual(c.segment[2]._layers, 1) # layers = 1 self.assertEqual(c.segment[2]._layers, 1) # layers = 1
self.assertEqual(c.segment[2].SPcod[3], 1) # mct self.assertEqual(c.segment[2].spcod[3], 1) # mct
self.assertEqual(c.segment[2].SPcod[4], 5) # levels self.assertEqual(c.segment[2].spcod[4], 5) # levels
self.assertEqual(tuple(c.segment[2]._code_block_size), self.assertEqual(tuple(c.segment[2]._code_block_size),
(64, 64)) # cblksz (64, 64)) # cblksz
# Selective arithmetic coding BYPASS # Selective arithmetic coding BYPASS
self.assertFalse(c.segment[2].SPcod[7] & 0x01) self.assertFalse(c.segment[2].spcod[7] & 0x01)
# RESET context probabilities (RESET) # RESET context probabilities (RESET)
self.assertFalse(c.segment[2].SPcod[7] & 0x02) self.assertFalse(c.segment[2].spcod[7] & 0x02)
# Termination on each coding pass, RESTART(TERMALL) # Termination on each coding pass, RESTART(TERMALL)
self.assertFalse(c.segment[2].SPcod[7] & 0x04) self.assertFalse(c.segment[2].spcod[7] & 0x04)
# Vertically causal context (VSC) # Vertically causal context (VSC)
self.assertFalse(c.segment[2].SPcod[7] & 0x08) self.assertFalse(c.segment[2].spcod[7] & 0x08)
# Predictable termination, ERTERM(SEGTERM) # Predictable termination, ERTERM(SEGTERM)
self.assertFalse(c.segment[2].SPcod[7] & 0x0010) self.assertFalse(c.segment[2].spcod[7] & 0x0010)
# Segmentation symbols, SEGMARK(SEGSYSM) # Segmentation symbols, SEGMARK(SEGSYSM)
self.assertFalse(c.segment[2].SPcod[7] & 0x0020) self.assertFalse(c.segment[2].spcod[7] & 0x0020)
self.assertEqual(c.segment[2].SPcod[8], self.assertEqual(c.segment[2].spcod[8],
glymur.core.WAVELET_TRANSFORM_5x3_REVERSIBLE) glymur.core.WAVELET_XFORM_5X3_REVERSIBLE)
self.assertEqual(len(c.segment[2].SPcod), 9) self.assertEqual(len(c.segment[2].spcod), 9)
def test_NR_ENC_Cevennes1_bmp_9_encode(self): def test_NR_ENC_Cevennes1_bmp_9_encode(self):
infile = os.path.join(data_root, 'input/nonregression/Cevennes1.bmp') infile = os.path.join(data_root, 'input/nonregression/Cevennes1.bmp')
@ -555,50 +557,50 @@ class TestSuiteWrite(unittest.TestCase):
# SIZ: Image and tile size # SIZ: Image and tile size
# Profile: "0" means profile 2 # Profile: "0" means profile 2
self.assertEqual(c.segment[1].Rsiz, 0) self.assertEqual(c.segment[1].rsiz, 0)
# Reference grid size # Reference grid size
self.assertEqual((c.segment[1].Xsiz, c.segment[1].Ysiz), self.assertEqual((c.segment[1].xsiz, c.segment[1].ysiz),
(2592, 1944)) (2592, 1944))
# Reference grid offset # Reference grid offset
self.assertEqual((c.segment[1].XOsiz, c.segment[1].YOsiz), (0, 0)) self.assertEqual((c.segment[1].xosiz, c.segment[1].yosiz), (0, 0))
# Tile size # Tile size
self.assertEqual((c.segment[1].XTsiz, c.segment[1].YTsiz), self.assertEqual((c.segment[1].xtsiz, c.segment[1].ytsiz),
(2592, 1944)) (2592, 1944))
# Tile offset # Tile offset
self.assertEqual((c.segment[1].XTOsiz, c.segment[1].YTOsiz), self.assertEqual((c.segment[1].xtosiz, c.segment[1].ytosiz),
(0, 0)) (0, 0))
# bitdepth # bitdepth
self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8)) self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8))
# signed # signed
self.assertEqual(c.segment[1]._signed, (False, False, False)) self.assertEqual(c.segment[1]._signed, (False, False, False))
# subsampling # subsampling
self.assertEqual(list(zip(c.segment[1].XRsiz, c.segment[1].YRsiz)), self.assertEqual(list(zip(c.segment[1].xrsiz, c.segment[1].yrsiz)),
[(1, 1)] * 3) [(1, 1)] * 3)
# COD: Coding style default # COD: Coding style default
self.assertFalse(c.segment[2].Scod & 2) # no sop self.assertFalse(c.segment[2].scod & 2) # no sop
self.assertFalse(c.segment[2].Scod & 4) # no eph self.assertFalse(c.segment[2].scod & 4) # no eph
self.assertEqual(c.segment[2].SPcod[0], glymur.core.LRCP) self.assertEqual(c.segment[2].spcod[0], glymur.core.LRCP)
self.assertEqual(c.segment[2]._layers, 1) # layers = 1 self.assertEqual(c.segment[2]._layers, 1) # layers = 1
self.assertEqual(c.segment[2].SPcod[3], 1) # mct self.assertEqual(c.segment[2].spcod[3], 1) # mct
self.assertEqual(c.segment[2].SPcod[4], 5) # levels self.assertEqual(c.segment[2].spcod[4], 5) # levels
self.assertEqual(tuple(c.segment[2]._code_block_size), self.assertEqual(tuple(c.segment[2]._code_block_size),
(64, 64)) # cblksz (64, 64)) # cblksz
# Selective arithmetic coding BYPASS # Selective arithmetic coding BYPASS
self.assertFalse(c.segment[2].SPcod[7] & 0x01) self.assertFalse(c.segment[2].spcod[7] & 0x01)
# RESET context probabilities (RESET) # RESET context probabilities (RESET)
self.assertFalse(c.segment[2].SPcod[7] & 0x02) self.assertFalse(c.segment[2].spcod[7] & 0x02)
# Termination on each coding pass, RESTART(TERMALL) # Termination on each coding pass, RESTART(TERMALL)
self.assertFalse(c.segment[2].SPcod[7] & 0x04) self.assertFalse(c.segment[2].spcod[7] & 0x04)
# Vertically causal context (VSC) # Vertically causal context (VSC)
self.assertFalse(c.segment[2].SPcod[7] & 0x08) self.assertFalse(c.segment[2].spcod[7] & 0x08)
# Predictable termination, ERTERM(SEGTERM) # Predictable termination, ERTERM(SEGTERM)
self.assertFalse(c.segment[2].SPcod[7] & 0x0010) self.assertFalse(c.segment[2].spcod[7] & 0x0010)
# Segmentation symbols, SEGMARK(SEGSYSM) # Segmentation symbols, SEGMARK(SEGSYSM)
self.assertFalse(c.segment[2].SPcod[7] & 0x0020) self.assertFalse(c.segment[2].spcod[7] & 0x0020)
self.assertEqual(c.segment[2].SPcod[8], self.assertEqual(c.segment[2].spcod[8],
glymur.core.WAVELET_TRANSFORM_5x3_REVERSIBLE) glymur.core.WAVELET_XFORM_5X3_REVERSIBLE)
self.assertEqual(len(c.segment[2].SPcod), 9) self.assertEqual(len(c.segment[2].spcod), 9)
def test_NR_ENC_Cevennes2_ppm_10_encode(self): def test_NR_ENC_Cevennes2_ppm_10_encode(self):
infile = os.path.join(data_root, 'input/nonregression/Cevennes2.ppm') infile = os.path.join(data_root, 'input/nonregression/Cevennes2.ppm')
@ -611,50 +613,50 @@ class TestSuiteWrite(unittest.TestCase):
# SIZ: Image and tile size # SIZ: Image and tile size
# Profile: "0" means profile 2 # Profile: "0" means profile 2
self.assertEqual(c.segment[1].Rsiz, 0) self.assertEqual(c.segment[1].rsiz, 0)
# Reference grid size # Reference grid size
self.assertEqual((c.segment[1].Xsiz, c.segment[1].Ysiz), self.assertEqual((c.segment[1].xsiz, c.segment[1].ysiz),
(640, 480)) (640, 480))
# Reference grid offset # Reference grid offset
self.assertEqual((c.segment[1].XOsiz, c.segment[1].YOsiz), (0, 0)) self.assertEqual((c.segment[1].xosiz, c.segment[1].yosiz), (0, 0))
# Tile size # Tile size
self.assertEqual((c.segment[1].XTsiz, c.segment[1].YTsiz), self.assertEqual((c.segment[1].xtsiz, c.segment[1].ytsiz),
(640, 480)) (640, 480))
# Tile offset # Tile offset
self.assertEqual((c.segment[1].XTOsiz, c.segment[1].YTOsiz), self.assertEqual((c.segment[1].xtosiz, c.segment[1].ytosiz),
(0, 0)) (0, 0))
# bitdepth # bitdepth
self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8)) self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8))
# signed # signed
self.assertEqual(c.segment[1]._signed, (False, False, False)) self.assertEqual(c.segment[1]._signed, (False, False, False))
# subsampling # subsampling
self.assertEqual(list(zip(c.segment[1].XRsiz, c.segment[1].YRsiz)), self.assertEqual(list(zip(c.segment[1].xrsiz, c.segment[1].yrsiz)),
[(1, 1)] * 3) [(1, 1)] * 3)
# COD: Coding style default # COD: Coding style default
self.assertFalse(c.segment[2].Scod & 2) # no sop self.assertFalse(c.segment[2].scod & 2) # no sop
self.assertFalse(c.segment[2].Scod & 4) # no eph self.assertFalse(c.segment[2].scod & 4) # no eph
self.assertEqual(c.segment[2].SPcod[0], glymur.core.LRCP) self.assertEqual(c.segment[2].spcod[0], glymur.core.LRCP)
self.assertEqual(c.segment[2]._layers, 1) # layers = 1 self.assertEqual(c.segment[2]._layers, 1) # layers = 1
self.assertEqual(c.segment[2].SPcod[3], 1) # mct self.assertEqual(c.segment[2].spcod[3], 1) # mct
self.assertEqual(c.segment[2].SPcod[4], 5) # levels self.assertEqual(c.segment[2].spcod[4], 5) # levels
self.assertEqual(tuple(c.segment[2]._code_block_size), self.assertEqual(tuple(c.segment[2]._code_block_size),
(64, 64)) # cblksz (64, 64)) # cblksz
# Selective arithmetic coding BYPASS # Selective arithmetic coding BYPASS
self.assertFalse(c.segment[2].SPcod[7] & 0x01) self.assertFalse(c.segment[2].spcod[7] & 0x01)
# RESET context probabilities (RESET) # RESET context probabilities (RESET)
self.assertFalse(c.segment[2].SPcod[7] & 0x02) self.assertFalse(c.segment[2].spcod[7] & 0x02)
# Termination on each coding pass, RESTART(TERMALL) # Termination on each coding pass, RESTART(TERMALL)
self.assertFalse(c.segment[2].SPcod[7] & 0x04) self.assertFalse(c.segment[2].spcod[7] & 0x04)
# Vertically causal context (VSC) # Vertically causal context (VSC)
self.assertFalse(c.segment[2].SPcod[7] & 0x08) self.assertFalse(c.segment[2].spcod[7] & 0x08)
# Predictable termination, ERTERM(SEGTERM) # Predictable termination, ERTERM(SEGTERM)
self.assertFalse(c.segment[2].SPcod[7] & 0x0010) self.assertFalse(c.segment[2].spcod[7] & 0x0010)
# Segmentation symbols, SEGMARK(SEGSYSM) # Segmentation symbols, SEGMARK(SEGSYSM)
self.assertFalse(c.segment[2].SPcod[7] & 0x0020) self.assertFalse(c.segment[2].spcod[7] & 0x0020)
self.assertEqual(c.segment[2].SPcod[8], self.assertEqual(c.segment[2].spcod[8],
glymur.core.WAVELET_TRANSFORM_5x3_REVERSIBLE) glymur.core.WAVELET_XFORM_5X3_REVERSIBLE)
self.assertEqual(len(c.segment[2].SPcod), 9) self.assertEqual(len(c.segment[2].spcod), 9)
def test_NR_ENC_Rome_bmp_11_encode(self): def test_NR_ENC_Rome_bmp_11_encode(self):
infile = os.path.join(data_root, 'input/nonregression/Rome.bmp') infile = os.path.join(data_root, 'input/nonregression/Rome.bmp')
@ -663,11 +665,11 @@ class TestSuiteWrite(unittest.TestCase):
jp2 = Jp2k(tfile.name, 'wb') jp2 = Jp2k(tfile.name, 'wb')
jp2.write(data, psnr=[30, 35, 50], prog='LRCP', numres=3) jp2.write(data, psnr=[30, 35, 50], prog='LRCP', numres=3)
ids = [box.id for box in jp2.box] ids = [box.box_id for box in jp2.box]
lst = ['jP ', 'ftyp', 'jp2h', 'jp2c'] lst = ['jP ', 'ftyp', 'jp2h', 'jp2c']
self.assertEqual(ids, lst) self.assertEqual(ids, lst)
ids = [box.id for box in jp2.box[2].box] ids = [box.box_id for box in jp2.box[2].box]
self.assertEqual(ids, ['ihdr', 'colr']) self.assertEqual(ids, ['ihdr', 'colr'])
# Signature box. Check for corruption. # Signature box. Check for corruption.
@ -701,51 +703,51 @@ class TestSuiteWrite(unittest.TestCase):
# SIZ: Image and tile size # SIZ: Image and tile size
# Profile: "0" means profile 2 # Profile: "0" means profile 2
self.assertEqual(c.segment[1].Rsiz, 0) self.assertEqual(c.segment[1].rsiz, 0)
# Reference grid size # Reference grid size
self.assertEqual((c.segment[1].Xsiz, c.segment[1].Ysiz), self.assertEqual((c.segment[1].xsiz, c.segment[1].ysiz),
(640, 480)) (640, 480))
# Reference grid offset # Reference grid offset
self.assertEqual((c.segment[1].XOsiz, c.segment[1].YOsiz), self.assertEqual((c.segment[1].xosiz, c.segment[1].yosiz),
(0, 0)) (0, 0))
# Tile size # Tile size
self.assertEqual((c.segment[1].XTsiz, c.segment[1].YTsiz), self.assertEqual((c.segment[1].xtsiz, c.segment[1].ytsiz),
(640, 480)) (640, 480))
# Tile offset # Tile offset
self.assertEqual((c.segment[1].XTOsiz, c.segment[1].YTOsiz), self.assertEqual((c.segment[1].xtosiz, c.segment[1].ytosiz),
(0, 0)) (0, 0))
# bitdepth # bitdepth
self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8)) self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8))
# signed # signed
self.assertEqual(c.segment[1]._signed, (False, False, False)) self.assertEqual(c.segment[1]._signed, (False, False, False))
# subsampling # subsampling
self.assertEqual(list(zip(c.segment[1].XRsiz, c.segment[1].YRsiz)), self.assertEqual(list(zip(c.segment[1].xrsiz, c.segment[1].yrsiz)),
[(1, 1)] * 3) [(1, 1)] * 3)
# COD: Coding style default # COD: Coding style default
self.assertFalse(c.segment[2].Scod & 2) # no sop self.assertFalse(c.segment[2].scod & 2) # no sop
self.assertFalse(c.segment[2].Scod & 4) # no eph self.assertFalse(c.segment[2].scod & 4) # no eph
self.assertEqual(c.segment[2].SPcod[0], glymur.core.LRCP) self.assertEqual(c.segment[2].spcod[0], glymur.core.LRCP)
self.assertEqual(c.segment[2]._layers, 3) # layers = 3 self.assertEqual(c.segment[2]._layers, 3) # layers = 3
self.assertEqual(c.segment[2].SPcod[3], 1) # mct self.assertEqual(c.segment[2].spcod[3], 1) # mct
self.assertEqual(c.segment[2].SPcod[4], 2) # levels self.assertEqual(c.segment[2].spcod[4], 2) # levels
self.assertEqual(tuple(c.segment[2]._code_block_size), self.assertEqual(tuple(c.segment[2]._code_block_size),
(64, 64)) # cblksz (64, 64)) # cblksz
# Selective arithmetic coding BYPASS # Selective arithmetic coding BYPASS
self.assertFalse(c.segment[2].SPcod[7] & 0x01) self.assertFalse(c.segment[2].spcod[7] & 0x01)
# RESET context probabilities (RESET) # RESET context probabilities (RESET)
self.assertFalse(c.segment[2].SPcod[7] & 0x02) self.assertFalse(c.segment[2].spcod[7] & 0x02)
# Termination on each coding pass, RESTART(TERMALL) # Termination on each coding pass, RESTART(TERMALL)
self.assertFalse(c.segment[2].SPcod[7] & 0x04) self.assertFalse(c.segment[2].spcod[7] & 0x04)
# Vertically causal context (VSC) # Vertically causal context (VSC)
self.assertFalse(c.segment[2].SPcod[7] & 0x08) self.assertFalse(c.segment[2].spcod[7] & 0x08)
# Predictable termination, ERTERM(SEGTERM) # Predictable termination, ERTERM(SEGTERM)
self.assertFalse(c.segment[2].SPcod[7] & 0x0010) self.assertFalse(c.segment[2].spcod[7] & 0x0010)
# Segmentation symbols, SEGMARK(SEGSYSM) # Segmentation symbols, SEGMARK(SEGSYSM)
self.assertFalse(c.segment[2].SPcod[7] & 0x0020) self.assertFalse(c.segment[2].spcod[7] & 0x0020)
self.assertEqual(c.segment[2].SPcod[8], self.assertEqual(c.segment[2].spcod[8],
glymur.core.WAVELET_TRANSFORM_5x3_REVERSIBLE) glymur.core.WAVELET_XFORM_5X3_REVERSIBLE)
self.assertEqual(len(c.segment[2].SPcod), 9) self.assertEqual(len(c.segment[2].spcod), 9)
@unittest.skip("Known failure in openjpeg test suite.") @unittest.skip("Known failure in openjpeg test suite.")
def test_NR_ENC_random_issue_0005_tif_12_encode(self): def test_NR_ENC_random_issue_0005_tif_12_encode(self):
@ -762,50 +764,50 @@ class TestSuiteWrite(unittest.TestCase):
# SIZ: Image and tile size # SIZ: Image and tile size
# Profile: "0" means profile 2 # Profile: "0" means profile 2
self.assertEqual(c.segment[1].Rsiz, 0) self.assertEqual(c.segment[1].rsiz, 0)
# Reference grid size # Reference grid size
self.assertEqual((c.segment[1].Xsiz, c.segment[1].Ysiz), self.assertEqual((c.segment[1].xsiz, c.segment[1].ysiz),
(1024, 1024)) (1024, 1024))
# Reference grid offset # Reference grid offset
self.assertEqual((c.segment[1].XOsiz, c.segment[1].YOsiz), (0, 0)) self.assertEqual((c.segment[1].xosiz, c.segment[1].yosiz), (0, 0))
# Tile size # Tile size
self.assertEqual((c.segment[1].XTsiz, c.segment[1].YTsiz), self.assertEqual((c.segment[1].xtsiz, c.segment[1].ytsiz),
(1024, 1024)) (1024, 1024))
# Tile offset # Tile offset
self.assertEqual((c.segment[1].XTOsiz, c.segment[1].YTOsiz), self.assertEqual((c.segment[1].xtosiz, c.segment[1].ytosiz),
(0, 0)) (0, 0))
# bitdepth # bitdepth
self.assertEqual(c.segment[1]._bitdepth, (16,)) self.assertEqual(c.segment[1]._bitdepth, (16,))
# signed # signed
self.assertEqual(c.segment[1]._signed, (False,)) self.assertEqual(c.segment[1]._signed, (False,))
# subsampling # subsampling
self.assertEqual(list(zip(c.segment[1].XRsiz, c.segment[1].YRsiz)), self.assertEqual(list(zip(c.segment[1].xrsiz, c.segment[1].yrsiz)),
[(1, 1)]) [(1, 1)])
# COD: Coding style default # COD: Coding style default
self.assertFalse(c.segment[2].Scod & 2) # no sop self.assertFalse(c.segment[2].scod & 2) # no sop
self.assertFalse(c.segment[2].Scod & 4) # no eph self.assertFalse(c.segment[2].scod & 4) # no eph
self.assertEqual(c.segment[2].SPcod[0], glymur.core.LRCP) self.assertEqual(c.segment[2].spcod[0], glymur.core.LRCP)
self.assertEqual(c.segment[2]._layers, 1) # layers = 1 self.assertEqual(c.segment[2]._layers, 1) # layers = 1
self.assertEqual(c.segment[2].SPcod[3], 0) # mct self.assertEqual(c.segment[2].spcod[3], 0) # mct
self.assertEqual(c.segment[2].SPcod[4], 5) # levels self.assertEqual(c.segment[2].spcod[4], 5) # levels
self.assertEqual(tuple(c.segment[2]._code_block_size), self.assertEqual(tuple(c.segment[2]._code_block_size),
(64, 64)) # cblksz (64, 64)) # cblksz
# Selective arithmetic coding BYPASS # Selective arithmetic coding BYPASS
self.assertFalse(c.segment[2].SPcod[7] & 0x01) self.assertFalse(c.segment[2].spcod[7] & 0x01)
# RESET context probabilities (RESET) # RESET context probabilities (RESET)
self.assertFalse(c.segment[2].SPcod[7] & 0x02) self.assertFalse(c.segment[2].spcod[7] & 0x02)
# Termination on each coding pass, RESTART(TERMALL) # Termination on each coding pass, RESTART(TERMALL)
self.assertFalse(c.segment[2].SPcod[7] & 0x04) self.assertFalse(c.segment[2].spcod[7] & 0x04)
# Vertically causal context (VSC) # Vertically causal context (VSC)
self.assertFalse(c.segment[2].SPcod[7] & 0x08) self.assertFalse(c.segment[2].spcod[7] & 0x08)
# Predictable termination, ERTERM(SEGTERM) # Predictable termination, ERTERM(SEGTERM)
self.assertFalse(c.segment[2].SPcod[7] & 0x0010) self.assertFalse(c.segment[2].spcod[7] & 0x0010)
# Segmentation symbols, SEGMARK(SEGSYSM) # Segmentation symbols, SEGMARK(SEGSYSM)
self.assertFalse(c.segment[2].SPcod[7] & 0x0020) self.assertFalse(c.segment[2].spcod[7] & 0x0020)
self.assertEqual(c.segment[2].SPcod[8], self.assertEqual(c.segment[2].spcod[8],
glymur.core.WAVELET_TRANSFORM_5x3_REVERSIBLE) glymur.core.WAVELET_XFORM_5X3_REVERSIBLE)
self.assertEqual(len(c.segment[2].SPcod), 9) self.assertEqual(len(c.segment[2].spcod), 9)
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

View file

@ -1,3 +1,4 @@
#pylint: disable-all
import os import os
import pkg_resources import pkg_resources
import struct import struct
@ -21,7 +22,8 @@ except:
raise raise
@unittest.skipIf(glymur.lib.openjp2._OPENJP2 is None, @unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
@unittest.skipIf(glymur.lib.openjp2.OPENJP2 is None,
"Missing openjp2 library.") "Missing openjp2 library.")
class TestPrintingNeedsLib(unittest.TestCase): class TestPrintingNeedsLib(unittest.TestCase):
"""These tests require the library, mostly in order to just setup the test. """These tests require the library, mostly in order to just setup the test.
@ -34,7 +36,7 @@ class TestPrintingNeedsLib(unittest.TestCase):
with tempfile.NamedTemporaryFile(suffix='.jp2', delete=False) as tfile: with tempfile.NamedTemporaryFile(suffix='.jp2', delete=False) as tfile:
cls._plain_nemo_file = tfile.name cls._plain_nemo_file = tfile.name
ijfile = Jp2k(jp2file) ijfile = Jp2k(jp2file)
data = ijfile.read(reduce=3) data = ijfile.read(rlevel=1)
ojfile = Jp2k(cls._plain_nemo_file, 'wb') ojfile = Jp2k(cls._plain_nemo_file, 'wb')
ojfile.write(data) ojfile.write(data)
@ -43,8 +45,9 @@ class TestPrintingNeedsLib(unittest.TestCase):
os.unlink(cls._plain_nemo_file) os.unlink(cls._plain_nemo_file)
def setUp(self): def setUp(self):
self.jp2file = pkg_resources.resource_filename(glymur.__name__, self.jp2file = glymur.data.nemo()
"data/nemo.jp2") self.j2kfile = glymur.data.goodstuff()
# Save sys.stdout. # Save sys.stdout.
self.stdout = sys.stdout self.stdout = sys.stdout
sys.stdout = StringIO() sys.stdout = StringIO()
@ -57,7 +60,7 @@ class TestPrintingNeedsLib(unittest.TestCase):
" Compatibility: ['jp2 ']", " Compatibility: ['jp2 ']",
'JP2 Header Box (jp2h) @ (32, 45)', 'JP2 Header Box (jp2h) @ (32, 45)',
' Image Header Box (ihdr) @ (40, 22)', ' Image Header Box (ihdr) @ (40, 22)',
' Size: [182 324 3]', ' Size: [728 1296 3]',
' Bitdepth: 8', ' Bitdepth: 8',
' Signed: False', ' Signed: False',
' Compression: wavelet', ' Compression: wavelet',
@ -66,15 +69,15 @@ class TestPrintingNeedsLib(unittest.TestCase):
' Method: enumerated colorspace', ' Method: enumerated colorspace',
' Precedence: 0', ' Precedence: 0',
' Colorspace: sRGB', ' Colorspace: sRGB',
'Contiguous Codestream Box (jp2c) @ (77, 112814)', 'Contiguous Codestream Box (jp2c) @ (77, 1632355)',
' Main header:', ' Main header:',
' SOC marker segment @ (85, 0)', ' SOC marker segment @ (85, 0)',
' SIZ marker segment @ (87, 47)', ' SIZ marker segment @ (87, 47)',
' Profile: 2', ' Profile: 2',
' Reference Grid Height, Width: (182 x 324)', ' Reference Grid Height, Width: (728 x 1296)',
' Vertical, Horizontal Reference Grid Offset: ' ' Vertical, Horizontal Reference Grid Offset: '
+ '(0 x 0)', + '(0 x 0)',
' Reference Tile Height, Width: (182 x 324)', ' Reference Tile Height, Width: (728 x 1296)',
' Vertical, Horizontal Reference Tile Offset: ' ' Vertical, Horizontal Reference Tile Offset: '
+ '(0 x 0)', + '(0 x 0)',
' Bitdepth: (8, 8, 8)', ' Bitdepth: (8, 8, 8)',
@ -121,7 +124,7 @@ class TestPrintingNeedsLib(unittest.TestCase):
def test_asoc_label_box(self): def test_asoc_label_box(self):
# Construct a fake file with an asoc and a label box, as # Construct a fake file with an asoc and a label box, as
# OpenJPEG doesn't have such a file. # OpenJPEG doesn't have such a file.
data = glymur.Jp2k(self.jp2file).read(reduce=3) data = glymur.Jp2k(self.jp2file).read(rlevel=1)
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
j = glymur.Jp2k(tfile.name, 'wb') j = glymur.Jp2k(tfile.name, 'wb')
j.write(data) j.write(data)
@ -177,7 +180,7 @@ class TestPrintingNeedsLib(unittest.TestCase):
lst = actual.split('\n') lst = actual.split('\n')
lst = lst[1:] lst = lst[1:]
actual = '\n'.join(lst) actual = '\n'.join(lst)
self.maxDiff = None
self.assertEqual(actual, self.expectedPlain) self.assertEqual(actual, self.expectedPlain)
def test_entire_file(self): def test_entire_file(self):
@ -209,15 +212,15 @@ class TestPrinting(unittest.TestCase):
def test_COC_segment(self): def test_COC_segment(self):
j = glymur.Jp2k(self.jp2file) j = glymur.Jp2k(self.jp2file)
codestream = j.get_codestream(header_only=False) codestream = j.get_codestream(header_only=False)
print(codestream.segment[5]) print(codestream.segment[6])
actual = sys.stdout.getvalue().strip() actual = sys.stdout.getvalue().strip()
lines = ['COC marker segment @ (3233, 9)', lines = ['COC marker segment @ (3260, 9)',
' Associated component: 1', ' Associated component: 1',
' Coding style for this component: ' ' Coding style for this component: '
+ 'Entropy coder, PARTITION = 0', + 'Entropy coder, PARTITION = 0',
' Coding style parameters:', ' Coding style parameters:',
' Number of resolutions: 6', ' Number of resolutions: 2',
' Code block height, width: (64 x 64)', ' Code block height, width: (64 x 64)',
' Wavelet transform: 5-3 reversible', ' Wavelet transform: 5-3 reversible',
' Code block context:', ' Code block context:',
@ -230,6 +233,7 @@ class TestPrinting(unittest.TestCase):
' Segmentation symbols: False'] ' Segmentation symbols: False']
expected = '\n'.join(lines) expected = '\n'.join(lines)
self.maxDiff = None
self.assertEqual(actual, expected) self.assertEqual(actual, expected)
def test_COD_segment(self): def test_COD_segment(self):
@ -245,10 +249,10 @@ class TestPrinting(unittest.TestCase):
' EPH marker segments: False', ' EPH marker segments: False',
' Coding style parameters:', ' Coding style parameters:',
' Progression order: LRCP', ' Progression order: LRCP',
' Number of layers: 3', ' Number of layers: 2',
' Multiple component transformation usage: ' ' Multiple component transformation usage: '
+ 'reversible', + 'reversible',
' Number of resolutions: 6', ' Number of resolutions: 2',
' Code block height, width: (64 x 64)', ' Code block height, width: (64 x 64)',
' Wavelet transform: 5-3 reversible', ' Wavelet transform: 5-3 reversible',
' Precinct size: default, 2^15 x 2^15', ' Precinct size: default, 2^15 x 2^15',
@ -392,7 +396,7 @@ class TestPrinting(unittest.TestCase):
print(codestream.segment[-1]) print(codestream.segment[-1])
actual = sys.stdout.getvalue().strip() actual = sys.stdout.getvalue().strip()
lines = ['EOC marker segment @ (1136552, 0)'] lines = ['EOC marker segment @ (1135421, 0)']
expected = '\n'.join(lines) expected = '\n'.join(lines)
self.assertEqual(actual, expected) self.assertEqual(actual, expected)
@ -476,15 +480,13 @@ class TestPrinting(unittest.TestCase):
def test_QCC_segment(self): def test_QCC_segment(self):
j = glymur.Jp2k(self.jp2file) j = glymur.Jp2k(self.jp2file)
codestream = j.get_codestream(header_only=False) codestream = j.get_codestream(header_only=False)
print(codestream.segment[6]) print(codestream.segment[7])
actual = sys.stdout.getvalue().strip() actual = sys.stdout.getvalue().strip()
lines = ['QCC marker segment @ (3244, 20)', lines = ['QCC marker segment @ (3271, 8)',
' Associated Component: 1', ' Associated Component: 1',
' Quantization style: no quantization, 2 guard bits', ' Quantization style: no quantization, 2 guard bits',
' Step size: [(0, 8), (0, 9), (0, 9), (0, 10), (0, 9), ' ' Step size: [(0, 8), (0, 9), (0, 9), (0, 10)]']
+ '(0, 9), (0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), '
+ '(0, 10), (0, 9), (0, 9), (0, 10)]']
expected = '\n'.join(lines) expected = '\n'.join(lines)
self.assertEqual(actual, expected) self.assertEqual(actual, expected)
@ -495,11 +497,9 @@ class TestPrinting(unittest.TestCase):
print(codestream.segment[3]) print(codestream.segment[3])
actual = sys.stdout.getvalue().strip() actual = sys.stdout.getvalue().strip()
lines = ['QCD marker segment @ (3200, 19)', lines = ['QCD marker segment @ (3200, 7)',
' Quantization style: no quantization, 2 guard bits', ' Quantization style: no quantization, 2 guard bits',
' Step size: [(0, 8), (0, 9), (0, 9), (0, 10), (0, 9), ' ' Step size: [(0, 8), (0, 9), (0, 9), (0, 10)]']
+ '(0, 9), (0, 10), (0, 9), (0, 9), (0, 10), (0, 9), '
+ '(0, 9), (0, 10), (0, 9), (0, 9), (0, 10)]']
expected = '\n'.join(lines) expected = '\n'.join(lines)
self.assertEqual(actual, expected) self.assertEqual(actual, expected)
@ -514,7 +514,7 @@ class TestPrinting(unittest.TestCase):
' Profile: 2', ' Profile: 2',
' Reference Grid Height, Width: (1456 x 2592)', ' Reference Grid Height, Width: (1456 x 2592)',
' Vertical, Horizontal Reference Grid Offset: (0 x 0)', ' 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)', ' Vertical, Horizontal Reference Tile Offset: (0 x 0)',
' Bitdepth: (8, 8, 8)', ' Bitdepth: (8, 8, 8)',
' Signed: (False, False, False)', ' Signed: (False, False, False)',
@ -537,26 +537,27 @@ class TestPrinting(unittest.TestCase):
def test_SOD_segment(self): def test_SOD_segment(self):
j = glymur.Jp2k(self.jp2file) j = glymur.Jp2k(self.jp2file)
codestream = j.get_codestream(header_only=False) codestream = j.get_codestream(header_only=False)
print(codestream.segment[9]) print(codestream.segment[10])
actual = sys.stdout.getvalue().strip() actual = sys.stdout.getvalue().strip()
lines = ['SOD marker segment @ (3299, 0)'] lines = ['SOD marker segment @ (3302, 0)']
expected = '\n'.join(lines) expected = '\n'.join(lines)
self.assertEqual(actual, expected) self.assertEqual(actual, expected)
def test_SOT_segment(self): def test_SOT_segment(self):
j = glymur.Jp2k(self.jp2file) j = glymur.Jp2k(self.jp2file)
codestream = j.get_codestream(header_only=False) codestream = j.get_codestream(header_only=False)
print(codestream.segment[4]) print(codestream.segment[5])
actual = sys.stdout.getvalue().strip() actual = sys.stdout.getvalue().strip()
lines = ['SOT marker segment @ (3221, 10)', lines = ['SOT marker segment @ (3248, 10)',
' Tile part index: 0', ' Tile part index: 0',
' Tile part length: 78629', ' Tile part length: 1132173',
' Tile part instance: 0', ' Tile part instance: 0',
' Number of tile parts: 1'] ' Number of tile parts: 1']
expected = '\n'.join(lines) expected = '\n'.join(lines)
self.maxDiff = None
self.assertEqual(actual, expected) self.assertEqual(actual, expected)
@unittest.skipIf(data_root is None, @unittest.skipIf(data_root is None,
@ -606,7 +607,7 @@ class TestPrinting(unittest.TestCase):
' Profile: 2', ' Profile: 2',
' Reference Grid Height, Width: (1456 x 2592)', ' Reference Grid Height, Width: (1456 x 2592)',
' Vertical, Horizontal Reference Grid Offset: (0 x 0)', ' 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)', ' Vertical, Horizontal Reference Tile Offset: (0 x 0)',
' Bitdepth: (8, 8, 8)', ' Bitdepth: (8, 8, 8)',
' Signed: (False, False, False)', ' Signed: (False, False, False)',
@ -619,10 +620,10 @@ class TestPrinting(unittest.TestCase):
' EPH marker segments: False', ' EPH marker segments: False',
' Coding style parameters:', ' Coding style parameters:',
' Progression order: LRCP', ' Progression order: LRCP',
' Number of layers: 3', ' Number of layers: 2',
' Multiple component transformation usage: ' ' Multiple component transformation usage: '
+ 'reversible', + 'reversible',
' Number of resolutions: 6', ' Number of resolutions: 2',
' Code block height, width: (64 x 64)', ' Code block height, width: (64 x 64)',
' Wavelet transform: 5-3 reversible', ' Wavelet transform: 5-3 reversible',
' Precinct size: default, 2^15 x 2^15', ' Precinct size: default, 2^15 x 2^15',
@ -634,14 +635,14 @@ class TestPrinting(unittest.TestCase):
' Vertically stripe causal context: False', ' Vertically stripe causal context: False',
' Predictable termination: False', ' Predictable termination: False',
' Segmentation symbols: False', ' Segmentation symbols: False',
' QCD marker segment @ (3200, 19)', ' QCD marker segment @ (3200, 7)',
' Quantization style: no quantization, ' ' Quantization style: no quantization, '
+ '2 guard bits', + '2 guard bits',
' Step size: [(0, 8), (0, 9), (0, 9), ' ' Step size: [(0, 8), (0, 9), (0, 9), (0, 10)]',
+ '(0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), ' ' CME marker segment @ (3209, 37)',
+ '(0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), ' ' "Created by OpenJPEG version 2.0.0"']
+ '(0, 10)]']
expected = '\n'.join(lst) expected = '\n'.join(lst)
self.maxDiff = None
self.assertEqual(actual, expected) self.assertEqual(actual, expected)
@unittest.skipIf(data_root is None, @unittest.skipIf(data_root is None,
@ -783,6 +784,7 @@ class TestPrinting(unittest.TestCase):
expected = '\n'.join(lines) expected = '\n'.join(lines)
self.assertEqual(actual, expected) self.assertEqual(actual, expected)
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
def test_less_common_boxes(self): def test_less_common_boxes(self):
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
with open(self.jp2file, 'rb') as ifile: with open(self.jp2file, 'rb') as ifile:

30
release.txt Normal file
View file

@ -0,0 +1,30 @@
| OS | Python 2.7 | Python 3.3 | Notes |
+------------+------------+------------+--------------------------------------+
| Windows | X | | Python(xy) with OpenJPEG 1.5.1. At |
| | | | least 155 of 444 tests should pass. |
+------------+------------+------------+--------------------------------------+
| Windows | X | | Python(xy) with OpenJPEG 1.5.1 and |
| | | | OpenJPEG svn. At least 282 of 444 |
| | | | tests should pass. |
+------------+------------+------------+--------------------------------------+
| Mac | X | | MacPorts with both OpenJPEG 1.5.1 |
| | | | and OpenJPEG svn. 370 of 450 tests |
| | | | should pass. |
+------------+------------+------------+--------------------------------------+
| Mac | | X | MacPorts with both OpenJPEG 1.5.1 |
| | | | and OpenJPEG svn. 390 of 450 |
| | | | tests should pass. |
+------------+------------+------------+--------------------------------------+
| Fedora 19 | | X | Ships with 1.5.1. 390 of 450 tests |
| | | | should pass. |
+------------+------------+------------+--------------------------------------+
| Fedora 18 | | X | Ships with 1.5.1. 167 of 445 tests |
| | | | should pass. |
+------------+------------+------------+--------------------------------------+
| Fedora 17 | X | | Ships with 1.4. Should error out |
| | | | gracefully. |
+------------+------------+------------+--------------------------------------+
Pylint on entire package should be at least 0.95.
pep8 should be pass cleanly.
Coverage should exceed 95%.

View file

@ -2,7 +2,7 @@ from setuptools import setup, find_packages
import sys import sys
kwargs = {'name': 'Glymur', kwargs = {'name': 'Glymur',
'version': '0.1.10', 'version': '0.2.0',
'description': 'Tools for accessing JPEG2000 files', 'description': 'Tools for accessing JPEG2000 files',
'long_description': open('README.md').read(), 'long_description': open('README.md').read(),
'author': 'John Evans', 'author': 'John Evans',
@ -26,9 +26,10 @@ clssfrs = ["Programming Language :: Python",
"Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.3",
"Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: CPython",
"License :: OSI Approved :: MIT License", "License :: OSI Approved :: MIT License",
"Development Status :: 3 - Alpha", "Development Status :: 5 - Production/Stable",
"Operating System :: MacOS", "Operating System :: MacOS",
"Operating System :: POSIX :: Linux", "Operating System :: POSIX :: Linux",
"Operating System :: Microsoft :: Windows :: Windows XP",
"Intended Audience :: Science/Research", "Intended Audience :: Science/Research",
"Intended Audience :: Information Technology", "Intended Audience :: Information Technology",
"Topic :: Software Development :: Libraries :: Python Modules"] "Topic :: Software Development :: Libraries :: Python Modules"]