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
parameter to specify multi component transform. Added a raw codestream
file.

View file

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

View file

@ -78,7 +78,7 @@ copyright = u'2013, John Evans'
# The short X.Y version.
version = '0.1'
# 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
# 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
use your system's package manager to install as many required
packages/RPMs/ports/whatever without going through pip. Otherwise go on to
the next page.
packages/RPMs/ports/whatever without going through pip.
''''''''
Platform
''''''''
Mac OS X
--------
@ -27,13 +68,29 @@ additionally be installed:
* py33-scikit-image and either py33-Pillow or freeimage
* 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
-----
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 ships with Python 3.3, so all the necessary RPMs are available to
meet the minimal set of requirements.
Fedora 18 ships with Python 3.3 and the following RPMs are available to
meet the minimal set of requirements for running glymur.
* python3
* python3-numpy
@ -56,31 +113,10 @@ repositories::
$ pip-python3 install Pillow --user
$ 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 ships with Python 3.2 and 2.7, so these steps detail working with
2.7.
Fedora 17 ships with Python 3.2 and 2.7, but OpenJPEG is only at version 1.4,
so these steps detail working with Python 2.7 and the svn version of OpenJPEG.
Required RPMs include::
@ -111,19 +147,23 @@ it was installable via pip::
Windows
-------
Not currently supported.
The only configuration I've tested is Python(xy), which uses Python 2.7.
Python(xy) already comes with numpy, but you will have to install pip and then
contextlib2 and mock as well. Both 1.5.1 and the svn development versions of
openjpeg work.
'''''''
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 ... ::
>>> import glymur
>>> glymur.runtests()
or from the unix command line. ::
or from the command line. ::
$ cd /to/where/you/unpacked/glymur
$ python -m unittest discover

View file

@ -6,13 +6,13 @@ How do I...?
Read the lowest resolution thumbnail?
=====================================
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
level. ::
COD segment section), but you can take a shortcut by supplying -1 as the
resolution level. ::
>>> import glymur
>>> file = glymur.data.nemo()
>>> j = glymur.Jp2k(file)
>>> thumbnail = j.read(reduce=-1)
>>> thumbnail = j.read(rlevel=-1)
Display metadata?
=================

View file

@ -2,14 +2,9 @@
Glymur: a Python interface for JPEG 2000
----------------------------------------
**Glymur** contains a Python interface to the OpenJPEG library
which allows linux and mac users to read and write JPEG 2000 files. For more
information about OpenJPEG, please consult http://www.openjpeg.org. Glymur
currently relies upon a development version of the OpenJPEG library, and so,
while useable, it is totally at the mercy of any upstream changes
made to the development version of OpenJPEG.
Glymur supports both reading and writing of JPEG 2000 images (part 1). Writing
**Glymur** is an interface to the OpenJPEG library
which allows one to read and write JPEG 2000 files from within Python.
Glymur supports both reading and writing of JPEG 2000 images. Writing
JPEG 2000 images is currently limited to images that can fit in memory,
however.
@ -23,34 +18,18 @@ Glymur works on Python 2.7 and 3.3. Python 3.3 is strongly recommended.
OpenJPEG Installation
=====================
OpenJPEG must be built as a shared library. In addition, you
currently must compile OpenJPEG from the developmental source that
you can retrieve via subversion. As of this time of writing, svn
revision 2345 works. You should download the test data for the purpose
of configuring and running OpenJPEG's test suite, check their instructions for
all this. You should set the **OPJ_DATA_ROOT** environment variable for the
purpose of running Glymur's test suite. ::
The OpenJPEG library version must be either 1.5.1 or the trunk/development
version of OpenJPEG. Version 2.0.0 or versions earlier than 1.5.0
are not supported. Furthermore, the 1.5.x version of OpenJPEG is
currently only utilized for read-only purposes. For more information
about OpenJPEG, please consult http://www.openjpeg.org.
$ svn co http://openjpeg.googlecode.com/svn/data
$ export OPJ_DATA_ROOT=`pwd`/data
Earlier versions of OpenJPEG through the 2.0 official release will **NOT**
work and are not supported.
Glymur uses ctypes (for the moment) to access the openjp2 library, and
because ctypes access libraries in a platform-dependent manner, it is
recommended that you create a configuration file to help Glymur properly find
the openjp2 library. You may create the configuration file as follows::
$ mkdir -p ~/.config/glymur
$ cd ~/.config/glymur
$ cat > glymurrc << EOF
> [library]
> openjp2: /opt/openjp2-svn/lib/libopenjp2.so
> EOF
That assumes, of course, that you've installed OpenJPEG into /opt/openjp2-svn.
You may also substitute **$XDG_CONFIG_HOME** for **$HOME/.config**.
If you use MacPorts on the mac or if you have a sufficiently recent version of
Linux, your package manager should already provide you with at least version
1.5.1 of OpenJPEG, which means that glymur can be installed ready to read JPEG
2000 images. If you use windows, I suggest using the 1.5.1 windows installer
provided to you by the OpenJPEG folks at
https://code.google.com/p/openjpeg/downloads/list .
Glymur Installation
===================
@ -76,5 +55,6 @@ You can run the tests from within python as follows::
>>> import glymur
>>> glymur.runtests()
Many tests are currently skipped; the important thing is whether or not any
tests fail.
Many tests are currently skipped; in fact most of them are skipped if you
are relying on OpenJPEG 1.5.1. But the important thing, 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
LRCP = 0
RLCP = 1
@ -5,44 +7,21 @@ RPCL = 2
PCRL = 3
CPRL = 4
_progression_order_display = {
LRCP: 'LRCP',
RLCP: 'RLCP',
RPCL: 'RPCL',
PCRL: 'PCRL',
CPRL: 'CPRL'}
progression_order = {
PROGRESSION_ORDER = {
'LRCP': LRCP,
'RLCP': RLCP,
'RPCL': RPCL,
'PCRL': PCRL,
'CPRL': CPRL}
WAVELET_TRANSFORM_9x7_IRREVERSIBLE = 0
WAVELET_TRANSFORM_5x3_REVERSIBLE = 1
_wavelet_transform_display = {
WAVELET_TRANSFORM_9x7_IRREVERSIBLE: '9-7 irreversible',
WAVELET_TRANSFORM_5x3_REVERSIBLE: '5-3 reversible'}
WAVELET_XFORM_9X7_IRREVERSIBLE = 0
WAVELET_XFORM_5X3_REVERSIBLE = 1
ENUMERATED_COLORSPACE = 1
RESTRICTED_ICC_PROFILE = 2
ANY_ICC_PROFILE = 3
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.
RCME_BINARY = 0 # binary value comments
RCME_ISO_8859_1 = 1 # comments in latin-1 codec
@ -55,7 +34,7 @@ YCC = 18
E_SRGB = 20
ROMM_RGB = 21
_colorspace_map_display = {
_COLORSPACE_MAP_DISPLAY = {
CMYK: 'CMYK',
SRGB: 'sRGB',
GREYSCALE: 'greyscale',
@ -68,7 +47,7 @@ _COLOR = 0
_OPACITY = 1
_PRE_MULTIPLIED_OPACITY = 2
_UNSPECIFIED = 65535
_color_type_map_display = {
_COLOR_TYPE_MAP_DISPLAY = {
_COLOR: 'color',
_OPACITY: 'opacity',
_PRE_MULTIPLIED_OPACITY: 'pre-multiplied opacity',
@ -81,49 +60,15 @@ BLUE = 3
GREY = 1
# enumerated color channel associations
_rgb_colorspace = {"R": 1, "G": 2, "B": 3}
_greyscale_colorspace = {"Y": 1}
_ycbcr_colorspace = {"Y": 1, "Cb": 2, "Cr": 3}
_colorspace = {SRGB: _rgb_colorspace,
GREYSCALE: _greyscale_colorspace,
YCC: _ycbcr_colorspace,
E_SRGB: _rgb_colorspace,
ROMM_RGB: _rgb_colorspace}
_COLORSPACE = {SRGB: {"R": 1, "G": 2, "B": 3},
GREYSCALE: {"Y": 1},
YCC: {"Y": 1, "Cb": 2, "Cr": 3},
E_SRGB: {"R": 1, "G": 2, "B": 3},
ROMM_RGB: {"R": 1, "G": 2, "B": 3}}
# How to display the codestream profile.
_capabilities_display = {
_CAPABILITIES_DISPLAY = {
0: '2',
1: '0',
2: '1',
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
Platform-independent path to nemo.jp2.
"""
file = pkg_resources.resource_filename(__name__, "nemo.jp2")
return file
filename = pkg_resources.resource_filename(__name__, "nemo.jp2")
return filename
def goodstuff():
@ -29,5 +29,5 @@ def goodstuff():
file : str
Platform-independent path to goodstuff.j2k.
"""
file = pkg_resources.resource_filename(__name__, "goodstuff.j2k")
return file
filename = pkg_resources.resource_filename(__name__, "goodstuff.j2k")
return filename

Binary file not shown.

File diff suppressed because it is too large Load diff

View file

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

View file

@ -1,3 +1,4 @@
"""This package organizes individual libraries employed by glymur."""
from . import openjp2
from . import test
from . import openjp2 as _openjp2
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 os
import pkg_resources
@ -12,13 +13,8 @@ import numpy as np
import glymur
def load_tests(loader, tests, ignore):
if glymur.lib.openjp2._OPENJP2 is not None:
tests.addTests(doctest.DocTestSuite('glymur.lib.openjp2'))
return tests
@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.")
class TestOpenJP2(unittest.TestCase):
@ -29,7 +25,7 @@ class TestOpenJP2(unittest.TestCase):
pass
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.cblockw_init, 64)
@ -38,7 +34,7 @@ class TestOpenJP2(unittest.TestCase):
self.assertEqual(cparams.subsampling_dx, 1)
self.assertEqual(cparams.subsampling_dy, 1)
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.cp_tx0, 0)
self.assertEqual(cparams.cp_ty0, 0)
@ -46,7 +42,7 @@ class TestOpenJP2(unittest.TestCase):
self.assertEqual(cparams.irreversible, 0)
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_y0, 0)
@ -55,34 +51,34 @@ class TestOpenJP2(unittest.TestCase):
def tile_macro(self, codec, stream, imagep, tidx):
# 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):
self.assertIsNotNone(imagep.contents.comps[j].data)
def j2k_random_tile_access(self, filename, codec_format=None):
# 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()
nelts = glymur.lib.openjp2._PATH_LEN - len(infile)
nelts = glymur.lib._openjp2.PATH_LEN - len(infile)
infile += b'0' * nelts
dparam.infile = infile
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_warning_handler(codec, None)
glymur.lib.openjp2._set_error_handler(codec, None)
glymur.lib._openjp2.set_info_handler(codec, None)
glymur.lib._openjp2.set_warning_handler(codec, None)
glymur.lib._openjp2.set_error_handler(codec, None)
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)
image = glymur.lib.openjp2._read_header(stream, codec)
glymur.lib._openjp2.setup_decoder(codec, dparam)
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_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_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._destroy_codec(codec)
glymur.lib.openjp2._stream_destroy_v3(stream)
glymur.lib.openjp2._image_destroy(image)
glymur.lib._openjp2.end_decompress(codec, stream)
glymur.lib._openjp2.destroy_codec(codec)
glymur.lib._openjp2.stream_destroy_v3(stream)
glymur.lib._openjp2.image_destroy(image)
def tile_decoder(self, x0=None, y0=None, x1=None, y1=None, filename=None,
codec_format=None):
x = (filename, True)
stream = glymur.lib.openjp2._stream_create_default_file_stream_v3(*x)
dparam = glymur.lib.openjp2._set_default_decoder_parameters()
stream = glymur.lib._openjp2.stream_create_default_file_stream_v3(*x)
dparam = glymur.lib._openjp2.set_default_decoder_parameters()
dparam.decod_format = codec_format
@ -115,30 +111,30 @@ class TestOpenJP2(unittest.TestCase):
# do not use resolution reductions.
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_warning_handler(codec, None)
glymur.lib.openjp2._set_error_handler(codec, None)
glymur.lib._openjp2.set_info_handler(codec, None)
glymur.lib._openjp2.set_warning_handler(codec, None)
glymur.lib._openjp2.set_error_handler(codec, None)
glymur.lib.openjp2._setup_decoder(codec, dparam)
image = glymur.lib.openjp2._read_header(stream, codec)
glymur.lib.openjp2._set_decode_area(codec, image, x0, y0, x1, y1)
glymur.lib._openjp2.setup_decoder(codec, dparam)
image = glymur.lib._openjp2.read_header(stream, codec)
glymur.lib._openjp2.set_decode_area(codec, image, x0, y0, x1, y1)
data = np.zeros((1150, 2048, 3), dtype=np.uint8)
while True:
rargs = glymur.lib.openjp2._read_tile_header(codec, stream)
rargs = glymur.lib._openjp2.read_tile_header(codec, stream)
tidx = rargs[0]
sz = rargs[1]
go_on = rargs[-1]
if not go_on:
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._destroy_codec(codec)
glymur.lib.openjp2._stream_destroy_v3(stream)
glymur.lib.openjp2._image_destroy(image)
glymur.lib._openjp2.end_decompress(codec, stream)
glymur.lib._openjp2.destroy_codec(codec)
glymur.lib._openjp2.stream_destroy_v3(stream)
glymur.lib._openjp2.image_destroy(image)
def tile_encoder(self, num_comps=None, tile_width=None, tile_height=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 = (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.cp_fixed_quality = 1
@ -170,9 +166,9 @@ class TestOpenJP2(unittest.TestCase):
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):
l_params[j].dx = 1
l_params[j].dy = 1
@ -183,38 +179,38 @@ class TestOpenJP2(unittest.TestCase):
l_params[j].x0 = 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_warning_handler(codec, None)
glymur.lib.openjp2._set_error_handler(codec, None)
glymur.lib._openjp2.set_info_handler(codec, None)
glymur.lib._openjp2.set_warning_handler(codec, None)
glymur.lib._openjp2.set_error_handler(codec, None)
cspace = glymur.lib.openjp2._CLRSPC_SRGB
l_image = glymur.lib.openjp2._image_tile_create(l_params, cspace)
cspace = glymur.lib._openjp2.CLRSPC_SRGB
l_image = glymur.lib._openjp2.image_tile_create(l_params, cspace)
l_image.contents.x0 = 0
l_image.contents.y0 = 0
l_image.contents.x1 = image_width
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)
stream = glymur.lib.openjp2._stream_create_default_file_stream_v3(*x)
glymur.lib.openjp2._start_compress(codec, l_image, stream)
stream = glymur.lib._openjp2.stream_create_default_file_stream_v3(*x)
glymur.lib._openjp2.start_compress(codec, l_image, stream)
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._stream_destroy_v3(stream)
glymur.lib.openjp2._destroy_codec(codec)
glymur.lib.openjp2._image_destroy(l_image)
glymur.lib._openjp2.end_compress(codec, stream)
glymur.lib._openjp2.stream_destroy_v3(stream)
glymur.lib._openjp2.destroy_codec(codec)
glymur.lib._openjp2.image_destroy(l_image)
def tte0_setup(self, filename):
kwargs = {'filename': filename,
'codec': glymur.lib.openjp2._CODEC_J2K,
'codec': glymur.lib._openjp2.CODEC_J2K,
'comp_prec': 8,
'irreversible': 1,
'num_comps': 3,
@ -241,12 +237,12 @@ class TestOpenJP2(unittest.TestCase):
'x1': 1000,
'y1': 1000,
'filename': tfile.name,
'codec_format': glymur.lib.openjp2._CODEC_J2K}
'codec_format': glymur.lib._openjp2.CODEC_J2K}
self.tile_decoder(**kwargs)
def tte1_setup(self, filename):
kwargs = {'filename': filename,
'codec': glymur.lib.openjp2._CODEC_J2K,
'codec': glymur.lib._openjp2.CODEC_J2K,
'comp_prec': 8,
'irreversible': 1,
'num_comps': 3,
@ -273,7 +269,7 @@ class TestOpenJP2(unittest.TestCase):
'x1': 128,
'y1': 128,
'filename': tfile.name,
'codec_format': glymur.lib.openjp2._CODEC_J2K}
'codec_format': glymur.lib._openjp2.CODEC_J2K}
self.tile_decoder(**kwargs)
def test_rta1(self):
@ -281,12 +277,12 @@ class TestOpenJP2(unittest.TestCase):
# Runs test designated rta1 in OpenJPEG test suite.
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)
def tte2_setup(self, filename):
kwargs = {'filename': filename,
'codec': glymur.lib.openjp2._CODEC_JP2,
'codec': glymur.lib._openjp2.CODEC_JP2,
'comp_prec': 8,
'irreversible': 1,
'num_comps': 3,
@ -312,7 +308,7 @@ class TestOpenJP2(unittest.TestCase):
'x1': 128,
'y1': 128,
'filename': tfile.name,
'codec_format': glymur.lib.openjp2._CODEC_JP2}
'codec_format': glymur.lib._openjp2.CODEC_JP2}
self.tile_decoder(**kwargs)
def test_rta2(self):
@ -320,12 +316,12 @@ class TestOpenJP2(unittest.TestCase):
# Runs test designated rta2 in OpenJPEG test suite.
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)
def tte3_setup(self, filename):
kwargs = {'filename': filename,
'codec': glymur.lib.openjp2._CODEC_J2K,
'codec': glymur.lib._openjp2.CODEC_J2K,
'comp_prec': 8,
'irreversible': 1,
'num_comps': 1,
@ -345,12 +341,12 @@ class TestOpenJP2(unittest.TestCase):
with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile:
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)
def tte4_setup(self, filename):
kwargs = {'filename': filename,
'codec': glymur.lib.openjp2._CODEC_J2K,
'codec': glymur.lib._openjp2.CODEC_J2K,
'comp_prec': 8,
'irreversible': 0,
'num_comps': 1,
@ -370,12 +366,12 @@ class TestOpenJP2(unittest.TestCase):
with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile:
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)
def tte5_setup(self, filename):
kwargs = {'filename': filename,
'codec': glymur.lib.openjp2._CODEC_J2K,
'codec': glymur.lib._openjp2.CODEC_J2K,
'comp_prec': 8,
'irreversible': 0,
'num_comps': 1,
@ -395,7 +391,7 @@ class TestOpenJP2(unittest.TestCase):
with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile:
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)
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_codestream import TestCodestream as codestream
from .test_config import TestSuite as config
from .test_jp2k import TestJp2k as jp2k
from .test_icc import TestICC as icc
from .test_printing import TestPrinting as printing
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_neg import TestSuiteNegative as suiteneg
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 pkg_resources
import re
import sys
import tempfile
import unittest
@ -13,7 +15,7 @@ else:
import glymur
@unittest.skipIf(glymur.lib.openjp2._OPENJP2 is None,
@unittest.skipIf(glymur.lib.openjp2.OPENJP2 is None,
"Missing openjp2 library.")
class TestCallbacks(unittest.TestCase):
@ -21,13 +23,14 @@ class TestCallbacks(unittest.TestCase):
# Save sys.stdout.
self.stdout = sys.stdout
sys.stdout = StringIO()
self.jp2file = pkg_resources.resource_filename(glymur.__name__,
"data/nemo.jp2")
self.jp2file = glymur.data.nemo()
self.j2kfile = glymur.data.goodstuff()
def tearDown(self):
# Restore stdout.
sys.stdout = self.stdout
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
def test_info_callback_on_write(self):
# Verify the messages printed when writing an image in verbose mode.
j = glymur.Jp2k(self.jp2file)
@ -44,24 +47,69 @@ class TestCallbacks(unittest.TestCase):
def test_info_warning_callbacks_on_read(self):
# Verify that we get the expected stdio output when our internal info
# callback handler is enabled.
j = glymur.Jp2k(self.jp2file)
d = j.read(reduce=3, verbose=True, area=(0, 0, 512, 1024))
j = glymur.Jp2k(self.j2kfile)
d = j.read(rlevel=1, verbose=True, area=(0, 0, 200, 150))
actual = sys.stdout.getvalue().strip()
lines = ['[INFO] Start to read j2k main header (3135).',
lines = ['[INFO] Start to read j2k main header (0).',
'[INFO] Main header has been correctly decoded.',
'[INFO] Setting decoding area to 0,0,1024,512',
'[INFO] Header of tile 0 / 17 has been read.',
'[INFO] Tile 1/18 has been decoded.',
'[INFO] Image data has been updated with tile 1.',
'[INFO] Header of tile 1 / 17 has been read.',
'[INFO] Tile 2/18 has been decoded.',
'[INFO] Image data has been updated with tile 2.',
'[INFO] Stream reached its end !']
'[INFO] Setting decoding area to 0,0,150,200',
'[INFO] Header of tile 0 / 0 has been read.',
'[INFO] Tile 1/1 has been decoded.',
'[INFO] Image data has been updated with tile 1.']
expected = '\n'.join(lines)
self.assertEqual(actual, expected)
@unittest.skipIf(glymur.lib.openjp2.OPENJPEG is None,
"Missing openjpeg library.")
class TestCallbacks15(unittest.TestCase):
"""This test suite is for OpenJPEG 1.5.1 properties.
"""
@classmethod
def setUpClass(cls):
# Monkey patch the package so as to use OPENJPEG instead of OPENJP2
cls.openjp2 = glymur.lib.openjp2.OPENJP2
glymur.lib.openjp2.OPENJP2 = None
@classmethod
def tearDownClass(cls):
# Restore OPENJP2
glymur.lib.openjp2.OPENJP2 = cls.openjp2
def setUp(self):
# Save sys.stdout.
self.stdout = sys.stdout
sys.stdout = StringIO()
self.jp2file = glymur.data.nemo()
self.j2kfile = glymur.data.goodstuff()
def tearDown(self):
# Restore stdout.
sys.stdout = self.stdout
def test_info_callbacks_on_read(self):
# Verify that we get the expected stdio output when our internal info
# callback handler is enabled.
j = glymur.Jp2k(self.j2kfile)
d = j.read(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__":
unittest.main()

View file

@ -1,3 +1,4 @@
#pylint: disable-all
import os
import struct
import sys
@ -29,6 +30,7 @@ class TestCodestream(unittest.TestCase):
def tearDown(self):
pass
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
def test_reserved_marker_segment(self):
# Some marker segments were reserved in FCD15444-1. Since that
# standard is old, some of them may have come into use.
@ -54,10 +56,11 @@ class TestCodestream(unittest.TestCase):
j = Jp2k(tfile.name)
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].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,
"Uses features introduced in 3.2.")
def test_unknown_marker_segment(self):
@ -84,9 +87,9 @@ class TestCodestream(unittest.TestCase):
j = Jp2k(tfile.name)
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].data, b'\x00')
self.assertEqual(c.segment[2]._data, b'\x00')
if __name__ == "__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 os
import sys
@ -16,14 +21,28 @@ from glymur import Jp2k
from glymur.lib import openjp2 as opj2
@unittest.skip("Cannot work when both OPENJPEG and OPENJP2 are both present.")
@unittest.skipIf(glymur.lib.openjp2.OPENJP2 is None,
"Needs openjp2 library first before these tests make sense.")
@unittest.skipIf(sys.hexversion < 0x03020000,
"Uses features introduced in 3.2.")
class TestJp2k(unittest.TestCase):
class TestSuite(unittest.TestCase):
@classmethod
def setUpClass(cls):
# Monkey patch the package so as to ignore OPENJPEG if it exists.
cls.openjpeg = glymur.lib.openjpeg._OPENJPEG
glymur.lib.openjp2._OPENJPEG = None
@classmethod
def tearDownClass(cls):
# Restore OPENJPEG
glymur.lib.openjpeg._OPENJPEG = cls.openjpeg
def setUp(self):
self.jp2file = pkg_resources.resource_filename(glymur.__name__,
"data/nemo.jp2")
imp.reload(glymur)
imp.reload(glymur.lib.openjp2)
self.jp2file = glymur.data.nemo()
def tearDown(self):
imp.reload(glymur)
@ -37,7 +56,7 @@ class TestJp2k(unittest.TestCase):
filename = os.path.join(configdir, 'glymurrc')
with open(filename, 'wb') as tfile:
tfile.write('[library]\n'.encode())
libloc = glymur.lib.openjp2._OPENJP2._name
libloc = glymur.lib.openjp2.OPENJP2._name
line = 'openjp2: {0}\n'.format(libloc)
tfile.write(line.encode())
tfile.flush()

View file

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

View file

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

View file

@ -1,7 +1,5 @@
import contextlib
import ctypes
# pylint: disable-all
import doctest
import imp
import os
import re
import shutil
@ -33,14 +31,18 @@ except:
# Doc tests should be run as well.
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'))
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.")
class TestJp2k(unittest.TestCase):
class TestJp2kBadXmlFile(unittest.TestCase):
@classmethod
def setUpClass(cls):
@ -74,10 +76,8 @@ class TestJp2k(unittest.TestCase):
os.unlink(cls._bad_xml_file)
def setUp(self):
self.jp2file = pkg_resources.resource_filename(glymur.__name__,
"data/nemo.jp2")
self.j2kfile = pkg_resources.resource_filename(glymur.__name__,
"data/goodstuff.j2k")
self.jp2file = glymur.data.nemo()
self.j2kfile = glymur.data.goodstuff()
def tearDown(self):
pass
@ -90,25 +90,37 @@ class TestJp2k(unittest.TestCase):
with self.assertWarns(UserWarning) as cw:
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):
# Should be able to recover from xml box with bad xml.
with warnings.catch_warnings():
warnings.simplefilter("ignore")
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].length, 28)
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):
# Verify that we error out appropriately if given a bad area parameter.
j = Jp2k(self.jp2file)
@ -122,11 +134,11 @@ class TestJp2k(unittest.TestCase):
# End corner must be >= start corner
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.
j = Jp2k(self.jp2file)
with self.assertRaises(IOError):
d = j.read(reduce=6)
d = j.read(rlevel=6)
def test_not_JPEG2000(self):
# 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.'
jp2k = Jp2k(filename)
def test_nemo_tile(self):
# Issue 134, trouble reading first nemo tile.
j = Jp2k(self.jp2file)
with warnings.catch_warnings():
warnings.simplefilter("ignore")
tiledata = j.read(tile=0)
subsetdata = j.read(area=(0, 0, 512, 512))
np.testing.assert_array_equal(tiledata, subsetdata)
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
def test_write_srgb_without_mct(self):
j2k = Jp2k(self.j2kfile)
expdata = j2k.read()
@ -164,8 +168,9 @@ class TestJp2k(unittest.TestCase):
np.testing.assert_array_equal(actdata, expdata)
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):
# MCT usage makes no sense for grayscale images.
j2k = Jp2k(self.j2kfile)
@ -175,10 +180,11 @@ class TestJp2k(unittest.TestCase):
with self.assertRaises(IOError):
ofile.write(expdata[:, :, 0], mct=True)
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
def test_write_cprl(self):
# Issue 17
j = Jp2k(self.jp2file)
expdata = j.read(reduce=2)
expdata = j.read(rlevel=1)
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
ofile = Jp2k(tfile.name, 'wb')
ofile.write(expdata, prog='CPRL')
@ -186,7 +192,7 @@ class TestJp2k(unittest.TestCase):
np.testing.assert_array_equal(actdata, expdata)
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):
# Verify the boxes of a JP2 file.
@ -195,37 +201,37 @@ class TestJp2k(unittest.TestCase):
# top-level boxes
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].length, 12)
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].length, 20)
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].length, 45)
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].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].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].length, 1133427)
self.assertEqual(jp2k.box[5].length, 1132296)
# jp2h super box
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].length, 22)
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].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].length, 15)
self.assertEqual(jp2k.box[2].box[1].longname, 'Colour Specification')
@ -255,6 +261,7 @@ class TestJp2k(unittest.TestCase):
jp2k = Jp2k(filename)
self.assertEqual(len(jp2k.box), 0)
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
def test_64bit_XL_field(self):
# 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
@ -283,10 +290,11 @@ class TestJp2k(unittest.TestCase):
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].length, 1133427 + 8)
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
def test_L_is_zero(self):
# 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.
@ -314,8 +322,8 @@ class TestJp2k(unittest.TestCase):
# The top level boxes in each file should match.
for j in range(len(baseline_jp2.box)):
self.assertEqual(new_jp2.box[j].id,
baseline_jp2.box[j].id)
self.assertEqual(new_jp2.box[j].box_id,
baseline_jp2.box[j].box_id)
self.assertEqual(new_jp2.box[j].offset,
baseline_jp2.box[j].offset)
self.assertEqual(new_jp2.box[j].length,
@ -330,7 +338,7 @@ class TestJp2k(unittest.TestCase):
# Issue 86.
filename = os.path.join(data_root, 'input/conformance/p0_05.j2k')
j = Jp2k(filename)
with self.assertRaises(IOError):
with self.assertRaises(RuntimeError):
j.read()
@unittest.skipIf(data_root is None,
@ -342,6 +350,7 @@ class TestJp2k(unittest.TestCase):
j = Jp2k(filename)
self.assertEqual(j.box, [])
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
def test_code_block_height_different_than_width(self):
# Verify that we can set a code block size where height does not equal
# width.
@ -355,8 +364,9 @@ class TestJp2k(unittest.TestCase):
c = j.get_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):
# OpenJP2 only allows 2D or 3D images.
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)
j.write(data)
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
def test_unrecognized_jp2_colorspace(self):
# We only allow RGB and GRAYSCALE.
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
@ -373,6 +384,7 @@ class TestJp2k(unittest.TestCase):
data = np.zeros((128, 128, 3), dtype=np.uint8)
j.write(data, colorspace='cmyk')
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
def test_2D_rgb(self):
# RGB must have at least 3 components.
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
@ -381,6 +393,7 @@ class TestJp2k(unittest.TestCase):
data = np.zeros((128, 128, 2), dtype=np.uint8)
j.write(data, colorspace='rgb')
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
def test_colorspace_with_j2k(self):
# Specifying a colorspace with J2K does not make sense.
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
@ -389,6 +402,7 @@ class TestJp2k(unittest.TestCase):
data = np.zeros((128, 128, 3), dtype=np.uint8)
j.write(data, colorspace='rgb')
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
def test_specify_rgb(self):
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
j = Jp2k(tfile.name, 'wb')
@ -396,6 +410,7 @@ class TestJp2k(unittest.TestCase):
j.write(data, colorspace='rgb')
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):
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
j = Jp2k(tfile.name, 'wb')
@ -404,6 +419,7 @@ class TestJp2k(unittest.TestCase):
self.assertEqual(j.box[2].box[1].colorspace,
glymur.core.GREYSCALE)
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
def test_specify_grey(self):
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
j = Jp2k(tfile.name, 'wb')
@ -412,6 +428,7 @@ class TestJp2k(unittest.TestCase):
self.assertEqual(j.box[2].box[1].colorspace,
glymur.core.GREYSCALE)
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
def test_grey_with_extra_component(self):
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
j = Jp2k(tfile.name, 'wb')
@ -423,6 +440,7 @@ class TestJp2k(unittest.TestCase):
self.assertEqual(j.box[2].box[1].colorspace,
glymur.core.GREYSCALE)
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
def test_grey_with_two_extra_components(self):
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
j = Jp2k(tfile.name, 'wb')
@ -434,6 +452,7 @@ class TestJp2k(unittest.TestCase):
self.assertEqual(j.box[2].box[1].colorspace,
glymur.core.GREYSCALE)
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
def test_rgb_with_extra_component(self):
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
j = Jp2k(tfile.name, 'wb')
@ -452,6 +471,7 @@ class TestJp2k(unittest.TestCase):
data = np.zeros((128, 128, 3), dtype=np.uint8)
j.write(data, colorspace='ycc')
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
def test_uinf_ulst_url_boxes(self):
# 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
@ -490,24 +510,25 @@ class TestJp2k(unittest.TestCase):
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].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].length, 26)
ulst = []
ulst.append(uuid.UUID('00000000-0000-0000-0000-000000000000'))
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].length, 16)
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].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):
# ElementTree does not like trailing null chars after valid XML
# text.
@ -533,14 +554,15 @@ class TestJp2k(unittest.TestCase):
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].length, 36)
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
def test_asoc_label_box(self):
# Construct a fake file with an asoc and a label box, as
# OpenJPEG doesn't have such a file.
data = Jp2k(self.jp2file).read(reduce=3)
data = Jp2k(self.jp2file).read(rlevel=1)
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
j = Jp2k(tfile.name, 'wb')
j.write(data)
@ -578,11 +600,12 @@ class TestJp2k(unittest.TestCase):
tfile2.flush()
jasoc = Jp2k(tfile2.name)
self.assertEqual(jasoc.box[3].id, 'asoc')
self.assertEqual(jasoc.box[3].box[0].id, 'lbl ')
self.assertEqual(jasoc.box[3].box_id, 'asoc')
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[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):
# Verify the error message produced by the openjpeg library.
# 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+
:\sdx=1\sdy=0''', re.VERBOSE)
if sys.hexversion < 0x03020000:
with self.assertRaisesRegexp(IOError, regexp) as ce:
d = j.read(reduce=3)
with self.assertRaisesRegexp((IOError, OSError), regexp):
d = j.read(rlevel=1)
else:
with self.assertRaisesRegex(IOError, regexp) as ce:
d = j.read(reduce=3)
with self.assertRaisesRegex((IOError, OSError), regexp):
d = j.read(rlevel=1)
def test_xmp_attribute(self):
# 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)]
self.assertEqual(attr_value, 'glymur')
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
def test_unrecognized_exif_tag(self):
# An unrecognized exif tag should be handled gracefully.
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
@ -659,5 +683,72 @@ class TestJp2k(unittest.TestCase):
self.assertFalse('Make' in exif['Image'].keys())
@unittest.skipIf(glymur.lib.openjpeg.OPENJPEG is None,
"Missing openjpeg library.")
class TestJp2k15(unittest.TestCase):
@classmethod
def setUpClass(cls):
# Monkey patch the package so as to use OPENJPEG instead of OPENJP2
cls.openjp2 = glymur.lib.openjp2.OPENJP2
glymur.lib.openjp2.OPENJP2 = None
@classmethod
def tearDownClass(cls):
# Restore OPENJP2
glymur.lib.openjp2.OPENJP2 = cls.openjp2
def setUp(self):
self.jp2file = glymur.data.nemo()
self.j2kfile = glymur.data.goodstuff()
def tearDown(self):
pass
def test_bands(self):
"""Reading individual bands is an advanced maneuver.
"""
jp2k = Jp2k(self.j2kfile)
with self.assertRaises(NotImplementedError):
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__":
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
seem like logical negative tests to add.
"""
#pylint: disable-all
import os
import sys
import tempfile
@ -57,7 +58,7 @@ def read_image(infile):
return data
@unittest.skipIf(glymur.lib.openjp2._OPENJP2 is None,
@unittest.skipIf(glymur.lib.openjp2.OPENJP2 is None,
"Missing openjp2 library.")
@unittest.skipIf(no_read_backend, no_read_backend_msg)
@unittest.skipIf(data_root is None,
@ -65,12 +66,13 @@ def read_image(infile):
class TestSuiteNegative(unittest.TestCase):
def setUp(self):
self.jp2file = pkg_resources.resource_filename(glymur.__name__,
"data/nemo.jp2")
self.jp2file = glymur.data.nemo()
self.j2kfile = glymur.data.goodstuff()
def tearDown(self):
pass
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
def test_negative_psnr_with_cratios(self):
# Using psnr with cratios options is not allowed.
# 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,
# not EOC. Codestream parsing should stop when we try to jump to
# the end of SOT.
self.assertEqual(c.segment[-1].id, 'SOD')
self.assertEqual(c.segment[-1].marker_id, 'SOD')
@unittest.skipIf(sys.hexversion < 0x03020000,
"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,
# not EOC. Codestream parsing should stop when we try to jump to
# the end of SOT.
self.assertEqual(c.segment[-1].id, 'SOD')
self.assertEqual(c.segment[-1].marker_id, 'SOD')
@unittest.skipIf(sys.hexversion < 0x03020000,
"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,
# not EOC. Codestream parsing should stop when we try to jump to
# 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):
# opj_compress doesn't allow the dimensions of a codeblock
# 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:
j = Jp2k(infile)
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
def test_precinct_size_not_multiple_of_two(self):
# Seems like precinct sizes should be powers of two.
ifile = Jp2k(self.jp2file)
data = ifile.read(reduce=3)
ifile = Jp2k(self.j2kfile)
data = ifile.read(rlevel=2)
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
ofile = Jp2k(tfile.name, 'wb')
with self.assertRaises(IOError) as ce:
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):
# Seems like code block sizes should be powers of two.
ifile = Jp2k(self.jp2file)
data = ifile.read(reduce=3)
ifile = Jp2k(self.j2kfile)
data = ifile.read(rlevel=2)
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
ofile = Jp2k(tfile.name, 'wb')
with self.assertRaises(IOError) as ce:
ofile.write(data, cbsize=(13, 12))
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
def test_codeblock_size_with_precinct_size(self):
# Seems like code block sizes should never exceed half that of
# precinct size.
ifile = Jp2k(self.jp2file)
data = ifile.read(reduce=3)
ifile = Jp2k(self.j2kfile)
data = ifile.read(rlevel=2)
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
ofile = Jp2k(tfile.name, 'wb')
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
suite.
"""
#pylint: disable-all
import os
import platform
import sys
@ -57,7 +58,8 @@ def read_image(infile):
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.")
@unittest.skipIf(no_read_backend, no_read_backend_msg)
@unittest.skipIf(data_root is None,
@ -83,50 +85,50 @@ class TestSuiteWrite(unittest.TestCase):
# SIZ: Image and tile size
# Profile: "0" means profile 2
self.assertEqual(c.segment[1].Rsiz, 0)
self.assertEqual(c.segment[1].rsiz, 0)
# 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))
# 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
self.assertEqual((c.segment[1].XTsiz, c.segment[1].YTsiz),
self.assertEqual((c.segment[1].xtsiz, c.segment[1].ytsiz),
(640, 480))
# Tile offset
self.assertEqual((c.segment[1].XTOsiz, c.segment[1].YTOsiz),
self.assertEqual((c.segment[1].xtosiz, c.segment[1].ytosiz),
(0, 0))
# bitdepth
self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8))
# signed
self.assertEqual(c.segment[1]._signed, (False, False, False))
# 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)
# COD: Coding style default
self.assertFalse(c.segment[2].Scod & 2) # no sop
self.assertFalse(c.segment[2].Scod & 4) # no eph
self.assertEqual(c.segment[2].SPcod[0], glymur.core.LRCP)
self.assertFalse(c.segment[2].scod & 2) # no sop
self.assertFalse(c.segment[2].scod & 4) # no eph
self.assertEqual(c.segment[2].spcod[0], glymur.core.LRCP)
self.assertEqual(c.segment[2]._layers, 3) # layers = 3
self.assertEqual(c.segment[2].SPcod[3], 1) # mct
self.assertEqual(c.segment[2].SPcod[4], 5) # levels
self.assertEqual(c.segment[2].spcod[3], 1) # mct
self.assertEqual(c.segment[2].spcod[4], 5) # levels
self.assertEqual(tuple(c.segment[2]._code_block_size),
(64, 64)) # cblksz
# Selective arithmetic coding bypass
self.assertFalse(c.segment[2].SPcod[7] & 0x01)
self.assertFalse(c.segment[2].spcod[7] & 0x01)
# Reset context probabilities
self.assertFalse(c.segment[2].SPcod[7] & 0x02)
self.assertFalse(c.segment[2].spcod[7] & 0x02)
# Termination on each coding pass
self.assertFalse(c.segment[2].SPcod[7] & 0x04)
self.assertFalse(c.segment[2].spcod[7] & 0x04)
# Vertically causal context
self.assertFalse(c.segment[2].SPcod[7] & 0x08)
self.assertFalse(c.segment[2].spcod[7] & 0x08)
# Predictable termination
self.assertFalse(c.segment[2].SPcod[7] & 0x0010)
self.assertFalse(c.segment[2].spcod[7] & 0x0010)
# Segmentation symbols
self.assertFalse(c.segment[2].SPcod[7] & 0x0020)
self.assertEqual(c.segment[2].SPcod[8],
glymur.core.WAVELET_TRANSFORM_5x3_REVERSIBLE)
self.assertEqual(len(c.segment[2].SPcod), 9)
self.assertFalse(c.segment[2].spcod[7] & 0x0020)
self.assertEqual(c.segment[2].spcod[8],
glymur.core.WAVELET_XFORM_5X3_REVERSIBLE)
self.assertEqual(len(c.segment[2].spcod), 9)
def test_NR_ENC_Bretagne1_ppm_2_encode(self):
# NR-ENC-Bretagne1.ppm-2-encode
@ -141,50 +143,50 @@ class TestSuiteWrite(unittest.TestCase):
# SIZ: Image and tile size
# Profile: "0" means profile 2
self.assertEqual(c.segment[1].Rsiz, 0)
self.assertEqual(c.segment[1].rsiz, 0)
# 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))
# 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
self.assertEqual((c.segment[1].XTsiz, c.segment[1].YTsiz),
self.assertEqual((c.segment[1].xtsiz, c.segment[1].ytsiz),
(640, 480))
# Tile offset
self.assertEqual((c.segment[1].XTOsiz, c.segment[1].YTOsiz),
self.assertEqual((c.segment[1].xtosiz, c.segment[1].ytosiz),
(0, 0))
# bitdepth
self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8))
# signed
self.assertEqual(c.segment[1]._signed, (False, False, False))
# 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)
# COD: Coding style default
self.assertFalse(c.segment[2].Scod & 2) # no sop
self.assertFalse(c.segment[2].Scod & 4) # no eph
self.assertEqual(c.segment[2].SPcod[0], glymur.core.LRCP)
self.assertFalse(c.segment[2].scod & 2) # no sop
self.assertFalse(c.segment[2].scod & 4) # no eph
self.assertEqual(c.segment[2].spcod[0], glymur.core.LRCP)
self.assertEqual(c.segment[2]._layers, 3) # layers = 3
self.assertEqual(c.segment[2].SPcod[3], 1) # mct
self.assertEqual(c.segment[2].SPcod[4], 1) # levels
self.assertEqual(c.segment[2].spcod[3], 1) # mct
self.assertEqual(c.segment[2].spcod[4], 1) # levels
self.assertEqual(tuple(c.segment[2]._code_block_size),
(64, 64)) # cblksz
# Selective arithmetic coding bypass
self.assertFalse(c.segment[2].SPcod[7] & 0x01)
self.assertFalse(c.segment[2].spcod[7] & 0x01)
# Reset context probabilities
self.assertFalse(c.segment[2].SPcod[7] & 0x02)
self.assertFalse(c.segment[2].spcod[7] & 0x02)
# Termination on each coding pass
self.assertFalse(c.segment[2].SPcod[7] & 0x04)
self.assertFalse(c.segment[2].spcod[7] & 0x04)
# Vertically causal context
self.assertFalse(c.segment[2].SPcod[7] & 0x08)
self.assertFalse(c.segment[2].spcod[7] & 0x08)
# Predictable termination
self.assertFalse(c.segment[2].SPcod[7] & 0x0010)
self.assertFalse(c.segment[2].spcod[7] & 0x0010)
# Segmentation symbols
self.assertFalse(c.segment[2].SPcod[7] & 0x0020)
self.assertEqual(c.segment[2].SPcod[8],
glymur.core.WAVELET_TRANSFORM_5x3_REVERSIBLE)
self.assertEqual(len(c.segment[2].SPcod), 9)
self.assertFalse(c.segment[2].spcod[7] & 0x0020)
self.assertEqual(c.segment[2].spcod[8],
glymur.core.WAVELET_XFORM_5X3_REVERSIBLE)
self.assertEqual(len(c.segment[2].spcod), 9)
def test_NR_ENC_Bretagne1_ppm_3_encode(self):
# NR-ENC-Bretagne1.ppm-3-encode
@ -200,49 +202,49 @@ class TestSuiteWrite(unittest.TestCase):
# SIZ: Image and tile size
# Profile: "0" means profile 2
self.assertEqual(c.segment[1].Rsiz, 0)
self.assertEqual(c.segment[1].rsiz, 0)
# 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))
# 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
self.assertEqual((c.segment[1].XTsiz, c.segment[1].YTsiz),
self.assertEqual((c.segment[1].xtsiz, c.segment[1].ytsiz),
(640, 480))
# Tile offset
self.assertEqual((c.segment[1].XTOsiz, c.segment[1].YTOsiz),
self.assertEqual((c.segment[1].xtosiz, c.segment[1].ytosiz),
(0, 0))
# bitdepth
self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8))
# signed
self.assertEqual(c.segment[1]._signed, (False, False, False))
# 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)
# COD: Coding style default
self.assertFalse(c.segment[2].Scod & 2) # no sop
self.assertFalse(c.segment[2].Scod & 4) # no eph
self.assertEqual(c.segment[2].SPcod[0], glymur.core.LRCP)
self.assertFalse(c.segment[2].scod & 2) # no sop
self.assertFalse(c.segment[2].scod & 4) # no eph
self.assertEqual(c.segment[2].spcod[0], glymur.core.LRCP)
self.assertEqual(c.segment[2]._layers, 3) # layers = 3
self.assertEqual(c.segment[2].SPcod[3], 1) # mct
self.assertEqual(c.segment[2].SPcod[4], 5) # levels
self.assertEqual(c.segment[2].spcod[3], 1) # mct
self.assertEqual(c.segment[2].spcod[4], 5) # levels
self.assertEqual(tuple(c.segment[2]._code_block_size),
(16, 16)) # cblksz
# Selective arithmetic coding bypass
self.assertFalse(c.segment[2].SPcod[7] & 0x01)
self.assertFalse(c.segment[2].spcod[7] & 0x01)
# Reset context probabilities
self.assertFalse(c.segment[2].SPcod[7] & 0x02)
self.assertFalse(c.segment[2].spcod[7] & 0x02)
# Termination on each coding pass
self.assertFalse(c.segment[2].SPcod[7] & 0x04)
self.assertFalse(c.segment[2].spcod[7] & 0x04)
# Vertically causal context
self.assertFalse(c.segment[2].SPcod[7] & 0x08)
self.assertFalse(c.segment[2].spcod[7] & 0x08)
# Predictable termination
self.assertFalse(c.segment[2].SPcod[7] & 0x0010)
self.assertFalse(c.segment[2].spcod[7] & 0x0010)
# Segmentation symbols
self.assertFalse(c.segment[2].SPcod[7] & 0x0020)
self.assertEqual(c.segment[2].SPcod[8],
glymur.core.WAVELET_TRANSFORM_5x3_REVERSIBLE)
self.assertFalse(c.segment[2].spcod[7] & 0x0020)
self.assertEqual(c.segment[2].spcod[8],
glymur.core.WAVELET_XFORM_5X3_REVERSIBLE)
self.assertEqual(c.segment[2]._precinct_size,
[(2, 2), (4, 4), (8, 8), (16, 16), (32, 32),
(64, 64)])
@ -263,49 +265,49 @@ class TestSuiteWrite(unittest.TestCase):
# SIZ: Image and tile size
# Profile: "0" means profile 2
self.assertEqual(c.segment[1].Rsiz, 0)
self.assertEqual(c.segment[1].rsiz, 0)
# 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]))
# 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.
self.assertEqual((c.segment[1].XTsiz, c.segment[1].YTsiz),
self.assertEqual((c.segment[1].xtsiz, c.segment[1].ytsiz),
(640, 480))
# Tile offset
self.assertEqual((c.segment[1].XTOsiz, c.segment[1].YTOsiz),
self.assertEqual((c.segment[1].xtosiz, c.segment[1].ytosiz),
(0, 0))
# bitdepth
self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8))
# signed
self.assertEqual(c.segment[1]._signed, (False, False, False))
# 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)
# COD: Coding style default
self.assertFalse(c.segment[2].Scod & 2) # no sop
self.assertFalse(c.segment[2].Scod & 4) # no eph
self.assertEqual(c.segment[2].SPcod[0], glymur.core.LRCP)
self.assertFalse(c.segment[2].scod & 2) # no sop
self.assertFalse(c.segment[2].scod & 4) # no eph
self.assertEqual(c.segment[2].spcod[0], glymur.core.LRCP)
self.assertEqual(c.segment[2]._layers, 3) # layers = 3
self.assertEqual(c.segment[2].SPcod[3], 1) # mct
self.assertEqual(c.segment[2].SPcod[4], 5) # levels
self.assertEqual(c.segment[2].spcod[3], 1) # mct
self.assertEqual(c.segment[2].spcod[4], 5) # levels
self.assertEqual(tuple(c.segment[2]._code_block_size),
(32, 32)) # cblksz
# Selective arithmetic coding bypass
self.assertFalse(c.segment[2].SPcod[7] & 0x01)
self.assertFalse(c.segment[2].spcod[7] & 0x01)
# Reset context probabilities
self.assertFalse(c.segment[2].SPcod[7] & 0x02)
self.assertFalse(c.segment[2].spcod[7] & 0x02)
# Termination on each coding pass
self.assertFalse(c.segment[2].SPcod[7] & 0x04)
self.assertFalse(c.segment[2].spcod[7] & 0x04)
# Vertically causal context
self.assertFalse(c.segment[2].SPcod[7] & 0x08)
self.assertFalse(c.segment[2].spcod[7] & 0x08)
# Predictable termination
self.assertFalse(c.segment[2].SPcod[7] & 0x0010)
self.assertFalse(c.segment[2].spcod[7] & 0x0010)
# Segmentation symbols
self.assertFalse(c.segment[2].SPcod[7] & 0x0020)
self.assertEqual(c.segment[2].SPcod[8],
glymur.core.WAVELET_TRANSFORM_5x3_REVERSIBLE)
self.assertFalse(c.segment[2].spcod[7] & 0x0020)
self.assertEqual(c.segment[2].spcod[8],
glymur.core.WAVELET_XFORM_5X3_REVERSIBLE)
self.assertEqual(c.segment[2]._precinct_size,
[(16, 16), (32, 32), (64, 64)] + [(128, 128)] * 3)
@ -321,50 +323,50 @@ class TestSuiteWrite(unittest.TestCase):
# SIZ: Image and tile size
# Profile: "0" means profile 2
self.assertEqual(c.segment[1].Rsiz, 0)
self.assertEqual(c.segment[1].rsiz, 0)
# 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]))
# 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
self.assertEqual((c.segment[1].XTsiz, c.segment[1].YTsiz),
self.assertEqual((c.segment[1].xtsiz, c.segment[1].ytsiz),
(127, 127))
# Tile offset
self.assertEqual((c.segment[1].XTOsiz, c.segment[1].YTOsiz),
self.assertEqual((c.segment[1].xtosiz, c.segment[1].ytosiz),
(0, 0))
# bitdepth
self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8))
# signed
self.assertEqual(c.segment[1]._signed, (False, False, False))
# 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)
# COD: Coding style default
self.assertFalse(c.segment[2].Scod & 2) # no sop
self.assertFalse(c.segment[2].Scod & 4) # no eph
self.assertEqual(c.segment[2].SPcod[0], glymur.core.PCRL)
self.assertFalse(c.segment[2].scod & 2) # no sop
self.assertFalse(c.segment[2].scod & 4) # no eph
self.assertEqual(c.segment[2].spcod[0], glymur.core.PCRL)
self.assertEqual(c.segment[2]._layers, 1) # layers = 1
self.assertEqual(c.segment[2].SPcod[3], 1) # mct
self.assertEqual(c.segment[2].SPcod[4], 5) # levels
self.assertEqual(c.segment[2].spcod[3], 1) # mct
self.assertEqual(c.segment[2].spcod[4], 5) # levels
self.assertEqual(tuple(c.segment[2]._code_block_size),
(64, 64)) # cblksz
# Selective arithmetic coding bypass
self.assertFalse(c.segment[2].SPcod[7] & 0x01)
self.assertFalse(c.segment[2].spcod[7] & 0x01)
# Reset context probabilities
self.assertFalse(c.segment[2].SPcod[7] & 0x02)
self.assertFalse(c.segment[2].spcod[7] & 0x02)
# Termination on each coding pass
self.assertFalse(c.segment[2].SPcod[7] & 0x04)
self.assertFalse(c.segment[2].spcod[7] & 0x04)
# Vertically causal context
self.assertFalse(c.segment[2].SPcod[7] & 0x08)
self.assertFalse(c.segment[2].spcod[7] & 0x08)
# Predictable termination
self.assertFalse(c.segment[2].SPcod[7] & 0x0010)
self.assertFalse(c.segment[2].spcod[7] & 0x0010)
# Segmentation symbols
self.assertFalse(c.segment[2].SPcod[7] & 0x0020)
self.assertEqual(c.segment[2].SPcod[8],
glymur.core.WAVELET_TRANSFORM_5x3_REVERSIBLE)
self.assertEqual(len(c.segment[2].SPcod), 9)
self.assertFalse(c.segment[2].spcod[7] & 0x0020)
self.assertEqual(c.segment[2].spcod[8],
glymur.core.WAVELET_XFORM_5X3_REVERSIBLE)
self.assertEqual(len(c.segment[2].spcod), 9)
def test_NR_ENC_Bretagne2_ppm_6_encode(self):
# NR-ENC-Bretagne2.ppm-6-encode
@ -378,53 +380,53 @@ class TestSuiteWrite(unittest.TestCase):
# SIZ: Image and tile size
# Profile: "0" means profile 2
self.assertEqual(c.segment[1].Rsiz, 0)
self.assertEqual(c.segment[1].rsiz, 0)
# 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))
# 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
self.assertEqual((c.segment[1].XTsiz, c.segment[1].YTsiz),
self.assertEqual((c.segment[1].xtsiz, c.segment[1].ytsiz),
(5183, 3887))
# Tile offset
self.assertEqual((c.segment[1].XTOsiz, c.segment[1].YTOsiz),
self.assertEqual((c.segment[1].xtosiz, c.segment[1].ytosiz),
(0, 0))
# bitdepth
self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8))
# signed
self.assertEqual(c.segment[1]._signed, (False, False, False))
# 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)
# COD: Coding style default
self.assertTrue(c.segment[2].Scod & 2) # sop
self.assertFalse(c.segment[2].Scod & 4) # no eph
self.assertEqual(c.segment[2].SPcod[0], glymur.core.LRCP)
self.assertTrue(c.segment[2].scod & 2) # sop
self.assertFalse(c.segment[2].scod & 4) # no eph
self.assertEqual(c.segment[2].spcod[0], glymur.core.LRCP)
self.assertEqual(c.segment[2]._layers, 1) # layers = 1
self.assertEqual(c.segment[2].SPcod[3], 1) # mct
self.assertEqual(c.segment[2].SPcod[4], 5) # levels
self.assertEqual(c.segment[2].spcod[3], 1) # mct
self.assertEqual(c.segment[2].spcod[4], 5) # levels
self.assertEqual(tuple(c.segment[2]._code_block_size),
(64, 64)) # cblksz
# Selective arithmetic coding bypass
self.assertFalse(c.segment[2].SPcod[7] & 0x01)
self.assertFalse(c.segment[2].spcod[7] & 0x01)
# Reset context probabilities
self.assertFalse(c.segment[2].SPcod[7] & 0x02)
self.assertFalse(c.segment[2].spcod[7] & 0x02)
# Termination on each coding pass
self.assertFalse(c.segment[2].SPcod[7] & 0x04)
self.assertFalse(c.segment[2].spcod[7] & 0x04)
# Vertically causal context
self.assertFalse(c.segment[2].SPcod[7] & 0x08)
self.assertFalse(c.segment[2].spcod[7] & 0x08)
# Predictable termination
self.assertFalse(c.segment[2].SPcod[7] & 0x0010)
self.assertFalse(c.segment[2].spcod[7] & 0x0010)
# Segmentation symbols
self.assertFalse(c.segment[2].SPcod[7] & 0x0020)
self.assertEqual(c.segment[2].SPcod[8],
glymur.core.WAVELET_TRANSFORM_5x3_REVERSIBLE)
self.assertEqual(len(c.segment[2].SPcod), 9)
self.assertFalse(c.segment[2].spcod[7] & 0x0020)
self.assertEqual(c.segment[2].spcod[8],
glymur.core.WAVELET_XFORM_5X3_REVERSIBLE)
self.assertEqual(len(c.segment[2].spcod), 9)
# 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)))
def test_NR_ENC_Bretagne2_ppm_7_encode(self):
@ -438,53 +440,53 @@ class TestSuiteWrite(unittest.TestCase):
# SIZ: Image and tile size
# Profile: "0" means profile 2
self.assertEqual(c.segment[1].Rsiz, 0)
self.assertEqual(c.segment[1].rsiz, 0)
# 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))
# 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
self.assertEqual((c.segment[1].XTsiz, c.segment[1].YTsiz),
self.assertEqual((c.segment[1].xtsiz, c.segment[1].ytsiz),
(2592, 1944))
# Tile offset
self.assertEqual((c.segment[1].XTOsiz, c.segment[1].YTOsiz),
self.assertEqual((c.segment[1].xtosiz, c.segment[1].ytosiz),
(0, 0))
# bitdepth
self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8))
# signed
self.assertEqual(c.segment[1]._signed, (False, False, False))
# 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)
# COD: Coding style default
self.assertFalse(c.segment[2].Scod & 2) # no sop
self.assertTrue(c.segment[2].Scod & 4) # eph
self.assertEqual(c.segment[2].SPcod[0], glymur.core.LRCP)
self.assertFalse(c.segment[2].scod & 2) # no sop
self.assertTrue(c.segment[2].scod & 4) # eph
self.assertEqual(c.segment[2].spcod[0], glymur.core.LRCP)
self.assertEqual(c.segment[2]._layers, 1) # layers = 1
self.assertEqual(c.segment[2].SPcod[3], 1) # mct
self.assertEqual(c.segment[2].SPcod[4], 5) # levels
self.assertEqual(c.segment[2].spcod[3], 1) # mct
self.assertEqual(c.segment[2].spcod[4], 5) # levels
self.assertEqual(tuple(c.segment[2]._code_block_size),
(64, 64)) # cblksz
# Selective arithmetic coding BYPASS
self.assertFalse(c.segment[2].SPcod[7] & 0x01)
self.assertFalse(c.segment[2].spcod[7] & 0x01)
# 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)
self.assertTrue(c.segment[2].SPcod[7] & 0x04)
self.assertTrue(c.segment[2].spcod[7] & 0x04)
# Vertically causal context (VSC)
self.assertFalse(c.segment[2].SPcod[7] & 0x08)
self.assertFalse(c.segment[2].spcod[7] & 0x08)
# Predictable termination, ERTERM(SEGTERM)
self.assertFalse(c.segment[2].SPcod[7] & 0x0010)
self.assertFalse(c.segment[2].spcod[7] & 0x0010)
# Segmentation symbols, SEGMARK(SEGSYSM)
self.assertTrue(c.segment[2].SPcod[7] & 0x0020)
self.assertEqual(c.segment[2].SPcod[8],
glymur.core.WAVELET_TRANSFORM_5x3_REVERSIBLE)
self.assertEqual(len(c.segment[2].SPcod), 9)
self.assertTrue(c.segment[2].spcod[7] & 0x0020)
self.assertEqual(c.segment[2].spcod[8],
glymur.core.WAVELET_XFORM_5X3_REVERSIBLE)
self.assertEqual(len(c.segment[2].spcod), 9)
# 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)
def test_NR_ENC_Bretagne2_ppm_8_encode(self):
@ -498,51 +500,51 @@ class TestSuiteWrite(unittest.TestCase):
# SIZ: Image and tile size
# Profile: "0" means profile 2
self.assertEqual(c.segment[1].Rsiz, 0)
self.assertEqual(c.segment[1].rsiz, 0)
# 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))
# 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))
# Tile size
self.assertEqual((c.segment[1].XTsiz, c.segment[1].YTsiz),
self.assertEqual((c.segment[1].xtsiz, c.segment[1].ytsiz),
(2742, 2244))
# Tile offset
self.assertEqual((c.segment[1].XTOsiz, c.segment[1].YTOsiz),
self.assertEqual((c.segment[1].xtosiz, c.segment[1].ytosiz),
(0, 0))
# bitdepth
self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8))
# signed
self.assertEqual(c.segment[1]._signed, (False, False, False))
# 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)
# COD: Coding style default
self.assertFalse(c.segment[2].Scod & 2) # no sop
self.assertFalse(c.segment[2].Scod & 4) # no eph
self.assertEqual(c.segment[2].SPcod[0], glymur.core.LRCP)
self.assertFalse(c.segment[2].scod & 2) # no sop
self.assertFalse(c.segment[2].scod & 4) # no eph
self.assertEqual(c.segment[2].spcod[0], glymur.core.LRCP)
self.assertEqual(c.segment[2]._layers, 1) # layers = 1
self.assertEqual(c.segment[2].SPcod[3], 1) # mct
self.assertEqual(c.segment[2].SPcod[4], 5) # levels
self.assertEqual(c.segment[2].spcod[3], 1) # mct
self.assertEqual(c.segment[2].spcod[4], 5) # levels
self.assertEqual(tuple(c.segment[2]._code_block_size),
(64, 64)) # cblksz
# Selective arithmetic coding BYPASS
self.assertFalse(c.segment[2].SPcod[7] & 0x01)
self.assertFalse(c.segment[2].spcod[7] & 0x01)
# 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)
self.assertFalse(c.segment[2].SPcod[7] & 0x04)
self.assertFalse(c.segment[2].spcod[7] & 0x04)
# Vertically causal context (VSC)
self.assertFalse(c.segment[2].SPcod[7] & 0x08)
self.assertFalse(c.segment[2].spcod[7] & 0x08)
# Predictable termination, ERTERM(SEGTERM)
self.assertFalse(c.segment[2].SPcod[7] & 0x0010)
self.assertFalse(c.segment[2].spcod[7] & 0x0010)
# Segmentation symbols, SEGMARK(SEGSYSM)
self.assertFalse(c.segment[2].SPcod[7] & 0x0020)
self.assertEqual(c.segment[2].SPcod[8],
glymur.core.WAVELET_TRANSFORM_5x3_REVERSIBLE)
self.assertEqual(len(c.segment[2].SPcod), 9)
self.assertFalse(c.segment[2].spcod[7] & 0x0020)
self.assertEqual(c.segment[2].spcod[8],
glymur.core.WAVELET_XFORM_5X3_REVERSIBLE)
self.assertEqual(len(c.segment[2].spcod), 9)
def test_NR_ENC_Cevennes1_bmp_9_encode(self):
infile = os.path.join(data_root, 'input/nonregression/Cevennes1.bmp')
@ -555,50 +557,50 @@ class TestSuiteWrite(unittest.TestCase):
# SIZ: Image and tile size
# Profile: "0" means profile 2
self.assertEqual(c.segment[1].Rsiz, 0)
self.assertEqual(c.segment[1].rsiz, 0)
# 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))
# 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
self.assertEqual((c.segment[1].XTsiz, c.segment[1].YTsiz),
self.assertEqual((c.segment[1].xtsiz, c.segment[1].ytsiz),
(2592, 1944))
# Tile offset
self.assertEqual((c.segment[1].XTOsiz, c.segment[1].YTOsiz),
self.assertEqual((c.segment[1].xtosiz, c.segment[1].ytosiz),
(0, 0))
# bitdepth
self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8))
# signed
self.assertEqual(c.segment[1]._signed, (False, False, False))
# 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)
# COD: Coding style default
self.assertFalse(c.segment[2].Scod & 2) # no sop
self.assertFalse(c.segment[2].Scod & 4) # no eph
self.assertEqual(c.segment[2].SPcod[0], glymur.core.LRCP)
self.assertFalse(c.segment[2].scod & 2) # no sop
self.assertFalse(c.segment[2].scod & 4) # no eph
self.assertEqual(c.segment[2].spcod[0], glymur.core.LRCP)
self.assertEqual(c.segment[2]._layers, 1) # layers = 1
self.assertEqual(c.segment[2].SPcod[3], 1) # mct
self.assertEqual(c.segment[2].SPcod[4], 5) # levels
self.assertEqual(c.segment[2].spcod[3], 1) # mct
self.assertEqual(c.segment[2].spcod[4], 5) # levels
self.assertEqual(tuple(c.segment[2]._code_block_size),
(64, 64)) # cblksz
# Selective arithmetic coding BYPASS
self.assertFalse(c.segment[2].SPcod[7] & 0x01)
self.assertFalse(c.segment[2].spcod[7] & 0x01)
# 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)
self.assertFalse(c.segment[2].SPcod[7] & 0x04)
self.assertFalse(c.segment[2].spcod[7] & 0x04)
# Vertically causal context (VSC)
self.assertFalse(c.segment[2].SPcod[7] & 0x08)
self.assertFalse(c.segment[2].spcod[7] & 0x08)
# Predictable termination, ERTERM(SEGTERM)
self.assertFalse(c.segment[2].SPcod[7] & 0x0010)
self.assertFalse(c.segment[2].spcod[7] & 0x0010)
# Segmentation symbols, SEGMARK(SEGSYSM)
self.assertFalse(c.segment[2].SPcod[7] & 0x0020)
self.assertEqual(c.segment[2].SPcod[8],
glymur.core.WAVELET_TRANSFORM_5x3_REVERSIBLE)
self.assertEqual(len(c.segment[2].SPcod), 9)
self.assertFalse(c.segment[2].spcod[7] & 0x0020)
self.assertEqual(c.segment[2].spcod[8],
glymur.core.WAVELET_XFORM_5X3_REVERSIBLE)
self.assertEqual(len(c.segment[2].spcod), 9)
def test_NR_ENC_Cevennes2_ppm_10_encode(self):
infile = os.path.join(data_root, 'input/nonregression/Cevennes2.ppm')
@ -611,50 +613,50 @@ class TestSuiteWrite(unittest.TestCase):
# SIZ: Image and tile size
# Profile: "0" means profile 2
self.assertEqual(c.segment[1].Rsiz, 0)
self.assertEqual(c.segment[1].rsiz, 0)
# 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))
# 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
self.assertEqual((c.segment[1].XTsiz, c.segment[1].YTsiz),
self.assertEqual((c.segment[1].xtsiz, c.segment[1].ytsiz),
(640, 480))
# Tile offset
self.assertEqual((c.segment[1].XTOsiz, c.segment[1].YTOsiz),
self.assertEqual((c.segment[1].xtosiz, c.segment[1].ytosiz),
(0, 0))
# bitdepth
self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8))
# signed
self.assertEqual(c.segment[1]._signed, (False, False, False))
# 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)
# COD: Coding style default
self.assertFalse(c.segment[2].Scod & 2) # no sop
self.assertFalse(c.segment[2].Scod & 4) # no eph
self.assertEqual(c.segment[2].SPcod[0], glymur.core.LRCP)
self.assertFalse(c.segment[2].scod & 2) # no sop
self.assertFalse(c.segment[2].scod & 4) # no eph
self.assertEqual(c.segment[2].spcod[0], glymur.core.LRCP)
self.assertEqual(c.segment[2]._layers, 1) # layers = 1
self.assertEqual(c.segment[2].SPcod[3], 1) # mct
self.assertEqual(c.segment[2].SPcod[4], 5) # levels
self.assertEqual(c.segment[2].spcod[3], 1) # mct
self.assertEqual(c.segment[2].spcod[4], 5) # levels
self.assertEqual(tuple(c.segment[2]._code_block_size),
(64, 64)) # cblksz
# Selective arithmetic coding BYPASS
self.assertFalse(c.segment[2].SPcod[7] & 0x01)
self.assertFalse(c.segment[2].spcod[7] & 0x01)
# 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)
self.assertFalse(c.segment[2].SPcod[7] & 0x04)
self.assertFalse(c.segment[2].spcod[7] & 0x04)
# Vertically causal context (VSC)
self.assertFalse(c.segment[2].SPcod[7] & 0x08)
self.assertFalse(c.segment[2].spcod[7] & 0x08)
# Predictable termination, ERTERM(SEGTERM)
self.assertFalse(c.segment[2].SPcod[7] & 0x0010)
self.assertFalse(c.segment[2].spcod[7] & 0x0010)
# Segmentation symbols, SEGMARK(SEGSYSM)
self.assertFalse(c.segment[2].SPcod[7] & 0x0020)
self.assertEqual(c.segment[2].SPcod[8],
glymur.core.WAVELET_TRANSFORM_5x3_REVERSIBLE)
self.assertEqual(len(c.segment[2].SPcod), 9)
self.assertFalse(c.segment[2].spcod[7] & 0x0020)
self.assertEqual(c.segment[2].spcod[8],
glymur.core.WAVELET_XFORM_5X3_REVERSIBLE)
self.assertEqual(len(c.segment[2].spcod), 9)
def test_NR_ENC_Rome_bmp_11_encode(self):
infile = os.path.join(data_root, 'input/nonregression/Rome.bmp')
@ -663,11 +665,11 @@ class TestSuiteWrite(unittest.TestCase):
jp2 = Jp2k(tfile.name, 'wb')
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']
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'])
# Signature box. Check for corruption.
@ -701,51 +703,51 @@ class TestSuiteWrite(unittest.TestCase):
# SIZ: Image and tile size
# Profile: "0" means profile 2
self.assertEqual(c.segment[1].Rsiz, 0)
self.assertEqual(c.segment[1].rsiz, 0)
# 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))
# 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))
# Tile size
self.assertEqual((c.segment[1].XTsiz, c.segment[1].YTsiz),
self.assertEqual((c.segment[1].xtsiz, c.segment[1].ytsiz),
(640, 480))
# Tile offset
self.assertEqual((c.segment[1].XTOsiz, c.segment[1].YTOsiz),
self.assertEqual((c.segment[1].xtosiz, c.segment[1].ytosiz),
(0, 0))
# bitdepth
self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8))
# signed
self.assertEqual(c.segment[1]._signed, (False, False, False))
# 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)
# COD: Coding style default
self.assertFalse(c.segment[2].Scod & 2) # no sop
self.assertFalse(c.segment[2].Scod & 4) # no eph
self.assertEqual(c.segment[2].SPcod[0], glymur.core.LRCP)
self.assertFalse(c.segment[2].scod & 2) # no sop
self.assertFalse(c.segment[2].scod & 4) # no eph
self.assertEqual(c.segment[2].spcod[0], glymur.core.LRCP)
self.assertEqual(c.segment[2]._layers, 3) # layers = 3
self.assertEqual(c.segment[2].SPcod[3], 1) # mct
self.assertEqual(c.segment[2].SPcod[4], 2) # levels
self.assertEqual(c.segment[2].spcod[3], 1) # mct
self.assertEqual(c.segment[2].spcod[4], 2) # levels
self.assertEqual(tuple(c.segment[2]._code_block_size),
(64, 64)) # cblksz
# Selective arithmetic coding BYPASS
self.assertFalse(c.segment[2].SPcod[7] & 0x01)
self.assertFalse(c.segment[2].spcod[7] & 0x01)
# 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)
self.assertFalse(c.segment[2].SPcod[7] & 0x04)
self.assertFalse(c.segment[2].spcod[7] & 0x04)
# Vertically causal context (VSC)
self.assertFalse(c.segment[2].SPcod[7] & 0x08)
self.assertFalse(c.segment[2].spcod[7] & 0x08)
# Predictable termination, ERTERM(SEGTERM)
self.assertFalse(c.segment[2].SPcod[7] & 0x0010)
self.assertFalse(c.segment[2].spcod[7] & 0x0010)
# Segmentation symbols, SEGMARK(SEGSYSM)
self.assertFalse(c.segment[2].SPcod[7] & 0x0020)
self.assertEqual(c.segment[2].SPcod[8],
glymur.core.WAVELET_TRANSFORM_5x3_REVERSIBLE)
self.assertEqual(len(c.segment[2].SPcod), 9)
self.assertFalse(c.segment[2].spcod[7] & 0x0020)
self.assertEqual(c.segment[2].spcod[8],
glymur.core.WAVELET_XFORM_5X3_REVERSIBLE)
self.assertEqual(len(c.segment[2].spcod), 9)
@unittest.skip("Known failure in openjpeg test suite.")
def test_NR_ENC_random_issue_0005_tif_12_encode(self):
@ -762,50 +764,50 @@ class TestSuiteWrite(unittest.TestCase):
# SIZ: Image and tile size
# Profile: "0" means profile 2
self.assertEqual(c.segment[1].Rsiz, 0)
self.assertEqual(c.segment[1].rsiz, 0)
# 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))
# 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
self.assertEqual((c.segment[1].XTsiz, c.segment[1].YTsiz),
self.assertEqual((c.segment[1].xtsiz, c.segment[1].ytsiz),
(1024, 1024))
# Tile offset
self.assertEqual((c.segment[1].XTOsiz, c.segment[1].YTOsiz),
self.assertEqual((c.segment[1].xtosiz, c.segment[1].ytosiz),
(0, 0))
# bitdepth
self.assertEqual(c.segment[1]._bitdepth, (16,))
# signed
self.assertEqual(c.segment[1]._signed, (False,))
# 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)])
# COD: Coding style default
self.assertFalse(c.segment[2].Scod & 2) # no sop
self.assertFalse(c.segment[2].Scod & 4) # no eph
self.assertEqual(c.segment[2].SPcod[0], glymur.core.LRCP)
self.assertFalse(c.segment[2].scod & 2) # no sop
self.assertFalse(c.segment[2].scod & 4) # no eph
self.assertEqual(c.segment[2].spcod[0], glymur.core.LRCP)
self.assertEqual(c.segment[2]._layers, 1) # layers = 1
self.assertEqual(c.segment[2].SPcod[3], 0) # mct
self.assertEqual(c.segment[2].SPcod[4], 5) # levels
self.assertEqual(c.segment[2].spcod[3], 0) # mct
self.assertEqual(c.segment[2].spcod[4], 5) # levels
self.assertEqual(tuple(c.segment[2]._code_block_size),
(64, 64)) # cblksz
# Selective arithmetic coding BYPASS
self.assertFalse(c.segment[2].SPcod[7] & 0x01)
self.assertFalse(c.segment[2].spcod[7] & 0x01)
# 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)
self.assertFalse(c.segment[2].SPcod[7] & 0x04)
self.assertFalse(c.segment[2].spcod[7] & 0x04)
# Vertically causal context (VSC)
self.assertFalse(c.segment[2].SPcod[7] & 0x08)
self.assertFalse(c.segment[2].spcod[7] & 0x08)
# Predictable termination, ERTERM(SEGTERM)
self.assertFalse(c.segment[2].SPcod[7] & 0x0010)
self.assertFalse(c.segment[2].spcod[7] & 0x0010)
# Segmentation symbols, SEGMARK(SEGSYSM)
self.assertFalse(c.segment[2].SPcod[7] & 0x0020)
self.assertEqual(c.segment[2].SPcod[8],
glymur.core.WAVELET_TRANSFORM_5x3_REVERSIBLE)
self.assertEqual(len(c.segment[2].SPcod), 9)
self.assertFalse(c.segment[2].spcod[7] & 0x0020)
self.assertEqual(c.segment[2].spcod[8],
glymur.core.WAVELET_XFORM_5X3_REVERSIBLE)
self.assertEqual(len(c.segment[2].spcod), 9)
if __name__ == "__main__":
unittest.main()

View file

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