diff --git a/CHANGES.txt b/CHANGES.txt index 042e351..a3179f6 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,7 +1,5 @@ -Jan 10, 2015 - v0.8.0 Reduced number of steps required for writing - images. Deprecated old read and write methods in favor of - array-style slicing. Added ignore_pclr_cmap_cdef, verbose, - shape, codestream, layer properties. +Nov 18, 2014 - v0.7.3 Added read support back for metadata when the + library is not installed. Oct 06, 2014 - v0.7.2 Added ellipsis support in array-style slicing. diff --git a/docs/source/api.rst b/docs/source/api.rst new file mode 100644 index 0000000..7584718 --- /dev/null +++ b/docs/source/api.rst @@ -0,0 +1,116 @@ +--- +API +--- + +Jp2k +---- +.. autoclass:: glymur.Jp2k + :members: read, write, wrap, read_bands, get_codestream + +Individual Boxes +---------------- +Jp2kbox +''''''' +.. autoclass:: glymur.jp2box.Jp2kBox + :members: + +AssociationBox +'''''''''''''' +.. autoclass:: glymur.jp2box.AssociationBox + :members: + +ColourSpecificationBox +'''''''''''''''''''''' +.. autoclass:: glymur.jp2box.ColourSpecificationBox + :members: + +ChannelDefinitionBox +'''''''''''''''''''''' +.. autoclass:: glymur.jp2box.ChannelDefinitionBox + :members: + +ComponentMappingBox +''''''''''''''''''' +.. autoclass:: glymur.jp2box.ComponentMappingBox + :members: + +ContiguousCodestreamBox +''''''''''''''''''''''' +.. autoclass:: glymur.jp2box.ContiguousCodestreamBox + :members: + +DataEntryURLBox +''''''''''''''' +.. autoclass:: glymur.jp2box.DataEntryURLBox + :members: + +FileTypeBox +''''''''''' +.. autoclass:: glymur.jp2box.FileTypeBox + :members: + +ImageHeaderBox +'''''''''''''' +.. autoclass:: glymur.jp2box.ImageHeaderBox + :members: + +JP2HeaderBox +'''''''''''' +.. autoclass:: glymur.jp2box.JP2HeaderBox + :members: + +JPEG2000SignatureBox +'''''''''''''''''''' +.. autoclass:: glymur.jp2box.JPEG2000SignatureBox + :members: + +LabelBox +'''''''' +.. autoclass:: glymur.jp2box.LabelBox + :members: + +PaletteBox +'''''''''' +.. autoclass:: glymur.jp2box.PaletteBox + :members: + +ReaderRequirementsBox +''''''''''''''''''''' +.. autoclass:: glymur.jp2box.ReaderRequirementsBox + :members: + +ResolutionBox +''''''''''''' +.. autoclass:: glymur.jp2box.ResolutionBox + :members: + +CaptureResolutionBox +'''''''''''''''''''' +.. autoclass:: glymur.jp2box.CaptureResolutionBox + :members: + +DisplayResolutionBox +'''''''''''''''''''' +.. autoclass:: glymur.jp2box.DisplayResolutionBox + :members: + +UUIDBox +''''''' +.. autoclass:: glymur.jp2box.UUIDBox + :members: + +UUIDInfoBox +''''''''''' +.. autoclass:: glymur.jp2box.UUIDInfoBox + :members: + +UUIDListBox +''''''''''' +.. autoclass:: glymur.jp2box.UUIDListBox + :members: + +XMLBox +'''''' +.. autoclass:: glymur.jp2box.XMLBox + :members: + diff --git a/docs/source/conf.py b/docs/source/conf.py index 76f96f6..9704a89 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -13,6 +13,7 @@ # serve to show the default. import sys +import os class Mock(object): @@ -41,12 +42,12 @@ for mod_name in MOCK_MODULES: # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -# sys.path.insert(0, os.path.abspath('.')) +#sys.path.insert(0, os.path.abspath('.')) # -- General configuration ---------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. -# needs_sphinx = '1.0' +#needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. @@ -61,7 +62,7 @@ templates_path = ['_templates'] source_suffix = '.rst' # The encoding of source files. -# source_encoding = 'utf-8-sig' +#source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' @@ -75,19 +76,19 @@ copyright = u'2013, John Evans' # built documents. # # The short X.Y version. -version = '0.8' +version = '0.7' # The full version, including alpha/beta/rc tags. -release = '0.8.0' +release = '0.7.3' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. -# language = None +#language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -# today = '' +#today = '' # Else, today_fmt is used as the format for a strftime call. -# today_fmt = '%B %d, %Y' +#today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. @@ -95,24 +96,24 @@ exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all # documents. -# default_role = None +#default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. -# add_function_parentheses = True +#add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -# add_module_names = True +#add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -# show_authors = False +#show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. -# modindex_common_prefix = [] +#modindex_common_prefix = [] # -- Options for HTML output -------------------------------------------------- @@ -124,26 +125,26 @@ html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -# html_theme_options = {} +#html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. -# html_theme_path = [] +#html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -# html_title = None +#html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -# html_short_title = None +#html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. -# html_logo = None +#html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -# html_favicon = None +#html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, @@ -152,44 +153,44 @@ html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -# html_last_updated_fmt = '%b %d, %Y' +#html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -# html_use_smartypants = True +#html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -# html_sidebars = {} +#html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. -# html_additional_pages = {} +#html_additional_pages = {} # If false, no module index is generated. -# html_domain_indices = True +#html_domain_indices = True # If false, no index is generated. -# html_use_index = True +#html_use_index = True # If true, the index is split into individual pages for each letter. -# html_split_index = False +#html_split_index = False # If true, links to the reST sources are added to the pages. html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -# html_show_sphinx = True +#html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -# html_show_copyright = True +#html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -# html_use_opensearch = '' +#html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). -# html_file_suffix = None +#html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'glymurdoc' @@ -198,13 +199,13 @@ htmlhelp_basename = 'glymurdoc' # -- Options for LaTeX output ------------------------------------------------- # The paper size ('letterpaper' or 'a4paper'). -# 'papersize': 'letterpaper', +#'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). -# 'pointsize': '10pt', +#'pointsize': '10pt', # Additional stuff for the LaTeX preamble. -# 'preamble': '', +#'preamble': '', latex_elements = {} # Grouping the document tree into LaTeX files. List of tuples @@ -215,23 +216,23 @@ latex_documents = [('index', 'glymur.tex', u'glymur Documentation', # The name of an image file (relative to this directory) to place at the top of # the title page. -# latex_logo = None +#latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. -# latex_use_parts = False +#latex_use_parts = False # If true, show page references after internal links. -# latex_show_pagerefs = False +#latex_show_pagerefs = False # If true, show URL addresses after external links. -# latex_show_urls = False +#latex_show_urls = False # Documents to append as an appendix to all manuals. -# latex_appendices = [] +#latex_appendices = [] # If false, no module index is generated. -# latex_domain_indices = True +#latex_domain_indices = True # -- Options for manual page output ------------------------------------------- @@ -244,7 +245,7 @@ man_pages = [ ] # If true, show URL addresses after external links. -# man_show_urls = False +#man_show_urls = False # -- Options for Texinfo output ----------------------------------------------- @@ -257,13 +258,13 @@ texinfo_documents = [('index', 'glymur', u'glymur Documentation', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. -# texinfo_appendices = [] +#texinfo_appendices = [] # If false, no module index is generated. -# texinfo_domain_indices = True +#texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. -# texinfo_show_urls = 'footnote' +#texinfo_show_urls = 'footnote' # Example configuration for intersphinx: refer to the Python standard library. diff --git a/docs/source/detailed_installation.rst b/docs/source/detailed_installation.rst index 7068b56..2401048 100644 --- a/docs/source/detailed_installation.rst +++ b/docs/source/detailed_installation.rst @@ -6,7 +6,7 @@ Advanced Installation Instructions Glymur Configuration '''''''''''''''''''''' -The default glymur installation process relies upon OpenJPEG being +The default glymur installation process expects OpenJPEG to be properly installed on your system as a shared library. If you have OpenJPEG installed through your system’s package manager on linux or if you use MacPorts on the mac, you are probably already set to @@ -15,7 +15,7 @@ or if you use windows, then read on. Glymur uses ctypes to access the openjp2/openjpeg libraries, and because ctypes accesses libraries in a platform-dependent manner, -it is recommended that **if** you compile and install OpenJPEG into a +it is recommended that if you compile and install OpenJPEG into a non-standard location, you should then create a configuration file to help Glymur properly find the openjpeg or openjp2 libraries (linux users or macports users don’t need to bother with this if @@ -50,8 +50,8 @@ installed in a non-standard place, i.e. :: [library] openjpeg: /somewhere/lib/libopenjpeg.so -Once again, you should not have to bother with a configuration file if you use -mac or linux and OpenJPEG is provided by your package manager. +If your platform is windows, it is strongly recommended that you compile the +2.1.0 version of the openjpeg library from source. ''''''' Testing diff --git a/docs/source/how_do_i.rst b/docs/source/how_do_i.rst index 88d94fa..5d11d67 100644 --- a/docs/source/how_do_i.rst +++ b/docs/source/how_do_i.rst @@ -14,22 +14,27 @@ retrieve a full resolution and first lower-resolution image :: >>> jp2file = glymur.data.nemo() # just a path to a JPEG2000 file >>> jp2 = glymur.Jp2k(jp2file) >>> fullres = jp2[:] - >>> fullres.shape + >>> print(fullres.shape) (1456, 2592, 3) >>> thumbnail = jp2[::2, ::2] - >>> thumbnail.shape + >>> print(thumbnail.shape) (728, 1296, 3) +The :py:meth:`read` method exposes many more options for other JPEG 2000 +features such as quality layers. + ... write images? ================= -It's pretty simple, just supply the image data as the 2nd argument to the Jp2k -constructor. +So long as the image data can fit entirely into memory, array-style slicing may +also be used to write JPEG 2000 files. >>> import glymur, numpy as np - >>> jp2 = glymur.Jp2k('zeros.jp2', data=np.zeros((640, 480), dtype=np.uint8) + >>> jp2 = glymur.Jp2k('zeros.jp2', mode='wb') + >>> jp2[:] = np.zeros((640, 480), dtype=np.uint8) -You must have OpenJPEG version 1.5 or more recent in order to write JPEG 2000 -images with glymur. +The :py:meth:`write` method exposes many more options for other JPEG 2000 +features. You should have OpenJPEG version 1.5 or more recent before writing +JPEG 2000 images. ... display metadata? ===================== @@ -214,9 +219,9 @@ making use of the :py:meth:`set_printoptions` function:: UUID: be7acfcb-97a9-42e8-9c71-999491e3afac (XMP) Contiguous Codestream Box (jp2c) @ (3223, 1132296) -It is possible to easily print the codestream header details as well, i.e. :: +It is possible to print all the gory codestream details as well, i.e. :: - >>> print(j.codestream) # details not show + >>> print(j.get_codestream()) # details not shown ... add XML metadata? ===================== @@ -349,14 +354,15 @@ image isn't square. :: >>> import numpy as np >>> import glymur >>> from glymur import Jp2k - >>> rgb = Jp2k(glymur.data.goodstuff())[:] + >>> rgb = Jp2k(glymur.data.goodstuff()).read() >>> lx, ly = rgb.shape[0:2] >>> X, Y = np.ogrid[0:lx, 0:ly] >>> mask = ly**2*(X - lx / 2) ** 2 + lx**2*(Y - ly / 2) ** 2 > (lx * ly / 2)**2 >>> alpha = 255 * np.ones((lx, ly, 1), dtype=np.uint8) >>> alpha[mask] = 0 >>> rgba = np.concatenate((rgb, alpha), axis=2) - >>> jp2 = Jp2k('tmp.jp2', data=rgba) + >>> jp2 = Jp2k('tmp.jp2', 'wb') + >>> jp2[:] = rgba Next we need to specify what types of channels we have. The first three channels are color channels, but we identify the fourth as @@ -442,7 +448,7 @@ following 'Google' But that would be painful. A better solution is to install the Python XMP -Toolkit (make sure it is at least version 2.0):: +Toolkit (make sure it is version 2.0):: >>> from libxmp import XMPMeta >>> from libxmp.consts import XMP_NS_XMP as NS_XAP @@ -459,7 +465,8 @@ http://photojournal.jpl.nasa.gov/tiff/PIA17145.tif info JPEG 2000:: >>> import skimage.io >>> image = skimage.io.imread('PIA17145.tif') >>> from glymur import Jp2k - >>> jp2 = Jp2k('PIA17145.jp2', data=image) + >>> jp2 = Jp2k('PIA17145.jp2', 'wb') + >>> jp2[:] = image Next you can extract the XMP metadata. diff --git a/docs/source/index.rst b/docs/source/index.rst index 62e118d..8c1fb03 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -17,6 +17,7 @@ Contents: how_do_i whatsnew/index roadmap + api ------------------ Indices and tables diff --git a/docs/source/whatsnew/0.7.rst b/docs/source/whatsnew/0.7.rst index 3ed7715..e3a541d 100644 --- a/docs/source/whatsnew/0.7.rst +++ b/docs/source/whatsnew/0.7.rst @@ -4,24 +4,20 @@ Changes in glymur 0.7 Changes in 0.7.3 ================= - - * added read support back for metadata only when the OpenJPEG library is - not installed + * added read support back for metadata only when the OpenJPEG library is not installed Changes in 0.7.2 ================= - * added ellipsis support in array-style slicing Changes in 0.7.1 ================= - * fixed release notes regarding Python 3.4 Changes in 0.7.0 ================= - * implemented :py:meth:`__getitem__`, :py:meth:`__setitem__` support + * implemented array-style slicing * added back windows support * box_id and longname are class attributes now instead of instance - attributes + attributes diff --git a/docs/source/whatsnew/0.8.rst b/docs/source/whatsnew/0.8.rst deleted file mode 100644 index e7215ad..0000000 --- a/docs/source/whatsnew/0.8.rst +++ /dev/null @@ -1,24 +0,0 @@ -===================== -Changes in glymur 0.8 -===================== - -Changes in 0.8.0 -================= - - * Simplified writing images by moving data and options into the - constructor. - * Deprecated :py:meth:`read` method in favor of array-style slicing. - In order to retain certain functionality, the following parameters - to the :py:meth:`read` method have become top-level properties - - * verbose - * layer - * ignore_pclr_cmap_cdef - - * Two additional properties were introduced. - - * codestream - * shape - - - diff --git a/docs/source/whatsnew/index.rst b/docs/source/whatsnew/index.rst index 4569abd..38d0435 100644 --- a/docs/source/whatsnew/index.rst +++ b/docs/source/whatsnew/index.rst @@ -8,7 +8,6 @@ These document the changes between minor (or major) versions of glymur. .. toctree:: - 0.8 0.7 0.6 0.5 diff --git a/glymur/__init__.py b/glymur/__init__.py index d8971b2..eb0139c 100644 --- a/glymur/__init__.py +++ b/glymur/__init__.py @@ -1,25 +1,19 @@ """glymur - read, write, and interrogate JPEG 2000 files """ +import sys import unittest from glymur import version __version__ = version.version from .jp2k import Jp2k -from .jp2box import (get_printoptions, - set_printoptions, - get_parseoptions, - set_parseoptions) +from .jp2box import get_printoptions, set_printoptions +from .jp2box import get_parseoptions, set_parseoptions from . import data - def runtests(): """Discover and run all tests for the glymur package. """ suite = unittest.defaultTestLoader.discover(__path__[0]) unittest.TextTestRunner(verbosity=2).run(suite) - - -__all__ = [__version__, Jp2k, get_printoptions, set_printoptions, - get_parseoptions, set_parseoptions, data, runtests] diff --git a/glymur/_uuid_io.py b/glymur/_uuid_io.py index 3c63b0a..bf9960a 100644 --- a/glymur/_uuid_io.py +++ b/glymur/_uuid_io.py @@ -3,13 +3,14 @@ Part of glymur. """ from collections import OrderedDict +import pprint +import re import struct import sys import warnings import lxml.etree as ET - def xml(raw_data): """ XMP data to be parsed as XML. @@ -22,7 +23,6 @@ def xml(raw_data): return ET.ElementTree(elt) - def tiff_header(read_buffer): """ Interpret the uuid raw data as a tiff header. @@ -37,8 +37,8 @@ def tiff_header(read_buffer): # big endian endian = '>' else: - msg = "The byte order indication in the TIFF header ({0}) is " - msg += "invalid. It should be either {1} or {2}." + msg = "The byte order indication in the TIFF header ({0}) is invalid. " + msg += "It should be either {1} or {2}." msg = msg.format(read_buffer[6:8], bytes([73, 73]), bytes([77, 77])) raise IOError(msg) @@ -503,3 +503,6 @@ class _ExifInteroperabilityIfd(_Ifd): def __init__(self, endian, read_buffer, offset): _Ifd.__init__(self, endian, read_buffer, offset) self.post_process(self.tagnum2name) + + + diff --git a/glymur/codestream.py b/glymur/codestream.py index ea94afd..c87c2d2 100644 --- a/glymur/codestream.py +++ b/glymur/codestream.py @@ -6,13 +6,16 @@ codestreams. # The number of lines in the module is long and that's ok. It would not help # matters to move anything out to another file. +# pylint: disable=C0302 # "Too many instance attributes", "Too many arguments" # Some segments just have a lot of information. # It doesn't make sense to subclass just for that. +# pylint: disable=R0902,R0913 # "Too few public methods" Some segments don't define any new methods from # the base Segment class. +# pylint: disable=R0903 import math import struct @@ -21,22 +24,23 @@ import warnings import numpy as np -from .core import (LRCP, RLCP, RPCL, PCRL, CPRL, - WAVELET_XFORM_9X7_IRREVERSIBLE, - WAVELET_XFORM_5X3_REVERSIBLE, - _Keydefaultdict) +from .core import LRCP, RLCP, RPCL, PCRL, CPRL +from .core import WAVELET_XFORM_9X7_IRREVERSIBLE +from .core import WAVELET_XFORM_5X3_REVERSIBLE +from .core import _Keydefaultdict from .lib import openjp2 as opj2 _factory = lambda x: '{0} (invalid)'.format(x) -_PROGRESSION_ORDER_DISPLAY = _Keydefaultdict(_factory, {LRCP: 'LRCP', - RLCP: 'RLCP', - RPCL: 'RPCL', - PCRL: 'PCRL', - CPRL: 'CPRL'}) +_PROGRESSION_ORDER_DISPLAY = _Keydefaultdict(_factory, + { LRCP: 'LRCP', + RLCP: 'RLCP', + RPCL: 'RPCL', + PCRL: 'PCRL', + CPRL: 'CPRL'}) -_keysvalues = {WAVELET_XFORM_9X7_IRREVERSIBLE: '9-7 irreversible', - WAVELET_XFORM_5X3_REVERSIBLE: '5-3 reversible'} -_WAVELET_TRANSFORM_DISPLAY = _Keydefaultdict(_factory, _keysvalues) +_WAVELET_TRANSFORM_DISPLAY = _Keydefaultdict(_factory, + { WAVELET_XFORM_9X7_IRREVERSIBLE: '9-7 irreversible', + WAVELET_XFORM_5X3_REVERSIBLE: '5-3 reversible'}) _NO_PROFILE = 0 _PROFILE_0 = 1 @@ -47,11 +51,12 @@ _PROFILE_4 = 4 _KNOWN_PROFILES = [_NO_PROFILE, _PROFILE_0, _PROFILE_1, _PROFILE_3, _PROFILE_4] # How to display the codestream profile. -_CAPABILITIES_DISPLAY = _Keydefaultdict(_factory, {_NO_PROFILE: 'no profile', - _PROFILE_0: '0', - _PROFILE_1: '1', - _PROFILE_3: 'Cinema 2K', - _PROFILE_4: 'Cinema 4K'}) +_CAPABILITIES_DISPLAY = _Keydefaultdict(_factory, + { _NO_PROFILE: 'no profile', + _PROFILE_0: '0', + _PROFILE_1: '1', + _PROFILE_3: 'Cinema 2K', + _PROFILE_4: 'Cinema 4K'} ) # Need a catch-all list of valid markers. # See table A-1 in ISO/IEC FCD15444-1. @@ -293,6 +298,7 @@ class Codestream(object): msg += ''.join(strs) return msg + # pylint: disable=R0201 def _parse_cme_segment(self, fptr): """Parse the CME marker segment. @@ -688,7 +694,7 @@ class Codestream(object): try: num_tiles_x = (xysiz[0] - xyosiz[0]) / (xytsiz[0] - xytosiz[0]) num_tiles_y = (xysiz[1] - xyosiz[1]) / (xytsiz[1] - xytosiz[1]) - except ZeroDivisionError: + except ZeroDivisionError as err: warnings.warn("Invalid tile dimensions.") else: numtiles = math.ceil(num_tiles_x) * math.ceil(num_tiles_y) @@ -696,6 +702,7 @@ class Codestream(object): msg = "Invalid number of tiles ({0}).".format(numtiles) warnings.warn(msg) + kwargs = {'rsiz': rsiz, 'xysiz': xysiz, 'xyosiz': xyosiz, @@ -822,6 +829,7 @@ class Codestream(object): return TLMsegment(length, offset, ztlm, ttlm, ptlm) + # pylint: disable=W0613 def _parse_reserved_marker(self, fptr): """Marker range between 0xff30 and 0xff39. """ @@ -1605,7 +1613,6 @@ class SOCsegment(Segment): msg = "glymur.codestream.SOCsegment()" return msg - class SODsegment(Segment): """Container for Start of Data (SOD) segment information. diff --git a/glymur/command_line.py b/glymur/command_line.py index 3d1d57e..62b9057 100644 --- a/glymur/command_line.py +++ b/glymur/command_line.py @@ -2,36 +2,32 @@ Entry point for console script jp2dump. """ import argparse -import os +import sys import warnings - -from . import Jp2k, set_printoptions, set_parseoptions, lib - +from . import Jp2k, set_printoptions def main(): """ Entry point for console script jp2dump. """ - kwargs = {'description': 'Print JPEG2000 metadata.', - 'formatter_class': argparse.ArgumentDefaultsHelpFormatter} - parser = argparse.ArgumentParser(**kwargs) + description='Print JPEG2000 metadata.' + parser = argparse.ArgumentParser(description=description) parser.add_argument('-x', '--noxml', - help='suppress XML', - action='store_true') + help='Suppress XML.', + action='store_true') parser.add_argument('-s', '--short', - help='only print box id, offset, and length', - action='store_true') + help='Only print box id, offset, and length.', + action='store_true') - chelp = 'Level of codestream information. 0 suppresses all details, ' - chelp += '1 prints the main header, 2 prints the full codestream.' + chelp = 'Level of codestream information. 0 suppressed all details, ' + chelp += '1 prints headers, 2 prints the full codestream' parser.add_argument('-c', '--codestream', - help=chelp, - metavar='LEVEL', - nargs=1, - type=int, - default=[1]) + help=chelp, + nargs=1, + type=int, + default=[0]) parser.add_argument('filename') @@ -40,33 +36,30 @@ def main(): set_printoptions(xml=False) if args.short: set_printoptions(short=True) - + codestream_level = args.codestream[0] if codestream_level not in [0, 1, 2]: raise ValueError("Invalid level of codestream information specified.") if codestream_level == 0: set_printoptions(codestream=False) - elif codestream_level == 2: - set_parseoptions(full_codestream=True) - + print_full_codestream = False + elif codestream_level == 1: + print_full_codestream = False + else: + print_full_codestream = True + filename = args.filename - + with warnings.catch_warnings(record=True) as wctx: # JP2 metadata can be extensive, so don't print any warnings until we # are done with the metadata. - jp2 = Jp2k(filename) - if jp2._codec_format == lib.openjp2.CODEC_J2K: - if codestream_level == 0: - print('File: {0}'.format(os.path.basename(filename))) - elif codestream_level == 1: - print(jp2) - elif codestream_level == 2: - print('File: {0}'.format(os.path.basename(filename))) - print(jp2.get_codestream(header_only=False)) + j = Jp2k(filename) + if print_full_codestream: + print(j.get_codestream(header_only=False)) else: - print(jp2) + print(j) # Re-emit any warnings that may have been suppressed. if len(wctx) > 0: diff --git a/glymur/core.py b/glymur/core.py index 644dcfd..4d9a3af 100644 --- a/glymur/core.py +++ b/glymur/core.py @@ -1,7 +1,8 @@ """Core definitions to be shared amongst the modules. """ import collections - +import copy +import lxml.etree as ET class _Keydefaultdict(collections.defaultdict): """Unlisted keys help form their own error message. @@ -120,12 +121,12 @@ ROMM_RGB = 21 _factory = lambda x: '{0} (unrecognized)'.format(x) _COLORSPACE_MAP_DISPLAY = _Keydefaultdict(_factory, - {CMYK: 'CMYK', - SRGB: 'sRGB', - GREYSCALE: 'greyscale', - YCC: 'YCC', - E_SRGB: 'e-sRGB', - ROMM_RGB: 'ROMM-RGB'}) + { CMYK: 'CMYK', + SRGB: 'sRGB', + GREYSCALE: 'greyscale', + YCC: 'YCC', + E_SRGB: 'e-sRGB', + ROMM_RGB: 'ROMM-RGB'} ) # enumerated color channel types COLOR = 0 @@ -133,11 +134,11 @@ OPACITY = 1 PRE_MULTIPLIED_OPACITY = 2 _UNSPECIFIED = 65535 _factory = lambda x: '{0} (invalid)'.format(x) -_dict = {COLOR: 'color', - OPACITY: 'opacity', - PRE_MULTIPLIED_OPACITY: 'pre-multiplied opacity', - _UNSPECIFIED: 'unspecified'} -_COLOR_TYPE_MAP_DISPLAY = _Keydefaultdict(_factory, _dict) +_COLOR_TYPE_MAP_DISPLAY = _Keydefaultdict(_factory, + { COLOR: 'color', + OPACITY: 'opacity', + PRE_MULTIPLIED_OPACITY: 'pre-multiplied opacity', + _UNSPECIFIED: 'unspecified'}) # color channel definitions. RED = 1 @@ -152,3 +153,4 @@ _COLORSPACE = {SRGB: {"R": 1, "G": 2, "B": 3}, YCC: {"Y": 1, "Cb": 2, "Cr": 3}, E_SRGB: {"R": 1, "G": 2, "B": 3}, ROMM_RGB: {"R": 1, "G": 2, "B": 3}} + diff --git a/glymur/data/__init__.py b/glymur/data/__init__.py index 066edd2..de1e62a 100644 --- a/glymur/data/__init__.py +++ b/glymur/data/__init__.py @@ -43,3 +43,4 @@ def jpxfile(): """ filename = pkg_resources.resource_filename(__name__, "heliov.jpx") return filename + diff --git a/glymur/jp2box.py b/glymur/jp2box.py index 98b0e6a..c3bd8e3 100644 --- a/glymur/jp2box.py +++ b/glymur/jp2box.py @@ -11,6 +11,8 @@ References Extensions """ +# pylint: disable=C0302,R0903,R0913,W0142 + from collections import OrderedDict import datetime import io @@ -20,37 +22,34 @@ import pprint import struct import sys import textwrap -from uuid import UUID +import uuid import warnings import lxml.etree as ET import numpy as np from .codestream import Codestream -from .core import (_COLORSPACE_MAP_DISPLAY, _COLOR_TYPE_MAP_DISPLAY, - SRGB, GREYSCALE, YCC, - ENUMERATED_COLORSPACE, RESTRICTED_ICC_PROFILE, - ANY_ICC_PROFILE, VENDOR_COLOR_METHOD, - _Keydefaultdict) +from .core import _COLORSPACE_MAP_DISPLAY +from .core import _COLOR_TYPE_MAP_DISPLAY +from .core import SRGB, GREYSCALE, YCC +from .core import ENUMERATED_COLORSPACE, RESTRICTED_ICC_PROFILE +from .core import ANY_ICC_PROFILE, VENDOR_COLOR_METHOD +from .core import _Keydefaultdict from . import _uuid_io -_factory = lambda x: '{0} (invalid)'.format(x) -_keysvalues = {ENUMERATED_COLORSPACE: 'enumerated colorspace', - RESTRICTED_ICC_PROFILE: 'restricted ICC profile', - ANY_ICC_PROFILE: 'any ICC profile', - VENDOR_COLOR_METHOD: 'vendor color method'} -_METHOD_DISPLAY = _Keydefaultdict(_factory, _keysvalues) +_METHOD_DISPLAY = { + ENUMERATED_COLORSPACE: 'enumerated colorspace', + RESTRICTED_ICC_PROFILE: 'restricted ICC profile', + ANY_ICC_PROFILE: 'any ICC profile', + VENDOR_COLOR_METHOD: 'vendor color method'} _factory = lambda x: '{0} (invalid)'.format(x) -_keysvalues = {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'} -_APPROX_DISPLAY = _Keydefaultdict(_factory, _keysvalues) - +_APPROX_DISPLAY = _Keydefaultdict(_factory, + {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'}) class Jp2kBox(object): """Superclass for JPEG 2000 boxes. @@ -109,6 +108,7 @@ class Jp2kBox(object): msg += '\n' + self._indent(boxstr) return msg + def _indent(self, textstr, indent_level=4): """ Indent a string. @@ -134,6 +134,7 @@ class Jp2kBox(object): lst = [(' ' * indent_level + x) for x in textstr.split('\n')] return '\n'.join(lst) + def _write_superbox(self, fptr, box_id): """Write a superbox. @@ -189,14 +190,13 @@ class Jp2kBox(object): try: box = parser(fptr, start, num_bytes) except ValueError as err: - msg = "Encountered an unrecoverable ValueError while parsing a " - msg += "{0} box at byte offset {1}. The original error message " - msg += "was \"{2}\"" + msg = "Encountered an unrecoverable ValueError while parsing a {0} " + msg += "box at byte offset {1}. The original error message was " + msg += "\"{2}\"" msg = msg.format(_BOX_WITH_ID[box_id].longname, start, str(err)) warnings.warn(msg, UserWarning) box = UnknownBox(box_id.decode('utf-8'), - length=num_bytes, - offset=start, longname='Unknown') + length=num_bytes, offset=start, longname='Unknown') return box @@ -298,7 +298,6 @@ class ColourSpecificationBox(Jp2kBox): """ longname = 'Colour Specification' box_id = 'colr' - def __init__(self, method=ENUMERATED_COLORSPACE, precedence=0, approximation=0, colorspace=None, icc_profile=None, length=0, offset=-1): @@ -337,16 +336,16 @@ class ColourSpecificationBox(Jp2kBox): if self.icc_profile is None: if self.colorspace not in [SRGB, GREYSCALE, YCC]: - msg = "Colorspace should correspond to one of SRGB, " - msg += "GREYSCALE, or YCC." + msg = "Colorspace should correspond to one of SRGB, GREYSCALE, " + msg += "or YCC." self._dispatch_validation_error(msg, writing=True) self._validate(writing=True) + def __repr__(self): msg = "glymur.jp2box.ColourSpecificationBox(" - msg += "method={0}, precedence={1}, approximation={2}, " - msg += "colorspace={3}, " + msg += "method={0}, precedence={1}, approximation={2}, colorspace={3}, " msg += "icc_profile={4})" msg = msg.format(self.method, self.precedence, @@ -357,7 +356,7 @@ class ColourSpecificationBox(Jp2kBox): def __str__(self): msg = Jp2kBox.__str__(self) - if _printoptions['short'] is True: + if _printoptions['short'] == True: return msg msg += '\n Method: {0}'.format(_METHOD_DISPLAY[self.method]) @@ -619,9 +618,10 @@ class ChannelDefinitionBox(Jp2kBox): msg += " 65535 - unspecified" self._dispatch_validation_error(msg, writing=writing) + def __str__(self): msg = Jp2kBox.__str__(self) - if _printoptions['short'] is True: + if _printoptions['short'] == True: return msg for j in range(len(self.association)): @@ -841,7 +841,7 @@ class CompositingLayerHeaderBox(Jp2kBox): List of boxes contained in this superbox. """ box_id = 'jplh' - longname = 'Compositing Layer Header' + longname='Compositing Layer Header' def __init__(self, box=None, length=0, offset=-1): Jp2kBox.__init__(self) @@ -930,7 +930,7 @@ class ComponentMappingBox(Jp2kBox): def __str__(self): msg = Jp2kBox.__str__(self) - if _printoptions['short'] is True: + if _printoptions['short'] == True: return msg for k in range(len(self.component_index)): @@ -1001,19 +1001,18 @@ class ContiguousCodestreamBox(Jp2kBox): offset of the box from the start of the file. longname : str more verbose description of the box. - codestream : Codestream object - Contains list of codestream marker/segments. By default, only the main - header is retrieved. + main_header : Codestream object + contains list of main header marker/segments main_header_offset : int offset of main header from start of file """ box_id = 'jp2c' longname = 'Contiguous Codestream' - def __init__(self, codestream=None, main_header_offset=None, length=0, + def __init__(self, main_header=None, main_header_offset=None, length=0, offset=-1): Jp2kBox.__init__(self) - self._codestream = codestream + self._main_header = main_header self.length = length self.offset = offset self.main_header_offset = main_header_offset @@ -1022,33 +1021,29 @@ class ContiguousCodestreamBox(Jp2kBox): self._filename = None @property - def codestream(self): - if _parseoptions['full_codestream'] is True: - header_only = False - else: - header_only = True - if self._codestream is None: + def main_header(self): + if self._main_header is None: if self._filename is not None: with open(self._filename, 'rb') as fptr: fptr.seek(self.main_header_offset) - codestream = Codestream(fptr, self._length, - header_only=header_only) - self._codestream = codestream - return self._codestream + main_header = Codestream(fptr, self._length, header_only=True) + self._main_header = main_header + return self._main_header def __repr__(self): - msg = "glymur.jp2box.ContiguousCodeStreamBox(codestream={0})" - return msg.format(repr(self.codestream)) + msg = "glymur.jp2box.ContiguousCodeStreamBox(main_header={0})" + return msg.format(repr(self.main_header)) def __str__(self): msg = Jp2kBox.__str__(self) - if _printoptions['short'] is True: + if _printoptions['short'] == True: return msg - if _printoptions['codestream'] is False: + if _printoptions['codestream'] == False: return msg - for segment in self.codestream.segment: - msg += '\n' + self._indent(str(segment), indent_level=4) + msg += '\n Main header:' + for segment in self.main_header.segment: + msg += '\n' + self._indent(str(segment), indent_level=8) return msg @@ -1070,11 +1065,11 @@ class ContiguousCodestreamBox(Jp2kBox): ContiguousCodestreamBox instance """ main_header_offset = fptr.tell() - if _parseoptions['full_codestream'] is True: - codestream = Codestream(fptr, length, header_only=False) + if _parseoptions['codestream'] is True: + main_header = Codestream(fptr, length, header_only=True) else: - codestream = None - box = cls(codestream, main_header_offset=main_header_offset, + main_header = None + box = cls(main_header, main_header_offset=main_header_offset, length=length, offset=offset) box._filename = fptr.name box._length = length @@ -1122,8 +1117,7 @@ class DataReferenceBox(Jp2kBox): """Verify that the box obeys the specifications for writing. """ if len(self.DR) == 0: - msg = "A data reference box cannot be empty when written to a " - msg += "file." + msg = "A data reference box cannot be empty when written to a file." self._dispatch_validation_error(msg, writing=True) self._validate(writing=True) @@ -1150,7 +1144,7 @@ class DataReferenceBox(Jp2kBox): def __str__(self): msg = Jp2kBox.__str__(self) - if _printoptions['short'] is True: + if _printoptions['short'] == True: return msg for box in self.DR: @@ -1253,7 +1247,7 @@ class FileTypeBox(Jp2kBox): def __str__(self): msg = Jp2kBox.__str__(self) - if _printoptions['short'] is True: + if _printoptions['short'] == True: return msg lst = [msg, @@ -1316,18 +1310,12 @@ class FileTypeBox(Jp2kBox): brand = brand.decode('utf-8') # Extract the compatibility list. Each entry has 4 bytes. - num_entries = int((length - 16) / 4) + num_entries = int((length - 16)/ 4) compatibility_list = [] for j in range(int(num_entries)): entry, = struct.unpack_from('>4s', read_buffer, 8 + j * 4) if sys.hexversion >= 0x03000000: - try: - entry = entry.decode('utf-8') - except UnicodeDecodeError: - # The entry is invalid, but we've got code to catch this - # later on. - pass - + entry = entry.decode('utf-8') compatibility_list.append(entry) return cls(brand=brand, minor_version=minor_version, @@ -1385,7 +1373,7 @@ class FragmentListBox(Jp2kBox): def __str__(self): msg = Jp2kBox.__str__(self) - if _printoptions['short'] is True: + if _printoptions['short'] == True: return msg for j in range(len(self.fragment_offset)): @@ -1469,10 +1457,7 @@ class FragmentTableBox(Jp2kBox): def __repr__(self): msg = "glymur.jp2box.FragmentTableBox(box={0})" - if len(self.box) == 0: - msg = msg.format(None) - else: - msg = msg.format(self.box) + msg = msg.format(None) if (len(self.box) == 0) else msg.format(self.box) return msg def __str__(self): @@ -1519,6 +1504,7 @@ class FragmentTableBox(Jp2kBox): self._write_superbox(fptr, b'ftbl') + class FreeBox(Jp2kBox): """Container for JPX free box information. @@ -1547,7 +1533,7 @@ class FreeBox(Jp2kBox): def __str__(self): msg = Jp2kBox.__str__(self) - if _printoptions['short'] is True: + if _printoptions['short'] == True: return msg return msg @@ -1643,7 +1629,7 @@ class ImageHeaderBox(Jp2kBox): def __str__(self): msg = Jp2kBox.__str__(self) - if _printoptions['short'] is True: + if _printoptions['short'] == True: return msg msg = "{0}" @@ -1874,7 +1860,7 @@ class JPEG2000SignatureBox(Jp2kBox): def __str__(self): msg = Jp2kBox.__str__(self) - if _printoptions['short'] is True: + if _printoptions['short'] == True: return msg msg += '\n Signature: {0:02x}{1:02x}{2:02x}{3:02x}' @@ -1963,7 +1949,7 @@ class PaletteBox(Jp2kBox): def __str__(self): msg = Jp2kBox.__str__(self) - if _printoptions['short'] is True: + if _printoptions['short'] == True: return msg msg += '\n Size: ({0} x {1})'.format(*self.palette.shape) @@ -1993,6 +1979,7 @@ class PaletteBox(Jp2kBox): *bps_signed) fptr.write(write_buffer) + bps = self.bits_per_component # All components are the same. Writing is straightforward. if self.bits_per_component[0] <= 8: write_buffer = memoryview(self.palette.astype(np.uint8)) @@ -2032,10 +2019,13 @@ class PaletteBox(Jp2kBox): # Ok the palette has the same datatype for all columns. We should # be able to efficiently read it. if bps[0] <= 8: + nbytes_per_row = ncols dtype = np.uint8 elif bps[0] <= 16: + nbytes_per_row = 2 * ncols dtype = np.uint16 elif bps[0] <= 32: + nbytes_per_row = 3 * ncols dtype = np.uint32 palette = np.frombuffer(read_buffer[3 + ncols:], dtype=dtype) @@ -2079,80 +2069,80 @@ _READER_REQUIREMENTS_DISPLAY = { 7: 'JPEG codestream as defined in ISO/IEC 10918-1', 8: 'Deprecated - does not contain opacity', 9: 'Non-premultiplied opacity channel', - 10: 'Premultiplied opacity channel', - 11: 'Chroma-key based opacity', - 12: 'Deprecated - codestream is contiguous', - 13: 'Fragmented codestream where all fragments are in file and in order', - 14: ('Fragmented codestream where all fragments are in file ' - 'but are out of order'), - 15: ('Fragmented codestream where not all fragments are within the file ' - 'but are all in locally accessible files'), - 16: ('Fragmented codestream where some fragments may be accessible ' - 'only through a URL specified network connection'), - 17: ('Compositing required to produce rendered result from multiple ' - 'compositing layers'), - 18: 'Deprecated - support for compositing is not required', - 19: ('Deprecated - contains multiple, discrete layers that should not ' - 'be combined through either animation or compositing'), - 20: ('Deprecated - compositing layers each contain only a single ' - 'codestream'), - 21: 'At least one compositing layer consists of multiple codestreams', - 22: 'Deprecated - all compositing layers are in the same colourspace', - 23: ('Colourspace transformations are required to combine compositing ' - 'layers; not all compositing layers are in the same colourspace'), - 24: 'Deprecated - rendered result created without using animation', - 25: ('Deprecated - animated, but first layer covers entire area and is ' - 'opaque'), - 26: 'First animation layer does not cover entire rendered result', - 27: 'Deprecated - animated, and no layer is reused', - 28: 'Reuse of animation layers', - 29: 'Deprecated - animated, but layers are reused', - 30: 'Some animated frames are non-persistent', - 31: 'Deprecated - rendered result created without using scaling', - 32: 'Rendered result involves scaling within a layer', - 33: 'Rendered result involves scaling between layers', - 34: 'ROI metadata', - 35: 'IPR metadata', - 36: 'Content metadata', - 37: 'History metadata', - 38: 'Creation metadata', - 39: 'JPX digital signatures', - 40: 'JPX checksums', - 41: 'Desires Graphics Arts Reproduction specified', - 42: 'Deprecated - compositing layer uses palettized colour', - 43: 'Deprecated - compositing layer uses restricted ICC profile', - 44: 'Compositing layer uses Any ICC profile', - 45: 'Deprecated - compositing layer uses sRGB enumerated colourspace', - 46: 'Deprecated - compositing layer uses sRGB-grey enumerated colourspace', - 47: 'BiLevel 1 enumerated colourspace', - 48: 'BiLevel 2 enumerated colourspace', - 49: 'YCbCr 1 enumerated colourspace', - 50: 'YCbCr 2 enumerated colourspace', - 51: 'YCbCr 3 enumerated colourspace', - 52: 'PhotoYCC enumerated colourspace', - 53: 'YCCK enumerated colourspace', - 54: 'CMY enumerated colourspace', - 55: 'CMYK enumerated colorspace', - 56: 'CIELab enumerated colourspace with default parameters', - 57: 'CIELab enumerated colourspace with non-default parameters', - 58: 'CIEJab enumerated colourspace with default parameters', - 59: 'CIEJab enumerated colourspace with non-default parameters', - 60: 'e-sRGB enumerated colorspace', - 61: 'ROMM_RGB enumerated colorspace', - 62: 'Non-square samples', - 63: 'Deprecated - compositing layers have labels', - 64: 'Deprecated - codestreams have labels', - 65: 'Deprecated - compositing layers have different colour spaces', - 66: 'Deprecated - compositing layers have different metadata', - 67: 'GIS metadata XML box', - 68: 'JPSEC extensions in codestream as specified by ISO/IEC 15444-8', - 69: 'JP3D extensions in codestream as specified by ISO/IEC 15444-10', - 70: 'Deprecated - compositing layer uses sYCC enumerated colour space', - 71: 'e-sYCC enumerated colourspace', - 72: ('JPEG 2000 Part 2 codestream as restricted by baseline conformance ' - 'requirements in M.9.2.3'), - 73: 'YPbPr(1125/60) enumerated colourspace', - 74: 'YPbPr(1250/50) enumerated colourspace'} + 10: 'Premultiplied opacity channel', + 11: 'Chroma-key based opacity', + 12: 'Deprecated - codestream is contiguous', + 13: 'Fragmented codestream where all fragments are in file and in order', + 14: 'Fragmented codestream where all fragments are in file ' + + 'but are out of order', + 15: 'Fragmented codestream where not all fragments are within the file ' + + 'but are all in locally accessible files', + 16: 'Fragmented codestream where some fragments may be accessible ' + + 'only through a URL specified network connection', + 17: 'Compositing required to produce rendered result from multiple ' + + 'compositing layers', + 18: 'Deprecated - support for compositing is not required', + 19: 'Deprecated - contains multiple, discrete layers that should not ' + + 'be combined through either animation or compositing', + 20: 'Deprecated - compositing layers each contain only a single ' + + 'codestream', + 21: 'At least one compositing layer consists of multiple codestreams', + 22: 'Deprecated - all compositing layers are in the same colourspace', + 23: 'Colourspace transformations are required to combine compositing ' + + 'layers; not all compositing layers are in the same colourspace', + 24: 'Deprecated - rendered result created without using animation', + 25: 'Deprecated - animated, but first layer covers entire area and is ' + + 'opaque', + 26: 'First animation layer does not cover entire rendered result', + 27: 'Deprecated - animated, and no layer is reused', + 28: 'Reuse of animation layers', + 29: 'Deprecated - animated, but layers are reused', + 30: 'Some animated frames are non-persistent', + 31: 'Deprecated - rendered result created without using scaling', + 32: 'Rendered result involves scaling within a layer', + 33: 'Rendered result involves scaling between layers', + 34: 'ROI metadata', + 35: 'IPR metadata', + 36: 'Content metadata', + 37: 'History metadata', + 38: 'Creation metadata', + 39: 'JPX digital signatures', + 40: 'JPX checksums', + 41: 'Desires Graphics Arts Reproduction specified', + 42: 'Deprecated - compositing layer uses palettized colour', + 43: 'Deprecated - compositing layer uses restricted ICC profile', + 44: 'Compositing layer uses Any ICC profile', + 45: 'Deprecated - compositing layer uses sRGB enumerated colourspace', + 46: 'Deprecated - compositing layer uses sRGB-grey enumerated colourspace', + 47: 'BiLevel 1 enumerated colourspace', + 48: 'BiLevel 2 enumerated colourspace', + 49: 'YCbCr 1 enumerated colourspace', + 50: 'YCbCr 2 enumerated colourspace', + 51: 'YCbCr 3 enumerated colourspace', + 52: 'PhotoYCC enumerated colourspace', + 53: 'YCCK enumerated colourspace', + 54: 'CMY enumerated colourspace', + 55: 'CMYK enumerated colorspace', + 56: 'CIELab enumerated colourspace with default parameters', + 57: 'CIELab enumerated colourspace with non-default parameters', + 58: 'CIEJab enumerated colourspace with default parameters', + 59: 'CIEJab enumerated colourspace with non-default parameters', + 60: 'e-sRGB enumerated colorspace', + 61: 'ROMM_RGB enumerated colorspace', + 62: 'Non-square samples', + 63: 'Deprecated - compositing layers have labels', + 64: 'Deprecated - codestreams have labels', + 65: 'Deprecated - compositing layers have different colour spaces', + 66: 'Deprecated - compositing layers have different metadata', + 67: 'GIS metadata XML box', + 68: 'JPSEC extensions in codestream as specified by ISO/IEC 15444-8', + 69: 'JP3D extensions in codestream as specified by ISO/IEC 15444-10', + 70: 'Deprecated - compositing layer uses sYCC enumerated colour space', + 71: 'e-sYCC enumerated colourspace', + 72: 'JPEG 2000 Part 2 codestream as restricted by baseline conformance ' + + 'requirements in M.9.2.3', + 73: 'YPbPr(1125/60) enumerated colourspace', + 74: 'YPbPr(1250/50) enumerated colourspace'} class ReaderRequirementsBox(Jp2kBox): @@ -2212,11 +2202,10 @@ class ReaderRequirementsBox(Jp2kBox): def __str__(self): msg = Jp2kBox.__str__(self) - if _printoptions['short'] is True: + if _printoptions['short'] == True: return msg - msg += '\n Fully Understands Aspect Mask: 0x{0:x}' - msg = msg.format(self.fuam) + msg += '\n Fully Understands Aspect Mask: 0x{0:x}'.format(self.fuam) msg += '\n Display Completely Mask: 0x{0:x}'.format(self.dcm) msg += '\n Standard Features and Masks:' @@ -2272,8 +2261,7 @@ class ReaderRequirementsBox(Jp2kBox): standard_flag, standard_mask = data nflags = len(standard_flag) - vendor_offset = (1 + 2 * mask_length + 2 - + (2 + mask_length) * nflags) + vendor_offset = 1 + 2 * mask_length + 2 + (2 + mask_length) * nflags data = _parse_vendor_features(read_buffer[vendor_offset:], mask_length) vendor_feature, vendor_mask = data @@ -2330,8 +2318,8 @@ def _parse_rreq3(read_buffer, length, offset): read_buffer = read_buffer[9 + num_standard_features * 10:] for j in range(num_vendor_features): uslice = slice(j * entry_length, (j + 1) * entry_length) - ubuffer = read_buffer[uslice] - vendor_feature.append(UUID(bytes=ubuffer[0:16])) + ubuffer = read_buffer[slice] + vendor_feature.append(uuid.UUID(bytes=ubuffer[0:16])) lst = struct.unpack('>BBB', ubuffer[16:]) vmask = lst[0] << 16 | lst[1] << 8 | lst[2] @@ -2359,11 +2347,14 @@ def _parse_standard_flag(read_buffer, mask_length): # from the buffer read from file. mask_format = {1: 'B', 2: 'H', 4: 'I'}[mask_length] + #read_buffer = fptr.read(2) num_standard_flags, = struct.unpack_from('>H', read_buffer, offset=0) # Read in standard flags and standard masks. Each standard flag should # be two bytes, but the standard mask flag is as long as specified by # the mask length. + #read_buffer = fptr.read(num_standard_flags * (2 + mask_length)) + fmt = '>' + ('H' + mask_format) * num_standard_flags data = struct.unpack_from(fmt, read_buffer, offset=2) @@ -2394,12 +2385,13 @@ def _parse_vendor_features(read_buffer, mask_length): # Each vendor feature consists of a 16-byte UUID plus a mask whose # length is specified by, you guessed it, "mask_length". entry_length = 16 + mask_length + #read_buffer = fptr.read(num_vendor_features * entry_length) vendor_feature = [] vendor_mask = [] for j in range(num_vendor_features): uslice = slice(2 + j * entry_length, 2 + (j + 1) * entry_length) ubuffer = read_buffer[uslice] - vendor_feature.append(UUID(bytes=ubuffer[0:16])) + vendor_feature.append(uuid.UUID(bytes=ubuffer[0:16])) vmask = struct.unpack('>' + mask_format, ubuffer[16:]) vendor_mask.append(vmask) @@ -2501,7 +2493,7 @@ class CaptureResolutionBox(Jp2kBox): def __str__(self): msg = Jp2kBox.__str__(self) - if _printoptions['short'] is True: + if _printoptions['short'] == True: return msg msg += '\n VCR: {0}'.format(self.vertical_resolution) @@ -2567,7 +2559,7 @@ class DisplayResolutionBox(Jp2kBox): def __str__(self): msg = Jp2kBox.__str__(self) - if _printoptions['short'] is True: + if _printoptions['short'] == True: return msg msg += '\n VDR: {0}'.format(self.vertical_resolution) @@ -2627,7 +2619,7 @@ class LabelBox(Jp2kBox): def __str__(self): msg = Jp2kBox.__str__(self) - if _printoptions['short'] is True: + if _printoptions['short'] == True: return msg msg += '\n Label: {0}'.format(self.label) @@ -2695,7 +2687,7 @@ class NumberListBox(Jp2kBox): def __str__(self): msg = Jp2kBox.__str__(self) - if _printoptions['short'] is True: + if _printoptions['short'] == True: return msg for j, association in enumerate(self.associations): @@ -2745,8 +2737,7 @@ class NumberListBox(Jp2kBox): def write(self, fptr): """Write a NumberList box to file. """ - fptr.write(struct.pack('>I4s', - len(self.associations) * 4 + 8, b'nlst')) + fptr.write(struct.pack('>I4s', len(self.associations) * 4 + 8, b'nlst')) fmt = '>' + 'I' * len(self.associations) write_buffer = struct.pack(fmt, *self.associations) @@ -2798,9 +2789,9 @@ class XMLBox(Jp2kBox): def __str__(self): msg = Jp2kBox.__str__(self) - if _printoptions['short'] is True: + if _printoptions['short'] == True: return msg - if _printoptions['xml'] is False: + if _printoptions['xml'] == False: return msg msg += '\n' @@ -2919,7 +2910,7 @@ class UUIDListBox(Jp2kBox): def __str__(self): msg = Jp2kBox.__str__(self) - if _printoptions['short'] is True: + if _printoptions['short'] == True: return msg for j, uuid_item in enumerate(self.ulst): @@ -2950,8 +2941,8 @@ class UUIDListBox(Jp2kBox): ulst = [] for j in range(num_uuids): - uuid_buffer = read_buffer[2 + j * 16:2 + (j + 1) * 16] - ulst.append(UUID(bytes=uuid_buffer)) + uuid_buffer = read_buffer[2 + j * 16 : 2 + (j + 1) * 16] + ulst.append(uuid.UUID(bytes=uuid_buffer)) return cls(ulst, length=length, offset=offset) @@ -3064,6 +3055,7 @@ class DataEntryURLBox(Jp2kBox): fptr.write(write_buffer) fptr.write(url) + def __repr__(self): msg = "glymur.jp2box.DataEntryURLBox({0}, {1}, '{2}')" msg = msg.format(self.version, self.flag, self.url) @@ -3071,7 +3063,7 @@ class DataEntryURLBox(Jp2kBox): def __str__(self): msg = Jp2kBox.__str__(self) - if _printoptions['short'] is True: + if _printoptions['short'] == True: return msg msg += '\n ' @@ -3209,7 +3201,7 @@ class UUIDBox(Jp2kBox): """ Private function for parsing UUID payloads if possible. """ - if self.uuid == UUID('be7acfcb-97a9-42e8-9c71-999491e3afac'): + if self.uuid == uuid.UUID('be7acfcb-97a9-42e8-9c71-999491e3afac'): self.data = _uuid_io.xml(self.raw_data) elif self.uuid.bytes == b'JpgTiffExif->JP2': self.data = _uuid_io.tiff_header(self.raw_data) @@ -3223,23 +3215,23 @@ class UUIDBox(Jp2kBox): def __str__(self): msg = Jp2kBox.__str__(self) - if _printoptions['short'] is True: + if _printoptions['short'] == True: return msg msg = '{0}\n UUID: {1}'.format(msg, self.uuid) - if self.uuid == UUID('be7acfcb-97a9-42e8-9c71-999491e3afac'): + if self.uuid == uuid.UUID('be7acfcb-97a9-42e8-9c71-999491e3afac'): msg += ' (XMP)' elif self.uuid.bytes == b'JpgTiffExif->JP2': msg += ' (EXIF)' else: msg += ' (unknown)' - if (((_printoptions['xml'] is False) and - (self.uuid == UUID('be7acfcb-97a9-42e8-9c71-999491e3afac')))): + if (((_printoptions['xml'] == False) and + (self.uuid == uuid.UUID('be7acfcb-97a9-42e8-9c71-999491e3afac')))): # If it's an XMP UUID, don't print the XML contents. return msg - if self.uuid == UUID('be7acfcb-97a9-42e8-9c71-999491e3afac'): + if self.uuid == uuid.UUID('be7acfcb-97a9-42e8-9c71-999491e3afac'): line = '\n UUID Data:\n{0}' xmlstring = ET.tostring(self.data, encoding='utf-8', @@ -3282,7 +3274,7 @@ class UUIDBox(Jp2kBox): """ num_bytes = offset + length - fptr.tell() read_buffer = fptr.read(num_bytes) - the_uuid = UUID(bytes=read_buffer[0:16]) + the_uuid = uuid.UUID(bytes=read_buffer[0:16]) return cls(the_uuid, read_buffer[16:], length=length, offset=offset) @@ -3317,20 +3309,18 @@ _BOX_WITH_ID = { b'uuid': UUIDBox, b'xml ': XMLBox} -_parseoptions = {'full_codestream': False} +_parseoptions = {'codestream': True} - -def set_parseoptions(full_codestream=True): +def set_parseoptions(codestream=True): """Set parsing options. These options determine the way JPEG 2000 boxes are parsed. Parameters ---------- - full_codestream : bool, defaults to True - When False, only the codestream header is parsed for metadata. This - can results in faster JP2/JPX parsing. When True, the entire - codestream is parsed for metadata. + codestream : bool, defaults to True + When False, the codestream header is only parsed when accessed. This + can results in faster JP2/JPX parsing. See also -------- @@ -3341,10 +3331,9 @@ def set_parseoptions(full_codestream=True): To put back the default options, you can use: >>> import glymur - >>> glymur.set_parseoptions(full_codestream=True) + >>> glymur.set_parseoptions(codestream=True) """ - _parseoptions['full_codestream'] = full_codestream - + _parseoptions['codestream'] = codestream def get_parseoptions(): """Return the current parsing options. @@ -3366,7 +3355,6 @@ def get_parseoptions(): _printoptions = {'short': False, 'xml': True, 'codestream': True} - def set_printoptions(**kwargs): """Set printing options. @@ -3376,15 +3364,12 @@ def set_printoptions(**kwargs): ---------- short : bool, optional When True, only the box ID, offset, and length are displayed. Useful - for displaying only the basic structure or skeleton of a JPEG 2000 - file. + for displaying only the basic structure or skeleton of a JPEG 2000 file. xml : bool, optional When False, printing of the XML contents of any XML boxes or UUID XMP boxes is suppressed. codestream : bool, optional - When False, only the codestream header is printed. When True, the - entire codestream is printed. This option has no effect when the - 'short' option is set to True. + When False, printing of the codestream contents is suppressed. See also -------- @@ -3402,7 +3387,6 @@ def set_printoptions(**kwargs): raise TypeError('"{0}" not a valid keyword parameter.'.format(key)) _printoptions[key] = value - def get_printoptions(): """Return the current print options. @@ -3422,3 +3406,5 @@ def get_printoptions(): set_printoptions """ return _printoptions + + diff --git a/glymur/jp2k.py b/glymur/jp2k.py index ee6eedc..e4a1a01 100644 --- a/glymur/jp2k.py +++ b/glymur/jp2k.py @@ -10,12 +10,13 @@ License: MIT import sys # Exitstack not found in contextlib in 2.7 +# pylint: disable=E0611 if sys.hexversion >= 0x03030000: from contextlib import ExitStack - from itertools import filterfalse + from itertools import compress, filterfalse else: from contextlib2 import ExitStack - from itertools import ifilterfalse as filterfalse + from itertools import compress, ifilterfalse as filterfalse from collections import Counter import ctypes @@ -29,12 +30,20 @@ import warnings import numpy as np from .codestream import Codestream -from . import core, version -from .jp2box import (Jp2kBox, JPEG2000SignatureBox, FileTypeBox, - JP2HeaderBox, ColourSpecificationBox, - ContiguousCodestreamBox, ImageHeaderBox) -from .lib import openjpeg as opj, openjp2 as opj2, c as libc +from . import core +from .jp2box import Jp2kBox +from .jp2box import JPEG2000SignatureBox, FileTypeBox, JP2HeaderBox +from .jp2box import ColourSpecificationBox, ContiguousCodestreamBox +from .jp2box import ImageHeaderBox +from .lib import openjpeg as opj +from .lib import openjp2 as opj2 +from . import version +from .lib import c as libc +JP2_IDS = ['colr', 'cdef', 'cmap', 'jp2c', 'ftyp', 'ihdr', 'jp2h', 'jP ', + 'pclr', 'res ', 'resc', 'resd', 'xml ', 'ulst', 'uinf', 'url ', + 'uuid'] +JPX_IDS = ['asoc', 'nlst'] class Jp2k(Jp2kBox): """JPEG 2000 file. @@ -43,200 +52,32 @@ class Jp2k(Jp2kBox): ---------- filename : str The path to the JPEG 2000 file. + mode : str + The mode used to open the file. box : sequence List of top-level boxes in the file. Each box may in turn contain its own list of boxes. Will be empty if the file consists only of a raw codestream. - shape : tuple - Size of the image. - - Properties - ---------- - ignore_pclr_cmap_cdef : bool - whether or not to ignore the pclr, cmap, or cdef boxes during any - color transformation, defaults to False. - layer : int - zero-based number of quality layer to decode - verbose : bool - whether or not to print informational messages produced by the - OpenJPEG library, defaults to false - codestream : object - JP2 or J2K codestream object - - Examples - -------- - >>> import glymur - >>> jfile = glymur.data.nemo() - >>> jp2 = glymur.Jp2k(jfile) - >>> jp2.shape - (1456, 2592, 3) - >>> image = jp2[:] - >>> image.shape - (1456, 2592, 3) - - Read a lower resolution thumbnail. - - >>> thumbnail = jp2[::2, ::2] - >>> thumbnail.shape - (728, 1296, 3) """ - def __init__(self, filename, data=None, shape=None, **kwargs): + def __init__(self, filename, mode='rb'): """ - Only the filename parameter is required in order to read a JPEG 2000 - file. - Parameters ---------- filename : str or file - the path to JPEG 2000 file - image_data : ndarray, optional - image data to be written - shape : tuple - size of image data, only required when image_data is not provided - cbsize : tuple, optional - code block size (DY, DX) - cinema2k : int, optional - frames per second, either 24 or 48 - cinema4k : bool, optional - set to True to specify Cinema4K mode, defaults to false - colorspace : str, optional - either 'rgb' or 'gray' - cratios : iterable - compression ratios for successive layers - eph : bool, optional - if true, write SOP marker after each header packet - grid_offset : tuple, optional - offset (DY, DX) of the origin of the image in the reference grid - irreversible : bool, optional - if true, use the irreversible DWT 9-7 transform - mct : bool, optional - specifies usage of the multi component transform, if not - specified, defaults to True if the colorspace is RGB - modesw : int, optional - mode switch - 1 = BYPASS(LAZY) - 2 = RESET - 4 = RESTART(TERMALL) - 8 = VSC - 16 = ERTERM(SEGTERM) - 32 = SEGMARK(SEGSYM) - numres : int, optional - number of resolutions - prog : str, optional - progression order, one of "LRCP" "RLCP", "RPCL", "PCRL", "CPRL" - psnr : iterable, optional - different PSNR for successive layers - psizes : list, optional - list of precinct sizes, each precinct size tuple is defined in - (height x width) - sop : bool, optional - if true, write SOP marker before each packet - subsam : tuple, optional - subsampling factors (dy, dx) - tilesize : tuple, optional - numeric tuple specifying tile size in terms of (numrows, numcols), - not (X, Y) - verbose : bool, optional - print informational messages produced by the OpenJPEG library + The path to JPEG 2000 file. + mode : str, optional + The mode used to open the file. """ Jp2kBox.__init__(self) self.filename = filename - + self.mode = mode self.box = [] self._codec_format = None - self._colorspace = None - self._layer = 0 - self._codestream = None - if data is not None: - self._shape = data.shape - else: - self._shape = shape - - self._ignore_pclr_cmap_cdef = False - self._verbose = False # Parse the file for JP2/JPX contents only if we are reading it. - if data is None and shape is None: + if mode == 'rb': self.parse() - elif data is not None: - self._write(data, **kwargs) - - @property - def ignore_pclr_cmap_cdef(self): - return self._ignore_pclr_cmap_cdef - - @ignore_pclr_cmap_cdef.setter - def ignore_pclr_cmap_cdef(self, ignore_pclr_cmap_cdef): - self._ignore_pclr_cmap_cdef = ignore_pclr_cmap_cdef - - @property - def layer(self): - return self._layer - - @layer.setter - def layer(self, layer): - if version.openjpeg_version_tuple[0] < 2: - msg = "Layer property not supported unless the version of " - msg += "OpenJPEG is 2.0 or higher." - raise RuntimeError(msg) - self._layer = layer - - @property - def codestream(self): - if self._codestream is None: - self._codestream = self.get_codestream(header_only=True) - return self._codestream - - @codestream.setter - def codestream(self, the_codestream): - self._codestream = the_codestream - - @property - def verbose(self): - return self._verbose - - @verbose.setter - def verbose(self, verbose): - self._verbose = verbose - - @property - def shape(self): - if self._shape is not None: - return self._shape - - if self._codec_format == opj2.CODEC_J2K: - # get the image size from the codestream - cstr = self.codestream - height = cstr.segment[1].ysiz - width = cstr.segment[1].xsiz - num_components = len(cstr.segment[1].xrsiz) - else: - # try to get the image size from the IHDR box - jp2h = [box for box in self.box if box.box_id == 'jp2h'][0] - ihdr = [box for box in jp2h.box if box.box_id == 'ihdr'][0] - - height, width = ihdr.height, ihdr.width - num_components = ihdr.num_components - - if num_components == 1: - # but if there is a PCLR box, then we need to check that as - # well, as that turns a single-channel image into a - # multi-channel image - pclr = [box for box in jp2h.box if box.box_id == 'pclr'] - if len(pclr) > 0: - num_components = len(pclr[0].signed) - - if num_components == 1: - self.shape = (height, width) - else: - self.shape = (height, width, num_components) - - return self._shape - - @shape.setter - def shape(self, shape): - self._shape = shape def __repr__(self): msg = "glymur.Jp2k('{0}')".format(self.filename) @@ -248,7 +89,8 @@ class Jp2k(Jp2kBox): for box in self.box: metadata.append(str(box)) else: - metadata.append(str(self.codestream)) + codestream = self.get_codestream() + metadata.append(str(codestream)) return '\n'.join(metadata) def parse(self): @@ -312,7 +154,7 @@ class Jp2k(Jp2kBox): msg += "profile if the file type box brand is 'jp2 '." warnings.warn(msg) - def _set_cinema_params(self, cinema_mode, fps): + def _set_cinema_params(self, cparams, cinema_mode, fps): """Populate compression parameters structure for cinema2K. Parameters @@ -325,14 +167,11 @@ class Jp2k(Jp2kBox): fps : int Frames per second, should be either 24 or 48. """ - if re.match("1.5|2.0.0", version.openjpeg_version) is not None: + if re.match("(1.5|2.0.0)", version.openjpeg_version) is not None: msg = "Writing Cinema2K or Cinema4K files is not supported with " msg += 'openjpeg library versions less than 2.0.1.' raise IOError(msg) - # Cinema modes imply MCT. - self._cparams.tcp_mct = 1 - if cinema_mode == 'cinema2k': if fps not in [24, 48]: raise IOError('Cinema2K frame rate must be either 24 or 48.') @@ -340,48 +179,77 @@ class Jp2k(Jp2kBox): if re.match("2.0", version.openjpeg_version) is not None: # 2.0 API if fps == 24: - self._cparams.cp_cinema = core.OPJ_CINEMA2K_24 + cparams.cp_cinema = core.OPJ_CINEMA2K_24 else: - self._cparams.cp_cinema = core.OPJ_CINEMA2K_48 + cparams.cp_cinema = core.OPJ_CINEMA2K_48 else: # 2.1 API if fps == 24: - self._cparams.rsiz = core.OPJ_PROFILE_CINEMA_2K - self._cparams.max_comp_size = core.OPJ_CINEMA_24_COMP - self._cparams.max_cs_size = core.OPJ_CINEMA_24_CS + cparams.rsiz = core.OPJ_PROFILE_CINEMA_2K + cparams.max_comp_size = core.OPJ_CINEMA_24_COMP + cparams.max_cs_size = core.OPJ_CINEMA_24_CS else: - self._cparams.rsiz = core.OPJ_PROFILE_CINEMA_2K - self._cparams.max_comp_size = core.OPJ_CINEMA_48_COMP - self._cparams.max_cs_size = core.OPJ_CINEMA_48_CS + cparams.rsiz = core.OPJ_PROFILE_CINEMA_2K + cparams.max_comp_size = core.OPJ_CINEMA_48_COMP + cparams.max_cs_size = core.OPJ_CINEMA_48_CS else: # cinema4k if re.match("2.0", version.openjpeg_version) is not None: # 2.0 API - self._cparams.cp_cinema = core.OPJ_CINEMA4K_24 + cparams.cp_cinema = core.OPJ_CINEMA4K_24 else: # 2.1 API - self._cparams.rsiz = core.OPJ_PROFILE_CINEMA_4K + cparams.rsiz = core.OPJ_PROFILE_CINEMA_4K - def _populate_cparams(self, img_array, **kwargs): - """Directs processing of write method arguments. + return + + def _populate_cparams(self, **kwargs): + """Populate compression parameters structure from input arguments. Parameters ---------- - img_array : ndarray - image data to be written to file - kwargs : dictionary - non-image keyword inputs provided to write method + cbsize : tuple, optional + Code block size (DY, DX). + cratios : iterable + Compression ratios for successive layers. + eph : bool, optional + If true, write SOP marker after each header packet. + grid_offset : tuple, optional + Offset (DY, DX) of the origin of the image in the reference grid. + mct : bool, optional + Specifies usage of the multi component transform. If not + specified, defaults to True if the colorspace is RGB. + modesw : int, optional + Mode switch. + 1 = BYPASS(LAZY) + 2 = RESET + 4 = RESTART(TERMALL) + 8 = VSC + 16 = ERTERM(SEGTERM) + 32 = SEGMARK(SEGSYM) + numres : int, optional + Number of resolutions. + prog : str, optional + Progression order, one of "LRCP" "RLCP", "RPCL", "PCRL", "CPRL". + psnr : iterable, optional + Different PSNR for successive layers. + psizes : list, optional + List of precinct sizes. Each precinct size tuple is defined in + (height x width). + sop : bool, optional + If true, write SOP marker before each packet. + subsam : tuple, optional + Subsampling factors (dy, dx). + tilesize : tuple, optional + Numeric tuple specifying tile size in terms of (numrows, numcols), + not (X, Y). + + Returns + ------- + cparams : CompressionParametersType(ctypes.Structure) + Corresponds to cparameters_t type in openjp2 headers. """ - if ((('cinema2k' in kwargs or 'cinema4k' in kwargs) and - (len(set(kwargs)) > 1))): - msg = "Cannot specify cinema2k/cinema4k along with other options." - raise IOError(msg) - - if 'cratios' in kwargs and 'psnr' in kwargs: - msg = "Cannot specify cratios and psnr together." - raise IOError(msg) - if version.openjpeg_version_tuple[0] == 1: cparams = opj.set_default_encoder_parameters() else: @@ -406,14 +274,12 @@ class Jp2k(Jp2kBox): cparams.irreversible = 1 if 'cinema2k' in kwargs: - self._cparams = cparams - self._set_cinema_params('cinema2k', kwargs['cinema2k']) - return + self._set_cinema_params(cparams, 'cinema2k', kwargs['cinema2k']) + return cparams if 'cinema4k' in kwargs: - self._cparams = cparams - self._set_cinema_params('cinema4k', kwargs['cinema4k']) - return + self._set_cinema_params(cparams, 'cinema4k', kwargs['cinema4k']) + return cparams if 'cbsize' in kwargs: cparams.cblockw_init = kwargs['cbsize'][1] @@ -470,9 +336,49 @@ class Jp2k(Jp2kBox): cparams.cp_tdy = kwargs['tilesize'][0] cparams.tile_size_on = opj2.TRUE + return cparams + + def _process_write_inputs(self, img_array, colorspace=None, **kwargs): + """Directs processing of write method arguments. + + It's somewhat awkward to process all the kwargs arguments at once. + The "colorspace" is not a parameter that gets processed into the + compression parameters structure, and it unfortunately must be handled + in the middle of the compression parameter processing. + + Parameters + ---------- + img_array : ndarray + Image data to be written to file. + colorspace : str, optional + Either 'rgb' or 'gray'. + + Returns + ------- + cparams : CompressionParametersType(ctypes.Structure) + Corresponds to cparameters_t type in openjp2 headers. + colorspace : int + Either CLRSPC_SRGB or CLRSPC_GRAY + """ + if (('cinema2k' in kwargs or 'cinema4k' in kwargs) and + (len(set(kwargs)) > 1)): + msg = "Cannot specify cinema2k/cinema4k along with other options." + raise IOError(msg) + + if 'cratios' in kwargs and 'psnr' in kwargs: + msg = "Cannot specify cratios and psnr together." + raise IOError(msg) + + cparams = self._populate_cparams(**kwargs) + _validate_compression_params(img_array, cparams) + + colorspace = _unpack_colorspace(colorspace, img_array, cparams) + try: mct = kwargs['mct'] - if mct and self._colorspace == opj2.CLRSPC_GRAY: + if mct and colorspace == opj2.CLRSPC_GRAY: + # Cannot check for this in the validate routine, as we need + # to know what the target colorspace has been determined to be. msg = "Cannot specify usage of the multi component transform " msg += "if the colorspace is gray." raise IOError(msg) @@ -480,62 +386,122 @@ class Jp2k(Jp2kBox): except KeyError: # If the multi component transform was not specified, we infer # that it should be used if the color space is RGB. - if self._colorspace == opj2.CLRSPC_SRGB: + if colorspace == opj2.CLRSPC_SRGB: cparams.tcp_mct = 1 else: cparams.tcp_mct = 0 - self._validate_compression_params(img_array, cparams, **kwargs) + return cparams, colorspace - self._cparams = cparams - - def _write(self, img_array, verbose=False, **kwargs): + def write(self, img_array, verbose=False, **kwargs): """Write image data to a JP2/JPX/J2k file. Intended usage of the various parameters follows that of OpenJPEG's opj_compress utility. This method can only be used to create JPEG 2000 images that can fit in memory. + + Parameters + ---------- + img_array : ndarray + Image data to be written to file. + cbsize : tuple, optional + Code block size (DY, DX). + cinema2k : int, optional + frames per second, either 24 or 48 + cinema4k : bool, optional + Set to True to specify Cinema4K mode, defaults to false. + colorspace : str, optional + Either 'rgb' or 'gray'. + cratios : iterable + Compression ratios for successive layers. + eph : bool, optional + If true, write SOP marker after each header packet. + grid_offset : tuple, optional + Offset (DY, DX) of the origin of the image in the reference grid. + irreversible : bool, optional + If true, use the irreversible DWT 9-7 transform. + mct : bool, optional + Specifies usage of the multi component transform. If not + specified, defaults to True if the colorspace is RGB. + modesw : int, optional + Mode switch. + 1 = BYPASS(LAZY) + 2 = RESET + 4 = RESTART(TERMALL) + 8 = VSC + 16 = ERTERM(SEGTERM) + 32 = SEGMARK(SEGSYM) + numres : int, optional + Number of resolutions. + prog : str, optional + Progression order, one of "LRCP" "RLCP", "RPCL", "PCRL", "CPRL". + psnr : iterable, optional + Different PSNR for successive layers. + psizes : list, optional + List of precinct sizes. Each precinct size tuple is defined in + (height x width). + sop : bool, optional + If true, write SOP marker before each packet. + subsam : tuple, optional + Subsampling factors (dy, dx). + tilesize : tuple, optional + Numeric tuple specifying tile size in terms of (numrows, numcols), + not (X, Y). + verbose : bool, optional + Print informational messages produced by the OpenJPEG library. + + Examples + -------- + >>> import glymur + >>> jfile = glymur.data.nemo() + >>> jp2 = glymur.Jp2k(jfile) + >>> 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)) + + Raises + ------ + glymur.LibraryNotFoundError + If glymur is unable to load the openjp2 library. """ - if re.match("0|1.[0-4]", version.openjpeg_version) is not None: - msg = "You must have at least version 1.5 of OpenJPEG " - msg += "in order to write images." - raise RuntimeError(msg) - - self._determine_colorspace(**kwargs) - self._populate_cparams(img_array, **kwargs) - if opj2.OPENJP2 is not None: - self._write_openjp2(img_array, verbose=verbose) + self._write_openjp2(img_array, verbose=verbose, **kwargs) + elif opj.OPENJPEG is not None: + self._write_openjpeg(img_array, verbose=verbose, **kwargs) else: - self._write_openjpeg(img_array, verbose=verbose) + raise LibraryNotFoundError("You must have at least version 1.5 of " + "OpenJPEG before using this " + "functionality.") - def _write_openjpeg(self, img_array, verbose=False): + def _write_openjpeg(self, img_array, verbose=False, **kwargs): """ Write JPEG 2000 file using OpenJPEG 1.5 interface. """ + cparams, colorspace = self._process_write_inputs(img_array, **kwargs) + if img_array.ndim == 2: # Force the image to be 3D. Just makes things easier later on. img_array = img_array.reshape(img_array.shape[0], img_array.shape[1], 1) - self._populate_comptparms(img_array) + comptparms = _populate_comptparms(img_array, cparams) with ExitStack() as stack: - image = opj.image_create(self._comptparms, self._colorspace) + image = opj.image_create(comptparms, colorspace) stack.callback(opj.image_destroy, image) numrows, numcols, numlayers = img_array.shape # set image offset and reference grid - image.contents.x0 = self._cparams.image_offset_x0 - image.contents.y0 = self._cparams.image_offset_y0 - image.contents.x1 = (image.contents.x0 - + (numcols - 1) * self._cparams.subsampling_dx - + 1) - image.contents.y1 = (image.contents.y0 - + (numrows - 1) * self._cparams.subsampling_dy - + 1) + image.contents.x0 = cparams.image_offset_x0 + image.contents.y0 = cparams.image_offset_y0 + image.contents.x1 = image.contents.x0 \ + + (numcols - 1) * cparams.subsampling_dx + 1 + image.contents.y1 = image.contents.y0 \ + + (numrows - 1) * cparams.subsampling_dy + 1 # Stage the image data to the openjpeg data structure. for k in range(0, numlayers): @@ -545,7 +511,7 @@ class Jp2k(Jp2kBox): src = layer.ctypes.data ctypes.memmove(dest, src, layer.nbytes) - cinfo = opj.create_compress(self._cparams.codec_fmt) + cinfo = opj.create_compress(cparams.codec_fmt) stack.callback(opj.destroy_compress, cinfo) # Setup the info, warning, and error handlers. @@ -559,7 +525,7 @@ class Jp2k(Jp2kBox): event_mgr.error_handler = ctypes.cast(_ERROR_CALLBACK, ctypes.c_void_p) - opj.setup_encoder(cinfo, ctypes.byref(self._cparams), image) + opj.setup_encoder(cinfo, ctypes.byref(cparams), image) cio = opj.cio_open(cinfo) stack.callback(opj.cio_close, cio) @@ -576,143 +542,35 @@ class Jp2k(Jp2kBox): self.parse() - def _validate_compression_params(self, img_array, cparams, **kwargs): - """Check that the compression parameters are valid. - Parameters - ---------- - img_array : ndarray - Image data to be written to file. - cparams : CompressionParametersType(ctypes.Structure) - Corresponds to cparameters_t type in openjp2 headers. + def _write_openjp2(self, img_array, verbose=False, **kwargs): """ - # Cannot specify a colorspace with J2K. - if cparams.codec_fmt == opj2.CODEC_J2K and 'colorspace' in kwargs: - msg = 'Do not specify a colorspace when writing a raw ' - msg += 'codestream.' - raise IOError(msg) - - # Code block size - code_block_specified = False - if cparams.cblockw_init != 0 and cparams.cblockh_init != 0: - # These fields ARE zero if uninitialized. - width = cparams.cblockw_init - height = cparams.cblockh_init - code_block_specified = True - if height * width > 4096 or height < 4 or width < 4: - msg = "Code block area cannot exceed 4096. " - msg += "Code block height and width must be larger than 4." - raise IOError(msg) - if ((math.log(height, 2) != math.floor(math.log(height, 2)) or - math.log(width, 2) != math.floor(math.log(width, 2)))): - msg = "Bad code block size ({0}, {1}), " - msg += "must be powers of 2." - raise IOError(msg.format(height, width)) - - # Precinct size - if cparams.res_spec != 0: - # precinct size was not specified if this field is zero. - for j in range(cparams.res_spec): - prch = cparams.prch_init[j] - prcw = cparams.prcw_init[j] - if j == 0 and code_block_specified: - height, width = cparams.cblockh_init, cparams.cblockw_init - if height * 2 > prch or width * 2 > prcw: - msg = "Highest Resolution precinct size must be at " - msg += "least twice that of the code block dimensions." - raise IOError(msg) - if ((math.log(prch, 2) != math.floor(math.log(prch, 2)) or - math.log(prcw, 2) != math.floor(math.log(prcw, 2)))): - msg = "Bad precinct sizes ({0}, {1}), " - msg += "must be powers of 2." - raise IOError(msg.format(prch, prcw)) - - # What would the point of 1D images be? - if img_array.ndim == 1 or img_array.ndim > 3: - msg = "{0}D imagery is not allowed.".format(img_array.ndim) - raise IOError(msg) - - if re.match("2.0.0", version.openjpeg_version) is not None: - if (((img_array.ndim != 2) and - (img_array.shape[2] != 1 and img_array.shape[2] != 3))): - msg = "Writing images is restricted to single-channel " - msg += "greyscale images or three-channel RGB images when " - msg += "the OpenJPEG library version is the official 2.0.0 " - msg += "release." - raise IOError(msg) - - if img_array.dtype != np.uint8 and img_array.dtype != np.uint16: - msg = "Only uint8 and uint16 datatypes are currently supported " - msg += "when writing." - raise RuntimeError(msg) - - def _determine_colorspace(self, colorspace=None, **kwargs): - """Determine the colorspace from the supplied inputs. - - Parameters - ---------- - colorspace : str, optional - Either 'rgb' or 'gray'. + Write JPEG 2000 file using OpenJPEG 2.0 interface. """ - if colorspace is None: - # Must infer the colorspace from the image dimensions. - if len(self.shape) < 3: - # A single channel image is grayscale. - self._colorspace = opj2.CLRSPC_GRAY - elif self.shape[2] == 1 or self.shape[2] == 2: - # A single channel image or an image with two channels is going - # to be greyscale. - self._colorspace = opj2.CLRSPC_GRAY - else: - # Anything else must be RGB, right? - self._colorspace = opj2.CLRSPC_SRGB - else: - if colorspace.lower() not in ('rgb', 'grey', 'gray'): - msg = 'Invalid colorspace "{0}"'.format(colorspace) - raise IOError(msg) - elif colorspace.lower() == 'rgb' and self.shape[2] < 3: - msg = 'RGB colorspace requires at least 3 components.' - raise IOError(msg) + cparams, colorspace = self._process_write_inputs(img_array, **kwargs) - # Turn the colorspace from a string to the enumerated value that - # the library expects. - COLORSPACE_MAP = {'rgb': opj2.CLRSPC_SRGB, - 'gray': opj2.CLRSPC_GRAY, - 'grey': opj2.CLRSPC_GRAY, - 'ycc': opj2.CLRSPC_YCC} - - self._colorspace = COLORSPACE_MAP[colorspace.lower()] - - def _write_openjp2(self, img_array, verbose=False): - """ - Write JPEG 2000 file using OpenJPEG 2.x interface. - """ if img_array.ndim == 2: # Force the image to be 3D. Just makes things easier later on. numrows, numcols = img_array.shape img_array = img_array.reshape(numrows, numcols, 1) - self._populate_comptparms(img_array) + comptparms = _populate_comptparms(img_array, cparams) with ExitStack() as stack: - image = opj2.image_create(self._comptparms, self._colorspace) + image = opj2.image_create(comptparms, colorspace) stack.callback(opj2.image_destroy, image) - self._populate_image_struct(image, img_array) + _populate_image_struct(cparams, image, img_array) - codec = opj2.create_compress(self._cparams.codec_fmt) + codec = opj2.create_compress(cparams.codec_fmt) stack.callback(opj2.destroy_codec, codec) - if self._verbose or verbose: - info_handler = _INFO_CALLBACK - else: - info_handler = None - + info_handler = _INFO_CALLBACK if verbose else None opj2.set_info_handler(codec, info_handler) opj2.set_warning_handler(codec, _WARNING_CALLBACK) opj2.set_error_handler(codec, _ERROR_CALLBACK) - opj2.setup_encoder(codec, self._cparams, image) + opj2.setup_encoder(codec, cparams, image) if re.match("2.0", version.openjpeg_version) is not None: fptr = libc.fopen(self.filename, 'wb') @@ -748,8 +606,7 @@ class Jp2k(Jp2kBox): if not ((box.box_id == 'xml ') or (box.box_id == 'uuid' and box.uuid == UUID('be7acfcb-97a9-42e8-9c71-999491e3afac'))): - msg = "Only XML boxes and XMP UUID boxes can currently be " - msg += "appended." + msg = "Only XML boxes and XMP UUID boxes can currently be appended." raise IOError(msg) # Check the last box. If the length field is zero, then rewrite @@ -845,7 +702,7 @@ class Jp2k(Jp2kBox): raise IOError(msg) # Find the first codestream in the file. - jp2c = [_box for _box in self.box if _box.box_id == 'jp2c'] + jp2c = [box for box in self.box if box.box_id == 'jp2c'] offset = jp2c[0].offset # Ready to write the codestream. @@ -879,9 +736,10 @@ class Jp2k(Jp2kBox): FileTypeBox(), JP2HeaderBox(), ContiguousCodestreamBox()] - height = self.codestream.segment[1].ysiz - width = self.codestream.segment[1].xsiz - num_components = len(self.codestream.segment[1].xrsiz) + codestream = self.get_codestream() + height = codestream.segment[1].ysiz + width = codestream.segment[1].xsiz + num_components = len(codestream.segment[1].xrsiz) if num_components < 3: colorspace = core.GREYSCALE else: @@ -905,13 +763,13 @@ class Jp2k(Jp2kBox): Slicing protocol. """ if ((isinstance(index, slice) and - (index.start is None and - index.stop is None and - index.step is None)) or (index is Ellipsis)): + (index.start == None and + index.stop == None and + index.step == None)) or (index is Ellipsis)): # Case of jp2[:] = data, i.e. write the entire image. # # Should have a slice object where start = stop = step = None - self._write(data) + self.write(data) else: msg = "Partial write operations are currently not allowed." raise TypeError(msg) @@ -920,32 +778,29 @@ class Jp2k(Jp2kBox): """ Slicing protocol. """ - if len(self.shape) == 2: - numrows, numcols = self.shape - numbands = 1 - else: - numrows, numcols, numbands = self.shape + codestream = self.get_codestream(header_only=True) + numrows = codestream.segment[1].ysiz + numcols = codestream.segment[1].xsiz + numbands = codestream.segment[1].Csiz if isinstance(pargs, int): # Not a very good use of this protocol, but technically legal. # This retrieves a single row. row = pargs area = (row, 0, row + 1, numcols) - return self._read(area=area).squeeze() + return self.read(area=area).squeeze() if pargs is Ellipsis: # Case of jp2[...] - return self._read() + return self.read() if isinstance(pargs, slice): - if (((pargs.start is None) and - (pargs.stop is None) and - (pargs.step is None))): + if pargs.start is None and pargs.stop is None and pargs.step is None: # Case of jp2[:] - return self._read() + return self.read() # Corner case of jp2[x] where x is a slice object with non-null - # members. Just augment it with an ellipsis and let the code + # members. Just augment it with an ellipsis and let the code # below handle it. pargs = (pargs, Ellipsis) @@ -988,7 +843,8 @@ class Jp2k(Jp2kBox): # Reduce dimensionality in the scalar dimension. return np.squeeze(data, axis=idx) - # Assuming pargs is a tuple of slices from now on. + + # Assuming pargs is a tuple of slices from now on. rows = pargs[0] cols = pargs[1] if len(pargs) == 2: @@ -1005,28 +861,49 @@ class Jp2k(Jp2kBox): # Ok, reduce layer step is the same in both xy directions, so just take # one of them. step = rows_step - + # Check if the step size is a power of 2. if np.abs(np.log2(step) - np.round(np.log2(step))) > 1e-6: msg = "Row and column strides must be powers of 2." raise IndexError(msg) rlevel = np.int(np.round(np.log2(step))) - area = (0 if rows.start is None else rows.start, + area = ( + 0 if rows.start is None else rows.start, 0 if cols.start is None else cols.start, numrows if rows.stop is None else rows.stop, numcols if cols.stop is None else cols.stop ) - data = self._read(area=area, rlevel=rlevel) + data = self.read(area=area, rlevel=rlevel) if len(pargs) == 2: return data # Ok, 3 arguments in pargs. return data[:, :, bands] - def _read(self, **kwargs): + + 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 the OpenJPEG version is 1.5 or earlier. + layer : int, optional + Number of quality layer to decode. + area : tuple, optional + Specifies decoding image area, + (first_row, first_col, last_row, last_col) + tile : int, optional + Number of tile to decode. + ignore_pclr_cmap_cdef : bool + Whether or not to ignore the pclr, cmap, or cdef boxes during any + color transformation. Defaults to False. + verbose : bool, optional + Print informational messages produced by the OpenJPEG library. + Returns ------- img_array : ndarray @@ -1034,67 +911,50 @@ class Jp2k(Jp2kBox): Raises ------ + glymur.LibraryNotFoundError + If glymur is unable to load either the openjpeg or openjp2 + libraries. IOError If the image has differing subsample factors. + + Examples + -------- + >>> import glymur + >>> jfile = glymur.data.nemo() + >>> jp = glymur.Jp2k(jfile) + >>> image = jp.read() + >>> image.shape + (1456, 2592, 3) + + Read the lowest resolution thumbnail. + + >>> thumbnail = jp.read(rlevel=-1) + >>> thumbnail.shape + (728, 1296, 3) """ - if version.openjpeg_version_tuple[0] < 2: + if opj2.OPENJP2 is not None: + img = self._read_openjp2(**kwargs) + elif opj.OPENJPEG is not None: img = self._read_openjpeg(**kwargs) else: - img = self._read_openjp2(**kwargs) - return img - - 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 the OpenJPEG version is 1.5 or earlier. - # layer : int, optional - # Number of quality layer to decode. - # 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 - # ------ - # IOError - # If the image has differing subsample factors. - - if 'ignore_pclr_cmap_cdef' in kwargs: - self.ignore_pclr_cmap_cdef = kwargs['ignore_pclr_cmap_cdef'] - warnings.warn("Use array-style slicing instead.", DeprecationWarning) - if version.openjpeg_version_tuple[0] < 2: - img = self._read_openjpeg(**kwargs) - else: - img = self._read_openjp2(**kwargs) + raise LibraryNotFoundError("You must have OpenJPEG installed " + "before reading a JPEG2000 image.") return img def _subsampling_sanity_check(self): """Check for differing subsample factors. """ - dxs = np.array(self.codestream.segment[1].xrsiz) - dys = np.array(self.codestream.segment[1].yrsiz) + codestream = self.get_codestream(header_only=True) + dxs = np.array(codestream.segment[1].xrsiz) + dys = np.array(codestream.segment[1].yrsiz) if np.any(dxs - dxs[0]) or np.any(dys - dys[0]): msg = "Components must all have the same subsampling factors " msg += "to use this method. Please consider using OPENJP2 and " msg += "the read_bands method instead." raise RuntimeError(msg) - def _read_openjpeg(self, rlevel=0, verbose=False, area=None): + def _read_openjpeg(self, rlevel=0, ignore_pclr_cmap_cdef=False, + verbose=False, area=None): """Read a JPEG 2000 image using libopenjpeg. Parameters @@ -1102,6 +962,9 @@ class Jp2k(Jp2kBox): rlevel : int, optional Factor by which to rlevel output resolution. Use -1 to get the lowest resolution thumbnail. + ignore_pclr_cmap_cdef : bool + Whether or not to ignore the pclr, cmap, or cdef boxes during any + color transformation. Defaults to False. verbose : bool, optional Print informational messages produced by the OpenJPEG library. area : tuple, optional @@ -1120,27 +983,24 @@ class Jp2k(Jp2kBox): """ self._subsampling_sanity_check() - self._populate_dparams(rlevel) + dparameters = self._populate_dparam(rlevel, ignore_pclr_cmap_cdef) with ExitStack() as stack: try: - self._dparams.decod_format = self._codec_format + dparameters.decod_format = self._codec_format - dinfo = opj.create_decompress(self._dparams.decod_format) + dinfo = opj.create_decompress(dparameters.decod_format) event_mgr = opj.EventMgrType() info_handler = ctypes.cast(_INFO_CALLBACK, ctypes.c_void_p) - if verbose or self._verbose: - event_mgr.info_handler = info_handler - else: - event_mgr.info_handler = None + 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, self._dparams) + opj.setup_decoder(dinfo, dparameters) with open(self.filename, 'rb') as fptr: src = fptr.read() @@ -1176,8 +1036,8 @@ class Jp2k(Jp2kBox): return data - def _read_openjp2(self, rlevel=0, layer=None, area=None, tile=None, - verbose=False): + def _read_openjp2(self, rlevel=0, layer=0, area=None, tile=None, + verbose=False, ignore_pclr_cmap_cdef=False): """Read a JPEG 2000 image using libopenjp2. Parameters @@ -1205,12 +1065,10 @@ class Jp2k(Jp2kBox): RuntimeError If the image has differing subsample factors. """ - if layer is not None: - self._layer = layer - self._subsampling_sanity_check() - self._populate_dparams(rlevel, tile=tile, area=area) + dparam = self._populate_dparam(rlevel, ignore_pclr_cmap_cdef, + layer=layer, tile=tile, area=area) with ExitStack() as stack: if re.match("2.1", version.openjpeg_version): @@ -1227,26 +1085,23 @@ class Jp2k(Jp2kBox): opj2.set_error_handler(codec, _ERROR_CALLBACK) opj2.set_warning_handler(codec, _WARNING_CALLBACK) - - if self._verbose or verbose: + if verbose: opj2.set_info_handler(codec, _INFO_CALLBACK) else: opj2.set_info_handler(codec, None) - opj2.setup_decoder(codec, self._dparams) + opj2.setup_decoder(codec, dparam) image = opj2.read_header(stream, codec) stack.callback(opj2.image_destroy, image) - if self._dparams.nb_tile_to_decode: - opj2.get_decoded_tile(codec, stream, image, - self._dparams.tile_index) + if dparam.nb_tile_to_decode: + opj2.get_decoded_tile(codec, stream, image, dparam.tile_index) else: opj2.set_decode_area(codec, image, - self._dparams.DA_x0, self._dparams.DA_y0, - self._dparams.DA_x1, self._dparams.DA_y1) + dparam.DA_x0, dparam.DA_y0, + dparam.DA_x1, dparam.DA_y1) opj2.decode(codec, stream, image) - - opj2.end_decompress(codec, stream) + opj2.end_decompress(codec, stream) img_array = extract_image_cube(image) @@ -1255,11 +1110,14 @@ class Jp2k(Jp2kBox): return img_array - def _populate_dparams(self, rlevel, tile=None, area=None): + def _populate_dparam(self, rlevel, ignore_pclr_cmap_cdef, tile=None, + layer=None, area=None): """Populate decompression structure with appropriate input parameters. Parameters ---------- + layer : int + Number of quality layer to decode. rlevel : int Factor by which to rlevel output resolution. area : tuple @@ -1267,6 +1125,14 @@ class Jp2k(Jp2kBox): (first_row, first_col, last_row, last_col) tile : int Number of tile to decode. + ignore_pclr_cmap_cdef : bool + Whether or not to ignore the pclr, cmap, or cdef boxes during any + color transformation. Defaults to False. + + Returns + ------- + dparam : DecompressionParametersType (ctypes) + Corresponds to openjp2 decompression parameters structure. """ if opj2.OPENJP2 is not None: dparam = opj2.set_default_decoder_parameters() @@ -1279,18 +1145,16 @@ class Jp2k(Jp2kBox): infile += b'0' * nelts dparam.infile = infile - if self.ignore_pclr_cmap_cdef: - # Return raw codestream components. - dparam.flags |= 1 - dparam.decod_format = self._codec_format - dparam.cp_layer = self._layer + if layer is not None: + dparam.cp_layer = layer # Must check the specified rlevel against the maximum. if rlevel != 0: # Must check the specified rlevel against the maximum. - max_rlevel = self.codestream.segment[2].spcod[4] + codestream = self.get_codestream() + max_rlevel = codestream.segment[2].spcod[4] if rlevel == -1: # -1 is shorthand for the largest rlevel rlevel = max_rlevel @@ -1315,13 +1179,13 @@ class Jp2k(Jp2kBox): dparam.tile_index = tile dparam.nb_tile_to_decode = 1 - if self.ignore_pclr_cmap_cdef: + if ignore_pclr_cmap_cdef is True: # Return raw codestream components. dparam.flags |= 1 - self._dparams = dparam + return dparam - def read_bands(self, rlevel=0, layer=None, area=None, tile=None, + def read_bands(self, rlevel=0, layer=0, area=None, tile=None, verbose=False, ignore_pclr_cmap_cdef=False): """Read a JPEG 2000 image. @@ -1361,16 +1225,19 @@ class Jp2k(Jp2kBox): >>> jfile = glymur.data.nemo() >>> jp = glymur.Jp2k(jfile) >>> components_lst = jp.read_bands(rlevel=1) + + Raises + ------ + glymur.LibraryNotFoundError + If glymur is unable to load the openjp2 library. """ if version.openjpeg_version_tuple[0] < 2: - raise RuntimeError("You must have at least version 2.0.0 of " - "OpenJPEG installed before using this " - "functionality.") + raise LibraryNotFoundError("You must have at least version 2.0.0 " + "of OpenJPEG installed before using " + "read_bands.") - self.ignore_pclr_cmap_cdef = ignore_pclr_cmap_cdef - if layer is not None: - self._layer = layer - self._populate_dparams(rlevel, tile=tile, area=area) + dparam = self._populate_dparam(rlevel, ignore_pclr_cmap_cdef, + layer=layer, tile=tile, area=area) with ExitStack() as stack: if re.match("2.1", version.openjpeg_version): @@ -1393,17 +1260,16 @@ class Jp2k(Jp2kBox): else: opj2.set_info_handler(codec, None) - opj2.setup_decoder(codec, self._dparams) + opj2.setup_decoder(codec, dparam) image = opj2.read_header(stream, codec) stack.callback(opj2.image_destroy, image) - if self._dparams.nb_tile_to_decode: - opj2.get_decoded_tile(codec, stream, image, - self._dparams.tile_index) + if dparam.nb_tile_to_decode: + opj2.get_decoded_tile(codec, stream, image, dparam.tile_index) else: opj2.set_decode_area(codec, image, - self._dparams.DA_x0, self._dparams.DA_y0, - self._dparams.DA_x1, self._dparams.DA_y1) + dparam.DA_x0, dparam.DA_y0, + dparam.DA_x1, dparam.DA_y1) opj2.decode(codec, stream, image) opj2.end_decompress(codec, stream) @@ -1446,6 +1312,7 @@ class Jp2k(Jp2kBox): codestream = Codestream(fptr, self.length, header_only=header_only) else: + ftyp = self.box[1] box = [x for x in self.box if x.box_id == 'jp2c'] fptr.seek(box[0].offset) read_buffer = fptr.read(8) @@ -1463,82 +1330,6 @@ class Jp2k(Jp2kBox): return codestream - def _populate_image_struct(self, image, imgdata): - """Populates image struct needed for compression. - - Parameters - ---------- - image : ImageType(ctypes.Structure) - Corresponds to image_t type in openjp2 headers. - img_array : ndarray - Image data to be written to file. - """ - - numrows, numcols, num_comps = imgdata.shape - - # set image offset and reference grid - image.contents.x0 = self._cparams.image_offset_x0 - image.contents.y0 = self._cparams.image_offset_y0 - image.contents.x1 = (image.contents.x0 + - (numcols - 1) * self._cparams.subsampling_dx + 1) - image.contents.y1 = (image.contents.y0 + - (numrows - 1) * self._cparams.subsampling_dy + 1) - - # Stage the image data to the openjpeg data structure. - for k in range(0, num_comps): - if re.match("2.0", version.openjpeg_version) is not None: - # 2.0 API - if self._cparams.cp_cinema: - image.contents.comps[k].prec = 12 - image.contents.comps[k].bpp = 12 - else: - # 2.1 API - if self._cparams.rsiz in (core.OPJ_PROFILE_CINEMA_2K, - core.OPJ_PROFILE_CINEMA_4K): - image.contents.comps[k].prec = 12 - image.contents.comps[k].bpp = 12 - - layer = np.ascontiguousarray(imgdata[:, :, k], dtype=np.int32) - dest = image.contents.comps[k].data - src = layer.ctypes.data - ctypes.memmove(dest, src, layer.nbytes) - - return image - - def _populate_comptparms(self, img_array): - """Instantiate and populate comptparms structure. - - This structure defines the image components. - - Parameters - ---------- - img_array : ndarray - Image data to be written to file. - """ - # Only two precisions are possible. - if img_array.dtype == np.uint8: - comp_prec = 8 - else: - comp_prec = 16 - - numrows, numcols, num_comps = img_array.shape - if version.openjpeg_version_tuple[0] == 1: - comptparms = (opj.ImageComptParmType * num_comps)() - else: - comptparms = (opj2.ImageComptParmType * num_comps)() - for j in range(num_comps): - comptparms[j].dx = self._cparams.subsampling_dx - comptparms[j].dy = self._cparams.subsampling_dy - comptparms[j].w = numcols - comptparms[j].h = numrows - comptparms[j].x0 = self._cparams.image_offset_x0 - comptparms[j].y0 = self._cparams.image_offset_y0 - comptparms[j].prec = comp_prec - comptparms[j].bpp = comp_prec - comptparms[j].sgnd = 0 - - self._comptparms = comptparms - def _component2dtype(component): """Take an OpenJPEG component structure and determine the numpy datatype. @@ -1585,7 +1376,6 @@ JP2_IDS = ['colr', 'cdef', 'cmap', 'jp2c', 'ftyp', 'ihdr', 'jp2h', 'jP ', 'pclr', 'res ', 'resc', 'resd', 'xml ', 'ulst', 'uinf', 'url ', 'uuid'] - def _validate_jp2_box_sequence(boxes): """Run through series of tests for JP2 box legality. @@ -1601,13 +1391,12 @@ def _validate_jp2_box_sequence(boxes): count = _collect_box_count(boxes) for box_id in count.keys(): if box_id not in JP2_IDS: - msg = "The presence of a '{0}' box requires that the file " - msg += "type brand be set to 'jpx '." + msg = "The presence of a '{0}' box requires that the file type " + msg += "brand be set to 'jpx '." raise IOError(msg.format(box_id)) _validate_jp2_colr(boxes) - def _validate_jp2_colr(boxes): """ Validate JP2 requirements on colour specification boxes. @@ -1619,7 +1408,6 @@ def _validate_jp2_colr(boxes): msg = "A JP2 colr box cannot have a non-zero approximation field." raise IOError(msg) - def _validate_jpx_box_sequence(boxes): """Run through series of tests for JPX box legality.""" _validate_label(boxes) @@ -1628,7 +1416,6 @@ def _validate_jpx_box_sequence(boxes): _validate_singletons(boxes) _validate_top_level(boxes) - def _validate_signature_compatibility(boxes): """Validate the file signature and compatibility status.""" # Check for a bad sequence of boxes. @@ -1716,8 +1503,6 @@ def _validate_channel_definition(jp2h, colr): JP2H_CHILDREN = set(['bpcc', 'cdef', 'cmap', 'ihdr', 'pclr']) - - def _check_jp2h_child_boxes(boxes, parent_box_name): """Certain boxes can only reside in the JP2 header.""" box_ids = set([box.box_id for box in boxes]) @@ -1745,7 +1530,6 @@ def _collect_box_count(boxes): TOP_LEVEL_ONLY_BOXES = set(['dtbl']) - def _check_superbox_for_top_levels(boxes): """Several boxes can only occur at the top level.""" # We are only looking at the boxes contained in a superbox, so if any of @@ -1761,7 +1545,6 @@ def _check_superbox_for_top_levels(boxes): if hasattr(box, 'box'): _check_superbox_for_top_levels(box.box) - def _validate_top_level(boxes): """Several boxes can only occur at the top level.""" # Add the counts in the superboxes. @@ -1781,7 +1564,6 @@ def _validate_top_level(boxes): msg += 'a fragment table box as well.' raise IOError(msg) - def _validate_singletons(boxes): """Several boxes can only occur once.""" count = _collect_box_count(boxes) @@ -1790,9 +1572,6 @@ def _validate_singletons(boxes): if 'dtbl' in multiples: raise IOError('There can only be one dtbl box in a file.') -JPX_IDS = ['asoc', 'nlst'] - - def _validate_jpx_brand(boxes, brand): """ If there is a JPX box then the brand must be 'jpx '. @@ -1807,7 +1586,6 @@ def _validate_jpx_brand(boxes, brand): # Same set of checks on any child boxes. _validate_jpx_brand(box.box, brand) - def _validate_jpx_compatibility(boxes, compatibility_list): """ If there is a JPX box then the compatibility list must also contain 'jpx '. @@ -1823,7 +1601,6 @@ def _validate_jpx_compatibility(boxes, compatibility_list): # Same set of checks on any child boxes. _validate_jpx_compatibility(box.box, compatibility_list) - def _validate_label(boxes): """ Label boxes can only be inside association, codestream headers, or @@ -1840,7 +1617,6 @@ def _validate_label(boxes): # Same set of checks on any child boxes. _validate_label(box.box) - def extract_image_cube(image): """Extract 3D image from openjpeg data structure. """ @@ -1896,6 +1672,206 @@ def extract_image_bands(image): return data +def _unpack_colorspace(colorspace, img_array, cparams): + """Determine the colorspace from the supplied inputs. + + Parameters + ---------- + colorspace : int + Either CLRSPC_SRGB or CLRSPC_GRAY + img_array : ndarray + Image data to be written to file. + cparams : CompressionParametersType(ctypes.Structure) + Corresponds to cparameters_t type in openjp2 headers. + """ + if colorspace is None: + # Must infer the colorspace from the image dimensions. + if img_array.ndim < 3: + # A single channel image is grayscale. + colorspace = opj2.CLRSPC_GRAY + elif img_array.shape[2] == 1 or img_array.shape[2] == 2: + # A single channel image or an image with two channels is going + # to be greyscale. + colorspace = opj2.CLRSPC_GRAY + else: + # Anything else must be RGB, right? + colorspace = opj2.CLRSPC_SRGB + else: + # Supplied a string colorspace, so we must validate it. + if cparams.codec_fmt == opj2.CODEC_J2K: + msg = 'Do not specify a colorspace when writing a raw ' + msg += 'codestream.' + raise IOError(msg) + if colorspace.lower() not in ('rgb', 'grey', 'gray'): + msg = 'Invalid colorspace "{0}"'.format(colorspace) + raise IOError(msg) + elif colorspace.lower() == 'rgb' and img_array.shape[2] < 3: + msg = 'RGB colorspace requires at least 3 components.' + raise IOError(msg) + + # Turn the colorspace from a string to the enumerated value that + # the library expects. + colorspace = _COLORSPACE_MAP[colorspace.lower()] + + return colorspace + + +def _populate_comptparms(img_array, cparams): + """Instantiate and populate comptparms structure. + + This structure defines the image components. + + Parameters + ---------- + img_array : ndarray + Image data to be written to file. + cparams : CompressionParametersType(ctypes.Structure) + Corresponds to cparameters_t type in openjp2 headers. + + Returns + ------- + comptparms : ImageCompType(ctypes.Structure) + Corresponds to image_comp_t type in openjp2 headers. + """ + # Only two precisions are possible. + if img_array.dtype == np.uint8: + comp_prec = 8 + else: + comp_prec = 16 + + numrows, numcols, num_comps = img_array.shape + if version.openjpeg_version_tuple[0] == 1: + comptparms = (opj.ImageComptParmType * num_comps)() + else: + comptparms = (opj2.ImageComptParmType * num_comps)() + for j in range(num_comps): + comptparms[j].dx = cparams.subsampling_dx + comptparms[j].dy = cparams.subsampling_dy + comptparms[j].w = numcols + comptparms[j].h = numrows + comptparms[j].x0 = cparams.image_offset_x0 + comptparms[j].y0 = cparams.image_offset_y0 + comptparms[j].prec = comp_prec + comptparms[j].bpp = comp_prec + comptparms[j].sgnd = 0 + + return comptparms + + +def _populate_image_struct(cparams, image, imgdata): + """Populates image struct needed for compression. + + Parameters + ---------- + cparams : CompressionParametersType(ctypes.Structure) + Corresponds to cparameters_t type in openjp2 headers. + image : ImageType(ctypes.Structure) + Corresponds to image_t type in openjp2 headers. + imgarray : ndarray + Image data to be written to file. + """ + + numrows, numcols, num_comps = imgdata.shape + + # set image offset and reference grid + image.contents.x0 = cparams.image_offset_x0 + image.contents.y0 = cparams.image_offset_y0 + image.contents.x1 = (image.contents.x0 + + (numcols - 1) * cparams.subsampling_dx + 1) + image.contents.y1 = (image.contents.y0 + + (numrows - 1) * cparams.subsampling_dy + 1) + + # Stage the image data to the openjpeg data structure. + for k in range(0, num_comps): + if re.match("2.0", version.openjpeg_version) is not None: + # 2.0 API + if cparams.cp_cinema: + image.contents.comps[k].prec = 12 + image.contents.comps[k].bpp = 12 + else: + # 2.1 API + if cparams.rsiz in (core.OPJ_PROFILE_CINEMA_2K, + core.OPJ_PROFILE_CINEMA_4K): + image.contents.comps[k].prec = 12 + image.contents.comps[k].bpp = 12 + + layer = np.ascontiguousarray(imgdata[:, :, k], dtype=np.int32) + dest = image.contents.comps[k].data + src = layer.ctypes.data + ctypes.memmove(dest, src, layer.nbytes) + + return image + + +def _validate_compression_params(img_array, cparams): + """Check that the compression parameters are valid. + + Parameters + ---------- + img_array : ndarray + Image data to be written to file. + cparams : CompressionParametersType(ctypes.Structure) + Corresponds to cparameters_t type in openjp2 headers. + """ + + # Code block size + code_block_specified = False + if cparams.cblockw_init != 0 and cparams.cblockh_init != 0: + # These fields ARE zero if uninitialized. + width = cparams.cblockw_init + height = cparams.cblockh_init + code_block_specified = True + if height * width > 4096 or height < 4 or width < 4: + msg = "Code block area cannot exceed 4096. " + msg += "Code block height and width must be larger than 4." + raise IOError(msg) + if ((math.log(height, 2) != math.floor(math.log(height, 2)) or + math.log(width, 2) != math.floor(math.log(width, 2)))): + msg = "Bad code block size ({0}, {1}), " + msg += "must be powers of 2." + raise IOError(msg.format(height, width)) + + # Precinct size + if cparams.res_spec != 0: + # precinct size was not specified if this field is zero. + for j in range(cparams.res_spec): + prch = cparams.prch_init[j] + prcw = cparams.prcw_init[j] + if j == 0 and code_block_specified: + height, width = cparams.cblockh_init, cparams.cblockw_init + if height * 2 > prch or width * 2 > prcw: + msg = "Highest Resolution precinct size must be at " + msg += "least twice that of the code block dimensions." + raise IOError(msg) + if ((math.log(prch, 2) != math.floor(math.log(prch, 2)) or + math.log(prcw, 2) != math.floor(math.log(prcw, 2)))): + msg = "Bad precinct sizes ({0}, {1}), " + msg += "must be powers of 2." + raise IOError(msg.format(prch, prcw)) + + # What would the point of 1D images be? + if img_array.ndim == 1 or img_array.ndim > 3: + msg = "{0}D imagery is not allowed.".format(img_array.ndim) + raise IOError(msg) + + if re.match("2.0.0", version.openjpeg_version) is not None: + if (((img_array.ndim != 2) and + (img_array.shape[2] != 1 and img_array.shape[2] != 3))): + msg = "Writing images is restricted to single-channel " + msg += "greyscale images or three-channel RGB images when " + msg += "the OpenJPEG library version is the official 2.0.0 " + msg += "release." + raise IOError(msg) + + if img_array.dtype != np.uint8 and img_array.dtype != np.uint16: + msg = "Only uint8 and uint16 images are currently supported." + raise RuntimeError(msg) + +_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 # what's going on here. @@ -1922,3 +1898,10 @@ def _default_warning_handler(library_msg, _): _ERROR_CALLBACK = _CMPFUNC(_default_error_handler) _INFO_CALLBACK = _CMPFUNC(_default_info_handler) _WARNING_CALLBACK = _CMPFUNC(_default_warning_handler) + + +class LibraryNotFoundError(IOError): + """Raised if functionality is requested without the necessary library. + """ + def __init__(self, msg): + IOError.__init__(self, msg) diff --git a/glymur/lib/__init__.py b/glymur/lib/__init__.py index ddce813..a283f7f 100644 --- a/glymur/lib/__init__.py +++ b/glymur/lib/__init__.py @@ -2,5 +2,3 @@ from . import openjp2 as openjp2 from . import openjpeg as openjpeg from . import c - -__all__ = [openjp2, openjpeg, c] diff --git a/glymur/lib/config.py b/glymur/lib/config.py index 8af038a..dc8ab29 100644 --- a/glymur/lib/config.py +++ b/glymur/lib/config.py @@ -1,6 +1,9 @@ """ Configure glymur to use installed libraries if possible. """ +# configparser is new in python3 (pylint/python-2.7) +# pylint: disable=F0401 + import ctypes from ctypes.util import find_library import os @@ -15,22 +18,6 @@ else: from configparser import ConfigParser from configparser import NoOptionError -# default library locations for MacPorts -_macports_default_location = {'openjp2': '/opt/local/lib/libopenjp2.dylib', - 'openjpeg': '/opt/local/lib/libopenjpeg.dylib'} - -# default library locations on Windows -_windows_default_location = {'openjp2': os.path.join('C:\\', - 'Program files', - 'OpenJPEG 2.0', - 'bin', - 'openjp2.dll'), - 'openjpeg': os.path.join('C:\\', - 'Program files', - 'OpenJPEG 1.5', - 'bin', - 'openjpeg.dll')} - def glymurrc_fname(): """Return the path to the configuration file. @@ -56,23 +43,51 @@ def glymurrc_fname(): return None -def load_openjpeg_library(libname): +def load_openjpeg(path): + """Load the openjpeg library, falling back on defaults if necessary. - path = read_config_file(libname) - if path is not None: - return load_library_handle(path) + Parameters + ---------- + path : str + Path to openjpeg 1.5 library as specified by configuration file. Will + be None if no configuration file specified. + """ + if path is None: + # Let ctypes try to find it. + path = find_library('openjpeg') - # No location specified by the configuration file, must look for it - # elsewhere. - path = find_library(libname) + # If we could not find it, then look in some likely locations on mac + # and win. + if path is None: + # Could not find a library via ctypes + if platform.system() == 'Darwin': + # MacPorts + path = '/opt/local/lib/libopenjpeg.dylib' + elif os.name == 'nt': + path = os.path.join('C:\\', 'Program files', 'OpenJPEG 1.5', + 'bin', 'openjpeg.dll') + + if path is not None and not os.path.exists(path): + # the mac/win default location does not exist. + return None + + return load_library_handle(path) + +def load_openjp2(path): + """Load the openjp2 library, falling back on defaults if necessary. + """ + if path is None: + # No help from the config file, try to find it via ctypes. + path = find_library('openjp2') if path is None: # Could not find a library via ctypes if platform.system() == 'Darwin': # MacPorts - path = _macports_default_location[libname] + path = '/opt/local/lib/libopenjp2.dylib' elif os.name == 'nt': - path = _windows_default_location[libname] + path = os.path.join('C:\\', 'Program files', 'OpenJPEG 2.0', + 'bin', 'openjp2.dll') if path is not None and not os.path.exists(path): # the mac/win default location does not exist. @@ -85,10 +100,9 @@ def load_library_handle(path): """Load the library, return the ctypes handle.""" if path is None or path in ['None', 'none']: - # Either could not find a library via ctypes or - # user-configuration-file, or we could not find it in any of the - # default locations, or possibly the user intentionally does not want - # one of the libraries to load. + # Either could not find a library via ctypes or user-configuration-file, + # or we could not find it in any of the default locations. + # This is probably a very old linux. return None try: @@ -97,59 +111,43 @@ def load_library_handle(path): else: opj_lib = ctypes.CDLL(path) except (TypeError, OSError): - msg = 'The library specified by configuration file at {0} could not ' - msg += 'be loaded.' - warnings.warn(msg.format(path), UserWarning) - opj_lib = None + msg = '"Library {0}" could not be loaded. Operating in degraded mode.' + msg = msg.format(path) + warnings.warn(msg, UserWarning) + opj_lib = None return opj_lib -def read_config_file(libname): +def read_config_file(): """ - Extract library locations from a configuration file. - - Parameters - ---------- - libname : str - One of either 'openjp2' or 'openjpeg' - - Returns - ------- - path : None or str - None if no location is specified, otherwise a path to the library + We must use a configuration file that the user must write. """ + lib = {'openjp2': None, 'openjpeg': None} filename = glymurrc_fname() - if filename is None: - # There's no library file path to return in this case. - return None + if filename is not None: + # Read the configuration file for the library location. + parser = ConfigParser() + parser.read(filename) + for name in ['openjp2', 'openjpeg']: + try: + lib[name] = parser.get('library', name) + except NoOptionError: + pass - # Read the configuration file for the library location. - parser = ConfigParser() - parser.read(filename) - try: - path = parser.get('library', libname) - except NoOptionError: - path = None - return path + return lib def glymur_config(): + """Try to ascertain locations of openjp2, openjpeg libraries. """ - Try to ascertain locations of openjp2, openjpeg libraries. - - Returns - ------- - tpl : tuple - tuple of library handles - """ - lst = [] - for libname in ['openjp2', 'openjpeg']: - lst.append(load_openjpeg_library(libname)) - if all(handle is None for handle in lst): + libs = read_config_file() + libopenjp2_handle = load_openjp2(libs['openjp2']) + libopenjpeg_handle = load_openjpeg(libs['openjpeg']) + if libopenjp2_handle is None and libopenjpeg_handle is None: msg = "Neither the openjp2 nor the openjpeg library could be loaded. " - warnings.warn(msg) - return tuple(lst) + warnings.warn(msg, UserWarning) + return libopenjp2_handle, libopenjpeg_handle def get_configdir(): diff --git a/glymur/lib/openjp2.py b/glymur/lib/openjp2.py index aed4db6..5b5f3c4 100644 --- a/glymur/lib/openjp2.py +++ b/glymur/lib/openjp2.py @@ -2,16 +2,15 @@ Wraps individual functions in openjp2 library. """ +# pylint: disable=C0302,R0903,W0201 + import ctypes import re import sys -import textwrap from .config import glymur_config - OPENJP2, OPENJPEG = glymur_config() - def version(): """Wrapper for opj_version library routine.""" try: @@ -49,6 +48,13 @@ JPWL_MAX_NO_TILESPECS = 16 TRUE = 1 FALSE = 0 +#PROFILE = {'none': 0, # No profile +# 0: 1, # Profile 0 +# 1: 2, # Profile 1 +# 'part2': 0x8000, # At least one extension +# 'Cinema2K': 0x0003, # 2K cinema profile +# 'Cinema4K': 0x0004, # 4K cinema profile + # supported color spaces CLRSPC_UNKNOWN = -1 CLRSPC_UNSPECIFIED = 0 @@ -131,13 +137,6 @@ class PocType(ctypes.Structure): ("tx0_t", ctypes.c_uint32), ("ty0_t", ctypes.c_uint32)] - def __str__(self): - msg = "{0}:\n".format(self.__class__) - for field_name, _ in self._fields_: - msg += " {0}: {1}\n".format( - field_name, getattr(self, field_name)) - return msg - class DecompressionParametersType(ctypes.Structure): """Decompression parameters. @@ -201,13 +200,6 @@ class DecompressionParametersType(ctypes.Structure): # maximum number of tiles ("flags", ctypes.c_uint32)] - def __str__(self): - msg = "{0}:\n".format(self.__class__) - for field_name, _ in self._fields_: - msg += " {0}: {1}\n".format( - field_name, getattr(self, field_name)) - return msg - class CompressionParametersType(ctypes.Structure): """Compression parameters. @@ -399,46 +391,6 @@ class CompressionParametersType(ctypes.Structure): # values. _fields_.append(("rsiz", ctypes.c_uint16)) - def __str__(self): - msg = "{0}:\n".format(self.__class__) - for field_name, _ in self._fields_: - - if field_name == 'poc': - msg += " numpocs: {0}\n".format(self.numpocs) - for j in range(self.numpocs): - msg += " [#{0}]:".format(j) - msg += " {0}".format(str(self.poc[j])) - - elif field_name in ['tcp_rates', 'tcp_distoratio']: - lst = [] - arr = getattr(self, field_name) - lst = [arr[j] for j in range(self.tcp_numlayers)] - msg += " {0}: {1}\n".format(field_name, lst) - - elif field_name in ['prcw_init', 'prch_init']: - pass - - elif field_name == 'res_spec': - prcw_init = [self.prcw_init[j] for j in range(self.res_spec)] - prch_init = [self.prch_init[j] for j in range(self.res_spec)] - msg += " res_spec: {0}\n".format(self.res_spec) - msg += " prch_init: {0}\n".format(prch_init) - msg += " prcw_init: {0}\n".format(prcw_init) - - elif field_name in [ - 'jpwl_hprot_tph_tileno', 'jpwl_hprot_tph', - 'jpwl_pprot_tileno', 'jpwl_pprot_packno', 'jpwl_pprot', - 'jpwl_sens_tph_tileno', 'jpwl_sens_tph']: - arr = getattr(self, field_name) - lst = [arr[j] for j in range(JPWL_MAX_NO_TILESPECS)] - msg += " {0}: {1}\n".format(field_name, lst) - - else: - msg += " {0}: {1}\n".format( - field_name, getattr(self, field_name)) - return msg - - class ImageCompType(ctypes.Structure): """Defines a single image component. @@ -480,14 +432,6 @@ class ImageCompType(ctypes.Structure): if _MINOR == '1': _fields_.append(("alpha", ctypes.c_uint16)) - def __str__(self): - msg = "{0}:\n".format(self.__class__) - for field_name, _ in self._fields_: - msg += " {0}: {1}\n".format( - field_name, getattr(self, field_name)) - return msg - - class ImageType(ctypes.Structure): """Defines image data and characteristics. @@ -518,26 +462,6 @@ class ImageType(ctypes.Structure): # restricted ICC profile buffer length ("icc_profile_len", ctypes.c_uint32)] - def __str__(self): - msg = "{0}:\n".format(self.__class__) - for field_name, _ in self._fields_: - - if field_name == "numcomps": - msg += " numcomps: {0}\n".format(self.numcomps) - for j in range(self.numcomps): - msg += " comps[#{0}]:\n".format(j) - msg += textwrap.indent(str(self.comps[j]), ' ' * 12) - - elif field_name == "comps": - # handled above - pass - - else: - msg += " {0}: {1}\n".format( - field_name, getattr(self, field_name)) - - return msg - class ImageComptParmType(ctypes.Structure): """Component parameters structure used by image_create function. @@ -567,12 +491,106 @@ class ImageComptParmType(ctypes.Structure): # signed (1) / unsigned (0) ("sgnd", ctypes.c_uint32)] - def __str__(self): - msg = "{0}:\n".format(self.__class__) - for field_name, _ in self._fields_: - msg += " {0}: {1}\n".format( - field_name, getattr(self, field_name)) - return msg + +class TccpInfo(ctypes.Structure): + """Tile-component coding parameters information. + + Corresponds to tccp_info_t type in openjp2 header file. + """ + _fields_ = [ + # component index + ("compno", ctypes.c_uint32), + + # coding style + ("csty", ctypes.c_uint32), + + # number of resolutions + ("numresolutions", ctypes.c_uint32), + + # code-blocks width + ("cblkw", ctypes.c_uint32), + + # code-blocks height + ("cblkh", ctypes.c_uint32), + + # code-block coding style + ("cblksty", ctypes.c_uint32), + + # discrete wavelet transform identifier + ("qmfbid", ctypes.c_uint32), + + # quantization style + ("qntsty", ctypes.c_uint32), + + # stepsizes used for quantization + ("stepsizes_mant", ctypes.c_uint32 * J2K_MAXBANDS), + ("stepsizes_expn", ctypes.c_uint32 * J2K_MAXBANDS), + + # stepsizes used for quantization + ("numgbits", ctypes.c_uint32), + + # region of interest shift + ("roishift", ctypes.c_int32), + + # precinct width + ("prcw", ctypes.c_uint32 * J2K_MAXRLVLS), + + # precinct width + ("prch", ctypes.c_uint32 * J2K_MAXRLVLS)] + + +class TileInfoV2(ctypes.Structure): + """Tile coding parameters information + + Corresponds to tile_info_v2_t type in openjp2 headers. + """ + _fields_ = [ + # number (index) of tile + ("tileno", ctypes.c_int32), + + # coding style + ("csty", ctypes.c_uint32), + + # progression order + ("prg", PROG_ORDER_TYPE), + + # number of layers + ("numlayers", ctypes.c_uint32), + + # multi-component transform identifier + ("mct", ctypes.c_uint32), + + # information concerning tile component parameters + ("tccp_info", ctypes.POINTER(TccpInfo))] + + +class CodestreamInfoV2(ctypes.Structure): + """information about the codestream. + + Corresponds to codestream_info_v2_t type in openjp2 header files. + """ + _fields_ = [ + # tile info + # tile origin in x, y (XTOsiz, YTOsiz) + ("tx0", ctypes.c_uint32), + ("ty0", ctypes.c_uint32), + + # tile size in x, y = XTsiz, YTsiz + ("tdx", ctypes.c_uint32), + ("tdy", ctypes.c_uint32), + + # number of tiles in X, Y + ("tw", ctypes.c_uint32), + ("th", ctypes.c_uint32), + + # number of components + ("nbcomps", ctypes.c_uint32), + + # default information regarding tiles inside of image + ("m_default_tile_info", TileInfoV2), + + # information regarding tiles inside of image + ("tile_info", ctypes.POINTER(TileInfoV2))] def check_error(status): @@ -737,6 +755,28 @@ def encode(codec, stream): OPENJP2.opj_encode(codec, stream) +def get_cstr_info(codec): + """get the codestream information from the codec + + Wraps the openjp2 library function opj_get_cstr_info. + + Parameters + ---------- + codec : CODEC_TYPE + The jpeg2000 codec. + + Returns + ------- + cstr_info_p : CodestreamInfoV2 + Reference to codestream information. + """ + OPENJP2.opj_get_cstr_info.argtypes = [CODEC_TYPE] + OPENJP2.opj_get_cstr_info.restype = ctypes.POINTER(CodestreamInfoV2) + + cstr_info_p = OPENJP2.opj_get_cstr_info(codec) + return cstr_info_p + + def get_decoded_tile(codec, stream, imagep, tile_index): """get the decoded tile from the codec @@ -767,6 +807,23 @@ def get_decoded_tile(codec, stream, imagep, tile_index): OPENJP2.opj_get_decoded_tile(codec, stream, imagep, tile_index) +def destroy_cstr_info(cstr_info_p): + """destroy codestream information after compression or decompression + + Wraps the openjp2 library function opj_destroy_cstr_info. + + Parameters + ---------- + cstr_info_p : CodestreamInfoV2 pointer + Pointer to codestream info structure. + """ + ARGTYPES = [ctypes.POINTER(ctypes.POINTER(CodestreamInfoV2))] + OPENJP2.opj_destroy_cstr_info.argtypes = ARGTYPES + OPENJP2.opj_destroy_cstr_info.restype = ctypes.c_void_p + + OPENJP2.opj_destroy_cstr_info(ctypes.byref(cstr_info_p)) + + def end_compress(codec, stream): """End of compressing the current image. @@ -909,7 +966,7 @@ def read_header(stream, codec): ARGTYPES = [STREAM_TYPE_P, CODEC_TYPE, ctypes.POINTER(ctypes.POINTER(ImageType))] OPENJP2.opj_read_header.argtypes = ARGTYPES - OPENJP2.opj_read_header.restype = check_error + OPENJP2.opj_read_header.restype = check_error imagep = ctypes.POINTER(ImageType)() OPENJP2.opj_read_header(stream, codec, ctypes.byref(imagep)) @@ -1268,7 +1325,6 @@ def _stream_create_default_file_stream_2p0(fptr, isa_read_stream): stream = OPENJP2.opj_stream_create_default_file_stream(fptr, read_stream) return stream - def _stream_create_default_file_stream_2p1(fname, isa_read_stream): """Wraps openjp2 library function opj_stream_create_default_vile_stream. @@ -1295,7 +1351,7 @@ def _stream_create_default_file_stream_2p1(fname, isa_read_stream): stream = OPENJP2.opj_stream_create_default_file_stream(file_argument, read_stream) return stream - + if re.match(r'''2.0''', version()): stream_create_default_file_stream = _stream_create_default_file_stream_2p0 else: diff --git a/glymur/lib/openjpeg.py b/glymur/lib/openjpeg.py index d2f156b..418e9df 100644 --- a/glymur/lib/openjpeg.py +++ b/glymur/lib/openjpeg.py @@ -1,11 +1,14 @@ """Wraps library calls to openjpeg. """ +# pylint: disable=R0903 + import ctypes import sys -from .config import glymur_config +import numpy as np +from .config import glymur_config _, OPENJPEG = glymur_config() # Maximum number of tile parts expected by JPWL: increase at your will @@ -55,10 +58,8 @@ class CommonStructType(ctypes.Structure): ("mj2_handle", ctypes.c_void_p)] -STREAM_READ = 0x0001 # The stream was opened for reading. -STREAM_WRITE = 0x0002 # The stream was opened for writing. - - +STREAM_READ = 0x0001 # The stream was opened for reading. +STREAM_WRITE = 0x0002 # The stream was opened for writing. class CioType(ctypes.Structure): """Byte input-output stream (CIO) @@ -89,57 +90,70 @@ class CompressionInfoType(CommonStructType): class PocType(ctypes.Structure): """Progression order changes.""" _fields_ = [("resno", ctypes.c_int), - # Resolution num start, Component num start, given by POC - ("compno0", ctypes.c_int), + # Resolution num start, Component num start, given by POC + ("compno0", ctypes.c_int), - # Layer num end,Resolution num end, Component num end, given - # by POC - ("layno1", ctypes.c_int), - ("resno1", ctypes.c_int), - ("compno1", ctypes.c_int), + # Layer num end,Resolution num end, Component num end, given by POC + ("layno1", ctypes.c_int), + ("resno1", ctypes.c_int), + ("compno1", ctypes.c_int), - # Layer num start,Precinct num start, Precinct num end - ("layno0", ctypes.c_int), - ("precno0", ctypes.c_int), - ("precno1", ctypes.c_int), + # Layer num start,Precinct num start, Precinct num end + ("layno0", ctypes.c_int), + ("precno0", ctypes.c_int), + ("precno1", ctypes.c_int), - # Progression order enum - # OPJ_PROG_ORDER prg1,prg; - ("prg1", ctypes.c_int), - ("prg", ctypes.c_int), + # Progression order enum + # OPJ_PROG_ORDER prg1,prg; + ("prg1", ctypes.c_int), + ("prg", ctypes.c_int), - # Progression order string - # char progorder[5]; - ("progorder", ctypes.c_char * 5), + # Progression order string + # char progorder[5]; + ("progorder", ctypes.c_char * 5), - # Tile number - # int tile; - ("tile", ctypes.c_int), + # Tile number + # int tile; + ("tile", ctypes.c_int), - ("tx0", ctypes.c_int), - ("tx1", ctypes.c_int), - ("ty0", ctypes.c_int), - ("ty1", ctypes.c_int), - ("layS", ctypes.c_int), - ("resS", ctypes.c_int), - ("compS", ctypes.c_int), - ("prcS", ctypes.c_int), - ("layE", ctypes.c_int), - ("resE", ctypes.c_int), - ("compE", ctypes.c_int), - ("prcE", ctypes.c_int), - ("txS", ctypes.c_int), - ("txE", ctypes.c_int), - ("tyS", ctypes.c_int), - ("tyE", ctypes.c_int), - ("dx", ctypes.c_int), - ("dy", ctypes.c_int), - ("lay_t", ctypes.c_int), - ("res_t", ctypes.c_int), - ("comp_t", ctypes.c_int), - ("prc_t", ctypes.c_int), - ("tx0_t", ctypes.c_int), - ("ty0_t", ctypes.c_int)] + # /** Start and end values for Tile width and height*/ + # int tx0,tx1,ty0,ty1; + ("tx0", ctypes.c_int), + ("tx1", ctypes.c_int), + ("ty0", ctypes.c_int), + ("ty1", ctypes.c_int), + + # /** Start value, initialised in pi_initialise_encode*/ + # int layS, resS, compS, prcS; + ("layS", ctypes.c_int), + ("resS", ctypes.c_int), + ("compS", ctypes.c_int), + ("prcS", ctypes.c_int), + + # /** End value, initialised in pi_initialise_encode */ + # int layE, resE, compE, prcE; + ("layE", ctypes.c_int), + ("resE", ctypes.c_int), + ("compE", ctypes.c_int), + ("prcE", ctypes.c_int), + + # Start and end values of Tile width and height, initialised in + # pi_initialise_encode int txS,txE,tyS,tyE,dx,dy; + ("txS", ctypes.c_int), + ("txE", ctypes.c_int), + ("tyS", ctypes.c_int), + ("tyE", ctypes.c_int), + ("dx", ctypes.c_int), + ("dy", ctypes.c_int), + + # Temporary values for Tile parts, initialised in pi_create_encode + # int lay_t, res_t, comp_t, prc_t,tx0_t,ty0_t; + ("lay_t", ctypes.c_int), + ("res_t", ctypes.c_int), + ("comp_t", ctypes.c_int), + ("prc_t", ctypes.c_int), + ("tx0_t", ctypes.c_int), + ("ty0_t", ctypes.c_int)] class CompressionParametersType(ctypes.Structure): @@ -360,47 +374,48 @@ class DecompressionParametersType(ctypes.Structure): class ImageComptParmType(ctypes.Structure): """Component parameters structure used by the opj_image_create function. """ - _fields_ = [("dx", ctypes.c_int), - # XRsiz: horizontal separation of a sample of ith component - # with respect to the reference grid + _fields_ = [ + # XRsiz: horizontal separation of a sample of ith component with + # respect to the reference grid + ("dx", ctypes.c_int), - # YRsiz: vertical separation of a sample of ith component with - # respect to the reference grid */ - ("dy", ctypes.c_int), + # YRsiz: vertical separation of a sample of ith component with + # respect to the reference grid */ + ("dy", ctypes.c_int), - # data width, height - ("w", ctypes.c_int), - ("h", ctypes.c_int), + # data width, height + ("w", ctypes.c_int), + ("h", ctypes.c_int), - # x component offset compared to the whole image - # y component offset compared to the whole image - ("x0", ctypes.c_int), - ("y0", ctypes.c_int), + # x component offset compared to the whole image + # y component offset compared to the whole image + ("x0", ctypes.c_int), + ("y0", ctypes.c_int), - # precision - ('prec', ctypes.c_int), + # precision + ('prec', ctypes.c_int), - # image depth in bits - ('bpp', ctypes.c_int), + # image depth in bits + ('bpp', ctypes.c_int), - # signed (1) / unsigned (0) - ('sgnd', ctypes.c_int)] + # signed (1) / unsigned (0) + ('sgnd', ctypes.c_int)] class ImageCompType(ctypes.Structure): """Defines a single image component. """ _fields_ = [("dx", ctypes.c_int), - ("dy", ctypes.c_int), - ("w", ctypes.c_int), - ("h", ctypes.c_int), - ("x0", ctypes.c_int), - ("y0", ctypes.c_int), - ("prec", ctypes.c_int), - ("bpp", ctypes.c_int), - ("sgnd", ctypes.c_int), - ("resno_decoded", ctypes.c_int), - ("factor", ctypes.c_int), - ("data", ctypes.POINTER(ctypes.c_int))] + ("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): @@ -452,7 +467,6 @@ def cio_tell(cio): pos = OPENJPEG.cio_tell(cio) return pos - def create_compress(fmt): """Wrapper for openjpeg library function opj_create_compress. @@ -523,11 +537,56 @@ def destroy_decompress(dinfo): OPENJPEG.opj_destroy_decompress(dinfo) +def image_cmptparm_t_from_np(np_image): + """Return appropriate image_cmptparm_t based on given numpy array. + """ + try: + num_comps = np_image.shape[2] + except IndexError: + num_comps = 1 + + cmpt_parm_array_t = ImageCmptparmType * num_comps + tarr = cmpt_parm_array_t() + + if np_image.dtype == np.uint8: + prec = 8 + bpp = 8 + sgnd = 0 + elif np_image.dtype == np.int8: + prec = 8 + bpp = 8 + sgnd = 1 + elif np_image.dtype == np.uint16: + prec = 16 + bpp = 16 + sgnd = 0 + elif np_image.dtype == np.int16: + prec = 16 + bpp = 16 + sgnd = 1 + else: + raise(TypeError("unhandled")) + + for j in range(0, num_comps): + tarr[j].dx = 1 + tarr[j].dy = 1 + tarr[j].w = np_image.shape[1] + tarr[j].h = np_image.shape[0] + tarr[j].x0 = 0 + tarr[j].y0 = 0 + tarr[j].prec = prec + tarr[j].bpp = bpp + tarr[j].sgnd = sgnd + + return(tarr) + + def image_create(cmptparms, cspace): """Wrapper for openjpeg library function opj_image_create. """ - lst = [ctypes.c_int, ctypes.POINTER(ImageComptParmType), ctypes.c_int] - OPENJPEG.opj_image_create.argtypes = lst + OPENJPEG.opj_image_create.argtypes = [ctypes.c_int, + ctypes.POINTER(ImageComptParmType), + ctypes.c_int] OPENJPEG.opj_image_create.restype = ctypes.POINTER(ImageType) image = OPENJPEG.opj_image_create(len(cmptparms), cmptparms, cspace) diff --git a/glymur/lib/test/fixtures.py b/glymur/lib/test/fixtures.py deleted file mode 100644 index b5b9648..0000000 --- a/glymur/lib/test/fixtures.py +++ /dev/null @@ -1,144 +0,0 @@ -decompression_parameters_type = """: - cp_reduce: 0 - cp_layer: 0 - infile: b'' - outfile: b'' - decod_format: -1 - cod_format: -1 - DA_x0: 0 - DA_x1: 0 - DA_y0: 0 - DA_y1: 0 - m_verbose: 0 - tile_index: 0 - nb_tile_to_decode: 0 - jpwl_correct: 0 - jpwl_exp_comps: 0 - jpwl_max_tiles: 0 - flags: 0""" - -default_progression_order_changes_type = """: - resno0: 0 - compno0: 0 - layno1: 0 - resno1: 0 - compno1: 0 - layno0: 0 - precno0: 0 - precno1: 0 - prg1: 0 - prg: 0 - progorder: b'' - tile: 0 - tx0: 0 - tx1: 0 - ty0: 0 - ty1: 0 - layS: 0 - resS: 0 - compS: 0 - prcS: 0 - layE: 0 - resE: 0 - compE: 0 - prcE: 0 - txS: 0 - txE: 0 - tyS: 0 - tyE: 0 - dx: 0 - dy: 0 - lay_t: 0 - res_t: 0 - comp_t: 0 - prec_t: 0 - tx0_t: 0 - ty0_t: 0""" - -default_compression_parameters_type = """: - tile_size_on: 0 - cp_tx0: 0 - cp_ty0: 0 - cp_tdx: 0 - cp_tdy: 0 - cp_disto_alloc: 0 - cp_fixed_alloc: 0 - cp_fixed_quality: 0 - cp_matrice: None - cp_comment: None - csty: 0 - prog_order: 0 - numpocs: 0 - numpocs: 0 - tcp_numlayers: 0 - tcp_rates: [] - tcp_distoratio: [] - numresolution: 6 - cblockw_init: 64 - cblockh_init: 64 - mode: 0 - irreversible: 0 - roi_compno: -1 - roi_shift: 0 - res_spec: 0 - prch_init: [] - prcw_init: [] - infile: b'' - outfile: b'' - index_on: 0 - index: b'' - image_offset_x0: 0 - image_offset_y0: 0 - subsampling_dx: 1 - subsampling_dy: 1 - decod_format: -1 - cod_format: -1 - jpwl_epc_on: 0 - jpwl_hprot_mh: 0 - jpwl_hprot_tph_tileno: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - jpwl_hprot_tph: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - jpwl_pprot_tileno: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - jpwl_pprot_packno: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - jpwl_pprot: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - jpwl_sens_size: 0 - jpwl_sens_addr: 0 - jpwl_sens_range: 0 - jpwl_sens_mh: 0 - jpwl_sens_tph_tileno: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - jpwl_sens_tph: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - cp_cinema: 0 - max_comp_size: 0 - cp_rsiz: 0 - tp_on: 0 - tp_flag: 0 - tcp_mct: 0 - jpip_on: 0 - mct_data: None - max_cs_size: 0 - rsiz: 0""" - -default_image_component_parameters = """: - dx: 0 - dy: 0 - w: 0 - h: 0 - x0: 0 - y0: 0 - prec: 0 - bpp: 0 - sgnd: 0""" - -# The "icc_profile_buf" field is problematic as it is a pointer value, i.e. -# -# icc_profile_buf: -# -# Have to treat it as a regular expression. -default_image_type = """: - x0: 0 - y0: 0 - x1: 0 - y1: 0 - numcomps: 0 - color_space: 0 - icc_profile_buf: - icc_profile_len: 0""" diff --git a/glymur/lib/test/test_openjp2.py b/glymur/lib/test/test_openjp2.py index c32ee89..70ef9b6 100644 --- a/glymur/lib/test/test_openjp2.py +++ b/glymur/lib/test/test_openjp2.py @@ -1,8 +1,13 @@ """ Tests for libopenjp2 wrapping functions. """ +# R0904: Seems like pylint is fooled in this situation +# W0142: using kwargs is ok in this context +# pylint: disable=R0904,W0142 + import os import re +import sys import tempfile import unittest @@ -16,8 +21,8 @@ from glymur.lib import openjp2 @unittest.skipIf(openjp2.OPENJP2 is None, "Missing openjp2 library.") @unittest.skipIf(re.match(r'''(1|2.0)''', - glymur.version.openjpeg_version) is not None, - "Not to be run until 2.1.0") + glymur.version.openjpeg_version) is not None, + "Not to be run until 2.1.0") class TestOpenJP2(unittest.TestCase): """Test openjp2 library functionality. @@ -51,6 +56,53 @@ class TestOpenJP2(unittest.TestCase): self.assertEqual(dparams.DA_x1, 0) self.assertEqual(dparams.DA_y1, 0) + def tile_macro(self, codec, stream, imagep, tidx): + """called only by j2k_random_tile_access""" + 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): + """fixture called by the test_rtaX methods""" + dparam = openjp2.set_default_decoder_parameters() + + infile = filename.encode() + nelts = openjp2.PATH_LEN - len(infile) + infile += b'0' * nelts + dparam.infile = infile + + dparam.decod_format = codec_format + + codec = openjp2.create_decompress(codec_format) + + openjp2.set_info_handler(codec, None) + openjp2.set_warning_handler(codec, None) + openjp2.set_error_handler(codec, None) + + stream = openjp2.stream_create_default_file_stream(filename, True) + + openjp2.setup_decoder(codec, dparam) + image = openjp2.read_header(stream, codec) + + cstr_info = openjp2.get_cstr_info(codec) + + tile_ul = 0 + tile_ur = cstr_info.contents.tw - 1 + tile_lr = cstr_info.contents.tw * cstr_info.contents.th - 1 + tile_ll = tile_lr - cstr_info.contents.tw + + self.tile_macro(codec, stream, image, tile_ul) + self.tile_macro(codec, stream, image, tile_ur) + self.tile_macro(codec, stream, image, tile_lr) + self.tile_macro(codec, stream, image, tile_ll) + + openjp2.destroy_cstr_info(cstr_info) + + openjp2.end_decompress(codec, stream) + openjp2.destroy_codec(codec) + openjp2.stream_destroy(stream) + openjp2.image_destroy(image) + def test_tte0(self): """Runs test designated tte0 in OpenJPEG test suite.""" with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile: @@ -108,6 +160,15 @@ class TestOpenJP2(unittest.TestCase): tile_decoder(**kwargs) self.assertTrue(True) + def test_rta1(self): + """Runs test designated rta1 in OpenJPEG test suite.""" + with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile: + self.xtx1_setup(tfile.name) + + codec_format = openjp2.CODEC_J2K + self.j2k_random_tile_access(tfile.name, codec_format) + self.assertTrue(True) + def test_tte2(self): """Runs test designated tte2 in OpenJPEG test suite.""" with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile: @@ -129,25 +190,62 @@ class TestOpenJP2(unittest.TestCase): tile_decoder(**kwargs) self.assertTrue(True) + def test_rta2(self): + """Runs test designated rta2 in OpenJPEG test suite.""" + with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile: + xtx2_setup(tfile.name) + + codec_format = openjp2.CODEC_JP2 + self.j2k_random_tile_access(tfile.name, codec_format) + def test_tte3(self): """Runs test designated tte3 in OpenJPEG test suite.""" with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile: xtx3_setup(tfile.name) self.assertTrue(True) + def test_rta3(self): + """Runs test designated rta3 in OpenJPEG test suite.""" + with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile: + xtx3_setup(tfile.name) + + codec_format = openjp2.CODEC_J2K + self.j2k_random_tile_access(tfile.name, codec_format) + self.assertTrue(True) + def test_tte4(self): """Runs test designated tte4 in OpenJPEG test suite.""" with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile: xtx4_setup(tfile.name) self.assertTrue(True) + def test_rta4(self): + """Runs test designated rta4 in OpenJPEG test suite.""" + with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile: + xtx4_setup(tfile.name) + + codec_format = openjp2.CODEC_J2K + self.j2k_random_tile_access(tfile.name, codec_format) + def test_tte5(self): """Runs test designated tte5 in OpenJPEG test suite.""" with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile: xtx5_setup(tfile.name) self.assertTrue(True) + def test_rta5(self): + """Runs test designated rta5 in OpenJPEG test suite.""" + with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile: + xtx5_setup(tfile.name) + codec_format = openjp2.CODEC_J2K + self.j2k_random_tile_access(tfile.name, codec_format) + + +#def tile_encoder(num_comps=None, tile_width=None, tile_height=None, +# filename=None, codec=None, comp_prec=None, +# image_width=None, image_height=None, +# irreversible=None): def tile_encoder(**kwargs): """Fixture used by many tests.""" num_tiles = ((kwargs['image_width'] / kwargs['tile_width']) * @@ -211,7 +309,7 @@ def tile_encoder(**kwargs): openjp2.setup_encoder(codec, l_param, l_image) stream = openjp2.stream_create_default_file_stream(kwargs['filename'], - False) + False) openjp2.start_compress(codec, l_image, stream) for j in np.arange(num_tiles): @@ -222,14 +320,13 @@ def tile_encoder(**kwargs): openjp2.destroy_codec(codec) openjp2.image_destroy(l_image) - def tile_decoder(**kwargs): """Fixture called with various configurations by many tests. Reads a tile. That's all it does. """ stream = openjp2.stream_create_default_file_stream(kwargs['filename'], - True) + True) dparam = openjp2.set_default_decoder_parameters() dparam.decod_format = kwargs['codec_format'] @@ -267,7 +364,6 @@ def tile_decoder(**kwargs): openjp2.stream_destroy(stream) openjp2.image_destroy(image) - def ttx0_setup(filename): """Runs tests tte0, tte0.""" kwargs = {'filename': filename, @@ -281,7 +377,6 @@ def ttx0_setup(filename): 'tile_width': 100} tile_encoder(**kwargs) - def xtx2_setup(filename): """Runs tests rta2, tte2, ttd2.""" kwargs = {'filename': filename, @@ -295,7 +390,6 @@ def xtx2_setup(filename): 'tile_width': 128} tile_encoder(**kwargs) - def xtx3_setup(filename): """Runs tests tte3, rta3.""" kwargs = {'filename': filename, @@ -309,7 +403,6 @@ def xtx3_setup(filename): 'tile_width': 128} tile_encoder(**kwargs) - def xtx4_setup(filename): """Runs tests rta4, tte4.""" kwargs = {'filename': filename, @@ -323,7 +416,6 @@ def xtx4_setup(filename): 'tile_width': 128} tile_encoder(**kwargs) - def xtx5_setup(filename): """Runs tests rta5, tte5.""" kwargs = {'filename': filename, @@ -336,3 +428,6 @@ def xtx5_setup(filename): 'tile_height': 256, 'tile_width': 256} tile_encoder(**kwargs) + +if __name__ == "__main__": + unittest.main() diff --git a/glymur/lib/test/test_openjpeg.py b/glymur/lib/test/test_openjpeg.py index 449083f..f28656c 100644 --- a/glymur/lib/test/test_openjpeg.py +++ b/glymur/lib/test/test_openjpeg.py @@ -1,6 +1,8 @@ """ Tests for OpenJPEG module. """ +# pylint: disable=E1101,R0904 + import ctypes import re import sys @@ -8,7 +10,6 @@ import unittest import glymur - @unittest.skipIf(glymur.lib.openjpeg.OPENJPEG is None, "Missing openjpeg library.") class TestOpenJPEG(unittest.TestCase): diff --git a/glymur/lib/test/test_printing.py b/glymur/lib/test/test_printing.py deleted file mode 100644 index c7be21c..0000000 --- a/glymur/lib/test/test_printing.py +++ /dev/null @@ -1,75 +0,0 @@ -# -*- coding: utf-8 -*- -"""Test suite for printing. -""" - -import re -import sys -import unittest - -if sys.hexversion < 0x03000000: - from mock import patch - from StringIO import StringIO -else: - from unittest.mock import patch - from io import StringIO - -import glymur -from . import fixtures - - -@unittest.skipIf(sys.hexversion < 0x03000000, "do not care about 2.7 here") -@unittest.skipIf(re.match('0|1|2.0', glymur.version.openjpeg_version), - "Requires openjpeg 2.1.0 or higher") -class TestPrintingOpenjp2(unittest.TestCase): - """Tests for verifying how printing works on openjp2 library structures.""" - def setUp(self): - self.jp2file = glymur.data.nemo() - - def tearDown(self): - pass - - def test_decompression_parameters(self): - """printing DecompressionParametersType""" - dparams = glymur.lib.openjp2.set_default_decoder_parameters() - with patch('sys.stdout', new=StringIO()) as fake_out: - print(dparams) - actual = fake_out.getvalue().strip() - expected = fixtures.decompression_parameters_type - self.assertEqual(actual, expected) - - def test_progression_order_changes(self): - """printing PocType""" - ptype = glymur.lib.openjp2.PocType() - with patch('sys.stdout', new=StringIO()) as fake_out: - print(ptype) - actual = fake_out.getvalue().strip() - expected = fixtures.default_progression_order_changes_type - self.assertEqual(actual, expected) - - def test_default_compression_parameters(self): - """printing default compression parameters""" - cparams = glymur.lib.openjp2.set_default_encoder_parameters() - with patch('sys.stdout', new=StringIO()) as fake_out: - print(cparams) - actual = fake_out.getvalue().strip() - expected = fixtures.default_compression_parameters_type - self.assertEqual(actual, expected) - - def test_default_component_parameters(self): - """printing default image component parameters""" - icpt = glymur.lib.openjp2.ImageComptParmType() - with patch('sys.stdout', new=StringIO()) as fake_out: - print(icpt) - actual = fake_out.getvalue().strip() - expected = fixtures.default_image_component_parameters - self.assertEqual(actual, expected) - - def test_default_image_type(self): - """printing default image type""" - it = glymur.lib.openjp2.ImageType() - with patch('sys.stdout', new=StringIO()) as fake_out: - print(it) - actual = fake_out.getvalue().strip() - - expected = fixtures.default_image_type - self.assertRegex(actual, expected) diff --git a/glymur/test/fixtures.py b/glymur/test/fixtures.py index 34327fc..417797f 100644 --- a/glymur/test/fixtures.py +++ b/glymur/test/fixtures.py @@ -14,12 +14,13 @@ import six import glymur # If openjpeg is not installed, many tests cannot be run. -if glymur.version.openjpeg_version == '0.0.0': +if glymur.version.openjpeg_version == "0.0.0": OPENJPEG_NOT_AVAILABLE = True - OPENJPEG_NOT_AVAILABLE_MSG = 'OpenJPEG library not installed' + OPENJPEG_NOT_AVAILABLE_MSG = "OpenJPEG library not installed" else: OPENJPEG_NOT_AVAILABLE = False OPENJPEG_NOT_AVAILABLE_MSG = None + # Some versions of "six" on python3 cause problems when verifying warnings. # Only use when the version is 1.7 or higher. @@ -34,10 +35,6 @@ elif re.match('1.[0-6]', six.__version__) is not None: msg = "Cannot run test with version {0} of python-six" WARNING_INFRASTRUCTURE_MSG = msg.format(six.__version__) -# Cannot reopen a named temporary file in windows. -WINDOWS_TMP_FILE_MSG = "cannot use NamedTemporaryFile like this in windows" - - class MetadataBase(unittest.TestCase): """ Base class for testing metadata. @@ -102,8 +99,8 @@ class MetadataBase(unittest.TestCase): """ verify the fields of a RGN segment """ - self.assertEqual(actual.crgn, expected.crgn) # 0 = component - self.assertEqual(actual.srgn, expected.srgn) # 0 = implicit + self.assertEqual(actual.crgn, expected.crgn) # 0 = component + self.assertEqual(actual.srgn, expected.srgn) # 0 = implicit self.assertEqual(actual.sprgn, expected.sprgn) def verifySOTsegment(self, actual, expected): @@ -126,9 +123,8 @@ class MetadataBase(unittest.TestCase): """ Verify the fields of the SIZ segment. """ - for field in ['rsiz', 'xsiz', 'ysiz', 'xosiz', 'yosiz', 'xtsiz', - 'ytsiz', 'xtosiz', 'ytosiz', 'bitdepth', - 'xrsiz', 'yrsiz']: + for field in ['rsiz', 'xsiz', 'ysiz', 'xosiz', 'yosiz', 'xtsiz', + 'ytsiz', 'xtosiz', 'ytosiz', 'bitdepth', 'xrsiz', 'yrsiz']: self.assertEqual(getattr(actual, field), getattr(expected, field)) def verifyImageHeaderBox(self, box1, box2): @@ -155,7 +151,7 @@ class MetadataBase(unittest.TestCase): else: self.assertEqual(actual.colorspace, expected.colorspace) self.assertIsNone(actual.icc_profile) - + # The Python XMP Toolkit may be used for XMP UUIDs, but only if available and # if the version is at least 2.0.0. @@ -185,7 +181,7 @@ except: # The Cinema2K/4K tests seem to need the freeimage backend to skimage.io # in order to work. Unfortunately, scikit-image/freeimage is about as wonky as # it gets. Anaconda can get totally weirded out on versions up through 3.6.4 -# on Python3 with scikit-image up through version 0.10.0. +# on Python3 with scikit-image up through version 0.10.0. NO_SKIMAGE_FREEIMAGE_SUPPORT = False try: import skimage @@ -213,7 +209,7 @@ def _indent(textstr): String to be indented. indent_level : str Number of spaces of indentation to add. - + Returns ------- indented_string : str @@ -236,6 +232,7 @@ try: # The whole point of trying to import PIL is to determine if it's there # or not. We won't use it directly. + # pylint: disable=F0401,W0611 import PIL NO_READ_BACKEND = False @@ -545,7 +542,7 @@ text_gbr_34 = """Colour Specification Box (colr) @ (179, 1339) dump = r'''JPEG 2000 Signature Box (jP ) @ (0, 12) Signature: 0d0a870a File Type Box (ftyp) @ (12, 20) - Brand: jp2 + Brand: jp2 Compatibility: ['jp2 '] JP2 Header Box (jp2h) @ (32, 45) Image Header Box (ihdr) @ (40, 22) @@ -601,6 +598,7 @@ Contiguous Codestream Box (jp2c) @ (3223, 1132296) "Created by OpenJPEG version 2.0.0"''' nemo_with_codestream_header = dump.format(_indent(nemo_xmp)) +#nemo_dump_full = dump.format(_indent(nemo_xmp)) nemo_dump_short = r"""JPEG 2000 Signature Box (jP ) @ (0, 12) File Type Box (ftyp) @ (12, 20) @@ -613,7 +611,7 @@ Contiguous Codestream Box (jp2c) @ (3223, 1132296)""" nemo_dump_no_xml = '''JPEG 2000 Signature Box (jP ) @ (0, 12) Signature: 0d0a870a File Type Box (ftyp) @ (12, 20) - Brand: jp2 + Brand: jp2 Compatibility: ['jp2 '] JP2 Header Box (jp2h) @ (32, 45) Image Header Box (ihdr) @ (40, 22) @@ -669,7 +667,7 @@ Contiguous Codestream Box (jp2c) @ (3223, 1132296) dump = r"""JPEG 2000 Signature Box (jP ) @ (0, 12) Signature: 0d0a870a File Type Box (ftyp) @ (12, 20) - Brand: jp2 + Brand: jp2 Compatibility: ['jp2 '] JP2 Header Box (jp2h) @ (32, 45) Image Header Box (ihdr) @ (40, 22) @@ -691,26 +689,6 @@ nemo_dump_no_codestream = dump.format(_indent(nemo_xmp)) nemo_dump_no_codestream_no_xml = r"""JPEG 2000 Signature Box (jP ) @ (0, 12) Signature: 0d0a870a -File Type Box (ftyp) @ (12, 20) - Brand: jp2 - Compatibility: ['jp2 '] -JP2 Header Box (jp2h) @ (32, 45) - Image Header Box (ihdr) @ (40, 22) - Size: [1456 2592 3] - Bitdepth: 8 - Signed: False - Compression: wavelet - Colorspace Unknown: False - Colour Specification Box (colr) @ (62, 15) - Method: enumerated colorspace - Precedence: 0 - Colorspace: sRGB -UUID Box (uuid) @ (77, 3146) - UUID: be7acfcb-97a9-42e8-9c71-999491e3afac (XMP) -Contiguous Codestream Box (jp2c) @ (3223, 1132296)""" - -nemo = """JPEG 2000 Signature Box (jP ) @ (0, 12) - Signature: 0d0a870a File Type Box (ftyp) @ (12, 20) Brand: jp2 Compatibility: ['jp2 '] @@ -727,171 +705,7 @@ JP2 Header Box (jp2h) @ (32, 45) Colorspace: sRGB UUID Box (uuid) @ (77, 3146) UUID: be7acfcb-97a9-42e8-9c71-999491e3afac (XMP) - UUID Data: - - - - - Google - 2013-02-09T14:47:53 - - - 1 - 72/1 - 72/1 - 2 - HTC - HTC Glacier - 2592 - 1456 - - - 8 - 8 - 8 - - - 2 - 3 - - - 1343036288/4294967295 - 1413044224/4294967295 - - - - - 2748779008/4294967295 - 1417339264/4294967295 - 1288490240/4294967295 - 2576980480/4294967295 - 644245120/4294967295 - 257698032/4294967295 - - - - - 1 - 2528 - 1424 - 353/100 - 0 - 0/1 - WGS-84 - 2013-02-09T14:47:53 - - - 76 - - - 0220 - 0100 - - - 1 - 2 - 3 - 0 - - - 42,20.56N - 71,5.29W - 2013-02-09T19:47:53Z - NETWORK - - - 2013-02-09T14:47:53 - - - - - Glymur - Python XMP Toolkit - - - - - - -Contiguous Codestream Box (jp2c) @ (3223, 1132296) - SOC marker segment @ (3231, 0) - SIZ marker segment @ (3233, 47) - Profile: no profile - Reference Grid Height, Width: (1456 x 2592) - Vertical, Horizontal Reference Grid Offset: (0 x 0) - Reference Tile Height, Width: (1456 x 2592) - Vertical, Horizontal Reference Tile Offset: (0 x 0) - Bitdepth: (8, 8, 8) - Signed: (False, False, False) - Vertical, Horizontal Subsampling: ((1, 1), (1, 1), (1, 1)) - COD marker segment @ (3282, 12) - Coding style: - Entropy coder, without partitions - SOP marker segments: False - EPH marker segments: False - Coding style parameters: - Progression order: LRCP - Number of layers: 2 - Multiple component transformation usage: reversible - Number of resolutions: 2 - Code block height, width: (64 x 64) - Wavelet transform: 5-3 reversible - Precinct size: default, 2^15 x 2^15 - Code block context: - Selective arithmetic coding bypass: False - Reset context probabilities on coding pass boundaries: False - Termination on each coding pass: False - Vertically stripe causal context: False - Predictable termination: False - Segmentation symbols: False - QCD marker segment @ (3296, 7) - Quantization style: no quantization, 2 guard bits - Step size: [(0, 8), (0, 9), (0, 9), (0, 10)] - CME marker segment @ (3305, 37) - "Created by OpenJPEG version 2.0.0" - SOT marker segment @ (3344, 10) - Tile part index: 0 - Tile part length: 1132173 - Tile part instance: 0 - Number of tile parts: 1 - COC marker segment @ (3356, 9) - Associated component: 1 - Coding style for this component: Entropy coder, PARTITION = 0 - Coding style parameters: - Number of resolutions: 2 - Code block height, width: (64 x 64) - Wavelet transform: 5-3 reversible - Code block context: - Selective arithmetic coding bypass: False - Reset context probabilities on coding pass boundaries: False - Termination on each coding pass: False - Vertically stripe causal context: False - Predictable termination: False - Segmentation symbols: False - QCC marker segment @ (3367, 8) - Associated Component: 1 - Quantization style: no quantization, 2 guard bits - Step size: [(0, 8), (0, 9), (0, 9), (0, 10)] - COC marker segment @ (3377, 9) - Associated component: 2 - Coding style for this component: Entropy coder, PARTITION = 0 - Coding style parameters: - Number of resolutions: 2 - Code block height, width: (64 x 64) - Wavelet transform: 5-3 reversible - Code block context: - Selective arithmetic coding bypass: False - Reset context probabilities on coding pass boundaries: False - Termination on each coding pass: False - Vertically stripe causal context: False - Predictable termination: False - Segmentation symbols: False - QCC marker segment @ (3388, 8) - Associated Component: 2 - Quantization style: no quantization, 2 guard bits - Step size: [(0, 8), (0, 9), (0, 9), (0, 10)] - SOD marker segment @ (3398, 0) - EOC marker segment @ (1135517, 0)""" +Contiguous Codestream Box (jp2c) @ (3223, 1132296)""" # Output of reader requirements printing for text_GBR.jp2 text_GBR_rreq = r"""Reader Requirements Box (rreq) @ (40, 109) @@ -927,7 +741,7 @@ issue_183_colr = """Colour Specification Box (colr) @ (62, 12) Method: restricted ICC profile Precedence: 0 ICC Profile: None""" - + # Progression order is invalid. issue_186_progression_order = """COD marker segment @ (174, 12) diff --git a/glymur/test/test_callbacks.py b/glymur/test/test_callbacks.py index cc30de8..e87b3af 100644 --- a/glymur/test/test_callbacks.py +++ b/glymur/test/test_callbacks.py @@ -1,6 +1,12 @@ """ Test suite for openjpeg's callback functions. """ +# R0904: Seems like pylint is fooled in this situation +# pylint: disable=R0904 + +# 'mock' most certainly is in unittest (Python 3.3) +# pylint: disable=E0611,F0401 + import os import re import sys @@ -19,7 +25,8 @@ import glymur from .fixtures import WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG - +@unittest.skipIf(glymur.lib.openjp2.OPENJP2 is None, + "Missing openjp2 library.") class TestCallbacks(unittest.TestCase): """Test suite for callbacks.""" @@ -30,60 +37,68 @@ class TestCallbacks(unittest.TestCase): def tearDown(self): pass - @unittest.skipIf(glymur.version.openjpeg_version[0] != '2', - "Missing openjp2 library.") @unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG) @unittest.skipIf(os.name == "nt", "Temporary file issue on window.") - def test_info_callback_on_write_backwards_compatibility(self): + def test_info_callback_on_write(self): """Verify messages printed when writing an image in verbose mode.""" j = glymur.Jp2k(self.jp2file) with self.assertWarns(UserWarning): tiledata = j.read(tile=0) with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: + j = glymur.Jp2k(tfile.name, 'wb') with patch('sys.stdout', new=StringIO()) as fake_out: - glymur.Jp2k(tfile.name, data=tiledata, verbose=True) - actual = fake_out.getvalue().strip() - expected = '[INFO] tile number 1 / 1' - self.assertEqual(actual, expected) - - @unittest.skipIf(glymur.version.openjpeg_version[0] != '2', - "Missing openjp2 library.") - @unittest.skipIf(os.name == "nt", "Temporary file issue on window.") - def test_info_callback_on_write(self): - """Verify messages printed when writing an image in verbose mode.""" - j = glymur.Jp2k(self.jp2file) - tiledata = j[:] - with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: - with patch('sys.stdout', new=StringIO()) as fake_out: - glymur.Jp2k(tfile.name, data=tiledata, verbose=True) + j.write(tiledata, verbose=True) actual = fake_out.getvalue().strip() expected = '[INFO] tile number 1 / 1' self.assertEqual(actual, expected) - @unittest.skipIf(glymur.version.openjpeg_version[0] == '0', - "Missing openjpeg/openjp2 library.") def test_info_callbacks_on_read(self): """stdio output when info callback handler is enabled""" # Verify that we get the expected stdio output when our internal info # callback handler is enabled. - jp2 = glymur.Jp2k(self.j2kfile) + j = glymur.Jp2k(self.j2kfile) with patch('sys.stdout', new=StringIO()) as fake_out: - jp2.verbose = True - jp2[::2, ::2] + j.read(rlevel=1, verbose=True, area=(0, 0, 200, 150)) actual = fake_out.getvalue().strip() - if glymur.version.openjpeg_version[0] == '2': - lines = ['[INFO] Start to read j2k main header (0).', - '[INFO] Main header has been correctly decoded.', - '[INFO] Setting decoding area to 0,0,480,800', - '[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.'] + lines = ['[INFO] Start to read j2k main header (0).', + '[INFO] Main header has been correctly decoded.', + '[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.openjpeg.OPENJPEG is None, + "Missing openjpeg library.") +class TestCallbacks15(unittest.TestCase): + """This test suite is for OpenJPEG 1.5.1 properties. + """ + + def setUp(self): + self.jp2file = glymur.data.nemo() + self.j2kfile = glymur.data.goodstuff() + + def tearDown(self): + pass + + def test_info_callbacks_on_read(self): + """Verify stdout when reading. + + Verify that we get the expected stdio output when our internal info + callback handler is enabled. + """ + with patch('glymur.lib.openjp2.OPENJP2', new=None): + # Force to use OPENJPEG instead of OPENJP2. + j = glymur.Jp2k(self.j2kfile) + with patch('sys.stdout', new=StringIO()) as fake_out: + j.read(rlevel=1, verbose=True) + actual = fake_out.getvalue().strip() - expected = '\n'.join(lines) - self.assertEqual(actual, expected) - else: regex = re.compile(r"""\[INFO\]\stile\s1\sof\s1\s+ \[INFO\]\s-\stiers-1\stook\s [0-9]+\.[0-9]+\ss\s+ @@ -93,7 +108,13 @@ class TestCallbacks(unittest.TestCase): [0-9]+\.[0-9]+\ss""", re.VERBOSE) + # assertRegex in Python 3.3 (python2.7/pylint issue) + # pylint: disable=E1101 if sys.hexversion <= 0x03020000: self.assertRegexpMatches(actual, regex) else: self.assertRegex(actual, regex) + + +if __name__ == "__main__": + unittest.main() diff --git a/glymur/test/test_codestream.py b/glymur/test/test_codestream.py new file mode 100644 index 0000000..e520713 --- /dev/null +++ b/glymur/test/test_codestream.py @@ -0,0 +1,149 @@ +""" +Test suite for codestream parsing. +""" + +# unittest doesn't work well with R0904. +# pylint: disable=R0904 + +import os +import struct +import sys +import tempfile +import unittest + +from glymur import Jp2k +import glymur + +from .fixtures import opj_data_file, OPJ_DATA_ROOT + +class TestCodestream(unittest.TestCase): + """Test suite for unusual codestream cases.""" + + def setUp(self): + self.jp2file = glymur.data.nemo() + + def tearDown(self): + pass + + def test_siz_segment_ssiz_unsigned(self): + """ssiz attribute to be removed in future release""" + j = Jp2k(self.jp2file) + codestream = j.get_codestream() + + # The ssiz attribute was simply a tuple of raw bytes. + # The first 7 bits are interpreted as the bitdepth, the MSB determines + # whether or not it is signed. + self.assertEqual(codestream.segment[1].ssiz, (7, 7, 7)) + + +@unittest.skipIf(OPJ_DATA_ROOT is None, + "OPJ_DATA_ROOT environment variable not set") +class TestCodestreamOpjData(unittest.TestCase): + """Test suite for unusual codestream cases. Uses OPJ_DATA_ROOT""" + + def setUp(self): + self.jp2file = glymur.data.nemo() + + def tearDown(self): + pass + + @unittest.skipIf(os.name == "nt", "Temporary file issue on window.") + def test_reserved_marker_segment(self): + """Reserved marker segments are ok.""" + + # Some marker segments were reserved in FCD15444-1. Since that + # standard is old, some of them may have come into use. + # + # Let's inject a reserved marker segment into a file that + # we know something about to make sure we can still parse it. + filename = os.path.join(OPJ_DATA_ROOT, 'input/conformance/p0_01.j2k') + with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: + with open(filename, 'rb') as ifile: + # Everything up until the first QCD marker. + read_buffer = ifile.read(45) + tfile.write(read_buffer) + + # Write the new marker segment, 0xff6f = 65391 + read_buffer = struct.pack('>HHB', int(65391), int(3), int(0)) + tfile.write(read_buffer) + + # Get the rest of the input file. + read_buffer = ifile.read() + tfile.write(read_buffer) + tfile.flush() + + codestream = Jp2k(tfile.name).get_codestream() + + self.assertEqual(codestream.segment[2].marker_id, '0xff6f') + self.assertEqual(codestream.segment[2].length, 3) + self.assertEqual(codestream.segment[2].data, b'\x00') + + def test_psot_is_zero(self): + """Psot=0 in SOT is perfectly legal. Issue #78.""" + filename = os.path.join(OPJ_DATA_ROOT, + 'input/nonregression/123.j2c') + j = Jp2k(filename) + codestream = j.get_codestream(header_only=False) + + # The codestream is valid, so we should be able to get the entire + # codestream, so the last one is EOC. + self.assertEqual(codestream.segment[-1].marker_id, 'EOC') + + + def test_siz_segment_ssiz_signed(self): + """ssiz attribute to be removed in future release""" + filename = os.path.join(OPJ_DATA_ROOT, 'input/conformance/p0_03.j2k') + j = Jp2k(filename) + codestream = j.get_codestream() + + # The ssiz attribute was simply a tuple of raw bytes. + # The first 7 bits are interpreted as the bitdepth, the MSB determines + # whether or not it is signed. + self.assertEqual(codestream.segment[1].ssiz, (131,)) + + +class TestCodestreamRepr(unittest.TestCase): + + def setUp(self): + self.jp2file = glymur.data.nemo() + + def tearDown(self): + pass + + def test_soc(self): + """Test SOC segment repr""" + segment = glymur.codestream.SOCsegment() + newseg = eval(repr(segment)) + self.assertEqual(newseg.marker_id, 'SOC') + + def test_siz(self): + """Test SIZ segment repr""" + kwargs = {'rsiz': 0, + 'xysiz': (2592, 1456), + 'xyosiz': (0, 0), + 'xytsiz': (2592, 1456), + 'xytosiz': (0, 0), + 'Csiz': 3, + 'bitdepth': (8, 8, 8), + 'signed': (False, False, False), + 'xyrsiz': ((1, 1, 1), (1, 1, 1))} + segment = glymur.codestream.SIZsegment(**kwargs) + newseg = eval(repr(segment)) + self.assertEqual(newseg.marker_id, 'SIZ') + self.assertEqual(newseg.xsiz, 2592) + self.assertEqual(newseg.ysiz, 1456) + self.assertEqual(newseg.xosiz, 0) + self.assertEqual(newseg.yosiz, 0) + self.assertEqual(newseg.xtsiz, 2592) + self.assertEqual(newseg.ytsiz, 1456) + self.assertEqual(newseg.xtosiz, 0) + self.assertEqual(newseg.ytosiz, 0) + + self.assertEqual(newseg.xrsiz, (1, 1, 1)) + self.assertEqual(newseg.yrsiz, (1, 1, 1)) + self.assertEqual(newseg.bitdepth, (8, 8, 8)) + self.assertEqual(newseg.signed, (False, False, False)) + + +if __name__ == "__main__": + unittest.main() diff --git a/glymur/test/test_config.py b/glymur/test/test_config.py index 59a8ef3..f908272 100644 --- a/glymur/test/test_config.py +++ b/glymur/test/test_config.py @@ -1,8 +1,16 @@ """These tests are for edge cases where OPENJPEG does not exist, but OPENJP2 may be present in some form or other. """ -import contextlib -import ctypes +# unittest doesn't work well with R0904. +# pylint: disable=R0904 + +# tempfile.TemporaryDirectory, unittest.assertWarns introduced in 3.2 +# pylint: disable=E1101 + +# unittest.mock only in Python 3.3 (python2.7/pylint import issue) +# pylint: disable=E0611,F0401 + + import imp import os import sys @@ -17,43 +25,7 @@ else: import glymur from glymur import Jp2k -from .fixtures import (WARNING_INFRASTRUCTURE_ISSUE, - WARNING_INFRASTRUCTURE_MSG, - WINDOWS_TMP_FILE_MSG) - - -def openjpeg_not_found_by_ctypes(): - """ - Need to know if openjpeg library can be picked right up by ctypes for one - of the tests. - """ - with patch.dict('os.environ', - {'DYLD_FALLBACK_LIBRARY_PATH': '/opt/local/lib'}): - if ctypes.util.find_library('openjpeg') is None: - return True - else: - return False - - -@contextlib.contextmanager -def chdir(dirname=None): - """ - This context manager restores the value of the current working directory - (cwd) after the enclosed code block completes or raises an exception. If a - directory name is supplied to the context manager then the cwd is changed - prior to running the code block. - - Shamelessly lifted from - http://www.astropython.org/snippet/2009/10/chdir-context-manager - """ - curdir = os.getcwd() - try: - if dirname is not None: - os.chdir(dirname) - yield - finally: - os.chdir(curdir) - +from .fixtures import WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG @unittest.skipIf(sys.hexversion < 0x03020000, "TemporaryDirectory introduced in 3.2.") @@ -89,6 +61,7 @@ class TestSuite(unittest.TestCase): # Need to reliably recover the location of the openjp2 library, # so using '_name' appears to be the only way to do it. + # pylint: disable=W0212 libloc = glymur.lib.openjp2.OPENJP2._name line = 'openjp2: {0}\n'.format(libloc) tfile.write(line) @@ -98,7 +71,7 @@ class TestSuite(unittest.TestCase): Jp2k(self.jp2file) @unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG) - @unittest.skipIf(os.name == "nt", WINDOWS_TMP_FILE_MSG) + @unittest.skipIf(os.name == "nt", 'named temporary file issue on windows') def test_xdg_env_config_file_is_bad(self): """A non-existant library location should be rejected.""" with tempfile.TemporaryDirectory() as tdir: @@ -117,56 +90,50 @@ class TestSuite(unittest.TestCase): with self.assertWarnsRegex(UserWarning, regex): imp.reload(glymur.lib.openjp2) - @unittest.skipIf(glymur.lib.openjp2.OPENJPEG is None, - "Needs openjp2 and openjpeg before this test make sense.") - @unittest.skipIf(os.name == "nt", WINDOWS_TMP_FILE_MSG) - def test_library_specified_as_None(self): - """Verify that we can stop library from being loaded by using None.""" - with tempfile.TemporaryDirectory() as tdir: - configdir = os.path.join(tdir, 'glymur') - os.mkdir(configdir) - fname = os.path.join(configdir, 'glymurrc') - with open(fname, 'w') as fptr: - # Essentially comment out openjp2 and preferentially load - # openjpeg instead. - fptr.write('[library]\n') - fptr.write('openjp2: None\n') - msg = 'openjpeg: {0}\n' - msg = msg.format(glymur.lib.openjp2.OPENJPEG._name) - fptr.write(msg) - fptr.flush() - with patch.dict('os.environ', {'XDG_CONFIG_HOME': tdir}): - imp.reload(glymur.lib.openjp2) - self.assertIsNone(glymur.lib.openjp2.OPENJP2) - self.assertIsNotNone(glymur.lib.openjp2.OPENJPEG) - @unittest.skipIf(glymur.lib.openjp2.OPENJPEG is None, - "Needs openjpeg before this test make sense.") - @unittest.skipIf(openjpeg_not_found_by_ctypes(), - "OpenJPEG must be found before this test can work.") - @unittest.skipIf(os.name == "nt", WINDOWS_TMP_FILE_MSG) - def test_config_dir_but_no_config_file(self): +@unittest.skipIf(glymur.lib.openjp2.OPENJP2 is None and + glymur.lib.openjpeg.OPENJPEG is None, + "Missing openjp2 library.") +class TestConfig(unittest.TestCase): + """Test suite for reading without proper library in place.""" - with tempfile.TemporaryDirectory() as tdir: - configdir = os.path.join(tdir, 'glymur') - os.mkdir(configdir) - with patch.dict('os.environ', {'XDG_CONFIG_HOME': tdir}): - # Should still be able to load openjpeg, despite the - # configuration file not being there - imp.reload(glymur.lib.openjpeg) - self.assertIsNotNone(glymur.lib.openjp2.OPENJPEG) + def setUp(self): + self.jp2file = glymur.data.nemo() + self.j2kfile = glymur.data.goodstuff() - @unittest.skipIf(os.name == "nt", WINDOWS_TMP_FILE_MSG) - def test_config_file_in_current_directory(self): - """A configuration file in the current directory should be honored.""" - libloc = glymur.lib.openjp2.OPENJP2._name - with tempfile.TemporaryDirectory() as tdir1: - fname = os.path.join(tdir1, 'glymurrc') - with open(fname, 'w') as fptr: - fptr.write('[library]\n') - fptr.write('openjp2: {0}\n'.format(libloc)) - fptr.flush() - with chdir(tdir1): - # Should be able to load openjp2 as before. - imp.reload(glymur.lib.openjp2) - self.assertEqual(glymur.lib.openjp2.OPENJP2._name, libloc) + def tearDown(self): + pass + + def test_read_without_library(self): + """Don't have either openjp2 or openjpeg libraries? Must error out. + """ + with patch('glymur.lib.openjp2.OPENJP2', new=None): + with patch('glymur.lib.openjpeg.OPENJPEG', new=None): + with self.assertRaises(glymur.jp2k.LibraryNotFoundError): + glymur.Jp2k(self.jp2file).read() + + def test_read_bands_without_library(self): + """Don't have openjp2 library? Must error out. + """ + with patch('glymur.lib.openjp2.OPENJP2', new=None): + with patch('glymur.lib.openjpeg.OPENJPEG', new=None): + with patch('glymur.version.openjpeg_version_tuple', + new=(0, 0, 0)): + with self.assertRaises(glymur.jp2k.LibraryNotFoundError): + glymur.Jp2k(self.jp2file).read_bands() + + @unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows") + def test_write_without_library(self): + """Don't have openjpeg libraries? Must error out. + """ + data = glymur.Jp2k(self.j2kfile).read() + with patch('glymur.lib.openjp2.OPENJP2', new=None): + with patch('glymur.lib.openjpeg.OPENJPEG', new=None): + with self.assertRaises(glymur.jp2k.LibraryNotFoundError): + with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: + ofile = Jp2k(tfile.name, 'wb') + ofile.write(data) + + +if __name__ == "__main__": + unittest.main() diff --git a/glymur/test/test_glymur_warnings.py b/glymur/test/test_glymur_warnings.py index 8086004..ebe445c 100644 --- a/glymur/test/test_glymur_warnings.py +++ b/glymur/test/test_glymur_warnings.py @@ -1,32 +1,34 @@ """ Test suite for warnings issued by glymur. """ + +# unittest doesn't work well with R0904. +# pylint: disable=R0904 + +import platform import os import re import struct +import sys import tempfile import unittest +import six + from glymur import Jp2k import glymur from .fixtures import opj_data_file, OPJ_DATA_ROOT from .fixtures import WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG - +@unittest.skipIf(sys.hexversion < 0x03040000 and platform.system() == 'Linux', + "inexplicable failures on 3.3 and linux") @unittest.skipIf(OPJ_DATA_ROOT is None, "OPJ_DATA_ROOT environment variable not set") @unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG) class TestWarnings(unittest.TestCase): """Test suite for warnings issued by glymur.""" - def test_invalid_compatibility_list_entry(self): - """should not error out with invalid compatibility list entry""" - filename = opj_data_file('input/nonregression/issue397.jp2') - with self.assertWarns(UserWarning): - Jp2k(filename) - self.assertTrue(True) - def test_exceeded_box_length(self): """ should warn if reading past end of a box @@ -53,11 +55,12 @@ class TestWarnings(unittest.TestCase): """ relpath = 'input/nonregression/issue188_beach_64bitsbox.jp2' jfile = opj_data_file(relpath) - pattern = r"""Unrecognized\sbox\s\(b'XML\s'\)\sencountered.""" - regex = re.compile(pattern, re.VERBOSE) + regex = re.compile(r"""Unrecognized\sbox\s\(b'XML\s'\)\sencountered.""", + re.VERBOSE) with self.assertWarnsRegex(UserWarning, regex): Jp2k(jfile) + def test_NR_gdal_fuzzer_unchecked_numresolutions_dump(self): """ Has an invalid number of resolutions. @@ -69,7 +72,7 @@ class TestWarnings(unittest.TestCase): \(\d+\)\.""", re.VERBOSE) with self.assertWarnsRegex(UserWarning, regex): - Jp2k(jfile).get_codestream() + Jp2k(jfile) @unittest.skipIf(re.match("1.5|2.0.0", glymur.version.openjpeg_version), "Test not passing on 1.5.x, not introduced until 2.x") @@ -84,7 +87,7 @@ class TestWarnings(unittest.TestCase): \(\d+\)\.""", re.VERBOSE) with self.assertWarnsRegex(UserWarning, regex): - Jp2k(jfile).get_codestream() + Jp2k(jfile) def test_NR_gdal_fuzzer_check_comp_dx_dy_jp2_dump(self): """ @@ -97,7 +100,7 @@ class TestWarnings(unittest.TestCase): dx=\d+,\s*dy=\d+""", re.VERBOSE) with self.assertWarnsRegex(UserWarning, regex): - Jp2k(jfile).get_codestream() + Jp2k(jfile) def test_NR_gdal_fuzzer_assert_in_opj_j2k_read_SQcd_SQcc_patch_jp2(self): lst = ['input', 'nonregression', @@ -107,32 +110,53 @@ class TestWarnings(unittest.TestCase): number\sof\scomponents\sis\sonly\s\d+""", re.VERBOSE) with self.assertWarnsRegex(UserWarning, regex): - Jp2k(jfile).get_codestream() + Jp2k(jfile) + + def test_NR_broken_jp2_dump(self): + """ + The colr box has a ridiculously incorrect box length. + """ + jfile = opj_data_file('input/nonregression/broken.jp2') + regex = re.compile(r'''b'colr'\sbox\shas\sincorrect\sbox\slength\s + \(\d+\)''', + re.VERBOSE) + with self.assertWarnsRegex(UserWarning, regex): + jp2 = Jp2k(jfile) + + def test_NR_broken2_jp2_dump(self): + """ + Invalid marker ID on codestream. + """ + jfile = opj_data_file('input/nonregression/broken2.jp2') + regex = re.compile(r'''Invalid\smarker\sid\sencountered\sat\sbyte\s + \d+\sin\scodestream:\s*"0x[a-fA-F0-9]{4}"''', + re.VERBOSE) + with self.assertWarnsRegex(UserWarning, regex): + Jp2k(jfile) def test_bad_rsiz(self): """Should warn if RSIZ is bad. Issue196""" filename = opj_data_file('input/nonregression/edf_c2_1002767.jp2') with self.assertWarnsRegex(UserWarning, 'Invalid profile'): - Jp2k(filename).get_codestream() + Jp2k(filename) def test_bad_wavelet_transform(self): """Should warn if wavelet transform is bad. Issue195""" filename = opj_data_file('input/nonregression/edf_c2_10025.jp2') with self.assertWarnsRegex(UserWarning, 'Invalid wavelet transform'): - Jp2k(filename).get_codestream() + Jp2k(filename) def test_invalid_progression_order(self): """Should still be able to parse even if prog order is invalid.""" jfile = opj_data_file('input/nonregression/2977.pdf.asan.67.2198.jp2') with self.assertWarnsRegex(UserWarning, 'Invalid progression order'): - Jp2k(jfile).get_codestream() + Jp2k(jfile) def test_tile_height_is_zero(self): """Zero tile height should not cause an exception.""" - filename = 'input/nonregression/2539.pdf.SIGFPE.706.1712.jp2' - filename = opj_data_file(filename) + filename = opj_data_file('input/nonregression/2539.pdf.SIGFPE.706.1712.jp2') with self.assertWarnsRegex(UserWarning, 'Invalid tile dimensions'): - Jp2k(filename).get_codestream() + Jp2k(filename) @unittest.skipIf(os.name == "nt", "Temporary file issue on window.") def test_unknown_marker_segment(self): @@ -155,9 +179,9 @@ class TestWarnings(unittest.TestCase): read_buffer = ifile.read() tfile.write(read_buffer) tfile.flush() - + with self.assertWarnsRegex(UserWarning, 'Unrecognized marker'): - Jp2k(tfile.name).get_codestream() + codestream = Jp2k(tfile.name).get_codestream() if __name__ == "__main__": diff --git a/glymur/test/test_icc.py b/glymur/test/test_icc.py index e18775c..c6b63e8 100644 --- a/glymur/test/test_icc.py +++ b/glymur/test/test_icc.py @@ -1,7 +1,13 @@ """ ICC profile tests. """ + +# unittest doesn't work well with R0904. +# pylint: disable=R0904 + import datetime +import os +import sys import unittest import numpy as np @@ -60,6 +66,11 @@ class TestICC(unittest.TestCase): """invalid ICC header data should cause UserWarning""" jfile = opj_data_file('input/nonregression/orb-blue10-lin-jp2.jp2') + # assertWarns in Python 3.3 (python2.7/pylint issue) + # pylint: disable=E1101 regex = 'ICC profile header is corrupt' with self.assertWarnsRegex(UserWarning, regex): Jp2k(jfile) + +if __name__ == "__main__": + unittest.main() diff --git a/glymur/test/test_jp2box.py b/glymur/test/test_jp2box.py index f9e7c5c..bf10900 100644 --- a/glymur/test/test_jp2box.py +++ b/glymur/test/test_jp2box.py @@ -1,6 +1,18 @@ """ Test suite specifically targeting JP2 box layout. """ +# E1103: return value from read may be list or np array +# pylint: disable=E1103 + +# R0902: More than 7 instance attributes are just fine for testing. +# pylint: disable=R0902 + +# R0904: Seems like pylint is fooled in this situation +# pylint: disable=R0904 + +# W0613: load_tests doesn't need to use ignore or loader arguments. +# pylint: disable=W0613 + import doctest import os import re @@ -8,6 +20,7 @@ import shutil import struct import sys import tempfile +import uuid from uuid import UUID import unittest @@ -21,14 +34,17 @@ from glymur.jp2box import FileTypeBox, ImageHeaderBox, JP2HeaderBox from glymur.jp2box import JPEG2000SignatureBox from glymur.core import COLOR, OPACITY from glymur.core import RED, GREEN, BLUE, GREY, WHOLE_IMAGE +from glymur.version import openjpeg_version -from .fixtures import (WARNING_INFRASTRUCTURE_ISSUE, - WARNING_INFRASTRUCTURE_MSG, - WINDOWS_TMP_FILE_MSG, MetadataBase) +from .fixtures import ( + WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG, + MetadataBase +) - -def docTearDown(doctest_obj): - glymur.set_parseoptions(full_codestream=False) +try: + FORMAT_CORPUS_DATA_ROOT = os.environ['FORMAT_CORPUS_DATA_ROOT'] +except KeyError: + FORMAT_CORPUS_DATA_ROOT = None def load_tests(loader, tests, ignore): @@ -36,35 +52,33 @@ 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', - tearDown=docTearDown)) + tests.addTests(doctest.DocTestSuite('glymur.jp2box')) return tests - -@unittest.skipIf(os.name == "nt", WINDOWS_TMP_FILE_MSG) +@unittest.skipIf(re.match('0|1.[0-2]', openjpeg_version) is not None, + "Not supported with OpenJPEG {0}".format(openjpeg_version)) +@unittest.skipIf(os.name == "nt", "Temporary file issue on window.") class TestDataEntryURL(unittest.TestCase): """Test suite for DataEntryURL boxes.""" def setUp(self): self.jp2file = glymur.data.nemo() - @unittest.skipIf(re.match("1.5|2", - glymur.version.openjpeg_version) is None, - "Must have openjpeg 1.5 or higher to run") def test_wrap_greyscale(self): """A single component should be wrapped as GREYSCALE.""" j = Jp2k(self.jp2file) - data = j[:] + data = j.read() red = data[:, :, 0] # Write it back out as a raw codestream. with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile1: - j2k = glymur.Jp2k(tfile1.name, data=red) + j2k = glymur.Jp2k(tfile1.name, 'wb') + j2k.write(data[:, :, 0]) # Ok, now rewrap it as JP2. The colorspace should be GREYSCALE. with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile2: jp2 = j2k.wrap(tfile2.name) self.assertEqual(jp2.box[2].box[1].colorspace, - glymur.core.GREYSCALE) + glymur.core.GREYSCALE) def test_basic_url(self): """Just your most basic URL box.""" @@ -86,7 +100,7 @@ class TestDataEntryURL(unittest.TestCase): self.assertEqual(jp22.box[4].url, url) def test_null_termination(self): - """I.9.3.2 specifies that location field must be null terminated.""" + """I.9.3.2 specifies that the location field must be null terminated.""" jp2 = Jp2k(self.jp2file) url = 'http://glymur.readthedocs.org' @@ -97,15 +111,12 @@ class TestDataEntryURL(unittest.TestCase): jp22 = jp2.wrap(tfile.name, boxes=boxes) self.assertEqual(jp22.box[-1].length, 42) - - # Go to the last box. Seek past the L, T, version, - # and flag fields. + + # Go to the last box. Seek past the L, T, version, and flag fields. with open(tfile.name, 'rb') as fptr: fptr.seek(jp22.box[-1].offset + 4 + 4 + 1 + 3) - - nbytes = (jp22.box[-1].offset + - jp22.box[-1].length - - fptr.tell()) + + nbytes = jp22.box[-1].offset + jp22.box[-1].length - fptr.tell() read_buffer = fptr.read(nbytes) read_url = read_buffer.decode('utf-8') self.assertEqual(url + chr(0), read_url) @@ -114,7 +125,7 @@ class TestDataEntryURL(unittest.TestCase): @unittest.skipIf(re.match(r'''0|1|2.0.0''', glymur.version.openjpeg_version) is not None, "Not supported until 2.1") -@unittest.skipIf(os.name == "nt", WINDOWS_TMP_FILE_MSG) +@unittest.skipIf(os.name == "nt", "Temporary file issue on window.") class TestChannelDefinition(unittest.TestCase): """Test suite for channel definition boxes.""" @@ -122,21 +133,24 @@ class TestChannelDefinition(unittest.TestCase): def setUpClass(cls): """Need a one_plane plane image for greyscale testing.""" j2k = Jp2k(glymur.data.goodstuff()) - data = j2k[:] + data = j2k.read() # Write the first component back out to file. with tempfile.NamedTemporaryFile(suffix=".j2k", delete=False) as tfile: - Jp2k(tfile.name, data=data[:, :, 0]) + grey_j2k = Jp2k(tfile.name, 'wb') + grey_j2k.write(data[:, :, 0]) cls.one_plane = tfile.name # Write the first two components back out to file. with tempfile.NamedTemporaryFile(suffix=".j2k", delete=False) as tfile: - Jp2k(tfile.name, data=data[:, :, 0:1]) + grey_j2k = Jp2k(tfile.name, 'wb') + grey_j2k.write(data[:, :, 0:1]) cls.two_planes = tfile.name # Write four components back out to file. with tempfile.NamedTemporaryFile(suffix=".j2k", delete=False) as tfile: + rgba_jp2 = Jp2k(tfile.name, 'wb') shape = (data.shape[0], data.shape[1], 1) alpha = np.zeros((shape), dtype=data.dtype) data4 = np.concatenate((data, alpha), axis=2) - Jp2k(tfile.name, data=data4) + rgba_jp2.write(data4) cls.four_planes = tfile.name @classmethod @@ -389,7 +403,7 @@ class TestFileTypeBox(unittest.TestCase): ftyp = glymur.jp2box.FileTypeBox(brand='jp3') with self.assertRaises(IOError): with tempfile.TemporaryFile() as tfile: - ftyp.write(tfile) + ftyp.write(tfile) @unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG) def test_cl_entry_unknown(self): @@ -399,8 +413,7 @@ class TestFileTypeBox(unittest.TestCase): ftyp = glymur.jp2box.FileTypeBox(compatibility_list=['jp3']) with self.assertRaises(IOError): with tempfile.TemporaryFile() as tfile: - ftyp.write(tfile) - + ftyp.write(tfile) class TestColourSpecificationBox(unittest.TestCase): """Test suite for colr box instantiation.""" @@ -424,7 +437,8 @@ class TestColourSpecificationBox(unittest.TestCase): def tearDown(self): pass - @unittest.skipIf(os.name == "nt", WINDOWS_TMP_FILE_MSG) + @unittest.skipIf(os.name == "nt", + "Problems using NamedTemporaryFile on windows.") def test_colr_with_out_enum_cspace(self): """must supply an enumerated colorspace when writing""" j2k = Jp2k(self.j2kfile) @@ -435,7 +449,7 @@ class TestColourSpecificationBox(unittest.TestCase): with self.assertRaises(IOError): j2k.wrap(tfile.name, boxes=boxes) - @unittest.skipIf(os.name == "nt", WINDOWS_TMP_FILE_MSG) + @unittest.skipIf(os.name == "nt", "Temporary file issue on window.") def test_missing_colr_box(self): """jp2h must have a colr box""" j2k = Jp2k(self.j2kfile) @@ -445,7 +459,7 @@ class TestColourSpecificationBox(unittest.TestCase): with self.assertRaises(IOError): j2k.wrap(tfile.name, boxes=boxes) - @unittest.skipIf(os.name == "nt", WINDOWS_TMP_FILE_MSG) + @unittest.skipIf(os.name == "nt", "Temporary file issue on window.") def test_bad_approx_jp2_field(self): """JP2 has requirements for approx field""" j2k = Jp2k(self.j2kfile) @@ -506,7 +520,8 @@ class TestColourSpecificationBox(unittest.TestCase): colr.write(tfile) -@unittest.skipIf(os.name == "nt", WINDOWS_TMP_FILE_MSG) +@unittest.skipIf(os.name == "nt", + "Problems using NamedTemporaryFile on windows.") class TestPaletteBox(unittest.TestCase): """Test suite for pclr box instantiation.""" @@ -523,8 +538,8 @@ class TestPaletteBox(unittest.TestCase): bps = (8, 8, 8) signed = (False, False) with self.assertWarns(UserWarning): - glymur.jp2box.PaletteBox(palette, bits_per_component=bps, - signed=signed) + pclr = glymur.jp2box.PaletteBox(palette, bits_per_component=bps, + signed=signed) @unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG) def test_mismatched_signed_palette(self): @@ -533,8 +548,8 @@ class TestPaletteBox(unittest.TestCase): bps = (8, 8, 8, 8) signed = (False, False, False, False) with self.assertWarns(UserWarning): - glymur.jp2box.PaletteBox(palette, bits_per_component=bps, - signed=signed) + pclr = glymur.jp2box.PaletteBox(palette, bits_per_component=bps, + signed=signed) def test_writing_with_different_bitdepths(self): """Bitdepths must be the same when writing.""" @@ -548,7 +563,7 @@ class TestPaletteBox(unittest.TestCase): pclr.write(tfile) -@unittest.skipIf(os.name == "nt", WINDOWS_TMP_FILE_MSG) +@unittest.skipIf(os.name == "nt", "Temporary file issue on window.") class TestAppend(unittest.TestCase): """Tests for append method.""" @@ -634,14 +649,14 @@ class TestAppend(unittest.TestCase): jp2 = Jp2k(tfile.name) # Make a UUID box. Only XMP UUID boxes can currently be appended. - uuid_instance = UUID('00000000-0000-0000-0000-000000000000') + uuid_instance = uuid.UUID('00000000-0000-0000-0000-000000000000') data = b'0123456789' uuidbox = glymur.jp2box.UUIDBox(uuid_instance, data) with self.assertRaises(IOError): jp2.append(uuidbox) -@unittest.skipIf(os.name == "nt", WINDOWS_TMP_FILE_MSG) +@unittest.skipIf(os.name == "nt", "Temporary file issue on window.") class TestWrap(unittest.TestCase): """Tests for wrap method.""" @@ -790,7 +805,7 @@ class TestWrap(unittest.TestCase): # list to trigger the error. boxes[2].box = [] with self.assertRaises(IOError): - jp2.wrap(tfile.name, boxes=boxes) + jp22 = jp2.wrap(tfile.name, boxes=boxes) def test_default_layout_with_boxes(self): """basic test for rewrapping a jp2 file, boxes specified""" @@ -855,8 +870,8 @@ class TestWrap(unittest.TestCase): """A palette box must reside in a JP2 header box.""" palette = np.array([[255, 0, 255], [0, 255, 0]], dtype=np.int32) bps = (8, 8, 8) - pclr = glymur.jp2box.PaletteBox(palette=palette, - bits_per_component=bps, + signed = (True, False, True) + pclr = glymur.jp2box.PaletteBox(palette=palette, bits_per_component=bps, signed=(True, False, True)) j2k = Jp2k(self.j2kfile) @@ -968,8 +983,7 @@ class TestWrap(unittest.TestCase): """Rewrap a jpx file.""" with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile1: jpx = Jp2k(self.jpxfile) - idx = (list(range(5)) + - list(range(9, 12)) + list(range(6, 9))) + [12] + idx = list(range(5)) + list(range(9, 12)) + list(range(6, 9)) + [12] boxes = [jpx.box[j] for j in idx] jpx2 = jpx.wrap(tfile1.name, boxes=boxes) exp_ids = [box.box_id for box in boxes] @@ -1021,11 +1035,11 @@ class TestJp2Boxes(unittest.TestCase): """Raw instantiation should not produce a main_header.""" box = ContiguousCodestreamBox() self.assertEqual(box.box_id, 'jp2c') - self.assertIsNone(box.codestream) + self.assertIsNone(box.main_header) def test_codestream_main_header_offset(self): """main_header_offset is an attribute of the CCS box""" - j = Jp2k(self.jpxfile) + j = Jp2k(self.jpxfile); self.assertEqual(j.box[5].main_header_offset, j.box[5].offset + 8) @@ -1226,8 +1240,8 @@ class TestRepr(MetadataBase): def test_uuidlist_box(self): """Verify __repr__ method on ulst box.""" - uuid1 = UUID('00000000-0000-0000-0000-000000000001') - uuid2 = UUID('00000000-0000-0000-0000-000000000002') + uuid1 = uuid.UUID('00000000-0000-0000-0000-000000000001') + uuid2 = uuid.UUID('00000000-0000-0000-0000-000000000002') uuids = [uuid1, uuid2] ulst = glymur.jp2box.UUIDListBox(ulst=uuids) newbox = eval(repr(ulst)) @@ -1239,6 +1253,7 @@ class TestRepr(MetadataBase): """Verify Palette box repr.""" palette = np.array([[255, 0, 1000], [0, 255, 0]], dtype=np.int32) bps = (8, 8, 16) + signed = (True, False, True) box = glymur.jp2box.PaletteBox(palette=palette, bits_per_component=bps, signed=(True, False, True)) @@ -1281,15 +1296,14 @@ class TestRepr(MetadataBase): def test_uuid_box_generic(self): """Verify uuid repr method.""" - uuid_instance = UUID('00000000-0000-0000-0000-000000000000') + uuid_instance = uuid.UUID('00000000-0000-0000-0000-000000000000') data = b'0123456789' box = glymur.jp2box.UUIDBox(the_uuid=uuid_instance, raw_data=data) # Since the raw_data parameter is a sequence of bytes which could be # quite long, don't bother trying to make it conform to eval(repr()). regexp = r"""glymur.jp2box.UUIDBox\(""" - regexp += """the_uuid=""" - regexp += """UUID\('00000000-0000-0000-0000-000000000000'\),\s""" + regexp += """the_uuid=UUID\('00000000-0000-0000-0000-000000000000'\),\s""" regexp += """raw_data=\)""" if sys.hexversion < 0x03000000: @@ -1306,8 +1320,7 @@ class TestRepr(MetadataBase): # Since the raw_data parameter is a sequence of bytes which could be # quite long, don't bother trying to make it conform to eval(repr()). regexp = r"""glymur.jp2box.UUIDBox\(""" - regexp += """the_uuid=""" - regexp += """UUID\('be7acfcb-97a9-42e8-9c71-999491e3afac'\),\s""" + regexp += """the_uuid=UUID\('be7acfcb-97a9-42e8-9c71-999491e3afac'\),\s""" regexp += """raw_data=\)""" if sys.hexversion < 0x03000000: @@ -1323,10 +1336,49 @@ class TestRepr(MetadataBase): # Difficult to eval(repr()) this, so just match the general pattern. regexp = "glymur.jp2box.ContiguousCodeStreamBox" - regexp += "[(]codestream=> 16, - standard_masks[j] & 0x0000ffff >> 8, + standard_masks[j] & 0x0000ffff>> 8, standard_masks[j] & 0x000000ff) struct.pack_into('>HBBB', rreq_buffer, 17 + j * 5, standard_flags[j], *mask) @@ -599,6 +599,7 @@ class TestJPX(unittest.TestCase): self.assertEqual(jpx.box[2].standard_flag, (5, 42, 45, 2, 18, 19, 1, 8, 12, 31, 20)) + def test_nlst(self): """Verify that we can handle a number list box.""" j = Jp2k(self.jpxfile) diff --git a/glymur/test/test_jp2box_uuid.py b/glymur/test/test_jp2box_uuid.py index 6886c30..8ec35a2 100644 --- a/glymur/test/test_jp2box_uuid.py +++ b/glymur/test/test_jp2box_uuid.py @@ -1,7 +1,17 @@ # -*- coding: utf-8 -*- """Test suite for printing. """ +# C0302: don't care too much about having too many lines in a test module +# pylint: disable=C0302 + +# E061: unittest.mock introduced in 3.3 (python-2.7/pylint issue) +# pylint: disable=E0611,F0401 + +# R0904: Not too many methods in unittest. +# pylint: disable=R0904 + import os +import re import shutil import struct import sys @@ -13,18 +23,30 @@ if sys.hexversion < 0x02070000: else: import unittest +if sys.hexversion < 0x03000000: + from StringIO import StringIO +else: + from io import StringIO + +if sys.hexversion <= 0x03030000: + from mock import patch +else: + from unittest.mock import patch + import lxml.etree -from .fixtures import (WARNING_INFRASTRUCTURE_ISSUE, - WARNING_INFRASTRUCTURE_MSG, - WINDOWS_TMP_FILE_MSG) +from .fixtures import HAS_PYTHON_XMP_TOOLKIT, OPJ_DATA_ROOT +from .fixtures import WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG + +if HAS_PYTHON_XMP_TOOLKIT: + from libxmp import XMPMeta import glymur from glymur import Jp2k -from .fixtures import SimpleRDF +from .fixtures import OPJ_DATA_ROOT, opj_data_file, SimpleRDF -@unittest.skipIf(os.name == "nt", WINDOWS_TMP_FILE_MSG) +@unittest.skipIf(os.name == "nt", "Unexplained failure on windows") class TestSuite(unittest.TestCase): """Tests for XMP, Exif UUIDs.""" @@ -79,9 +101,8 @@ class TestSuite(unittest.TestCase): jp2 = glymur.Jp2k(tfile.name) self.assertEqual(jp2.box[-1].data['Make'], "HTC") - @unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG) -@unittest.skipIf(os.name == "nt", WINDOWS_TMP_FILE_MSG) +@unittest.skipIf(os.name == "nt", "Unexplained failure on windows") class TestSuiteWarns(unittest.TestCase): """Tests for XMP, Exif UUIDs, issues warnings.""" @@ -90,7 +111,7 @@ class TestSuiteWarns(unittest.TestCase): def tearDown(self): pass - + def test_unrecognized_exif_tag(self): """Verify warning in case of unrecognized tag.""" with tempfile.NamedTemporaryFile(suffix='.jp2', mode='wb') as tfile: @@ -114,7 +135,7 @@ class TestSuiteWarns(unittest.TestCase): tfile.flush() with self.assertWarnsRegex(UserWarning, 'Unrecognized Exif tag'): - glymur.Jp2k(tfile.name) + j = glymur.Jp2k(tfile.name) def test_bad_tag_datatype(self): """Only certain datatypes are allowable""" @@ -170,3 +191,6 @@ class TestSuiteWarns(unittest.TestCase): jp2 = glymur.Jp2k(tfile.name) self.assertEqual(jp2.box[-1].box_id, 'uuid') + +if __name__ == "__main__": + unittest.main() diff --git a/glymur/test/test_jp2box_xml.py b/glymur/test/test_jp2box_xml.py index 87bdd38..eeb1fa8 100644 --- a/glymur/test/test_jp2box_xml.py +++ b/glymur/test/test_jp2box_xml.py @@ -2,12 +2,35 @@ """ Test suite specifically targeting JP2 box layout. """ +# E1103: return value from read may be list or np array +# pylint: disable=E1103 + +# R0902: More than 7 instance attributes are just fine for testing. +# pylint: disable=R0902 + +# R0904: Seems like pylint is fooled in this situation +# pylint: disable=R0904 + +# W0613: load_tests doesn't need to use ignore or loader arguments. +# pylint: disable=W0613 + import os import re import struct +import sys import tempfile import unittest +if sys.hexversion < 0x03000000: + from StringIO import StringIO +else: + from io import StringIO + +if sys.hexversion <= 0x03030000: + from mock import patch +else: + from unittest.mock import patch + import lxml.etree as ET import glymur @@ -18,10 +41,8 @@ from glymur.jp2box import JPEG2000SignatureBox from .fixtures import OPJ_DATA_ROOT, opj_data_file from .fixtures import WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG -from . import fixtures - -@unittest.skipIf(os.name == "nt", fixtures.WINDOWS_TMP_FILE_MSG) +@unittest.skipIf(os.name == "nt", "Temporary file issue on window.") class TestXML(unittest.TestCase): """Test suite for XML boxes.""" @@ -145,6 +166,7 @@ class TestXML(unittest.TestCase): u'Россия') + class TestJp2kBadXmlFile(unittest.TestCase): """Test suite for bad XML box situations""" @@ -197,7 +219,7 @@ class TestJp2kBadXmlFile(unittest.TestCase): self.assertIsNone(jp2k.box[3].xml) -@unittest.skipIf(os.name == "nt", fixtures.WINDOWS_TMP_FILE_MSG) +@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows") class TestBadButRecoverableXmlFile(unittest.TestCase): """Test suite for XML box that is bad, but we can still recover the XML.""" @@ -270,19 +292,22 @@ class TestXML_OpjDataRoot(unittest.TestCase): 'nonregression', 'issue171.jp2')) msg = 'An illegal BOM \(byte order marker\) was detected and removed ' - msg += 'from the XML contents in the box starting at byte offset \d+' + msg += 'from the XML contents in the box starting at byte offset \d+' with self.assertWarnsRegex(UserWarning, re.compile(msg)): jp2 = Jp2k(filename) self.assertIsNotNone(jp2.box[3].xml) + def test_invalid_utf8(self): """Bad byte sequence that cannot be parsed.""" - relname = '26ccf3651020967f7778238ef5af08af.SIGFPE.d25.527.jp2' filename = opj_data_file(os.path.join('input', 'nonregression', - relname)) + '26ccf3651020967f7778238ef5af08af.SIGFPE.d25.527.jp2')) with self.assertWarns((UserWarning, UserWarning)): jp2 = Jp2k(filename) self.assertIsNone(jp2.box[3].box[1].box[1].xml) + + + diff --git a/glymur/test/test_jp2k.py b/glymur/test/test_jp2k.py index 52945f9..02e850a 100644 --- a/glymur/test/test_jp2k.py +++ b/glymur/test/test_jp2k.py @@ -1,9 +1,19 @@ """ Tests for general glymur functionality. """ +# E1101: assertWarns introduced in python 3.2 +# pylint: disable=E1101 + +# R0904: Not too many methods in unittest. +# pylint: disable=R0904 + +# E0611: unittest.mock is unknown to python2.7/pylint +# pylint: disable=E0611,F0401 + import doctest import os import re +import shutil import struct import sys import tempfile @@ -12,11 +22,6 @@ import uuid import warnings from xml.etree import cElementTree as ET -if sys.hexversion <= 0x03030000: - from mock import patch -else: - from unittest.mock import patch - import numpy as np import pkg_resources @@ -26,7 +31,6 @@ from glymur.version import openjpeg_version from .fixtures import HAS_PYTHON_XMP_TOOLKIT from .fixtures import WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG -from .fixtures import OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG if HAS_PYTHON_XMP_TOOLKIT: import libxmp @@ -35,23 +39,23 @@ if HAS_PYTHON_XMP_TOOLKIT: from .fixtures import OPJ_DATA_ROOT, opj_data_file from . import fixtures - -def docTearDown(doctest_obj): - glymur.set_parseoptions(full_codestream=False) - - # Doc tests should be run as well. def load_tests(loader, tests, ignore): + # W0613: "loader" and "ignore" are necessary for the protocol + # They are unused here, however. + # pylint: disable=W0613 + """Should run doc tests as well""" 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', - tearDown=docTearDown)) + tests.addTests(doctest.DocTestSuite('glymur.jp2k')) return tests +@unittest.skipIf(fixtures.OPENJPEG_NOT_AVAILABLE, + fixtures.OPENJPEG_NOT_AVAILABLE_MSG) class SliceProtocolBase(unittest.TestCase): """ Test slice protocol, i.e. when using [ ] to read image data. @@ -60,29 +64,22 @@ class SliceProtocolBase(unittest.TestCase): def setUpClass(self): self.jp2 = Jp2k(glymur.data.nemo()) - self.jp2_data = self.jp2[:] - self.jp2_data_r1 = self.jp2[::2, ::2] + self.jp2_data = self.jp2.read() self.j2k = Jp2k(glymur.data.goodstuff()) - self.j2k_data = self.j2k[:] - - self.j2k_data_r1 = self.j2k[::2, ::2] - self.j2k_data_r5 = self.j2k[::32, ::32] + self.j2k_data = self.j2k.read() -@unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG) -@unittest.skipIf(re.match("1.5|2", glymur.version.openjpeg_version) is None, - "Must have openjpeg 1.5 or higher to run") -@unittest.skipIf(os.name == "nt", fixtures.WINDOWS_TMP_FILE_MSG) +@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows") class TestSliceProtocolBaseWrite(SliceProtocolBase): def test_write_ellipsis(self): expected = self.j2k_data with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: - j = Jp2k(tfile.name, shape=expected.shape) - j[...] = expected - actual = j[:] + j = Jp2k(tfile.name, 'wb') + j[...] = self.j2k_data + actual = j.read() np.testing.assert_array_equal(actual, expected) @@ -90,14 +87,15 @@ class TestSliceProtocolBaseWrite(SliceProtocolBase): expected = self.j2k_data with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: - j = Jp2k(tfile.name, data=self.j2k_data) - actual = j[:] + j = Jp2k(tfile.name, 'wb') + j[:] = self.j2k_data + actual = j.read() np.testing.assert_array_equal(actual, expected) def test_cannot_write_with_non_default_single_slice(self): with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: - j = Jp2k(tfile.name, shape=self.j2k_data.shape) + j = Jp2k(tfile.name, 'wb') with self.assertRaises(TypeError): j[slice(None, 0)] = self.j2k_data with self.assertRaises(TypeError): @@ -109,38 +107,42 @@ class TestSliceProtocolBaseWrite(SliceProtocolBase): def test_cannot_write_a_row(self): with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: - j = Jp2k(tfile.name, shape=self.j2k_data.shape) + j = Jp2k(tfile.name, 'wb') with self.assertRaises(TypeError): j[5] = self.j2k_data def test_cannot_write_a_pixel(self): with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: - j = Jp2k(tfile.name, shape=self.j2k_data.shape) + j = Jp2k(tfile.name, 'wb') with self.assertRaises(TypeError): j[25, 35] = self.j2k_data[25, 35] def test_cannot_write_a_column(self): with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: - j = Jp2k(tfile.name, shape=self.j2k_data.shape) + j = Jp2k(tfile.name, 'wb') with self.assertRaises(TypeError): j[:, 25, :] = self.j2k_data[:, :25, :] def test_cannot_write_a_band(self): with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: - j = Jp2k(tfile.name, shape=self.j2k_data.shape) + j = Jp2k(tfile.name, 'wb') with self.assertRaises(TypeError): j[:, :, 0] = self.j2k_data[:, :, 0] def test_cannot_write_a_subarray(self): with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: - j = Jp2k(tfile.name, shape=self.j2k_data.shape) + j = Jp2k(tfile.name, 'wb') with self.assertRaises(TypeError): j[:25, :45, :] = self.j2k_data[:25, :25, :] -@unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG) class TestSliceProtocolRead(SliceProtocolBase): + def test_resolution_strides_cannot_differ(self): + with self.assertRaises(IndexError): + # Strides in x/y directions cannot differ. + self.j2k[::2, ::3] + def test_resolution_strides_cannot_differ(self): with self.assertRaises(IndexError): # Strides in x/y directions cannot differ. @@ -157,14 +159,14 @@ class TestSliceProtocolRead(SliceProtocolBase): np.testing.assert_array_equal(self.j2k_data[:, :, j], band) def test_slice_in_third_dimension(self): - actual = self.j2k[:, :, 1:3] - expected = self.j2k_data[:, :, 1:3] + actual = self.j2k[:,:,1:3] + expected = self.j2k_data[:,:,1:3] np.testing.assert_array_equal(actual, expected) def test_reduce_resolution_and_slice_in_third_dimension(self): - actual = self.j2k[::2, ::2, 1:3] - expected = self.j2k_data_r1[:, :, 1:3] - np.testing.assert_array_equal(actual, expected) + d = self.j2k[::2, ::2, 1:3] + all = self.j2k.read(rlevel=1) + np.testing.assert_array_equal(all[:,:,1:3], d) def test_retrieve_single_row(self): actual = self.jp2[0] @@ -172,15 +174,80 @@ class TestSliceProtocolRead(SliceProtocolBase): np.testing.assert_array_equal(actual, expected) def test_retrieve_single_pixel(self): - actual = self.jp2[0, 0] + actual = self.jp2[0,0] expected = self.jp2_data[0, 0] np.testing.assert_array_equal(actual, expected) def test_retrieve_single_component(self): - actual = self.jp2[20, 20, 2] + actual = self.jp2[20,20,2] expected = self.jp2_data[20, 20, 2] np.testing.assert_array_equal(actual, expected) + def test_full_resolution_slicing_by_quarters_upper_left(self): + actual = self.jp2[:728, :1296] + expected = self.jp2_data[:728, :1296] + np.testing.assert_array_equal(actual, expected) + + def test_full_resolution_slicing_by_quarters_lower_left(self): + actual = self.jp2[728:, :1296] + expected = self.jp2_data[728:, :1296] + np.testing.assert_array_equal(actual, expected) + + def test_full_resolution_slicing_by_quarters_upper_right(self): + actual = self.jp2[:728, 1296:] + expected = self.jp2_data[:728, 1296:] + np.testing.assert_array_equal(actual, expected) + + def test_full_resolution_slicing_by_quarters_lower_right(self): + actual = self.jp2[728:, 1296:] + expected = self.jp2_data[728:, 1296:] + np.testing.assert_array_equal(actual, expected) + + def test_full_resolution_slicing_by_quarters_center(self): + actual = self.jp2[364:1092, 648:1942] + expected = self.jp2_data[364:1092, 648:1942] + np.testing.assert_array_equal(actual, expected) + + def test_full_resolution_slicing_by_halves_left(self): + actual = self.jp2[:, :1296] + expected = self.jp2_data[:, :1296] + np.testing.assert_array_equal(actual, expected) + + def test_full_resolution_slicing_by_right_half(self): + actual = self.jp2[:, 1296:] + expected = self.jp2_data[:, 1296:] + np.testing.assert_array_equal(actual, expected) + + def test_full_resolution_slicing_by_top_half(self): + actual = self.jp2[:728, :] + expected = self.jp2_data[:728, :] + np.testing.assert_array_equal(actual, expected) + + def test_full_resolution_slicing_by_bottom_half(self): + actual = self.jp2[728:, :] + expected = self.jp2_data[728:, :] + np.testing.assert_array_equal(actual, expected) + + def test_region_rlevel1(self): + actual = self.jp2[0:201:2, 0:201:2] + expected = self.jp2.read(area=(0, 0, 201, 201), rlevel=1) + np.testing.assert_array_equal(actual, expected) + + def test_region_rlevel1_slice_start_is_none(self): + actual = self.jp2[:201:2, :201:2] + expected = self.jp2.read(area=(0, 0, 201, 201), rlevel=1) + np.testing.assert_array_equal(actual, expected) + + def test_region_rlevel1_slice_stop_is_none(self): + actual = self.jp2[201::2, 201::2] + expected = self.jp2.read(area=(201, 201, 1456, 2592), rlevel=1) + np.testing.assert_array_equal(actual, expected) + + def test_region_rlevel1(self): + actual = self.jp2[0:202:2, 0:202:2] + expected = self.jp2.read(area=(0, 0, 202, 202), rlevel=1) + np.testing.assert_array_equal(actual, expected) + def test_ellipsis_full_read(self): actual = self.j2k[...] expected = self.j2k_data @@ -214,24 +281,191 @@ class TestSliceProtocolRead(SliceProtocolBase): def test_single_slice(self): rows = slice(3, 8) actual = self.j2k[rows] - expected = self.j2k_data[3:8, :, :] + expected = self.j2k_data[3:8, :,:] np.testing.assert_array_equal(actual, expected) - @unittest.skipIf(re.match("0|1", glymur.version.openjpeg_version), - "Must have openjpeg 2 or higher to run") + def test_slice_protocol_2d_reduce_resolution(self): + d = self.j2k[:] + self.assertEqual(d.shape, (800, 480, 3)) + + d = self.j2k[::1, ::1] + self.assertEqual(d.shape, (800, 480, 3)) + + d = self.j2k[::2, ::2] + self.assertEqual(d.shape, (400, 240, 3)) + + d = self.j2k[::4, ::4] + self.assertEqual(d.shape, (200, 120, 3)) + + d = self.j2k[::8, ::8] + self.assertEqual(d.shape, (100, 60, 3)) + + d = self.j2k[::16, ::16] + self.assertEqual(d.shape, (50, 30, 3)) + + d = self.j2k[::32, ::32] + self.assertEqual(d.shape, (25, 15, 3)) + def test_region_rlevel5(self): - """ - maximim rlevel - - There seems to be a difference between version of openjpeg, as - openjp2 produces an image of size (16, 13, 3) and openjpeg produced - (17, 12, 3). - """ actual = self.j2k[5:533:32, 27:423:32] - expected = self.j2k_data_r5[1:17, 1:14] + expected = self.j2k.read(area=(5, 27, 533, 423), rlevel=5) np.testing.assert_array_equal(actual, expected) +@unittest.skipIf(fixtures.OPENJPEG_NOT_AVAILABLE, + fixtures.OPENJPEG_NOT_AVAILABLE_MSG) +@unittest.skipIf(OPJ_DATA_ROOT is None, + "OPJ_DATA_ROOT environment variable not set") +class TestSliceProtocolOpjData(unittest.TestCase): + """ + Test slice protocol, i.e. when using [ ] to read image data. + These correspond to tests for the read method with the area parameter. + """ + @classmethod + def setUpClass(self): + jfile = opj_data_file('input/conformance/p1_04.j2k') + self.j2k = Jp2k(jfile) + self.j2k_data = self.j2k.read() + self.j2k_half_data = self.j2k.read(rlevel=1) + self.j2k_quarter_data = self.j2k.read(rlevel=2) + + def test_NR_DEC_p1_04_j2k_43_decode(self): + actual = self.j2k[:1024, :1024] + expected = self.j2k_data + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p1_04_j2k_44_decode(self): + actual = self.j2k[640:768, 512:640] + expected = self.j2k_data[640:768, 512:640] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p1_04_j2k_45_decode(self): + actual = self.j2k[896:1024, 896:1024] + expected = self.j2k_data[896:1024, 896:1024] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p1_04_j2k_46_decode(self): + actual = self.j2k[500:800, 100:300] + expected = self.j2k_data[500:800, 100:300] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p1_04_j2k_47_decode(self): + actual = self.j2k[520:600, 260:360] + expected = self.j2k_data[520:600, 260:360] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p1_04_j2k_48_decode(self): + actual = self.j2k[520:660, 260:360] + expected = self.j2k_data[520:660, 260:360] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p1_04_j2k_49_decode(self): + actual = self.j2k[520:600, 360:400] + expected = self.j2k_data[520:600, 360:400] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p1_04_j2k_50_decode(self): + actual = self.j2k[:1024:4, :1024:4] + expected = self.j2k_quarter_data[:256, :256] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p1_04_j2k_51_decode(self): + actual = self.j2k[640:768:4, 512:640:4] + expected = self.j2k_quarter_data[160:192, 128:160] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p1_04_j2k_52_decode(self): + actual = self.j2k[896:1024:4, 896:1024:4] + expected = self.j2k_quarter_data[224:352, 224:352] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p1_04_j2k_53_decode(self): + actual = self.j2k[500:800:4, 100:300:4] + expected = self.j2k_quarter_data[125:200, 25:75] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p1_04_j2k_54_decode(self): + actual = self.j2k[520:600:4, 260:360:4] + expected = self.j2k_quarter_data[130:150, 65:90] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p1_04_j2k_55_decode(self): + actual = self.j2k[520:660:4, 260:360:4] + expected = self.j2k_quarter_data[130:165, 65:90] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p1_04_j2k_56_decode(self): + actual = self.j2k[520:600:4, 360:400:4] + expected = self.j2k_quarter_data[130:150, 90:100] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p1_06_j2k_75_decode(self): + # Image size would be 0 x 0. + with self.assertRaises((IOError, OSError)): + self.j2k[9:12:4, 9:12:4] + + def test_NR_DEC_p0_04_j2k_85_decode(self): + actual = self.j2k[:256, :256] + expected = self.j2k_data[:256, :256] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p0_04_j2k_86_decode(self): + actual = self.j2k[:128, 128:256] + expected = self.j2k_data[:128, 128:256] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p0_04_j2k_87_decode(self): + actual = self.j2k[10:200, 50:120] + expected = self.j2k_data[10:200, 50:120] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p0_04_j2k_88_decode(self): + actual = self.j2k[150:210, 10:190] + expected = self.j2k_data[150:210, 10:190] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p0_04_j2k_89_decode(self): + actual = self.j2k[80:150, 100:200] + expected = self.j2k_data[80:150, 100:200] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p0_04_j2k_90_decode(self): + actual = self.j2k[20:50, 150:200] + expected = self.j2k_data[20:50, 150:200] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p0_04_j2k_91_decode(self): + actual = self.j2k[:256:4, :256:4] + expected = self.j2k_quarter_data[0:64, 0:64] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p0_04_j2k_92_decode(self): + actual = self.j2k[:128:4, 128:256:4] + expected = self.j2k_quarter_data[:32, 32:64] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p0_04_j2k_93_decode(self): + actual = self.j2k[10:200:4, 50:120:4] + expected = self.j2k_quarter_data[3:50, 13:30] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p0_04_j2k_94_decode(self): + actual = self.j2k[150:210:4, 10:190:4] + expected = self.j2k_quarter_data[38:53, 3:48] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p0_04_j2k_95_decode(self): + actual = self.j2k[80:150:4, 100:200:4] + expected = self.j2k_quarter_data[20:38, 25:50] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p0_04_j2k_96_decode(self): + actual = self.j2k[20:50:4, 150:200:4] + expected = self.j2k_quarter_data[5:13, 38:50] + np.testing.assert_array_equal(actual, expected) + +@unittest.skipIf(fixtures.OPENJPEG_NOT_AVAILABLE, + fixtures.OPENJPEG_NOT_AVAILABLE_MSG) class TestJp2k(unittest.TestCase): """These tests should be run by just about all configuration.""" @@ -243,80 +477,32 @@ class TestJp2k(unittest.TestCase): def tearDown(self): pass - @unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG) - @unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG) - def test_warn_if_using_read_method(self): - """Should warn if deprecated read method is called""" - with self.assertWarns(DeprecationWarning): - Jp2k(self.jp2file).read() - def test_shape_jp2(self): - """verify shape attribute for JP2 file - """ - jp2 = Jp2k(self.jp2file) - self.assertEqual(jp2.shape, (1456, 2592, 3)) - - @unittest.skipIf(OPJ_DATA_ROOT is None, - "OPJ_DATA_ROOT environment variable not set") - def test_shape_greyscale_jp2(self): - """verify shape attribute for greyscale JP2 file - """ - with warnings.catch_warnings(): - # Suppress a warning due to bad compatibility list entry. - warnings.simplefilter("ignore") - jfile = opj_data_file('input/conformance/file4.jp2') - jp2 = Jp2k(jfile) - self.assertEqual(jp2.shape, (512, 768)) - - @unittest.skipIf(OPJ_DATA_ROOT is None, - "OPJ_DATA_ROOT environment variable not set") - def test_shape_single_channel_j2k(self): - """verify shape attribute for single channel J2K file - """ - jfile = opj_data_file('input/conformance/p0_01.j2k') - jp2 = Jp2k(jfile) - self.assertEqual(jp2.shape, (128, 128)) - - def test_shape_j2k(self): - """verify shape attribute for J2K file - """ - j2k = Jp2k(self.j2kfile) - self.assertEqual(j2k.shape, (800, 480, 3)) - - def test_shape_jpx_jp2(self): - """verify shape attribute for JPX file with JP2 compatibility - """ - jpx = Jp2k(self.jpxfile) - self.assertEqual(jpx.shape, (1024, 1024, 3)) - - @unittest.skipIf(re.match("0|1.[0-4]", glymur.version.openjpeg_version), - "Must have openjpeg 1.5 or higher to run") @unittest.skipIf(os.name == "nt", "Unexplained failure on windows") def test_irreversible(self): """Irreversible""" j = Jp2k(self.jp2file) - expdata = j[:] + expdata = j.read() with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: - j2 = Jp2k(tfile.name, data=expdata, irreversible=True) + j2 = Jp2k(tfile.name, 'wb') + j2.write(expdata, irreversible=True) codestream = j2.get_codestream() self.assertEqual(codestream.segment[2].spcod[8], glymur.core.WAVELET_XFORM_9X7_IRREVERSIBLE) - actdata = j2[:] + actdata = j2.read() self.assertTrue(fixtures.mse(actdata[0], expdata[0]) < 0.38) - @unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG) - @unittest.skipIf(re.match('1.[0-4]', openjpeg_version) is not None, - "Not supported on OpenJPEG {0}".format(openjpeg_version)) + @unittest.skipIf(re.match('0|1.[0-4]', openjpeg_version) is not None, + "Not supported with OpenJPEG {0}".format(openjpeg_version)) @unittest.skipIf(re.match('1.5.(1|2)', openjpeg_version) is not None, "Mysteriously fails in 1.5.1 and 1.5.2") def test_no_cxform_pclr_jpx(self): """Indices for pclr jpxfile if no color transform""" j = Jp2k(self.jpxfile) - rgb = j[:] - j.ignore_pclr_cmap_cdef = True - idx = j[:] + rgb = j.read() + idx = j.read(ignore_pclr_cmap_cdef=True) nr, nc = 1024, 1024 self.assertEqual(rgb.shape, (nr, nc, 3)) self.assertEqual(idx.shape, (nr, nc)) @@ -337,37 +523,22 @@ class TestJp2k(unittest.TestCase): newjp2 = eval(repr(j)) self.assertEqual(newjp2.filename, self.j2kfile) + self.assertEqual(newjp2.mode, 'rb') self.assertEqual(len(newjp2.box), 0) - @unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG) - @unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG) - def test_rlevel_max_backwards_compatibility(self): - """ - Verify that rlevel=-1 gets us the lowest resolution image - - This is an old option only available via the read method, not via - array-style slicing. - """ + def test_rlevel_max(self): + """Verify that rlevel=-1 gets us the lowest resolution image""" j = Jp2k(self.j2kfile) - if sys.hexversion < 0x03000000: - with warnings.catch_warnings(): - # Suppress a warning due to deprecated syntax - # Not as easy to verify the warning under python2. - warnings.simplefilter("ignore") - thumbnail1 = j.read(rlevel=-1) - else: - with self.assertWarns(DeprecationWarning): - thumbnail1 = j.read(rlevel=-1) - thumbnail2 = j[::32, ::32] + thumbnail1 = j.read(rlevel=-1) + thumbnail2 = j.read(rlevel=5) np.testing.assert_array_equal(thumbnail1, thumbnail2) self.assertEqual(thumbnail1.shape, (25, 15, 3)) - @unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG) def test_rlevel_too_high(self): """Should error out appropriately if reduce level too high""" j = Jp2k(self.jp2file) with self.assertRaises(IOError): - j[::64, ::64] + j.read(rlevel=6) def test_not_jpeg2000(self): """Should error out appropriately if not given a JPEG 2000 file.""" @@ -444,7 +615,7 @@ class TestJp2k(unittest.TestCase): jp2k = Jp2k(self.j2kfile) self.assertEqual(len(jp2k.box), 0) - @unittest.skipIf(os.name == "nt", fixtures.WINDOWS_TMP_FILE_MSG) + @unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows") def test_64bit_xl_field(self): """XL field should be supported""" # Verify that boxes with the XL field are properly read. @@ -478,7 +649,7 @@ class TestJp2k(unittest.TestCase): self.assertEqual(jp2k.box[4].offset, 3223) self.assertEqual(jp2k.box[4].length, 1133427 + 8) - @unittest.skipIf(os.name == "nt", fixtures.WINDOWS_TMP_FILE_MSG) + @unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows") def test_length_field_is_zero(self): """L=0 (length field in box header) is allowed""" # Verify that boxes with the L field as zero are correctly read. @@ -514,21 +685,19 @@ class TestJp2k(unittest.TestCase): self.assertEqual(new_jp2.box[j].length, baseline_jp2.box[j].length) - @unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG) def test_basic_jp2(self): """Just a very basic test that reading a JP2 file does not error out. """ j2k = Jp2k(self.jp2file) - j2k[::2, ::2] + j2k.read(rlevel=1) - @unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG) 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[:] + j2k.read() def test_empty_box_with_j2k(self): """Verify that the list of boxes in a J2C/J2K file is present, but @@ -537,7 +706,7 @@ class TestJp2k(unittest.TestCase): j = Jp2k(self.j2kfile) self.assertEqual(j.box, []) - @unittest.skipIf(os.name == "nt", fixtures.WINDOWS_TMP_FILE_MSG) + @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""" # Verify that we can read UINF, ULST, and URL boxes. I don't have @@ -596,7 +765,7 @@ class TestJp2k(unittest.TestCase): self.assertEqual(jp2k.box[3].box[1].flag, (0, 0, 0)) self.assertEqual(jp2k.box[3].box[1].url, 'abcd') - @unittest.skipIf(os.name == "nt", fixtures.WINDOWS_TMP_FILE_MSG) + @unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows") def test_xml_with_trailing_nulls(self): """ElementTree doesn't like trailing null chars after valid XML text""" with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: @@ -627,7 +796,7 @@ class TestJp2k(unittest.TestCase): self.assertEqual(ET.tostring(jp2k.box[3].xml.getroot()), b'this is a test') - @unittest.skipIf(not HAS_PYTHON_XMP_TOOLKIT, + @unittest.skipIf(not HAS_PYTHON_XMP_TOOLKIT, "Requires Python XMP Toolkit >= 2.0") def test_xmp_attribute(self): """Verify the XMP packet in the shipping example file can be read.""" @@ -643,11 +812,9 @@ class TestJp2k(unittest.TestCase): xmp = XMPMeta() xmp.parse_from_str(j.box[3].raw_data.decode('utf-8'), xmpmeta_wrap=False) - creator_tool = xmp.get_property(libxmp.consts.XMP_NS_XMP, - 'CreatorTool') - self.assertEqual(creator_tool, 'Google') + creator_tool = xmp.get_property(libxmp.consts.XMP_NS_XMP, 'CreatorTool') + self.assertEqual(creator_tool, 'Google') - @unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG) @unittest.skipIf(re.match(r'''(1|2.0.0)''', glymur.version.openjpeg_version) is not None, "Not supported until 2.0.1") @@ -655,20 +822,13 @@ class TestJp2k(unittest.TestCase): """Read JPX codestream when jp2-compatible.""" # The file in question has multiple codestreams. jpx = Jp2k(self.jpxfile) - data = jpx[:] + data = jpx.read() self.assertEqual(data.shape, (1024, 1024, 3)) - def test_read_bands_without_openjp2(self): - """Don't have openjp2 library? Must error out.""" - with patch('glymur.version.openjpeg_version_tuple', new=(1, 5, 0)): - with self.assertRaises(RuntimeError): - glymur.Jp2k(self.jp2file).read_bands() - -@unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG) -@unittest.skipIf(re.match('1.[0-4]', openjpeg_version) is not None, +@unittest.skipIf(re.match('0|1.[0-4]', openjpeg_version) is not None, "Not supported with OpenJPEG {0}".format(openjpeg_version)) -@unittest.skipIf(os.name == "nt", fixtures.WINDOWS_TMP_FILE_MSG) +@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows") class TestJp2k_write(unittest.TestCase): """Write tests, can be run by versions 1.5+""" @@ -679,54 +839,17 @@ class TestJp2k_write(unittest.TestCase): def tearDown(self): pass - def test_precinct_size_too_small(self): - """first precinct size must be >= 2x that of the code block size""" - data = np.zeros((640, 480), dtype=np.uint8) - with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: - with self.assertRaises(IOError): - Jp2k(tfile.name, data=data, - cbsize=(16, 16), psizes=[(16, 16)]) - - def test_precinct_size_not_power_of_two(self): - """must be power of two""" - data = np.zeros((640, 480), dtype=np.uint8) - with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: - with self.assertRaises(IOError): - Jp2k(tfile.name, data=data, - cbsize=(16, 16), psizes=[(48, 48)]) - - def test_unsupported_int32(self): - """Should raise a runtime error if trying to write int32""" - data = np.zeros((128, 128), dtype=np.int32) - with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: - with self.assertRaises(RuntimeError): - Jp2k(tfile.name, data=data) - - def test_unsupported_uint32(self): - """Should raise a runtime error if trying to write uint32""" - data = np.zeros((128, 128), dtype=np.uint32) - with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: - with self.assertRaises(RuntimeError): - Jp2k(tfile.name, data=data) - - def test_write_with_version_too_early(self): - """Should raise a runtime error if trying to write with version 1.3""" - data = np.zeros((128, 128), dtype=np.uint8) - versions = ["1.0.0", "1.1.0", "1.2.0", "1.3.0"] - for version in versions: - with patch('glymur.version.openjpeg_version', new=version): - with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: - with self.assertRaises(RuntimeError): - Jp2k(tfile.name, data=data) - def test_cblkh_different_than_width(self): """Verify that we can set a code block size where height does not equal width. """ data = np.zeros((128, 128), dtype=np.uint8) with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: + j = Jp2k(tfile.name, 'wb') + # The code block dimensions are given as rows x columns. - j = Jp2k(tfile.name, data=data, cbsize=(16, 32)) + j.write(data, cbsize=(16, 32)) + codestream = j.get_codestream() # Code block size is reported as XY in the codestream. @@ -735,55 +858,59 @@ class TestJp2k_write(unittest.TestCase): def test_too_many_dimensions(self): """OpenJP2 only allows 2D or 3D images.""" with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: + j = Jp2k(tfile.name, 'wb') with self.assertRaises(IOError): - Jp2k(tfile.name, - data=np.zeros((128, 128, 2, 2), dtype=np.uint8)) + data = np.zeros((128, 128, 2, 2), dtype=np.uint8) + j.write(data) def test_2d_rgb(self): """RGB must have at least 3 components.""" with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: + j = Jp2k(tfile.name, 'wb') with self.assertRaises(IOError): - Jp2k(tfile.name, - data=np.zeros((128, 128, 2), dtype=np.uint8), - colorspace='rgb') + data = np.zeros((128, 128, 2), dtype=np.uint8) + j.write(data, colorspace='rgb') def test_colorspace_with_j2k(self): """Specifying a colorspace with J2K does not make sense""" with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: + j = Jp2k(tfile.name, 'wb') with self.assertRaises(IOError): - Jp2k(tfile.name, - data=np.zeros((128, 128, 3), dtype=np.uint8), - colorspace='rgb') + data = np.zeros((128, 128, 3), dtype=np.uint8) + j.write(data, colorspace='rgb') def test_specify_rgb(self): """specify RGB explicitly""" with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: - j = Jp2k(tfile.name, - data=np.zeros((128, 128, 3), dtype=np.uint8), - colorspace='rgb') + j = Jp2k(tfile.name, 'wb') + data = np.zeros((128, 128, 3), dtype=np.uint8) + j.write(data, colorspace='rgb') self.assertEqual(j.box[2].box[1].colorspace, glymur.core.SRGB) def test_specify_gray(self): """test gray explicitly specified (that's GRAY, not GREY)""" with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: + j = Jp2k(tfile.name, 'wb') data = np.zeros((128, 128), dtype=np.uint8) - j = Jp2k(tfile.name, data=data, colorspace='gray') + j.write(data, colorspace='gray') self.assertEqual(j.box[2].box[1].colorspace, glymur.core.GREYSCALE) def test_specify_grey(self): """test grey explicitly specified""" with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: + j = Jp2k(tfile.name, 'wb') data = np.zeros((128, 128), dtype=np.uint8) - j = Jp2k(tfile.name, data=data, colorspace='grey') + j.write(data, colorspace='grey') self.assertEqual(j.box[2].box[1].colorspace, glymur.core.GREYSCALE) def test_grey_with_two_extra_comps(self): """should be able to write gray + two extra components""" with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: + j = Jp2k(tfile.name, 'wb') data = np.zeros((128, 128, 3), dtype=np.uint8) - j = Jp2k(tfile.name, data=data, colorspace='gray') + j.write(data, colorspace='gray') self.assertEqual(j.box[2].box[0].height, 128) self.assertEqual(j.box[2].box[0].width, 128) self.assertEqual(j.box[2].box[0].num_components, 3) @@ -793,26 +920,29 @@ class TestJp2k_write(unittest.TestCase): def test_specify_ycc(self): """Should reject YCC""" with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: + j = Jp2k(tfile.name, 'wb') with self.assertRaises(IOError): data = np.zeros((128, 128, 3), dtype=np.uint8) - Jp2k(tfile.name, data=data, colorspace='ycc') + j.write(data, colorspace='ycc') def test_write_with_jp2_in_caps(self): """should be able to write with JP2 suffix.""" j2k = Jp2k(self.j2kfile) - expdata = j2k[:] + expdata = j2k.read() with tempfile.NamedTemporaryFile(suffix='.JP2') as tfile: - ofile = Jp2k(tfile.name, data=expdata) - actdata = ofile[:] + ofile = Jp2k(tfile.name, 'wb') + ofile.write(expdata) + actdata = ofile.read() np.testing.assert_array_equal(actdata, expdata) def test_write_srgb_without_mct(self): """should be able to write RGB without specifying mct""" j2k = Jp2k(self.j2kfile) - expdata = j2k[:] + expdata = j2k.read() with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: - ofile = Jp2k(tfile.name, data=expdata, mct=False) - actdata = ofile[:] + ofile = Jp2k(tfile.name, 'wb') + ofile.write(expdata, mct=False) + actdata = ofile.read() np.testing.assert_array_equal(actdata, expdata) codestream = ofile.get_codestream() @@ -821,25 +951,31 @@ class TestJp2k_write(unittest.TestCase): def test_write_grayscale_with_mct(self): """MCT usage makes no sense for grayscale images.""" j2k = Jp2k(self.j2kfile) - expdata = j2k[:] + expdata = j2k.read() with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: + ofile = Jp2k(tfile.name, 'wb') with self.assertRaises(IOError): - Jp2k(tfile.name, data=expdata[:, :, 0], mct=True) + ofile.write(expdata[:, :, 0], mct=True) def test_write_cprl(self): """Must be able to write a CPRL progression order file""" # Issue 17 j = Jp2k(self.jp2file) - expdata = j[::2, ::2] + expdata = j.read(rlevel=1) with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: - ofile = Jp2k(tfile.name, data=expdata, prog='CPRL') - actdata = ofile[:] + ofile = Jp2k(tfile.name, 'wb') + ofile.write(expdata, prog='CPRL') + actdata = ofile.read() np.testing.assert_array_equal(actdata, expdata) codestream = ofile.get_codestream() self.assertEqual(codestream.segment[2].spcod[0], glymur.core.CPRL) +@unittest.skipIf(fixtures.OPENJPEG_NOT_AVAILABLE, + fixtures.OPENJPEG_NOT_AVAILABLE_MSG) +@unittest.skipIf(glymur.version.openjpeg_version_tuple[0] >= 2, + "Negative tests only for version 1.x") class TestJp2k_1_x(unittest.TestCase): """Test suite for openjpeg 1.x, not appropriate for 2.x""" @@ -853,35 +989,32 @@ class TestJp2k_1_x(unittest.TestCase): def test_tile(self): """tile option not allowed for 1.x. """ - with patch('glymur.version.openjpeg_version_tuple', new=(1, 5, 0)): - j2k = Jp2k(self.j2kfile) - with warnings.catch_warnings(): - # The tile keyword is deprecated, so suppress the warning. - warnings.simplefilter('ignore') - with self.assertRaises(TypeError): - j2k.read(tile=0) + j2k = Jp2k(self.j2kfile) + with self.assertRaises(TypeError): + j2k.read(tile=0) def test_layer(self): """layer option not allowed for 1.x. """ - with patch('glymur.version.openjpeg_version_tuple', new=(1, 5, 0)): - j2k = Jp2k(self.j2kfile) - with self.assertRaises(RuntimeError): - j2k.layer = 1 + j2k = Jp2k(self.j2kfile) + with self.assertRaises(TypeError): + j2k.read(layer=1) -@unittest.skipIf(os.name == "nt", fixtures.WINDOWS_TMP_FILE_MSG) -@unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG) -class Test_2p0_official(unittest.TestCase): - """Tests specific to v2.0.0""" +@unittest.skipIf(re.match(r'''2.0.0''', + glymur.version.openjpeg_version) is None, + "Tests only to be run on 2.0 official.") +class TestJp2k_2_0_official(unittest.TestCase): + """Test suite to only be run on v2.0 official.""" + @unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows") def test_extra_components_on_v2(self): """Can only write 4 components on 2.0+, should error out otherwise.""" - with patch('glymur.version.openjpeg_version', new="2.0.0"): - with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: - data = np.zeros((128, 128, 4), dtype=np.uint8) - with self.assertRaises(IOError): - Jp2k(tfile.name, data=data) + with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: + j = Jp2k(tfile.name, 'wb') + data = np.zeros((128, 128, 4), dtype=np.uint8) + with self.assertRaises(IOError): + j.write(data) @unittest.skipIf(glymur.version.openjpeg_version_tuple[0] < 2, @@ -901,30 +1034,32 @@ class TestJp2k_2_0(unittest.TestCase): j = Jp2k(self.jp2file) with self.assertRaises(IOError): # Start corner must be >= 0 - j[-1:1, -1:1] + j.read(area=(-1, -1, 1, 1)) with self.assertRaises(IOError): # End corner must be > 0 - j[10:0, 10:0] + j.read(area=(10, 10, 0, 0)) with self.assertRaises(IOError): # End corner must be >= start corner - j[10:8, 10:8] + j.read(area=(10, 10, 8, 8)) - @unittest.skipIf(os.name == "nt", fixtures.WINDOWS_TMP_FILE_MSG) + @unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows") def test_unrecognized_jp2_clrspace(self): """We only allow RGB and GRAYSCALE. Should error out with others""" with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: - data = np.zeros((128, 128, 3), dtype=np.uint8) + j = Jp2k(tfile.name, 'wb') with self.assertRaises(IOError): - Jp2k(tfile.name, data=data, colorspace='cmyk') + data = np.zeros((128, 128, 3), dtype=np.uint8) + j.write(data, colorspace='cmyk') - @unittest.skipIf(os.name == "nt", fixtures.WINDOWS_TMP_FILE_MSG) + @unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows") def test_asoc_label_box(self): """Test asoc and label box""" # Construct a fake file with an asoc and a label box, as # OpenJPEG doesn't have such a file. - data = Jp2k(self.jp2file)[::2, ::2] + data = Jp2k(self.jp2file).read(rlevel=1) with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: - Jp2k(tfile.name, data=data) + j = Jp2k(tfile.name, 'wb') + j.write(data) with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile2: @@ -965,8 +1100,7 @@ class TestJp2k_2_0(unittest.TestCase): self.assertEqual(jasoc.box[3].box[1].box_id, 'xml ') -@unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG) -@unittest.skipIf(re.match(r'''(1|2.0.0)''', +@unittest.skipIf(re.match(r'''0|1|2.0.0''', glymur.version.openjpeg_version) is not None, "Not to be run until unless 2.0.1 or higher is present") class TestJp2k_2_1(unittest.TestCase): @@ -979,31 +1113,33 @@ class TestJp2k_2_1(unittest.TestCase): def tearDown(self): pass - @unittest.skipIf(os.name == "nt", fixtures.WINDOWS_TMP_FILE_MSG) + @unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows") def test_grey_with_extra_component(self): """version 2.0 cannot write gray + extra""" with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: + j = Jp2k(tfile.name, 'wb') data = np.zeros((128, 128, 2), dtype=np.uint8) - j = Jp2k(tfile.name, data=data) + j.write(data) self.assertEqual(j.box[2].box[0].height, 128) self.assertEqual(j.box[2].box[0].width, 128) self.assertEqual(j.box[2].box[0].num_components, 2) self.assertEqual(j.box[2].box[1].colorspace, glymur.core.GREYSCALE) - @unittest.skipIf(os.name == "nt", fixtures.WINDOWS_TMP_FILE_MSG) + @unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows") def test_rgb_with_extra_component(self): """v2.0+ should be able to write extra components""" with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: + j = Jp2k(tfile.name, 'wb') data = np.zeros((128, 128, 4), dtype=np.uint8) - j = Jp2k(tfile.name, data=data) + j.write(data) self.assertEqual(j.box[2].box[0].height, 128) self.assertEqual(j.box[2].box[0].width, 128) self.assertEqual(j.box[2].box[0].num_components, 4) self.assertEqual(j.box[2].box[1].colorspace, glymur.core.SRGB) @unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG) - @unittest.skipIf(os.name == "nt", fixtures.WINDOWS_TMP_FILE_MSG) + @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. @@ -1021,25 +1157,24 @@ class TestJp2k_2_1(unittest.TestCase): tfile.write(data[offset+53:offset+55]) tfile.write(b'\x00') tfile.write(data[offset+57:offset+59]) + #tfile.write(data[3184:3186]) tfile.write(b'\x00') tfile.write(data[offset+59:]) + #tfile.write(data[3186:]) tfile.flush() with warnings.catch_warnings(): warnings.simplefilter('ignore') j = Jp2k(tfile.name) - regexp = re.compile(r'''OpenJPEG\slibrary\serror:\s+ - Invalid\svalues\sfor\scomp\s=\s0\s+ - :\sdx=1\sdy=0''', re.VERBOSE) - if sys.hexversion < 0x03020000: - with self.assertRaisesRegexp((IOError, OSError), - regexp): - j[::2, ::2] - else: - with self.assertRaisesRegex((IOError, OSError), - regexp): - j[::2, ::2] - + regexp = re.compile(r'''OpenJPEG\slibrary\serror:\s+ + Invalid\svalues\sfor\scomp\s=\s0\s+ + :\sdx=1\sdy=0''', re.VERBOSE) + if sys.hexversion < 0x03020000: + with self.assertRaisesRegexp((IOError, OSError), regexp): + j.read(rlevel=1) + else: + with self.assertRaisesRegex((IOError, OSError), regexp): + j.read(rlevel=1) @unittest.skipIf(OPJ_DATA_ROOT is None, "OPJ_DATA_ROOT environment variable not set") @@ -1048,31 +1183,32 @@ class TestParsing(unittest.TestCase): def setUp(self): self.jp2file = glymur.data.nemo() # Reset parseoptions for every test. - glymur.set_parseoptions(full_codestream=False) + glymur.set_parseoptions(codestream=True) def tearDown(self): - glymur.set_parseoptions(full_codestream=False) + glymur.set_parseoptions(codestream=True) @unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG) def test_bad_rsiz(self): """Should not warn if RSIZ when parsing is turned off.""" filename = opj_data_file('input/nonregression/edf_c2_1002767.jp2') - glymur.set_parseoptions(full_codestream=False) - Jp2k(filename) + glymur.set_parseoptions(codestream=False) + j = Jp2k(filename) - glymur.set_parseoptions(full_codestream=True) + glymur.set_parseoptions(codestream=True) with self.assertWarnsRegex(UserWarning, 'Invalid profile'): - Jp2k(filename) + jp2 = Jp2k(filename) + #@unittest.skip('trouble is a brewing...') def test_main_header(self): - """verify that the main header isn't loaded during normal parsing""" + """Verify that the main header is not loaded when parsing turned off.""" # The hidden _main_header attribute should show up after accessing it. + glymur.set_parseoptions(codestream=False) jp2 = Jp2k(self.jp2file) jp2c = jp2.box[4] - self.assertIsNone(jp2c._codestream) - jp2c.codestream - self.assertIsNotNone(jp2c._codestream) - + self.assertIsNone(jp2c._main_header) + main_header = jp2c.main_header + self.assertIsNotNone(jp2c._main_header) @unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG) @unittest.skipIf(OPJ_DATA_ROOT is None, @@ -1095,13 +1231,13 @@ class TestJp2kOpjDataRootWarnings(unittest.TestCase): """Should warn in case of bad ftyp brand.""" filename = opj_data_file('input/nonregression/edf_c2_1000290.jp2') with self.assertWarns(UserWarning): - Jp2k(filename) + jp2 = Jp2k(filename) def test_invalid_approximation(self): """Should warn in case of invalid approximation.""" filename = opj_data_file('input/nonregression/edf_c2_1015644.jp2') with self.assertWarnsRegex(UserWarning, 'Invalid approximation'): - Jp2k(filename) + jp2 = Jp2k(filename) def test_invalid_colorspace(self): """ @@ -1111,36 +1247,35 @@ class TestJp2kOpjDataRootWarnings(unittest.TestCase): """ filename = opj_data_file('input/nonregression/edf_c2_1103421.jp2') with self.assertWarns(UserWarning): - Jp2k(filename) + jp2 = Jp2k(filename) def test_stupid_windows_eol_at_end(self): """Garbage characters at the end of the file.""" filename = opj_data_file('input/nonregression/issue211.jp2') with self.assertWarns(UserWarning): - Jp2k(filename) + jp2 = Jp2k(filename) @unittest.skipIf(OPJ_DATA_ROOT is None, "OPJ_DATA_ROOT environment variable not set") class TestJp2kOpjDataRoot(unittest.TestCase): - """These tests should be run by just about all configurations.""" + """These tests should be run by just about all configuration.""" - @unittest.skipIf(re.match("0|1.[0-4]", glymur.version.openjpeg_version), - "Must have openjpeg 1.5 or higher to run") - @unittest.skipIf(os.name == "nt", fixtures.WINDOWS_TMP_FILE_MSG) + @unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows") def test_irreversible(self): """Irreversible""" filename = opj_data_file('input/nonregression/issue141.rawl') expdata = np.fromfile(filename, dtype=np.uint16) - expdata.resize((32, 2048)) + expdata.resize((2816, 2048)) with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: - j = Jp2k(tfile.name, data=expdata, irreversible=True, numres=5) + j = Jp2k(tfile.name, 'wb') + j.write(expdata, irreversible=True) codestream = j.get_codestream() self.assertEqual(codestream.segment[2].spcod[8], glymur.core.WAVELET_XFORM_9X7_IRREVERSIBLE) - actdata = j[:] + actdata = j.read() self.assertTrue(fixtures.mse(actdata, expdata) < 250) @unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG) @@ -1148,22 +1283,23 @@ class TestJp2kOpjDataRoot(unittest.TestCase): """Indices for pclr jpxfile if no color transform""" filename = opj_data_file('input/conformance/file9.jp2') with self.assertWarns(UserWarning): - jp2 = Jp2k(filename) - rgb = jp2[:] - jp2.ignore_pclr_cmap_cdef = True - idx = jp2[:] + j = Jp2k(filename) + rgb = j.read() + idx = j.read(ignore_pclr_cmap_cdef=True) self.assertEqual(rgb.shape, (512, 768, 3)) self.assertEqual(idx.shape, (512, 768)) # Should be able to manually reconstruct the RGB image from the palette # and indices. - palette = jp2.box[2].box[1].palette + palette = j.box[2].box[1].palette rgb_from_idx = np.zeros(rgb.shape, dtype=np.uint8) for r in np.arange(rgb.shape[0]): for c in np.arange(rgb.shape[1]): rgb_from_idx[r, c] = palette[idx[r, c]] np.testing.assert_array_equal(rgb, rgb_from_idx) + @unittest.skipIf(fixtures.OPENJPEG_NOT_AVAILABLE, + fixtures.OPENJPEG_NOT_AVAILABLE_MSG) def test_read_differing_subsamples(self): """should error out with read used on differently subsampled images""" # Verify that we error out appropriately if we use the read method @@ -1173,8 +1309,8 @@ class TestJp2kOpjDataRoot(unittest.TestCase): filename = opj_data_file('input/conformance/p0_05.j2k') j = Jp2k(filename) with self.assertRaises(RuntimeError): - j[:] - + j.read() + @unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG) def test_no_cxform_cmap(self): """Bands as physically ordered, not as physically intended""" @@ -1184,150 +1320,16 @@ class TestJp2kOpjDataRoot(unittest.TestCase): with self.assertWarns(UserWarning): # The file has a bad compatibility list entry. Not important here. j = Jp2k(filename) - ycbcr = j[:] - j.ignore_pclr_cmap_cdef = True - crcby = j[:] + ycbcr = j.read() + crcby = j.read(ignore_pclr_cmap_cdef=True) expected = np.zeros(ycbcr.shape, ycbcr.dtype) for k in range(crcby.shape[2]): - expected[:, :, crcby.shape[2] - k - 1] = crcby[:, :, k] + expected[:,:,crcby.shape[2] - k - 1] = crcby[:,:,k] np.testing.assert_array_equal(ycbcr, expected) -@unittest.skipIf(OPJ_DATA_ROOT is None, - "OPJ_DATA_ROOT environment variable not set") -class TestCodestreamOpjData(unittest.TestCase): - """Test suite for unusual codestream cases. Uses OPJ_DATA_ROOT""" - def setUp(self): - self.jp2file = glymur.data.nemo() - - def tearDown(self): - pass - - @unittest.skipIf(os.name == "nt", "Temporary file issue on window.") - def test_reserved_marker_segment(self): - """Reserved marker segments are ok.""" - - # Some marker segments were reserved in FCD15444-1. Since that - # standard is old, some of them may have come into use. - # - # Let's inject a reserved marker segment into a file that - # we know something about to make sure we can still parse it. - filename = os.path.join(OPJ_DATA_ROOT, 'input/conformance/p0_01.j2k') - with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: - with open(filename, 'rb') as ifile: - # Everything up until the first QCD marker. - read_buffer = ifile.read(45) - tfile.write(read_buffer) - - # Write the new marker segment, 0xff6f = 65391 - read_buffer = struct.pack('>HHB', int(65391), int(3), int(0)) - tfile.write(read_buffer) - - # Get the rest of the input file. - read_buffer = ifile.read() - tfile.write(read_buffer) - tfile.flush() - - codestream = Jp2k(tfile.name).get_codestream() - - self.assertEqual(codestream.segment[2].marker_id, '0xff6f') - self.assertEqual(codestream.segment[2].length, 3) - self.assertEqual(codestream.segment[2].data, b'\x00') - - def test_psot_is_zero(self): - """Psot=0 in SOT is perfectly legal. Issue #78.""" - filename = os.path.join(OPJ_DATA_ROOT, - 'input/nonregression/123.j2c') - j = Jp2k(filename) - codestream = j.get_codestream(header_only=False) - - # The codestream is valid, so we should be able to get the entire - # codestream, so the last one is EOC. - self.assertEqual(codestream.segment[-1].marker_id, 'EOC') - - def test_siz_segment_ssiz_signed(self): - """ssiz attribute to be removed in future release""" - filename = os.path.join(OPJ_DATA_ROOT, 'input/conformance/p0_03.j2k') - j = Jp2k(filename) - codestream = j.get_codestream() - - # The ssiz attribute was simply a tuple of raw bytes. - # The first 7 bits are interpreted as the bitdepth, the MSB determines - # whether or not it is signed. - self.assertEqual(codestream.segment[1].ssiz, (131,)) - - -class TestCodestreamRepr(unittest.TestCase): - - def setUp(self): - self.jp2file = glymur.data.nemo() - - def tearDown(self): - pass - - def test_soc(self): - """Test SOC segment repr""" - segment = glymur.codestream.SOCsegment() - newseg = eval(repr(segment)) - self.assertEqual(newseg.marker_id, 'SOC') - - def test_siz(self): - """Test SIZ segment repr""" - kwargs = {'rsiz': 0, - 'xysiz': (2592, 1456), - 'xyosiz': (0, 0), - 'xytsiz': (2592, 1456), - 'xytosiz': (0, 0), - 'Csiz': 3, - 'bitdepth': (8, 8, 8), - 'signed': (False, False, False), - 'xyrsiz': ((1, 1, 1), (1, 1, 1))} - segment = glymur.codestream.SIZsegment(**kwargs) - newseg = eval(repr(segment)) - self.assertEqual(newseg.marker_id, 'SIZ') - self.assertEqual(newseg.xsiz, 2592) - self.assertEqual(newseg.ysiz, 1456) - self.assertEqual(newseg.xosiz, 0) - self.assertEqual(newseg.yosiz, 0) - self.assertEqual(newseg.xtsiz, 2592) - self.assertEqual(newseg.ytsiz, 1456) - self.assertEqual(newseg.xtosiz, 0) - self.assertEqual(newseg.ytosiz, 0) - - self.assertEqual(newseg.xrsiz, (1, 1, 1)) - self.assertEqual(newseg.yrsiz, (1, 1, 1)) - self.assertEqual(newseg.bitdepth, (8, 8, 8)) - self.assertEqual(newseg.signed, (False, False, False)) - - def test_siz_segment_ssiz_unsigned(self): - """ssiz attribute to be removed in future release""" - j = Jp2k(self.jp2file) - codestream = j.get_codestream() - - # The ssiz attribute was simply a tuple of raw bytes. - # The first 7 bits are interpreted as the bitdepth, the MSB determines - # whether or not it is signed. - self.assertEqual(codestream.segment[1].ssiz, (7, 7, 7)) - - -class TestCodestream(unittest.TestCase): - """Test suite for unusual codestream cases.""" - - def setUp(self): - self.jp2file = glymur.data.nemo() - - def tearDown(self): - pass - - def test_siz_segment_ssiz_unsigned(self): - """ssiz attribute to be removed in future release""" - j = Jp2k(self.jp2file) - codestream = j.get_codestream() - - # The ssiz attribute was simply a tuple of raw bytes. - # The first 7 bits are interpreted as the bitdepth, the MSB determines - # whether or not it is signed. - self.assertEqual(codestream.segment[1].ssiz, (7, 7, 7)) +if __name__ == "__main__": + unittest.main() diff --git a/glymur/test/test_opj_suite.py b/glymur/test/test_opj_suite.py index 9e8a0eb..8eb5956 100644 --- a/glymur/test/test_opj_suite.py +++ b/glymur/test/test_opj_suite.py @@ -2,10 +2,34 @@ The tests defined here roughly correspond to what is in the OpenJPEG test suite. """ + +# Some test names correspond with openjpeg tests. Long names are ok in this +# case. +# pylint: disable=C0103 + +# All of these tests correspond to tests in openjpeg, so no docstring is really +# needed. +# pylint: disable=C0111 + +# This module is very long, cannot be helped. +# pylint: disable=C0302 + +# unittest fools pylint with "too many public methods" +# pylint: disable=R0904 + +# Some tests use numpy test infrastructure, which means the tests never +# reference "self", so pylint claims it should be a function. No, no, no. +# pylint: disable=R0201 + +# Many tests are pretty long and that can't be helped. +# pylint: disable=R0915 + +# asserWarns introduced in python 3.2 (python2.7/pylint issue) +# pylint: disable=E1101 + import re import sys import unittest -import warnings import numpy as np @@ -13,14 +37,16 @@ import glymur from glymur import Jp2k from glymur.jp2box import FileTypeBox, ImageHeaderBox, ColourSpecificationBox -from .fixtures import (OPJ_DATA_ROOT, MetadataBase, - WARNING_INFRASTRUCTURE_ISSUE, - WARNING_INFRASTRUCTURE_MSG, - mse, peak_tolerance, read_pgx, opj_data_file, - OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG) +from . import fixtures +from .fixtures import ( + OPJ_DATA_ROOT, MetadataBase, + WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG, + mse, peak_tolerance, read_pgx, opj_data_file +) -@unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG) +@unittest.skipIf(fixtures.OPENJPEG_NOT_AVAILABLE, + fixtures.OPENJPEG_NOT_AVAILABLE_MSG) @unittest.skipIf(OPJ_DATA_ROOT is None, "OPJ_DATA_ROOT environment variable not set") class TestSuite(unittest.TestCase): @@ -34,7 +60,7 @@ class TestSuite(unittest.TestCase): def test_ETS_C1P0_p0_01_j2k(self): jfile = opj_data_file('input/conformance/p0_01.j2k') jp2k = Jp2k(jfile) - jpdata = jp2k[:] + jpdata = jp2k.read(rlevel=0) pgxfile = opj_data_file('baseline/conformance/c1p0_01_0.pgx') pgxdata = read_pgx(pgxfile) @@ -44,7 +70,7 @@ class TestSuite(unittest.TestCase): def test_ETS_C1P0_p0_03_j2k(self): jfile = opj_data_file('input/conformance/p0_03.j2k') jp2k = Jp2k(jfile) - jpdata = jp2k[:] + jpdata = jp2k.read(rlevel=0) pgxfile = opj_data_file('baseline/conformance/c1p0_03_0.pgx') pgxdata = read_pgx(pgxfile) @@ -54,7 +80,7 @@ class TestSuite(unittest.TestCase): def test_ETS_C1P0_p0_04_j2k(self): jfile = opj_data_file('input/conformance/p0_04.j2k') jp2k = Jp2k(jfile) - jpdata = jp2k[:] + jpdata = jp2k.read(rlevel=0) pgxfile = opj_data_file('baseline/conformance/c1p0_04_0.pgx') pgxdata = read_pgx(pgxfile) @@ -74,7 +100,7 @@ class TestSuite(unittest.TestCase): def test_ETS_C1P0_p0_08_j2k(self): jfile = opj_data_file('input/conformance/p0_08.j2k') jp2k = Jp2k(jfile) - jpdata = jp2k[::2, ::2] + jpdata = jp2k.read(rlevel=1) pgxfile = opj_data_file('baseline/conformance/c1p0_08_0.pgx') pgxdata = read_pgx(pgxfile) @@ -91,7 +117,7 @@ class TestSuite(unittest.TestCase): def test_ETS_C1P0_p0_09_j2k(self): jfile = opj_data_file('input/conformance/p0_09.j2k') jp2k = Jp2k(jfile) - jpdata = jp2k[:] + jpdata = jp2k.read(rlevel=0) pgxfile = opj_data_file('baseline/conformance/c1p0_09_0.pgx') pgxdata = read_pgx(pgxfile) @@ -100,7 +126,7 @@ class TestSuite(unittest.TestCase): def test_ETS_C1P0_p0_11_j2k(self): jfile = opj_data_file('input/conformance/p0_11.j2k') jp2k = Jp2k(jfile) - jpdata = jp2k[:] + jpdata = jp2k.read(rlevel=0) pgxfile = opj_data_file('baseline/conformance/c1p0_11_0.pgx') pgxdata = read_pgx(pgxfile) @@ -109,7 +135,7 @@ class TestSuite(unittest.TestCase): def test_ETS_C1P0_p0_14_j2k(self): jfile = opj_data_file('input/conformance/p0_14.j2k') jp2k = Jp2k(jfile) - jpdata = jp2k[:] + jpdata = jp2k.read(rlevel=0) pgxfile = opj_data_file('baseline/conformance/c1p0_14_0.pgx') pgxdata = read_pgx(pgxfile) @@ -126,7 +152,7 @@ class TestSuite(unittest.TestCase): def test_ETS_C1P0_p0_15_j2k(self): jfile = opj_data_file('input/conformance/p0_15.j2k') jp2k = Jp2k(jfile) - jpdata = jp2k[:] + jpdata = jp2k.read(rlevel=0) pgxfile = opj_data_file('baseline/conformance/c1p0_15_0.pgx') pgxdata = read_pgx(pgxfile) @@ -135,7 +161,7 @@ class TestSuite(unittest.TestCase): def test_ETS_C1P0_p0_16_j2k(self): jfile = opj_data_file('input/conformance/p0_16.j2k') jp2k = Jp2k(jfile) - jpdata = jp2k[:] + jpdata = jp2k.read(rlevel=0) pgxfile = opj_data_file('baseline/conformance/c1p0_16_0.pgx') pgxdata = read_pgx(pgxfile) @@ -144,7 +170,7 @@ class TestSuite(unittest.TestCase): def test_ETS_C1P1_p1_01_j2k(self): jfile = opj_data_file('input/conformance/p1_01.j2k') jp2k = Jp2k(jfile) - jpdata = jp2k[:] + jpdata = jp2k.read(rlevel=0) pgxfile = opj_data_file('baseline/conformance/c1p1_01_0.pgx') pgxdata = read_pgx(pgxfile) @@ -153,7 +179,7 @@ class TestSuite(unittest.TestCase): def test_ETS_C1P1_p1_02_j2k(self): jfile = opj_data_file('input/conformance/p1_02.j2k') jp2k = Jp2k(jfile) - jpdata = jp2k[:] + jpdata = jp2k.read(rlevel=0) pgxfile = opj_data_file('baseline/conformance/c1p1_02_0.pgx') pgxdata = read_pgx(pgxfile) @@ -173,7 +199,7 @@ class TestSuite(unittest.TestCase): def test_ETS_C1P1_p1_04_j2k(self): jfile = opj_data_file('input/conformance/p1_04.j2k') jp2k = Jp2k(jfile) - jpdata = jp2k[:] + jpdata = jp2k.read() pgxfile = opj_data_file('baseline/conformance/c1p1_04_0.pgx') pgxdata = read_pgx(pgxfile) @@ -183,95 +209,95 @@ class TestSuite(unittest.TestCase): def test_NR_DEC_Bretagne2_j2k_1_decode(self): jfile = opj_data_file('input/nonregression/Bretagne2.j2k') jp2 = Jp2k(jfile) - jp2[:] + jp2.read() self.assertTrue(True) def test_NR_DEC__00042_j2k_2_decode(self): jfile = opj_data_file('input/nonregression/_00042.j2k') jp2 = Jp2k(jfile) - jp2[:] + jp2.read() self.assertTrue(True) def test_NR_DEC_buxI_j2k_9_decode(self): jfile = opj_data_file('input/nonregression/buxI.j2k') - Jp2k(jfile)[:] + Jp2k(jfile).read() self.assertTrue(True) def test_NR_DEC_buxR_j2k_10_decode(self): jfile = opj_data_file('input/nonregression/buxR.j2k') - Jp2k(jfile)[:] + Jp2k(jfile).read() self.assertTrue(True) def test_NR_DEC_Cannotreaddatawithnosizeknown_j2k_11_decode(self): relpath = 'input/nonregression/Cannotreaddatawithnosizeknown.j2k' jfile = opj_data_file(relpath) - Jp2k(jfile)[:] + Jp2k(jfile).read() self.assertTrue(True) def test_NR_DEC_cthead1_j2k_12_decode(self): jfile = opj_data_file('input/nonregression/cthead1.j2k') - Jp2k(jfile)[:] + Jp2k(jfile).read() self.assertTrue(True) def test_NR_DEC_CT_Phillips_JPEG2K_Decompr_Problem_j2k_13_decode(self): relpath = 'input/nonregression/CT_Phillips_JPEG2K_Decompr_Problem.j2k' jfile = opj_data_file(relpath) - Jp2k(jfile)[:] + Jp2k(jfile).read() self.assertTrue(True) def test_NR_DEC_j2k32_j2k_15_decode(self): jfile = opj_data_file('input/nonregression/j2k32.j2k') - Jp2k(jfile)[:] + Jp2k(jfile).read() self.assertTrue(True) def test_NR_DEC_MarkerIsNotCompliant_j2k_17_decode(self): jfile = opj_data_file('input/nonregression/MarkerIsNotCompliant.j2k') - Jp2k(jfile)[:] + Jp2k(jfile).read() self.assertTrue(True) def test_NR_DEC_Marrin_jp2_18_decode(self): jfile = opj_data_file('input/nonregression/Marrin.jp2') - Jp2k(jfile)[:] + Jp2k(jfile).read() self.assertTrue(True) def test_NR_DEC_movie_00000_j2k_20_decode(self): jfile = opj_data_file('input/nonregression/movie_00000.j2k') - Jp2k(jfile)[:] + Jp2k(jfile).read() self.assertTrue(True) def test_NR_DEC_movie_00001_j2k_21_decode(self): jfile = opj_data_file('input/nonregression/movie_00001.j2k') - Jp2k(jfile)[:] + Jp2k(jfile).read() self.assertTrue(True) def test_NR_DEC_movie_00002_j2k_22_decode(self): jfile = opj_data_file('input/nonregression/movie_00002.j2k') - Jp2k(jfile)[:] + Jp2k(jfile).read() self.assertTrue(True) def test_NR_DEC_orb_blue_lin_j2k_j2k_23_decode(self): jfile = opj_data_file('input/nonregression/orb-blue10-lin-j2k.j2k') - Jp2k(jfile)[:] + Jp2k(jfile).read() self.assertTrue(True) def test_NR_DEC_orb_blue_win_j2k_j2k_24_decode(self): jfile = opj_data_file('input/nonregression/orb-blue10-win-j2k.j2k') - Jp2k(jfile)[:] + Jp2k(jfile).read() self.assertTrue(True) def test_NR_DEC_relax_jp2_27_decode(self): jfile = opj_data_file('input/nonregression/relax.jp2') - Jp2k(jfile)[:] + Jp2k(jfile).read() self.assertTrue(True) def test_NR_DEC_test_lossless_j2k_28_decode(self): jfile = opj_data_file('input/nonregression/test_lossless.j2k') - Jp2k(jfile)[:] + Jp2k(jfile).read() self.assertTrue(True) def test_NR_DEC_pacs_ge_j2k_30_decode(self): jfile = opj_data_file('input/nonregression/pacs.ge.j2k') - Jp2k(jfile)[:] + Jp2k(jfile).read() self.assertTrue(True) @@ -294,14 +320,14 @@ class TestSuiteWarns(MetadataBase): with self.assertWarns(UserWarning): # Bad compatibility list item. jp2k = Jp2k(jfile) - jpdata = jp2k[:] + jpdata = jp2k.read() self.assertEqual(jpdata.shape, (512, 768, 3)) def test_ETS_JP2_file2(self): jfile = opj_data_file('input/conformance/file2.jp2') with self.assertWarns(UserWarning): jp2k = Jp2k(jfile) - jpdata = jp2k[:] + jpdata = jp2k.read() self.assertEqual(jpdata.shape, (640, 480, 3)) @unittest.skipIf(glymur.version.openjpeg_version_tuple[0] < 2, @@ -319,7 +345,7 @@ class TestSuiteWarns(MetadataBase): jfile = opj_data_file('input/conformance/file4.jp2') with self.assertWarns(UserWarning): jp2k = Jp2k(jfile) - jpdata = jp2k[:] + jpdata = jp2k.read() self.assertEqual(jpdata.shape, (512, 768)) def test_ETS_JP2_file5(self): @@ -328,45 +354,42 @@ class TestSuiteWarns(MetadataBase): # There's a warning for an unknown compatibility entry. # Ignore it here. jp2k = Jp2k(jfile) - jpdata = jp2k[:] + jpdata = jp2k.read() self.assertEqual(jpdata.shape, (512, 768, 3)) def test_ETS_JP2_file6(self): jfile = opj_data_file('input/conformance/file6.jp2') with self.assertWarns(UserWarning): jp2k = Jp2k(jfile) - jpdata = jp2k[:] + jpdata = jp2k.read() self.assertEqual(jpdata.shape, (512, 768)) def test_ETS_JP2_file7(self): jfile = opj_data_file('input/conformance/file7.jp2') with self.assertWarns(UserWarning): jp2k = Jp2k(jfile) - jpdata = jp2k[:] + jpdata = jp2k.read() self.assertEqual(jpdata.shape, (640, 480, 3)) def test_ETS_JP2_file8(self): jfile = opj_data_file('input/conformance/file8.jp2') with self.assertWarns(UserWarning): jp2k = Jp2k(jfile) - jpdata = jp2k[:] + jpdata = jp2k.read() self.assertEqual(jpdata.shape, (400, 700)) def test_ETS_JP2_file9(self): jfile = opj_data_file('input/conformance/file9.jp2') with self.assertWarns(UserWarning): jp2k = Jp2k(jfile) - jpdata = jp2k[:] + jpdata = jp2k.read() self.assertEqual(jpdata.shape, (512, 768, 3)) - def test_NR_broken1_jp2_dump(self): - jfile = opj_data_file('input/nonregression/broken1.jp2') + def test_NR_broken_jp2_dump(self): + jfile = opj_data_file('input/nonregression/broken.jp2') - # The colr box has a ridiculously incorrect box length. - regex = re.compile(r'''b'colr'\sbox\shas\sincorrect\sbox\slength\s - \(\d+\)''', - re.VERBOSE) - with self.assertWarnsRegex(UserWarning, regex): + with self.assertWarns(UserWarning): + # colr box has bad length. jp2 = Jp2k(jfile) ids = [box.box_id for box in jp2.box] @@ -385,24 +408,23 @@ class TestSuiteWarns(MetadataBase): expected = ColourSpecificationBox(colorspace=glymur.core.SRGB) self.verifyColourSpecificationBox(jp2.box[2].box[1], expected) - c = jp2.box[3].codestream + c = jp2.box[3].main_header ids = [x.marker_id for x in c.segment] expected = ['SOC', 'SIZ', 'CME', 'COD', 'QCD', 'QCC', 'QCC'] self.assertEqual(ids, expected) kwargs = {'rsiz': 0, 'xysiz': (203, 152), 'xyosiz': (0, 0), - 'xytsiz': (203, 152), 'xytosiz': (0, 0), - 'bitdepth': (8, 8, 8), - 'signed': (False, False, False), - 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} + 'xytsiz': (203, 152), 'xytosiz': (0, 0), 'bitdepth': (8, 8, 8), + 'signed': (False, False, False), + 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + glymur.codestream.SIZsegment(**kwargs)) pargs = (glymur.core.RCME_ISO_8859_1, - "Creator: JasPer Version 1.701.0".encode()) + "Creator: JasPer Version 1.701.0".encode()) self.verifyCMEsegment(c.segment[2], - glymur.codestream.CMEsegment(*pargs)) + glymur.codestream.CMEsegment(*pargs)) # COD: Coding style default self.assertFalse(c.segment[3].scod & 2) # no sop @@ -414,7 +436,7 @@ class TestSuiteWarns(MetadataBase): self.assertEqual(tuple(c.segment[3].code_block_size), (64, 64)) # cblk self.verify_codeblock_style(c.segment[3].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[3].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(len(c.segment[3].spcod), 9) @@ -450,19 +472,21 @@ class TestSuiteWarns(MetadataBase): jfile = opj_data_file('input/nonregression/orb-blue10-lin-jp2.jp2') with self.assertWarns(UserWarning): # This file has an invalid ICC profile - Jp2k(jfile)[:] + Jp2k(jfile).read() self.assertTrue(True) def test_NR_DEC_orb_blue_win_jp2_26_decode(self): jfile = opj_data_file('input/nonregression/orb-blue10-win-jp2.jp2') with self.assertWarns(UserWarning): - Jp2k(jfile)[:] + Jp2k(jfile).read() self.assertTrue(True) +@unittest.skipIf(fixtures.OPENJPEG_NOT_AVAILABLE, + fixtures.OPENJPEG_NOT_AVAILABLE_MSG) @unittest.skipIf(OPJ_DATA_ROOT is None, "OPJ_DATA_ROOT environment variable not set") -@unittest.skipIf(glymur.version.openjpeg_version_tuple[0] != 2, +@unittest.skipIf(glymur.version.openjpeg_version_tuple[0] == 1, "Feature not supported in glymur until openjpeg 2.0") class TestSuiteBands(unittest.TestCase): """ @@ -555,9 +579,11 @@ class TestSuiteBands(unittest.TestCase): self.assertTrue(True) +@unittest.skipIf(fixtures.OPENJPEG_NOT_AVAILABLE, + fixtures.OPENJPEG_NOT_AVAILABLE_MSG) @unittest.skipIf(OPJ_DATA_ROOT is None, "OPJ_DATA_ROOT environment variable not set") -@unittest.skipIf(glymur.version.openjpeg_version_tuple[0] < 2, +@unittest.skipIf(glymur.version.openjpeg_version_tuple[0] == 1, "Tests not passing until 2.0") class TestSuite2point0(unittest.TestCase): """Runs tests introduced in version 2.0 or that pass only in 2.0""" @@ -571,7 +597,7 @@ class TestSuite2point0(unittest.TestCase): def test_ETS_C1P0_p0_10_j2k(self): jfile = opj_data_file('input/conformance/p0_10.j2k') jp2k = Jp2k(jfile) - jpdata = jp2k[:] + jpdata = jp2k.read(rlevel=0) pgxfile = opj_data_file('baseline/conformance/c1p0_10_0.pgx') pgxdata = read_pgx(pgxfile) @@ -587,16 +613,13 @@ class TestSuite2point0(unittest.TestCase): @unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG) def test_NR_DEC_broken2_jp2_5_decode(self): - """ - Invalid marker ID on codestream, Null pointer access upon read. - """ + # Null pointer access jfile = opj_data_file('input/nonregression/broken2.jp2') - regex = re.compile(r'''Invalid\smarker\sid\sencountered\sat\sbyte\s - \d+\sin\scodestream:\s*"0x[a-fA-F0-9]{4}"''', - re.VERBOSE) with self.assertRaises(IOError): - with self.assertWarnsRegex(UserWarning, regex): - Jp2k(jfile)[:] + with self.assertWarns(UserWarning): + # Invalid marker ID. + Jp2k(jfile).read() + self.assertTrue(True) @unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG) def test_NR_DEC_broken4_jp2_7_decode(self): @@ -604,7 +627,7 @@ class TestSuite2point0(unittest.TestCase): with self.assertRaises(IOError): with self.assertWarns(UserWarning): # invalid number of subbands, bad marker ID - Jp2k(jfile)[:] + Jp2k(jfile).read() self.assertTrue(True) @unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG) @@ -616,344 +639,11 @@ class TestSuite2point0(unittest.TestCase): if glymur.version.openjpeg_version_tuple[0] < 2: with self.assertWarns(UserWarning): # Incorrect warning issued about tile parts. - Jp2k(jfile)[:] + Jp2k(jfile).read() else: - Jp2k(jfile)[:] + Jp2k(jfile).read() self.assertTrue(True) -@unittest.skipIf(OPJ_DATA_ROOT is None, - "OPJ_DATA_ROOT environment variable not set") -@unittest.skipIf(re.match(r'''0|1|2.0.0''', - glymur.version.openjpeg_version) is not None, - "Only supported in 2.0.1 or higher") -class TestSuite2point1(unittest.TestCase): - """Runs tests introduced in version 2.0+ or that pass only in 2.0+""" - - def setUp(self): - pass - - def tearDown(self): - pass - - @unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG) - def test_NR_DEC_text_GBR_jp2_29_decode(self): - jfile = opj_data_file('input/nonregression/text_GBR.jp2') - with self.assertWarns(UserWarning): - # brand is 'jp2 ', but has any icc profile. - jp2 = Jp2k(jfile) - jp2[:] - self.assertTrue(True) - - def test_NR_DEC_kodak_2layers_lrcp_j2c_31_decode(self): - jfile = opj_data_file('input/nonregression/kodak_2layers_lrcp.j2c') - Jp2k(jfile)[:] - self.assertTrue(True) - - def test_NR_DEC_kodak_2layers_lrcp_j2c_32_decode(self): - jfile = opj_data_file('input/nonregression/kodak_2layers_lrcp.j2c') - Jp2k(jfile)[::4, ::4] - self.assertTrue(True) - - def test_NR_DEC_issue104_jpxstream_jp2_33_decode(self): - jfile = opj_data_file('input/nonregression/issue104_jpxstream.jp2') - Jp2k(jfile)[:] - self.assertTrue(True) - - def test_NR_DEC_mem_b2b86b74_2753_jp2_35_decode(self): - jfile = opj_data_file('input/nonregression/mem-b2b86b74-2753.jp2') - Jp2k(jfile)[:] - self.assertTrue(True) - - @unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG) - def test_NR_DEC_gdal_fuzzer_unchecked_num_resolutions_jp2_36_decode(self): - f = 'input/nonregression/gdal_fuzzer_unchecked_numresolutions.jp2' - jfile = opj_data_file(f) - with self.assertWarns(UserWarning): - # Invalid number of resolutions. - j = Jp2k(jfile) - with self.assertRaises(IOError): - j[:] - - @unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG) - def test_NR_DEC_gdal_fuzzer_check_number_of_tiles_jp2_38_decode(self): - relpath = 'input/nonregression/gdal_fuzzer_check_number_of_tiles.jp2' - jfile = opj_data_file(relpath) - with self.assertWarns(UserWarning): - # Invalid number of tiles. - j = Jp2k(jfile) - with self.assertRaises(IOError): - j[:] - - @unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG) - def test_NR_DEC_gdal_fuzzer_check_comp_dx_dy_jp2_39_decode(self): - relpath = 'input/nonregression/gdal_fuzzer_check_comp_dx_dy.jp2' - jfile = opj_data_file(relpath) - with self.assertWarns(UserWarning): - # Invalid subsampling value - with self.assertRaises(IOError): - Jp2k(jfile)[:] - - def test_NR_DEC_file_409752_jp2_40_decode(self): - jfile = opj_data_file('input/nonregression/file409752.jp2') - with self.assertRaises(RuntimeError): - Jp2k(jfile)[:] - - def test_NR_DEC_issue206_image_000_jp2_42_decode(self): - jfile = opj_data_file('input/nonregression/issue206_image-000.jp2') - Jp2k(jfile)[:] - self.assertTrue(True) - - def test_NR_DEC_p1_04_j2k_57_decode(self): - jfile = opj_data_file('input/conformance/p1_04.j2k') - jp2k = Jp2k(jfile) - tdata = jp2k[896:1024, 896:1024] # last tile - odata = jp2k[:] - np.testing.assert_array_equal(tdata, odata[896:1024, 896:1024]) - - @unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG) - def test_NR_DEC_p1_04_j2k_57_decode_0p7_backwards_compatibility(self): - """ - 0.7.x usage deprecated - """ - jfile = opj_data_file('input/conformance/p1_04.j2k') - jp2k = Jp2k(jfile) - if sys.hexversion < 0x03000000: - with warnings.catch_warnings(): - # Suppress a warning due to deprecated syntax - warnings.simplefilter("ignore") - tdata = jp2k.read(tile=63) # last tile - else: - with self.assertWarns(DeprecationWarning): - tdata = jp2k.read(tile=63) # last tile - odata = jp2k[:] - np.testing.assert_array_equal(tdata, odata[896:1024, 896:1024]) - - @unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG) - def test_NR_DEC_p1_04_j2k_58_decode_0p7_backwards_compatibility(self): - """ - 0.7.x usage deprecated - """ - jfile = opj_data_file('input/conformance/p1_04.j2k') - jp2k = Jp2k(jfile) - if sys.hexversion < 0x03000000: - with warnings.catch_warnings(): - # Suppress a warning due to deprecated syntax - tdata = jp2k.read(tile=63, rlevel=2) # last tile - else: - with self.assertWarns(DeprecationWarning): - tdata = jp2k.read(tile=63, rlevel=2) # last tile - odata = jp2k[::4, ::4] - np.testing.assert_array_equal(tdata, odata[224:256, 224:256]) - - def test_NR_DEC_p1_04_j2k_58_decode(self): - jfile = opj_data_file('input/conformance/p1_04.j2k') - jp2k = Jp2k(jfile) - tdata = jp2k[896:1024:4, 896:1024:4] # last tile - odata = jp2k[::4, ::4] - np.testing.assert_array_equal(tdata, odata[224:256, 224:256]) - - def test_NR_DEC_p1_04_j2k_59_decode(self): - jfile = opj_data_file('input/conformance/p1_04.j2k') - jp2k = Jp2k(jfile) - tdata = jp2k[128:256, 512:640] # 2nd row, 5th column - odata = jp2k[:] - np.testing.assert_array_equal(tdata, odata[128:256, 512:640]) - - def test_NR_DEC_p1_04_j2k_60_decode(self): - jfile = opj_data_file('input/conformance/p1_04.j2k') - jp2k = Jp2k(jfile) - tdata = jp2k[128:256:2, 512:640:2] # 2nd row, 5th column - odata = jp2k[::2, ::2] - np.testing.assert_array_equal(tdata, odata[64:128, 256:320]) - - @unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG) - def test_NR_DEC_jp2_36_decode(self): - lst = ('input', - 'nonregression', - 'gdal_fuzzer_assert_in_opj_j2k_read_SQcd_SQcc.patch.jp2') - jfile = opj_data_file('/'.join(lst)) - with self.assertWarns(UserWarning): - # Invalid component number. - j = Jp2k(jfile) - with self.assertRaises(IOError): - j[:] - - -@unittest.skipIf(OPJ_DATA_ROOT is None, - "OPJ_DATA_ROOT environment variable not set") -@unittest.skipIf(re.match(r'''0|1|2.0.0''', - glymur.version.openjpeg_version) is not None, - "Only supported in 2.0.1 or higher") -class TestReadArea(unittest.TestCase): - """ - Runs tests introduced in version 2.0+ or that pass only in 2.0+ - - Specifically for read method with area parameter. - """ - @classmethod - def setUpClass(self): - - jfile = opj_data_file('input/conformance/p1_04.j2k') - self.j2k = Jp2k(jfile) - self.j2k_data = self.j2k[:] - self.j2k_half_data = self.j2k[::2, ::2] - self.j2k_quarter_data = self.j2k[::4, ::4] - - jfile = opj_data_file('input/conformance/p1_06.j2k') - self.j2k_p1_06 = Jp2k(jfile) - - def test_NR_DEC_p1_04_j2k_43_decode(self): - actual = self.j2k[:1024, :1024] - expected = self.j2k_data - np.testing.assert_array_equal(actual, expected) - - def test_NR_DEC_p1_04_j2k_44_decode(self): - actual = self.j2k[640:768, 512:640] - expected = self.j2k_data[640:768, 512:640] - np.testing.assert_array_equal(actual, expected) - - def test_NR_DEC_p1_04_j2k_45_decode(self): - actual = self.j2k[896:1024, 896:1024] - expected = self.j2k_data[896:1024, 896:1024] - np.testing.assert_array_equal(actual, expected) - - def test_NR_DEC_p1_04_j2k_46_decode(self): - actual = self.j2k[500:800, 100:300] - expected = self.j2k_data[500:800, 100:300] - np.testing.assert_array_equal(actual, expected) - - def test_NR_DEC_p1_04_j2k_47_decode(self): - actual = self.j2k[520:600, 260:360] - expected = self.j2k_data[520:600, 260:360] - np.testing.assert_array_equal(actual, expected) - - def test_NR_DEC_p1_04_j2k_48_decode(self): - actual = self.j2k[520:660, 260:360] - expected = self.j2k_data[520:660, 260:360] - np.testing.assert_array_equal(actual, expected) - - def test_NR_DEC_p1_04_j2k_49_decode(self): - actual = self.j2k[520:600, 360:400] - expected = self.j2k_data[520:600, 360:400] - np.testing.assert_array_equal(actual, expected) - - def test_NR_DEC_p1_04_j2k_50_decode(self): - actual = self.j2k[:1024:4, :1024:4] - expected = self.j2k_quarter_data - np.testing.assert_array_equal(actual, expected) - - def test_NR_DEC_p1_04_j2k_51_decode(self): - actual = self.j2k[640:768:4, 512:640:4] - expected = self.j2k_quarter_data[160:192, 128:160] - np.testing.assert_array_equal(actual, expected) - - def test_NR_DEC_p1_04_j2k_52_decode(self): - actual = self.j2k[896:1024:4, 896:1024:4] - expected = self.j2k_quarter_data[224:352, 224:352] - np.testing.assert_array_equal(actual, expected) - - def test_NR_DEC_p1_04_j2k_53_decode(self): - actual = self.j2k[500:800:4, 100:300:4] - expected = self.j2k_quarter_data[125:200, 25:75] - np.testing.assert_array_equal(actual, expected) - - def test_NR_DEC_p1_04_j2k_54_decode(self): - actual = self.j2k[520:600:4, 260:360:4] - expected = self.j2k_quarter_data[130:150, 65:90] - np.testing.assert_array_equal(actual, expected) - - def test_NR_DEC_p1_04_j2k_55_decode(self): - actual = self.j2k[520:660:4, 260:360:4] - expected = self.j2k_quarter_data[130:165, 65:90] - np.testing.assert_array_equal(actual, expected) - - def test_NR_DEC_p1_04_j2k_56_decode(self): - actual = self.j2k[520:600:4, 360:400:4] - expected = self.j2k_quarter_data[130:150, 90:100] - np.testing.assert_array_equal(actual, expected) - - def test_NR_DEC_p1_06_j2k_70_decode(self): - actual = self.j2k_p1_06[9:12:2, 9:12:2] - self.assertEqual(actual.shape, (1, 1, 3)) - - def test_NR_DEC_p1_06_j2k_71_decode(self): - actual = self.j2k_p1_06[10:12:2, 4:10:2] - self.assertEqual(actual.shape, (1, 3, 3)) - - def test_NR_DEC_p1_06_j2k_72_decode(self): - ssdata = self.j2k_p1_06[3:9:2, 3:9:2] - self.assertEqual(ssdata.shape, (3, 3, 3)) - - def test_NR_DEC_p1_06_j2k_73_decode(self): - ssdata = self.j2k_p1_06[4:7:2, 4:7:2] - self.assertEqual(ssdata.shape, (2, 2, 3)) - - def test_NR_DEC_p1_06_j2k_74_decode(self): - ssdata = self.j2k_p1_06[4:5:2, 4:5:2] - self.assertEqual(ssdata.shape, (1, 1, 3)) - - def test_NR_DEC_p1_06_j2k_75_decode(self): - # Image size would be 0 x 0. - with self.assertRaises((IOError, OSError)): - self.j2k_p1_06[9:12:4, 9:12:4] - - def test_NR_DEC_p0_04_j2k_85_decode(self): - actual = self.j2k[:256, :256] - expected = self.j2k_data[:256, :256] - np.testing.assert_array_equal(actual, expected) - - def test_NR_DEC_p0_04_j2k_86_decode(self): - actual = self.j2k[:128, 128:256] - expected = self.j2k_data[:128, 128:256] - np.testing.assert_array_equal(actual, expected) - - def test_NR_DEC_p0_04_j2k_87_decode(self): - actual = self.j2k[10:200, 50:120] - expected = self.j2k_data[10:200, 50:120] - np.testing.assert_array_equal(actual, expected) - - def test_NR_DEC_p0_04_j2k_88_decode(self): - actual = self.j2k[150:210, 10:190] - expected = self.j2k_data[150:210, 10:190] - np.testing.assert_array_equal(actual, expected) - - def test_NR_DEC_p0_04_j2k_89_decode(self): - actual = self.j2k[80:150, 100:200] - expected = self.j2k_data[80:150, 100:200] - np.testing.assert_array_equal(actual, expected) - - def test_NR_DEC_p0_04_j2k_90_decode(self): - actual = self.j2k[20:50, 150:200] - expected = self.j2k_data[20:50, 150:200] - np.testing.assert_array_equal(actual, expected) - - def test_NR_DEC_p0_04_j2k_91_decode(self): - actual = self.j2k[:256:4, :256:4] - expected = self.j2k_quarter_data[0:64, 0:64] - np.testing.assert_array_equal(actual, expected) - - def test_NR_DEC_p0_04_j2k_92_decode(self): - actual = self.j2k[:128:4, 128:256:4] - expected = self.j2k_quarter_data[:32, 32:64] - np.testing.assert_array_equal(actual, expected) - - def test_NR_DEC_p0_04_j2k_93_decode(self): - actual = self.j2k[10:200:4, 50:120:4] - expected = self.j2k_quarter_data[3:50, 13:30] - np.testing.assert_array_equal(actual, expected) - - def test_NR_DEC_p0_04_j2k_94_decode(self): - actual = self.j2k[150:210:4, 10:190:4] - expected = self.j2k_quarter_data[38:53, 3:48] - np.testing.assert_array_equal(actual, expected) - - def test_NR_DEC_p0_04_j2k_95_decode(self): - actual = self.j2k[80:150:4, 100:200:4] - expected = self.j2k_quarter_data[20:38, 25:50] - np.testing.assert_array_equal(actual, expected) - - def test_NR_DEC_p0_04_j2k_96_decode(self): - actual = self.j2k[20:50:4, 150:200:4] - expected = self.j2k_quarter_data[5:13, 38:50] - np.testing.assert_array_equal(actual, expected) +if __name__ == "__main__": + unittest.main() diff --git a/glymur/test/test_opj_suite_2p1.py b/glymur/test/test_opj_suite_2p1.py new file mode 100644 index 0000000..4977a6e --- /dev/null +++ b/glymur/test/test_opj_suite_2p1.py @@ -0,0 +1,347 @@ +""" +The tests defined here roughly correspond to what is in the OpenJPEG test +suite. +""" + +# Some test names correspond with openjpeg tests. Long names are ok in this +# case. +# pylint: disable=C0103 + +# All of these tests correspond to tests in openjpeg, so no docstring is really +# needed. +# pylint: disable=C0111 + +# This module is very long, cannot be helped. +# pylint: disable=C0302 + +# unittest fools pylint with "too many public methods" +# pylint: disable=R0904 + +# Some tests use numpy test infrastructure, which means the tests never +# reference "self", so pylint claims it should be a function. No, no, no. +# pylint: disable=R0201 + +# Many tests are pretty long and that can't be helped. +# pylint: disable=R0915 + +# asserWarns introduced in python 3.2 (python2.7/pylint issue) +# pylint: disable=E1101 + +import re +import sys +import unittest + +import numpy as np + +from glymur import Jp2k +import glymur + +from . import fixtures +from .fixtures import OPJ_DATA_ROOT +from .fixtures import WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG +from .fixtures import mse, peak_tolerance, read_pgx, opj_data_file + + +@unittest.skipIf(fixtures.OPENJPEG_NOT_AVAILABLE, + fixtures.OPENJPEG_NOT_AVAILABLE_MSG) +@unittest.skipIf(OPJ_DATA_ROOT is None, + "OPJ_DATA_ROOT environment variable not set") +@unittest.skipIf(re.match(r'''(1|2.0.0)''', + glymur.version.openjpeg_version) is not None, + "Only supported in 2.0.1 or higher") +class TestSuite2point1(unittest.TestCase): + """Runs tests introduced in version 2.0+ or that pass only in 2.0+""" + + def setUp(self): + pass + + def tearDown(self): + pass + + @unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG) + def test_NR_DEC_text_GBR_jp2_29_decode(self): + jfile = opj_data_file('input/nonregression/text_GBR.jp2') + with self.assertWarns(UserWarning): + # brand is 'jp2 ', but has any icc profile. + jp2 = Jp2k(jfile) + jp2.read() + self.assertTrue(True) + + def test_NR_DEC_kodak_2layers_lrcp_j2c_31_decode(self): + jfile = opj_data_file('input/nonregression/kodak_2layers_lrcp.j2c') + Jp2k(jfile).read() + self.assertTrue(True) + + def test_NR_DEC_kodak_2layers_lrcp_j2c_32_decode(self): + jfile = opj_data_file('input/nonregression/kodak_2layers_lrcp.j2c') + Jp2k(jfile).read(layer=2) + self.assertTrue(True) + + def test_NR_DEC_issue104_jpxstream_jp2_33_decode(self): + jfile = opj_data_file('input/nonregression/issue104_jpxstream.jp2') + Jp2k(jfile).read() + self.assertTrue(True) + + def test_NR_DEC_mem_b2b86b74_2753_jp2_35_decode(self): + jfile = opj_data_file('input/nonregression/mem-b2b86b74-2753.jp2') + Jp2k(jfile).read() + self.assertTrue(True) + + @unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG) + def test_NR_DEC_gdal_fuzzer_unchecked_num_resolutions_jp2_36_decode(self): + f = 'input/nonregression/gdal_fuzzer_unchecked_numresolutions.jp2' + jfile = opj_data_file(f) + with self.assertWarns(UserWarning): + # Invalid number of resolutions. + j = Jp2k(jfile) + with self.assertRaises(IOError): + j.read() + + @unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG) + def test_NR_DEC_gdal_fuzzer_check_number_of_tiles_jp2_38_decode(self): + relpath = 'input/nonregression/gdal_fuzzer_check_number_of_tiles.jp2' + jfile = opj_data_file(relpath) + with self.assertWarns(UserWarning): + # Invalid number of tiles. + j = Jp2k(jfile) + with self.assertRaises(IOError): + j.read() + + @unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG) + def test_NR_DEC_gdal_fuzzer_check_comp_dx_dy_jp2_39_decode(self): + relpath = 'input/nonregression/gdal_fuzzer_check_comp_dx_dy.jp2' + jfile = opj_data_file(relpath) + with self.assertWarns(UserWarning): + # Invalid subsampling value + with self.assertRaises(IOError): + Jp2k(jfile).read() + + def test_NR_DEC_file_409752_jp2_40_decode(self): + jfile = opj_data_file('input/nonregression/file409752.jp2') + with self.assertRaises(RuntimeError): + Jp2k(jfile).read() + + def test_NR_DEC_issue206_image_000_jp2_42_decode(self): + jfile = opj_data_file('input/nonregression/issue206_image-000.jp2') + Jp2k(jfile).read() + self.assertTrue(True) + + def test_NR_DEC_p1_04_j2k_57_decode(self): + jfile = opj_data_file('input/conformance/p1_04.j2k') + jp2k = Jp2k(jfile) + tdata = jp2k.read(tile=63) # last tile + odata = jp2k.read() + np.testing.assert_array_equal(tdata, odata[896:1024, 896:1024]) + + def test_NR_DEC_p1_04_j2k_58_decode(self): + jfile = opj_data_file('input/conformance/p1_04.j2k') + jp2k = Jp2k(jfile) + tdata = jp2k.read(tile=63, rlevel=2) # last tile + odata = jp2k.read(rlevel=2) + np.testing.assert_array_equal(tdata, odata[224:256, 224:256]) + + def test_NR_DEC_p1_04_j2k_59_decode(self): + jfile = opj_data_file('input/conformance/p1_04.j2k') + jp2k = Jp2k(jfile) + tdata = jp2k.read(tile=12) # 2nd row, 5th column + odata = jp2k.read() + np.testing.assert_array_equal(tdata, odata[128:256, 512:640]) + + def test_NR_DEC_p1_04_j2k_60_decode(self): + jfile = opj_data_file('input/conformance/p1_04.j2k') + jp2k = Jp2k(jfile) + tdata = jp2k.read(tile=12, rlevel=1) # 2nd row, 5th column + odata = jp2k.read(rlevel=1) + np.testing.assert_array_equal(tdata, odata[64:128, 256:320]) + + @unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG) + def test_NR_DEC_jp2_36_decode(self): + lst = ('input', + 'nonregression', + 'gdal_fuzzer_assert_in_opj_j2k_read_SQcd_SQcc.patch.jp2') + jfile = opj_data_file('/'.join(lst)) + with self.assertWarns(UserWarning): + # Invalid component number. + j = Jp2k(jfile) + with self.assertRaises(IOError): + j.read() + +@unittest.skipIf(fixtures.OPENJPEG_NOT_AVAILABLE, + fixtures.OPENJPEG_NOT_AVAILABLE_MSG) +@unittest.skipIf(OPJ_DATA_ROOT is None, + "OPJ_DATA_ROOT environment variable not set") +@unittest.skipIf(re.match(r'''(1|2.0.0)''', + glymur.version.openjpeg_version) is not None, + "Only supported in 2.0.1 or higher") +class TestReadArea(unittest.TestCase): + """ + Runs tests introduced in version 2.0+ or that pass only in 2.0+ + + Specifically for read method with area parameter. + """ + @classmethod + def setUpClass(self): + + jfile = opj_data_file('input/conformance/p1_04.j2k') + self.j2k = Jp2k(jfile) + self.j2k_data = self.j2k.read() + self.j2k_half_data = self.j2k.read(rlevel=1) + self.j2k_quarter_data = self.j2k.read(rlevel=2) + + jfile = opj_data_file('input/conformance/p1_06.j2k') + self.j2k_p1_06 = Jp2k(jfile) + + def test_NR_DEC_p1_04_j2k_43_decode(self): + actual = self.j2k.read(area=(0, 0, 1024, 1024)) + expected = self.j2k_data + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p1_04_j2k_44_decode(self): + actual = self.j2k.read(area=(640, 512, 768, 640)) + expected = self.j2k_data[640:768, 512:640] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p1_04_j2k_45_decode(self): + actual = self.j2k.read(area=(896, 896, 1024, 1024)) + expected = self.j2k_data[896:1024, 896:1024] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p1_04_j2k_46_decode(self): + actual = self.j2k.read(area=(500, 100, 800, 300)) + expected = self.j2k_data[500:800, 100:300] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p1_04_j2k_47_decode(self): + actual = self.j2k.read(area=(520, 260, 600, 360)) + expected = self.j2k_data[520:600, 260:360] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p1_04_j2k_48_decode(self): + actual = self.j2k.read(area=(520, 260, 660, 360)) + expected = self.j2k_data[520:660, 260:360] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p1_04_j2k_49_decode(self): + actual = self.j2k.read(area=(520, 360, 600, 400)) + expected = self.j2k_data[520:600, 360:400] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p1_04_j2k_50_decode(self): + actual = self.j2k.read(area=(0, 0, 1024, 1024), rlevel=2) + expected = self.j2k_quarter_data + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p1_04_j2k_51_decode(self): + actual = self.j2k.read(area=(640, 512, 768, 640), rlevel=2) + expected = self.j2k_quarter_data[160:192, 128:160] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p1_04_j2k_52_decode(self): + actual = self.j2k.read(area=(896, 896, 1024, 1024), rlevel=2) + expected = self.j2k_quarter_data[224:352, 224:352] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p1_04_j2k_53_decode(self): + actual = self.j2k.read(area=(500, 100, 800, 300), rlevel=2) + expected = self.j2k_quarter_data[125:200, 25:75] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p1_04_j2k_54_decode(self): + actual = self.j2k.read(area=(520, 260, 600, 360), rlevel=2) + expected = self.j2k_quarter_data[130:150, 65:90] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p1_04_j2k_55_decode(self): + actual = self.j2k.read(area=(520, 260, 660, 360), rlevel=2) + expected = self.j2k_quarter_data[130:165, 65:90] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p1_04_j2k_56_decode(self): + actual = self.j2k.read(area=(520, 360, 600, 400), rlevel=2) + expected = self.j2k_quarter_data[130:150, 90:100] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p1_06_j2k_70_decode(self): + actual = self.j2k_p1_06.read(area=(9, 9, 12, 12), rlevel=1) + self.assertEqual(actual.shape, (1, 1, 3)) + + def test_NR_DEC_p1_06_j2k_71_decode(self): + actual = self.j2k_p1_06.read(area=(10, 4, 12, 10), rlevel=1) + self.assertEqual(actual.shape, (1, 3, 3)) + + def test_NR_DEC_p1_06_j2k_72_decode(self): + ssdata = self.j2k_p1_06.read(area=(3, 3, 9, 9), rlevel=1) + self.assertEqual(ssdata.shape, (3, 3, 3)) + + def test_NR_DEC_p1_06_j2k_73_decode(self): + ssdata = self.j2k_p1_06.read(area=(4, 4, 7, 7), rlevel=1) + self.assertEqual(ssdata.shape, (2, 2, 3)) + + def test_NR_DEC_p1_06_j2k_74_decode(self): + ssdata = self.j2k_p1_06.read(area=(4, 4, 5, 5), rlevel=1) + self.assertEqual(ssdata.shape, (1, 1, 3)) + + def test_NR_DEC_p1_06_j2k_75_decode(self): + # Image size would be 0 x 0. + with self.assertRaises((IOError, OSError)): + self.j2k_p1_06.read(area=(9, 9, 12, 12), rlevel=2) + + def test_NR_DEC_p0_04_j2k_85_decode(self): + actual = self.j2k.read(area=(0, 0, 256, 256)) + expected = self.j2k_data[:256, :256] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p0_04_j2k_86_decode(self): + actual = self.j2k.read(area=(0, 128, 128, 256)) + expected = self.j2k_data[:128, 128:256] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p0_04_j2k_87_decode(self): + actual = self.j2k.read(area=(10, 50, 200, 120)) + expected = self.j2k_data[10:200, 50:120] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p0_04_j2k_88_decode(self): + actual = self.j2k.read(area=(150, 10, 210, 190)) + expected = self.j2k_data[150:210, 10:190] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p0_04_j2k_89_decode(self): + actual = self.j2k.read(area=(80, 100, 150, 200)) + expected = self.j2k_data[80:150, 100:200] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p0_04_j2k_90_decode(self): + actual = self.j2k.read(area=(20, 150, 50, 200)) + expected = self.j2k_data[20:50, 150:200] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p0_04_j2k_91_decode(self): + actual = self.j2k.read(area=(0, 0, 256, 256), rlevel=2) + expected = self.j2k_quarter_data[0:64, 0:64] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p0_04_j2k_92_decode(self): + actual = self.j2k.read(area=(0, 128, 128, 256), rlevel=2) + expected = self.j2k_quarter_data[:32, 32:64] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p0_04_j2k_93_decode(self): + actual = self.j2k.read(area=(10, 50, 200, 120), rlevel=2) + expected = self.j2k_quarter_data[3:50, 13:30] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p0_04_j2k_94_decode(self): + actual = self.j2k.read(area=(150, 10, 210, 190), rlevel=2) + expected = self.j2k_quarter_data[38:53, 3:48] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p0_04_j2k_95_decode(self): + actual = self.j2k.read(area=(80, 100, 150, 200), rlevel=2) + expected = self.j2k_quarter_data[20:38, 25:50] + np.testing.assert_array_equal(actual, expected) + + def test_NR_DEC_p0_04_j2k_96_decode(self): + actual = self.j2k.read(area=(20, 150, 50, 200), rlevel=2) + expected = self.j2k_quarter_data[5:13, 38:50] + np.testing.assert_array_equal(actual, expected) diff --git a/glymur/test/test_opj_suite_dump.py b/glymur/test/test_opj_suite_dump.py index 7ac513f..76dc444 100644 --- a/glymur/test/test_opj_suite_dump.py +++ b/glymur/test/test_opj_suite_dump.py @@ -2,26 +2,48 @@ The tests defined here roughly correspond to what is in the OpenJPEG test suite. """ + +# Some test names correspond with openjpeg tests. Long names are ok in this +# case. +# pylint: disable=C0103 + +# All of these tests correspond to tests in openjpeg, so no docstring is really +# needed. +# pylint: disable=C0111 + +# This module is very long, cannot be helped. +# pylint: disable=C0302 + +# unittest fools pylint with "too many public methods" +# pylint: disable=R0904 + +# Some tests use numpy test infrastructure, which means the tests never +# reference "self", so pylint claims it should be a function. No, no, no. +# pylint: disable=R0201 + +# Many tests are pretty long and that can't be helped. +# pylint: disable=R0915 + +# asserWarns introduced in python 3.2 (python2.7/pylint issue) +# pylint: disable=E1101 + import re +import sys import unittest -import warnings import numpy as np import glymur from glymur import Jp2k from glymur.codestream import CMEsegment, SOTsegment, RGNsegment -from glymur.core import (RCME_ISO_8859_1, RCME_BINARY, SRGB, - GREYSCALE, RESTRICTED_ICC_PROFILE, - ENUMERATED_COLORSPACE) +from glymur.core import RCME_ISO_8859_1, RCME_BINARY from glymur.jp2box import FileTypeBox -from .fixtures import (MetadataBase, OPJ_DATA_ROOT, - WARNING_INFRASTRUCTURE_ISSUE, - WARNING_INFRASTRUCTURE_MSG, - opj_data_file) - -comment1 = "Creator: AV-J2K (c) 2000,2001 Algo Vision Technology" +from .fixtures import ( + MetadataBase, OPJ_DATA_ROOT, + WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG, + mse, peak_tolerance, read_pgx, opj_data_file +) @unittest.skipIf(OPJ_DATA_ROOT is None, @@ -53,19 +75,17 @@ class TestSuite(MetadataBase): colr = glymur.jp2box.ColourSpecificationBox(colorspace=glymur.core.YCC) self.verifyColourSpecificationBox(jp2.box[2].box[1], colr) - c = jp2.box[3].codestream + c = jp2.box[3].main_header ids = [x.marker_id for x in c.segment] expected = ['SOC', 'SIZ', 'COD', 'QCD'] self.assertEqual(ids, expected) kwargs = {'rsiz': 0, 'xysiz': (720, 243), 'xyosiz': (0, 0), - 'xytsiz': (720, 243), 'xytosiz': (0, 0), - 'bitdepth': (8, 8, 8), - 'signed': (False, False, False), - 'xyrsiz': [(1, 2, 2), (1, 1, 1)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (720, 243), 'xytosiz': (0, 0), 'bitdepth': (8, 8, 8), + 'signed': (False, False, False), + 'xyrsiz': [(1, 2, 2), (1, 1, 1)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) # no sop @@ -77,7 +97,7 @@ class TestSuite(MetadataBase): self.assertEqual(tuple(c.segment[2].code_block_size), (32, 128)) # cblk self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_9X7_IRREVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) @@ -101,10 +121,9 @@ class TestSuite(MetadataBase): self.assertEqual(actual, expected) kwargs = {'rsiz': 1, 'xysiz': (128, 128), 'xyosiz': (0, 0), - 'xytsiz': (128, 128), 'xytosiz': (0, 0), 'bitdepth': (8,), - 'signed': (False,), 'xyrsiz': [(1,), (1,)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (128, 128), 'xytosiz': (0, 0), 'bitdepth': (8,), + 'signed': (False,), 'xyrsiz': [(1,), (1,)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # QCD: Quantization default self.assertEqual(c.segment[2].sqcd & 0x1f, 0) @@ -124,7 +143,7 @@ class TestSuite(MetadataBase): self.assertEqual(tuple(c.segment[3].code_block_size), (64, 64)) # cblk self.verify_codeblock_style(c.segment[3].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[3].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) @@ -135,10 +154,9 @@ class TestSuite(MetadataBase): c = Jp2k(jfile).get_codestream(header_only=False) kwargs = {'rsiz': 1, 'xysiz': (127, 126), 'xyosiz': (0, 0), - 'xytsiz': (127, 126), 'xytosiz': (0, 0), 'bitdepth': (8,), - 'signed': (False,), 'xyrsiz': [(2,), (1,)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (127, 126), 'xytosiz': (0, 0), 'bitdepth': (8,), + 'signed': (False,), 'xyrsiz': [(2,), (1,)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertTrue(c.segment[2].scod & 2) # sop @@ -150,7 +168,7 @@ class TestSuite(MetadataBase): self.assertEqual(tuple(c.segment[2].code_block_size), (64, 64)) # cblk self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, True, False, True, True]) + [False, False, True, False, True, True]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_9X7_IRREVERSIBLE) @@ -160,7 +178,7 @@ class TestSuite(MetadataBase): self.assertEqual(tuple(c.segment[3].code_block_size), (32, 32)) # cblk self.verify_codeblock_style(c.segment[3].spcoc[3], - [False, False, True, False, True, True]) + [False, False, True, False, True, True]) self.assertEqual(c.segment[3].spcoc[4], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) @@ -173,8 +191,7 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[4].mantissa, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) - pargs = (RCME_ISO_8859_1, - "Creator: AV-J2K (c) 2000,2001 Algo Vision".encode()) + pargs = RCME_ISO_8859_1, "Creator: AV-J2K (c) 2000,2001 Algo Vision".encode() self.verifyCMEsegment(c.segment[5], CMEsegment(*pargs)) # One unknown marker @@ -200,10 +217,9 @@ class TestSuite(MetadataBase): c = Jp2k(jfile).get_codestream(header_only=False) kwargs = {'rsiz': 1, 'xysiz': (256, 256), 'xyosiz': (0, 0), - 'xytsiz': (128, 128), 'xytosiz': (0, 0), 'bitdepth': (4,), - 'signed': (True,), 'xyrsiz': [(1,), (1,)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (128, 128), 'xytosiz': (0, 0), 'bitdepth': (4,), + 'signed': (True,), 'xyrsiz': [(1,), (1,)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertTrue(c.segment[2].scod & 2) @@ -215,7 +231,7 @@ class TestSuite(MetadataBase): self.assertEqual(tuple(c.segment[2].code_block_size), (64, 64)) # cblk self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) @@ -247,11 +263,11 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[6].xcrg, (65424,)) self.assertEqual(c.segment[6].ycrg, (32558,)) - pargs = (RCME_ISO_8859_1, - "Creator: AV-J2K (c) 2000,2001 Algo Vision".encode()) + pargs = RCME_ISO_8859_1, "Creator: AV-J2K (c) 2000,2001 Algo Vision".encode() self.verifyCMEsegment(c.segment[7], CMEsegment(*pargs)) - pargs = (RCME_ISO_8859_1, comment1.encode()) + pargs = (RCME_ISO_8859_1, + "Creator: AV-J2K (c) 2000,2001 Algo Vision Technology".encode()) self.verifyCMEsegment(c.segment[8], CMEsegment(*pargs)) pargs = (RCME_BINARY, c.segment[9].ccme) @@ -274,12 +290,10 @@ class TestSuite(MetadataBase): c = Jp2k(jfile).get_codestream(header_only=False) kwargs = {'rsiz': 1, 'xysiz': (640, 480), 'xyosiz': (0, 0), - 'xytsiz': (640, 480), 'xytosiz': (0, 0), - 'bitdepth': (8, 8, 8), - 'signed': (False, False, False), - 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (640, 480), 'xytosiz': (0, 0), 'bitdepth': (8, 8, 8), + 'signed': (False, False, False), + 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) @@ -291,7 +305,7 @@ class TestSuite(MetadataBase): self.assertEqual(tuple(c.segment[2].code_block_size), (64, 64)) # cblk self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, True, False, False, False]) + [False, False, True, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_9X7_IRREVERSIBLE) self.assertEqual(c.segment[2].precinct_size, @@ -339,7 +353,7 @@ class TestSuite(MetadataBase): 2002, 1888]) pargs = (RCME_ISO_8859_1, - "Creator: AV-J2K (c) 2000,2001 Algo Vision".encode()) + "Creator: AV-J2K (c) 2000,2001 Algo Vision".encode()) self.verifyCMEsegment(c.segment[6], CMEsegment(*pargs)) self.verifySOTsegment(c.segment[7], SOTsegment(0, 264383, 0, 1)) @@ -353,12 +367,11 @@ class TestSuite(MetadataBase): c = Jp2k(jfile).get_codestream(header_only=False) kwargs = {'rsiz': 1, 'xysiz': (1024, 1024), 'xyosiz': (0, 0), - 'xytsiz': (1024, 1024), 'xytosiz': (0, 0), - 'bitdepth': (8, 8, 8, 8), - 'signed': (False, False, False, False), - 'xyrsiz': [(1, 1, 2, 2), (1, 1, 2, 2)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (1024, 1024), 'xytosiz': (0, 0), + 'bitdepth': (8, 8, 8, 8), + 'signed': (False, False, False, False), + 'xyrsiz': [(1, 1, 2, 2), (1, 1, 2, 2)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) @@ -370,7 +383,7 @@ class TestSuite(MetadataBase): self.assertEqual(tuple(c.segment[2].code_block_size), (32, 32)) # cblk self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_9X7_IRREVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) @@ -381,7 +394,7 @@ class TestSuite(MetadataBase): self.assertEqual(tuple(c.segment[3].code_block_size), (32, 32)) # cblk self.verify_codeblock_style(c.segment[3].spcoc[3], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[3].spcoc[4], glymur.core.WAVELET_XFORM_9X7_IRREVERSIBLE) @@ -391,7 +404,7 @@ class TestSuite(MetadataBase): self.assertEqual(tuple(c.segment[4].code_block_size), (32, 32)) # cblk self.verify_codeblock_style(c.segment[4].spcoc[3], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[4].spcoc[4], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) @@ -428,7 +441,7 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[7].mantissa, [0] * 19) pargs = (RCME_ISO_8859_1, - "Creator: AV-J2K (c) 2000,2001 Algo Vision".encode()) + "Creator: AV-J2K (c) 2000,2001 Algo Vision".encode()) self.verifyCMEsegment(c.segment[8], CMEsegment(*pargs)) # TLM (tile-part length) @@ -447,12 +460,11 @@ class TestSuite(MetadataBase): c = Jp2k(jfile).get_codestream(header_only=False) kwargs = {'rsiz': 2, 'xysiz': (513, 129), 'xyosiz': (0, 0), - 'xytsiz': (513, 129), 'xytosiz': (0, 0), - 'bitdepth': (12, 12, 12, 12), - 'signed': (False, False, False, False), - 'xyrsiz': [(1, 2, 1, 2), (1, 1, 2, 2)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (513, 129), 'xytosiz': (0, 0), + 'bitdepth': (12, 12, 12, 12), + 'signed': (False, False, False, False), + 'xyrsiz': [(1, 2, 1, 2), (1, 1, 2, 2)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) @@ -464,7 +476,7 @@ class TestSuite(MetadataBase): self.assertEqual(tuple(c.segment[2].code_block_size), (64, 64)) # cblk self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_9X7_IRREVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) @@ -523,7 +535,7 @@ class TestSuite(MetadataBase): self.assertEqual(tuple(c.segment[7].code_block_size), (64, 64)) # cblk self.verify_codeblock_style(c.segment[7].spcoc[3], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[7].spcoc[4], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) @@ -540,12 +552,10 @@ class TestSuite(MetadataBase): c = Jp2k(jfile).get_codestream(header_only=False) kwargs = {'rsiz': 1, 'xysiz': (2048, 2048), 'xyosiz': (0, 0), - 'xytsiz': (128, 128), 'xytosiz': (0, 0), - 'bitdepth': (12, 12, 12), - 'signed': (True, True, True), - 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (128, 128), 'xytosiz': (0, 0), 'bitdepth': (12, 12, 12), + 'signed': (True, True, True), + 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertTrue(c.segment[2].scod & 2) @@ -557,7 +567,7 @@ class TestSuite(MetadataBase): self.assertEqual(tuple(c.segment[2].code_block_size), (64, 64)) # cblk self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) @@ -585,6 +595,7 @@ class TestSuite(MetadataBase): # PLT: packet length, tile part self.assertEqual(c.segment[7].zplt, 0) + #self.assertEqual(c.segment[7].iplt), 99) # SOD: start of data self.assertEqual(c.segment[8].marker_id, 'SOD') @@ -594,12 +605,10 @@ class TestSuite(MetadataBase): c = Jp2k(jfile).get_codestream(header_only=False) kwargs = {'rsiz': 1, 'xysiz': (513, 3072), 'xyosiz': (0, 0), - 'xytsiz': (513, 3072), 'xytosiz': (0, 0), - 'bitdepth': (12, 12, 12), - 'signed': (True, True, True), - 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (513, 3072), 'xytosiz': (0, 0), 'bitdepth': (12, 12, 12), + 'signed': (True, True, True), + 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertTrue(c.segment[2].scod & 2) @@ -611,7 +620,7 @@ class TestSuite(MetadataBase): self.assertEqual(tuple(c.segment[2].code_block_size), (64, 64)) # cblk self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) @@ -622,7 +631,7 @@ class TestSuite(MetadataBase): self.assertEqual(tuple(c.segment[3].code_block_size), (64, 64)) # cblk self.verify_codeblock_style(c.segment[3].spcoc[3], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[3].spcoc[4], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) @@ -632,7 +641,7 @@ class TestSuite(MetadataBase): self.assertEqual(tuple(c.segment[4].code_block_size), (32, 32)) # cblk self.verify_codeblock_style(c.segment[4].spcoc[3], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[4].spcoc[4], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) @@ -642,7 +651,7 @@ class TestSuite(MetadataBase): self.assertEqual(tuple(c.segment[5].code_block_size), (64, 64)) # cblk self.verify_codeblock_style(c.segment[5].spcoc[3], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[5].spcoc[4], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) @@ -687,10 +696,9 @@ class TestSuite(MetadataBase): c = Jp2k(jfile).get_codestream(header_only=False) kwargs = {'rsiz': 0, 'xysiz': (17, 37), 'xyosiz': (0, 0), - 'xytsiz': (17, 37), 'xytosiz': (0, 0), 'bitdepth': (8,), - 'signed': (False,), 'xyrsiz': [(1,), (1,)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (17, 37), 'xytosiz': (0, 0), 'bitdepth': (8,), + 'signed': (False,), 'xyrsiz': [(1,), (1,)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) @@ -702,7 +710,7 @@ class TestSuite(MetadataBase): self.assertEqual(tuple(c.segment[2].code_block_size), (64, 64)) # cblk self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_9X7_IRREVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) @@ -735,12 +743,10 @@ class TestSuite(MetadataBase): c = Jp2k(jfile).get_codestream(header_only=False) kwargs = {'rsiz': 1, 'xysiz': (256, 256), 'xyosiz': (0, 0), - 'xytsiz': (128, 128), 'xytosiz': (0, 0), - 'bitdepth': (8, 8, 8), - 'signed': (False, False, False), - 'xyrsiz': [(4, 4, 4), (4, 4, 4)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (128, 128), 'xytosiz': (0, 0), 'bitdepth': (8, 8, 8), + 'signed': (False, False, False), + 'xyrsiz': [(4, 4, 4), (4, 4, 4)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) @@ -752,7 +758,7 @@ class TestSuite(MetadataBase): self.assertEqual(tuple(c.segment[2].code_block_size), (64, 64)) # cblk self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) @@ -799,10 +805,9 @@ class TestSuite(MetadataBase): c = Jp2k(jfile).get_codestream(header_only=False) kwargs = {'rsiz': 1, 'xysiz': (128, 1), 'xyosiz': (0, 0), - 'xytsiz': (128, 128), 'xytosiz': (0, 0), 'bitdepth': (8,), - 'signed': (False,), 'xyrsiz': [(1,), (1,)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (128, 128), 'xytosiz': (0, 0), 'bitdepth': (8,), + 'signed': (False,), 'xyrsiz': [(1,), (1,)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) @@ -814,7 +819,7 @@ class TestSuite(MetadataBase): self.assertEqual(tuple(c.segment[2].code_block_size), (64, 64)) # cblk self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, True]) + [False, False, False, False, False, True]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(c.segment[2].precinct_size, [(128, 2)]) @@ -827,7 +832,7 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[3].exponent, [8]) pargs = (RCME_ISO_8859_1, - "Creator: AV-J2K (c) 2000,2001 Algo Vision".encode()) + "Creator: AV-J2K (c) 2000,2001 Algo Vision".encode()) self.verifyCMEsegment(c.segment[4], CMEsegment(*pargs)) self.verifySOTsegment(c.segment[5], SOTsegment(0, 118, 0, 1)) @@ -849,11 +854,10 @@ class TestSuite(MetadataBase): c = Jp2k(jfile).get_codestream(header_only=False) kwargs = {'rsiz': 1, 'xysiz': (3, 5), 'xyosiz': (0, 0), - 'xytsiz': (3, 5), 'xytosiz': (0, 0), 'bitdepth': (8,), - 'signed': (False,), - 'xyrsiz': [(1,), (1,)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (3, 5), 'xytosiz': (0, 0), 'bitdepth': (8,), + 'signed': (False,), + 'xyrsiz': [(1,), (1,)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertTrue(c.segment[2].scod & 2) @@ -865,7 +869,7 @@ class TestSuite(MetadataBase): self.assertEqual(tuple(c.segment[2].code_block_size), (32, 32)) # cblk self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, True, False, False, False]) + [False, False, True, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) @@ -878,8 +882,7 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[3].exponent, [8, 9, 9, 10, 9, 9, 10, 9, 9, 10]) - pargs = (RCME_ISO_8859_1, - "Creator: AV-J2K (c) 2000,2001 Algo Vision".encode()) + pargs = (RCME_ISO_8859_1, "Creator: AV-J2K (c) 2000,2001 Algo Vision".encode()) self.verifyCMEsegment(c.segment[4], CMEsegment(*pargs)) self.verifySOTsegment(c.segment[5], SOTsegment(0, 162, 0, 1)) @@ -901,12 +904,10 @@ class TestSuite(MetadataBase): c = Jp2k(jfile).get_codestream(header_only=False) kwargs = {'rsiz': 1, 'xysiz': (1, 1), 'xyosiz': (0, 0), - 'xytsiz': (1, 1), 'xytosiz': (0, 0), - 'bitdepth': tuple([8] * 257), - 'signed': tuple([False] * 257), - 'xyrsiz': [tuple([1] * 257), tuple([1] * 257)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (1, 1), 'xytosiz': (0, 0), 'bitdepth': tuple([8] * 257), + 'signed': tuple([False] * 257), + 'xyrsiz': [tuple([1] * 257), tuple([1] * 257)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) # no sop @@ -917,7 +918,7 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[2].spcod[4], 1) # levels self.assertEqual(tuple(c.segment[2].code_block_size), (32, 32)) self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, True, False]) + [False, False, False, False, True, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) @@ -927,7 +928,7 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[3].spcoc[0], 1) # levels self.assertEqual(tuple(c.segment[3].code_block_size), (64, 64)) self.verify_codeblock_style(c.segment[3].spcoc[3], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[3].spcoc[4], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) @@ -968,8 +969,7 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[8].ppod, (glymur.core.RLCP, glymur.core.CPRL)) - pargs = (RCME_ISO_8859_1, - "Creator: AV-J2K (c) 2000,2001 Algo Vision".encode()) + pargs = (RCME_ISO_8859_1, "Creator: AV-J2K (c) 2000,2001 Algo Vision".encode()) self.verifyCMEsegment(c.segment[9], CMEsegment(*pargs)) self.verifySOTsegment(c.segment[10], SOTsegment(0, 1537, 0, 1)) @@ -985,11 +985,10 @@ class TestSuite(MetadataBase): c = Jp2k(jfile).get_codestream(header_only=False) kwargs = {'rsiz': 0, 'xysiz': (49, 49), 'xyosiz': (0, 0), - 'xytsiz': (49, 49), 'xytosiz': (0, 0), 'bitdepth': (8, 8, 8), - 'signed': (False, False, False), - 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (49, 49), 'xytosiz': (0, 0), 'bitdepth': (8, 8, 8), + 'signed': (False, False, False), + 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) @@ -1000,7 +999,7 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[2].spcod[4], 5) # levels self.assertEqual(tuple(c.segment[2].code_block_size), (64, 64)) self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) @@ -1026,11 +1025,10 @@ class TestSuite(MetadataBase): c = Jp2k(jfile).get_codestream(header_only=False) kwargs = {'rsiz': 1, 'xysiz': (256, 256), 'xyosiz': (0, 0), - 'xytsiz': (128, 128), 'xytosiz': (0, 0), 'bitdepth': (4,), - 'signed': (True,), - 'xyrsiz': [(1,), (1,)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (128, 128), 'xytosiz': (0, 0), 'bitdepth': (4,), + 'signed': (True,), + 'xyrsiz': [(1,), (1,)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertTrue(c.segment[2].scod & 2) @@ -1041,7 +1039,7 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[2].spcod[4], 1) # levels self.assertEqual(tuple(c.segment[2].code_block_size), (64, 64)) self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) @@ -1074,11 +1072,11 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[6].xcrg, (65424,)) self.assertEqual(c.segment[6].ycrg, (32558,)) - pargs = (RCME_ISO_8859_1, - "Creator: AV-J2K (c) 2000,2001 Algo Vision".encode()) + pargs = (RCME_ISO_8859_1, "Creator: AV-J2K (c) 2000,2001 Algo Vision".encode()) self.verifyCMEsegment(c.segment[7], CMEsegment(*pargs)) - pargs = (RCME_ISO_8859_1, comment1.encode()) + pargs = (RCME_ISO_8859_1, + "Creator: AV-J2K (c) 2000,2001 Algo Vision Technology".encode()) self.verifyCMEsegment(c.segment[8], CMEsegment(*pargs)) pargs = (RCME_BINARY, c.segment[9].ccme) @@ -1127,11 +1125,10 @@ class TestSuite(MetadataBase): c = Jp2k(jfile).get_codestream(header_only=False) kwargs = {'rsiz': 0, 'xysiz': (128, 128), 'xyosiz': (0, 0), - 'xytsiz': (128, 128), 'xytosiz': (0, 0), 'bitdepth': (8,), - 'signed': (False,), - 'xyrsiz': [(1,), (1,)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (128, 128), 'xytosiz': (0, 0), 'bitdepth': (8,), + 'signed': (False,), + 'xyrsiz': [(1,), (1,)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) @@ -1142,7 +1139,7 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[2].spcod[4], 3) # levels self.assertEqual(tuple(c.segment[2].code_block_size), (64, 64)) self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) @@ -1168,11 +1165,10 @@ class TestSuite(MetadataBase): c = Jp2k(jfile).get_codestream(header_only=False) kwargs = {'rsiz': 2, 'xysiz': (127, 227), 'xyosiz': (5, 128), - 'xytsiz': (127, 126), 'xytosiz': (1, 101), 'bitdepth': (8,), - 'signed': (False,), - 'xyrsiz': [(2,), (1,)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (127, 126), 'xytosiz': (1, 101), 'bitdepth': (8,), + 'signed': (False,), + 'xyrsiz': [(2,), (1,)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertTrue(c.segment[2].scod & 2) # SOP @@ -1183,7 +1179,7 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[2].spcod[4], 3) # level self.assertEqual(tuple(c.segment[2].code_block_size), (64, 64)) self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, True, False, True, True]) + [False, False, True, False, True, True]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_9X7_IRREVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) @@ -1193,7 +1189,7 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[3].spcoc[0], 3) # level self.assertEqual(tuple(c.segment[3].code_block_size), (32, 32)) self.verify_codeblock_style(c.segment[3].spcoc[3], - [False, False, True, False, True, True]) + [False, False, True, False, True, True]) self.assertEqual(c.segment[3].spcoc[4], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) @@ -1205,8 +1201,7 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[4].exponent, [8, 9, 9, 10, 9, 9, 10, 9, 9, 10]) - pargs = (RCME_ISO_8859_1, - "Creator: AV-J2K (c) 2000,2001 Algo Vision".encode()) + pargs = (RCME_ISO_8859_1, "Creator: AV-J2K (c) 2000,2001 Algo Vision".encode()) self.verifyCMEsegment(c.segment[5], CMEsegment(*pargs)) self.verifySOTsegment(c.segment[6], SOTsegment(0, 4627, 0, 1)) @@ -1228,12 +1223,10 @@ class TestSuite(MetadataBase): c = Jp2k(jfile).get_codestream(header_only=False) kwargs = {'rsiz': 2, 'xysiz': (640, 480), 'xyosiz': (0, 0), - 'xytsiz': (640, 480), 'xytosiz': (0, 0), - 'bitdepth': (8, 8, 8), - 'signed': (False, False, False), - 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (640, 480), 'xytosiz': (0, 0), 'bitdepth': (8, 8, 8), + 'signed': (False, False, False), + 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) # no sop @@ -1245,7 +1238,7 @@ class TestSuite(MetadataBase): self.assertEqual(tuple(c.segment[2].code_block_size), (64, 64)) # cblk self.verify_codeblock_style(c.segment[2].spcod[7], - [False, True, False, True, False, False]) + [False, True, False, True, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_9X7_IRREVERSIBLE) self.assertEqual(c.segment[2].precinct_size, @@ -1292,8 +1285,7 @@ class TestSuite(MetadataBase): [14, 14, 14, 14, 13, 13, 13, 12, 12, 12, 11, 11, 11, 9, 9, 9, 9, 9, 9]) - pargs = (RCME_ISO_8859_1, - "Creator: AV-J2K (c) 2000,2001 Algo Vision".encode()) + pargs = (RCME_ISO_8859_1, "Creator: AV-J2K (c) 2000,2001 Algo Vision".encode()) self.verifyCMEsegment(c.segment[6], CMEsegment(*pargs)) self.verifySOTsegment(c.segment[7], SOTsegment(0, 262838, 0, 1)) @@ -1313,12 +1305,11 @@ class TestSuite(MetadataBase): c = Jp2k(jfile).get_codestream(header_only=False) kwargs = {'rsiz': 2, 'xysiz': (1024, 1024), 'xyosiz': (0, 0), - 'xytsiz': (1024, 1024), 'xytosiz': (0, 0), - 'bitdepth': (8, 8, 8, 8), - 'signed': (False, False, False, False), - 'xyrsiz': [(1, 1, 2, 2), (1, 1, 2, 2)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (1024, 1024), 'xytosiz': (0, 0), + 'bitdepth': (8, 8, 8, 8), + 'signed': (False, False, False, False), + 'xyrsiz': [(1, 1, 2, 2), (1, 1, 2, 2)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) # no sop @@ -1329,7 +1320,7 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[2].spcod[4], 6) # level self.assertEqual(tuple(c.segment[2].code_block_size), (32, 32)) self.verify_codeblock_style(c.segment[2].spcod[7], - [True, False, True, False, False, False]) + [True, False, True, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_9X7_IRREVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) @@ -1339,7 +1330,7 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[3].spcoc[0], 3) # level self.assertEqual(tuple(c.segment[3].code_block_size), (32, 32)) self.verify_codeblock_style(c.segment[3].spcoc[3], - [True, False, True, False, False, False]) + [True, False, True, False, False, False]) self.assertEqual(c.segment[3].spcoc[4], glymur.core.WAVELET_XFORM_9X7_IRREVERSIBLE) @@ -1348,7 +1339,7 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[4].spcoc[0], 6) # level self.assertEqual(tuple(c.segment[4].code_block_size), (32, 32)) self.verify_codeblock_style(c.segment[4].spcoc[3], - [True, False, True, False, False, False]) + [True, False, True, False, False, False]) self.assertEqual(c.segment[4].spcoc[4], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) @@ -1384,8 +1375,7 @@ class TestSuite(MetadataBase): [8, 9, 9, 10, 9, 9, 10, 9, 9, 10, 9, 9, 10, 9, 9, 10, 9, 9, 10]) - pargs = (RCME_ISO_8859_1, - "Creator: AV-J2K (c) 2000,2001 Algo Vision".encode()) + pargs = (RCME_ISO_8859_1, "Creator: AV-J2K (c) 2000,2001 Algo Vision".encode()) self.verifyCMEsegment(c.segment[8], CMEsegment(*pargs)) # PPM: packed packet headers, main header @@ -1410,11 +1400,10 @@ class TestSuite(MetadataBase): c = Jp2k(jfile).get_codestream(header_only=False) kwargs = {'rsiz': 2, 'xysiz': (1024, 1024), 'xyosiz': (0, 0), - 'xytsiz': (128, 128), 'xytosiz': (0, 0), 'bitdepth': (12,), - 'signed': (False,), - 'xyrsiz': [(1,), (1,)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (128, 128), 'xytosiz': (0, 0), 'bitdepth': (12,), + 'signed': (False,), + 'xyrsiz': [(1,), (1,)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) # no sop @@ -1425,7 +1414,7 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[2].spcod[4], 3) # level self.assertEqual(tuple(c.segment[2].code_block_size), (64, 64)) self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_9X7_IRREVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) @@ -1498,12 +1487,10 @@ class TestSuite(MetadataBase): c = Jp2k(jfile).get_codestream(header_only=False) kwargs = {'rsiz': 2, 'xysiz': (529, 524), 'xyosiz': (17, 12), - 'xytsiz': (37, 37), 'xytosiz': (8, 2), - 'bitdepth': (8, 8, 8), - 'signed': (False, False, False), - 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (37, 37), 'xytosiz': (8, 2), 'bitdepth': (8, 8, 8), + 'signed': (False, False, False), + 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertTrue(c.segment[2].scod & 2) # sop @@ -1514,7 +1501,7 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[2].spcod[4], 7) # level self.assertEqual(tuple(c.segment[2].code_block_size), (64, 8)) # cblk self.verify_codeblock_style(c.segment[2].spcod[7], - [True, False, False, True, True, False]) + [True, False, False, True, True, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_9X7_IRREVERSIBLE) self.assertEqual(c.segment[2].precinct_size, [(16, 16)] * 8) @@ -1529,8 +1516,7 @@ class TestSuite(MetadataBase): [17, 17, 17, 17, 16, 16, 16, 15, 15, 15, 14, 14, 14, 13, 13, 13, 11, 11, 11, 11, 11, 11]) - pargs = (RCME_ISO_8859_1, - "Creator: AV-J2K (c) 2000,2001 Algo Vision".encode()) + pargs = (RCME_ISO_8859_1, "Creator: AV-J2K (c) 2000,2001 Algo Vision".encode()) self.verifyCMEsegment(c.segment[4], CMEsegment(*pargs)) # 225 consecutive PPM segments. @@ -1557,11 +1543,10 @@ class TestSuite(MetadataBase): c = Jp2k(jfile).get_codestream(header_only=False) kwargs = {'rsiz': 2, 'xysiz': (12, 12), 'xyosiz': (0, 0), - 'xytsiz': (3, 3), 'xytosiz': (0, 0), 'bitdepth': (8, 8, 8), - 'signed': (False, False, False), - 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (3, 3), 'xytosiz': (0, 0), 'bitdepth': (8, 8, 8), + 'signed': (False, False, False), + 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertTrue(c.segment[2].scod & 2) # sop @@ -1572,7 +1557,7 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[2].spcod[4], 4) # level self.assertEqual(tuple(c.segment[2].code_block_size), (32, 64)) self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, True, False, True]) + [False, False, False, True, False, True]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_9X7_IRREVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) @@ -1588,8 +1573,7 @@ class TestSuite(MetadataBase): [14, 14, 14, 14, 13, 13, 13, 11, 11, 11, 11, 11, 11]) - pargs = (RCME_ISO_8859_1, - "Creator: AV-J2K (c) 2000,2001 Algo Vision".encode()) + pargs = (RCME_ISO_8859_1, "Creator: AV-J2K (c) 2000,2001 Algo Vision".encode()) self.verifyCMEsegment(c.segment[4], CMEsegment(*pargs)) self.verifySOTsegment(c.segment[5], SOTsegment(0, 349, 0, 1)) @@ -1620,11 +1604,10 @@ class TestSuite(MetadataBase): c = Jp2k(jfile).get_codestream(header_only=False) kwargs = {'rsiz': 2, 'xysiz': (12, 12), 'xyosiz': (4, 0), - 'xytsiz': (12, 12), 'xytosiz': (4, 0), 'bitdepth': (8, 8), - 'signed': (False, False), - 'xyrsiz': [(4, 1), (1, 1)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (12, 12), 'xytosiz': (4, 0), 'bitdepth': (8, 8), + 'signed': (False, False), + 'xyrsiz': [(4, 1), (1, 1)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertTrue(c.segment[2].scod & 2) # sop @@ -1635,7 +1618,7 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[2].spcod[4], 1) # level self.assertEqual(tuple(c.segment[2].code_block_size), (64, 64)) self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(c.segment[2].precinct_size, [(1, 1), (2, 2)]) @@ -1645,7 +1628,7 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[3].spcoc[0], 1) # level self.assertEqual(tuple(c.segment[3].code_block_size), (64, 64)) self.verify_codeblock_style(c.segment[3].spcoc[3], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[3].spcoc[4], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(c.segment[3].precinct_size, [(2, 2), (4, 4)]) @@ -1657,8 +1640,7 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[4].mantissa, [0] * 4) self.assertEqual(c.segment[4].exponent, [8, 9, 9, 10]) - pargs = (RCME_ISO_8859_1, - "Creator: AV-J2K (c) 2000,2001 Algo Vision".encode()) + pargs = (RCME_ISO_8859_1, "Creator: AV-J2K (c) 2000,2001 Algo Vision".encode()) self.verifyCMEsegment(c.segment[5], CMEsegment(*pargs)) self.verifySOTsegment(c.segment[6], SOTsegment(0, 434, 0, 1)) @@ -1675,12 +1657,11 @@ class TestSuite(MetadataBase): c = jp2k.get_codestream(header_only=False) kwargs = {'rsiz': 3, 'xysiz': (1920, 1080), 'xyosiz': (0, 0), - 'xytsiz': (1920, 1080), 'xytosiz': (0, 0), - 'bitdepth': (12, 12, 12), - 'signed': (False, False, False), - 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (1920, 1080), 'xytosiz': (0, 0), + 'bitdepth': (12, 12, 12), + 'signed': (False, False, False), + 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) # no sop @@ -1691,7 +1672,7 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[2].spcod[4], 5) # level self.assertEqual(tuple(c.segment[2].code_block_size), (32, 32)) self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_9X7_IRREVERSIBLE) self.assertEqual(c.segment[2].precinct_size[0], (128, 128)) @@ -1713,7 +1694,7 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[4].spcoc[0], 5) # level self.assertEqual(tuple(c.segment[4].code_block_size), (32, 32)) self.verify_codeblock_style(c.segment[4].spcoc[3], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[4].spcoc[4], glymur.core.WAVELET_XFORM_9X7_IRREVERSIBLE) @@ -1735,7 +1716,7 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[6].spcoc[0], 5) # level self.assertEqual(tuple(c.segment[6].code_block_size), (32, 32)) self.verify_codeblock_style(c.segment[6].spcoc[3], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[6].spcoc[4], glymur.core.WAVELET_XFORM_9X7_IRREVERSIBLE) @@ -1780,12 +1761,10 @@ class TestSuite(MetadataBase): c = jp2k.get_codestream(header_only=False) kwargs = {'rsiz': 0, 'xysiz': (2592, 1944), 'xyosiz': (0, 0), - 'xytsiz': (640, 480), 'xytosiz': (0, 0), - 'bitdepth': (8, 8, 8), - 'signed': (False, False, False), - 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (640, 480), 'xytosiz': (0, 0), 'bitdepth': (8, 8, 8), + 'signed': (False, False, False), + 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) # no sop @@ -1796,7 +1775,7 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[2].spcod[4], 5) # level self.assertEqual(tuple(c.segment[2].code_block_size), (32, 32)) self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(c.segment[2].precinct_size, @@ -1815,11 +1794,10 @@ class TestSuite(MetadataBase): c = jp2k.get_codestream(header_only=False) kwargs = {'rsiz': 0, 'xysiz': (512, 512), 'xyosiz': (0, 0), - 'xytsiz': (512, 512), 'xytosiz': (0, 0), 'bitdepth': (16,), - 'signed': (False,), - 'xyrsiz': [(1,), (1,)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (512, 512), 'xytosiz': (0, 0), 'bitdepth': (16,), + 'signed': (False,), + 'xyrsiz': [(1,), (1,)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) # no sop @@ -1830,7 +1808,7 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[2].spcod[4], 5) # level self.assertEqual(tuple(c.segment[2].code_block_size), (64, 64)) self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_9X7_IRREVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) @@ -1845,11 +1823,10 @@ class TestSuite(MetadataBase): c = jp2k.get_codestream(header_only=False) kwargs = {'rsiz': 0, 'xysiz': (512, 512), 'xyosiz': (0, 0), - 'xytsiz': (512, 512), 'xytosiz': (0, 0), 'bitdepth': (16,), - 'signed': (False,), - 'xyrsiz': [(1,), (1,)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (512, 512), 'xytosiz': (0, 0), 'bitdepth': (16,), + 'signed': (False,), + 'xyrsiz': [(1,), (1,)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) # no sop @@ -1860,7 +1837,7 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[2].spcod[4], 5) # level self.assertEqual(tuple(c.segment[2].code_block_size), (64, 64)) self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) @@ -1883,11 +1860,10 @@ class TestSuite(MetadataBase): self.assertEqual(ids, expected) kwargs = {'rsiz': 0, 'xysiz': (1420, 1416), 'xyosiz': (0, 0), - 'xytsiz': (1420, 1416), 'xytosiz': (0, 0), 'bitdepth': (16,), - 'signed': (False,), - 'xyrsiz': [(1,), (1,)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (1420, 1416), 'xytosiz': (0, 0), 'bitdepth': (16,), + 'signed': (False,), + 'xyrsiz': [(1,), (1,)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) # no sop @@ -1898,7 +1874,7 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[2].spcod[4], 11) # level self.assertEqual(tuple(c.segment[2].code_block_size), (64, 64)) self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) @@ -1921,11 +1897,10 @@ class TestSuite(MetadataBase): self.assertEqual(ids, expected) kwargs = {'rsiz': 0, 'xysiz': (512, 614), 'xyosiz': (0, 0), - 'xytsiz': (512, 614), 'xytosiz': (0, 0), 'bitdepth': (12,), - 'signed': (False,), - 'xyrsiz': [(1,), (1,)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (512, 614), 'xytosiz': (0, 0), 'bitdepth': (12,), + 'signed': (False,), + 'xyrsiz': [(1,), (1,)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) # no sop @@ -1936,7 +1911,7 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[2].spcod[4], 5) # level self.assertEqual(tuple(c.segment[2].code_block_size), (64, 64)) self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_9X7_IRREVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) @@ -1965,11 +1940,10 @@ class TestSuite(MetadataBase): self.assertEqual(ids, expected) kwargs = {'rsiz': 0, 'xysiz': (256, 256), 'xyosiz': (0, 0), - 'xytsiz': (256, 256), 'xytosiz': (0, 0), 'bitdepth': (8,), - 'signed': (False,), - 'xyrsiz': [(1,), (1,)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (256, 256), 'xytosiz': (0, 0), 'bitdepth': (8,), + 'signed': (False,), + 'xyrsiz': [(1,), (1,)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) # no sop @@ -1980,7 +1954,7 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[2].spcod[4], 5) # level self.assertEqual(tuple(c.segment[2].code_block_size), (64, 64)) self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) @@ -2007,11 +1981,10 @@ class TestSuite(MetadataBase): self.assertEqual(ids, expected) kwargs = {'rsiz': 0, 'xysiz': (1420, 1416), 'xyosiz': (0, 0), - 'xytsiz': (1420, 1416), 'xytosiz': (0, 0), 'bitdepth': (16,), - 'signed': (False,), - 'xyrsiz': [(1,), (1,)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (1420, 1416), 'xytosiz': (0, 0), 'bitdepth': (16,), + 'signed': (False,), + 'xyrsiz': [(1,), (1,)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) # no sop @@ -2022,7 +1995,7 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[2].spcod[4], 11) # level self.assertEqual(tuple(c.segment[2].code_block_size), (64, 64)) self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) @@ -2044,12 +2017,10 @@ class TestSuite(MetadataBase): self.assertEqual(ids, expected) kwargs = {'rsiz': 0, 'xysiz': (256, 256), 'xyosiz': (0, 0), - 'xytsiz': (256, 256), 'xytosiz': (0, 0), - 'bitdepth': (8, 8, 8), - 'signed': (True, True, True), - 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (256, 256), 'xytosiz': (0, 0), 'bitdepth': (8, 8, 8), + 'signed': (True, True, True), + 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) # no sop @@ -2060,7 +2031,7 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[2].spcod[4], 5) # level self.assertEqual(tuple(c.segment[2].code_block_size), (64, 64)) self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) @@ -2083,11 +2054,10 @@ class TestSuite(MetadataBase): c = jp2k.get_codestream() kwargs = {'rsiz': 0, 'xysiz': (2048, 2500), 'xyosiz': (0, 0), - 'xytsiz': (2048, 2500), 'xytosiz': (0, 0), 'bitdepth': (16,), - 'signed': (False,), - 'xyrsiz': [(1,), (1,)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (2048, 2500), 'xytosiz': (0, 0), 'bitdepth': (16,), + 'signed': (False,), + 'xyrsiz': [(1,), (1,)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) # no sop @@ -2098,7 +2068,7 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[2].spcod[4], 8) # level self.assertEqual(tuple(c.segment[2].code_block_size), (64, 64)) self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) @@ -2131,17 +2101,17 @@ class TestSuite(MetadataBase): pargs = (RCME_ISO_8859_1, ccme.encode()) self.verifyCMEsegment(c.segment[5], CMEsegment(*pargs)) + def test_NR_MarkerIsNotCompliant_j2k_dump(self): jfile = opj_data_file('input/nonregression/MarkerIsNotCompliant.j2k') jp2k = Jp2k(jfile) c = jp2k.get_codestream() kwargs = {'rsiz': 0, 'xysiz': (1420, 1416), 'xyosiz': (0, 0), - 'xytsiz': (1420, 1416), 'xytosiz': (0, 0), 'bitdepth': (16,), - 'signed': (False,), - 'xyrsiz': [(1,), (1,)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (1420, 1416), 'xytosiz': (0, 0), 'bitdepth': (16,), + 'signed': (False,), + 'xyrsiz': [(1,), (1,)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) # no sop @@ -2152,7 +2122,7 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[2].spcod[4], 11) # level self.assertEqual(tuple(c.segment[2].code_block_size), (64, 64)) self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) @@ -2172,12 +2142,10 @@ class TestSuite(MetadataBase): c = jp2k.get_codestream() kwargs = {'rsiz': 0, 'xysiz': (1920, 1080), 'xyosiz': (0, 0), - 'xytsiz': (1920, 1080), 'xytosiz': (0, 0), - 'bitdepth': (8, 8, 8), - 'signed': (False, False, False), - 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (1920, 1080), 'xytosiz': (0, 0), 'bitdepth': (8, 8, 8), + 'signed': (False, False, False), + 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) # no sop @@ -2188,7 +2156,7 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[2].spcod[4], 5) # level self.assertEqual(tuple(c.segment[2].code_block_size), (64, 64)) self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) @@ -2206,12 +2174,10 @@ class TestSuite(MetadataBase): c = jp2k.get_codestream() kwargs = {'rsiz': 0, 'xysiz': (1920, 1080), 'xyosiz': (0, 0), - 'xytsiz': (1920, 1080), 'xytosiz': (0, 0), - 'bitdepth': (8, 8, 8), - 'signed': (False, False, False), - 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (1920, 1080), 'xytosiz': (0, 0), 'bitdepth': (8, 8, 8), + 'signed': (False, False, False), + 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) # no sop @@ -2222,7 +2188,7 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[2].spcod[4], 5) # level self.assertEqual(tuple(c.segment[2].code_block_size), (64, 64)) self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) @@ -2240,12 +2206,10 @@ class TestSuite(MetadataBase): c = jp2k.get_codestream() kwargs = {'rsiz': 0, 'xysiz': (1920, 1080), 'xyosiz': (0, 0), - 'xytsiz': (1920, 1080), 'xytosiz': (0, 0), - 'bitdepth': (8, 8, 8), - 'signed': (False, False, False), - 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (1920, 1080), 'xytosiz': (0, 0), 'bitdepth': (8, 8, 8), + 'signed': (False, False, False), + 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) # no sop @@ -2256,7 +2220,7 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[2].spcod[4], 5) # level self.assertEqual(tuple(c.segment[2].code_block_size), (64, 64)) self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) @@ -2278,12 +2242,10 @@ class TestSuite(MetadataBase): self.assertEqual(ids, expected) kwargs = {'rsiz': 0, 'xysiz': (117, 117), 'xyosiz': (0, 0), - 'xytsiz': (117, 117), 'xytosiz': (0, 0), - 'bitdepth': (8, 8, 8, 8), - 'signed': (False, False, False, False), - 'xyrsiz': [(1, 1, 1, 1), (1, 1, 1, 1)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (117, 117), 'xytosiz': (0, 0), 'bitdepth': (8, 8, 8, 8), + 'signed': (False, False, False, False), + 'xyrsiz': [(1, 1, 1, 1), (1, 1, 1, 1)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) # no sop @@ -2294,7 +2256,7 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[2].spcod[4], 5) # level self.assertEqual(tuple(c.segment[2].code_block_size), (64, 64)) self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) @@ -2316,12 +2278,10 @@ class TestSuite(MetadataBase): self.assertEqual(ids, expected) kwargs = {'rsiz': 0, 'xysiz': (117, 117), 'xyosiz': (0, 0), - 'xytsiz': (117, 117), 'xytosiz': (0, 0), - 'bitdepth': (8, 8, 8, 8), - 'signed': (False, False, False, False), - 'xyrsiz': [(1, 1, 1, 1), (1, 1, 1, 1)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (117, 117), 'xytosiz': (0, 0), 'bitdepth': (8, 8, 8, 8), + 'signed': (False, False, False, False), + 'xyrsiz': [(1, 1, 1, 1), (1, 1, 1, 1)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) # no sop @@ -2332,7 +2292,7 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[2].spcod[4], 5) # level self.assertEqual(tuple(c.segment[2].code_block_size), (64, 64)) self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) @@ -2354,12 +2314,10 @@ class TestSuite(MetadataBase): self.assertEqual(ids, expected) kwargs = {'rsiz': 0, 'xysiz': (512, 512), 'xyosiz': (0, 0), - 'xytsiz': (512, 512), 'xytosiz': (0, 0), - 'bitdepth': (16,), - 'signed': (False,), - 'xyrsiz': [(1,), (1,)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (512, 512), 'xytosiz': (0, 0), 'bitdepth': (16,), + 'signed': (False,), + 'xyrsiz': [(1,), (1,)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) # no sop @@ -2370,7 +2328,7 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[2].spcod[4], 5) # level self.assertEqual(tuple(c.segment[2].code_block_size), (64, 64)) self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) @@ -2396,12 +2354,10 @@ class TestSuite(MetadataBase): self.assertEqual(ids, expected) kwargs = {'rsiz': 0, 'xysiz': (1024, 1024), 'xyosiz': (0, 0), - 'xytsiz': (1024, 1024), 'xytosiz': (0, 0), - 'bitdepth': (12,), - 'signed': (False,), - 'xyrsiz': [(1,), (1,)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (1024, 1024), 'xytosiz': (0, 0), 'bitdepth': (12,), + 'signed': (False,), + 'xyrsiz': [(1,), (1,)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) # no sop @@ -2412,7 +2368,7 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[2].spcod[4], 5) # level self.assertEqual(tuple(c.segment[2].code_block_size), (64, 64)) self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) @@ -2438,12 +2394,10 @@ class TestSuite(MetadataBase): self.assertEqual(ids, expected) kwargs = {'rsiz': 0, 'xysiz': (1800, 1800), 'xyosiz': (0, 0), - 'xytsiz': (1800, 1800), 'xytosiz': (0, 0), - 'bitdepth': (16,), - 'signed': (False,), - 'xyrsiz': [(1,), (1,)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (1800, 1800), 'xytosiz': (0, 0), 'bitdepth': (16,), + 'signed': (False,), + 'xyrsiz': [(1,), (1,)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) # no sop @@ -2452,9 +2406,10 @@ class TestSuite(MetadataBase): 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], 11) # level - self.assertEqual(tuple(c.segment[2].code_block_size), (64, 64)) + self.assertEqual(tuple(c.segment[2].code_block_size), + (64, 64)) # cblk self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) @@ -2476,12 +2431,10 @@ class TestSuite(MetadataBase): self.assertEqual(ids, expected) kwargs = {'rsiz': 0, 'xysiz': (1800, 1800), 'xyosiz': (0, 0), - 'xytsiz': (1800, 1800), 'xytosiz': (0, 0), - 'bitdepth': (16,), - 'signed': (False,), - 'xyrsiz': [(1,), (1,)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (1800, 1800), 'xytosiz': (0, 0), 'bitdepth': (16,), + 'signed': (False,), + 'xyrsiz': [(1,), (1,)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) # no sop @@ -2490,9 +2443,10 @@ class TestSuite(MetadataBase): 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], 11) # level - self.assertEqual(tuple(c.segment[2].code_block_size), (64, 64)) + self.assertEqual(tuple(c.segment[2].code_block_size), + (64, 64)) # cblk self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) @@ -2514,12 +2468,10 @@ class TestSuite(MetadataBase): self.assertEqual(ids, expected) kwargs = {'rsiz': 0, 'xysiz': (2048, 1556), 'xyosiz': (0, 0), - 'xytsiz': (2048, 1556), 'xytosiz': (0, 0), - 'bitdepth': (12, 12, 12), - 'signed': (False, False, False), - 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (2048, 1556), 'xytosiz': (0, 0), 'bitdepth': (12, 12, 12), + 'signed': (False, False, False), + 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) # no sop @@ -2528,9 +2480,10 @@ class TestSuite(MetadataBase): self.assertEqual(c.segment[2].layers, 2) # layers = 2 self.assertEqual(c.segment[2].spcod[3], 1) # mct self.assertEqual(c.segment[2].spcod[4], 5) # level - self.assertEqual(tuple(c.segment[2].code_block_size), (32, 32)) + self.assertEqual(tuple(c.segment[2].code_block_size), + (32, 32)) # cblk self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_9X7_IRREVERSIBLE) self.assertEqual(c.segment[2].precinct_size, @@ -2559,9 +2512,7 @@ class TestSuite(MetadataBase): self.verifySignatureBox(jp2.box[0]) self.verify_filetype_box(jp2.box[1], - FileTypeBox(compatibility_list=['jp2 ', - 'jpxb', - 'jpx '])) + FileTypeBox(compatibility_list=['jp2 ', 'jpxb', 'jpx '])) # Reader requirements talk. # unrestricted jpeg 2000 part 1 @@ -2570,9 +2521,9 @@ class TestSuite(MetadataBase): ihdr = glymur.jp2box.ImageHeaderBox(203, 479, colorspace_unknown=True) self.verifyImageHeaderBox(jp2.box[3].box[0], ihdr) - colr = glymur.jp2box.ColourSpecificationBox(colorspace=SRGB, - approximation=1, - precedence=2) + colr = glymur.jp2box.ColourSpecificationBox( + colorspace=glymur.core.SRGB, + approximation=1, precedence=2) self.verifyColourSpecificationBox(jp2.box[3].box[1], colr) # Jp2 Header @@ -2585,19 +2536,17 @@ class TestSuite(MetadataBase): self.assertEqual(jp2.box[3].box[3].mapping_type, (1, 1, 1)) self.assertEqual(jp2.box[3].box[3].palette_index, (0, 1, 2)) - c = jp2.box[4].codestream + c = jp2.box[4].main_header ids = [x.marker_id for x in c.segment] expected = ['SOC', 'SIZ', 'COD', 'QCD'] self.assertEqual(ids, expected) kwargs = {'rsiz': 0, 'xysiz': (479, 203), 'xyosiz': (0, 0), - 'xytsiz': (256, 203), 'xytosiz': (0, 0), - 'bitdepth': (8,), - 'signed': (False,), - 'xyrsiz': [(1,), (1,)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (256, 203), 'xytosiz': (0, 0), 'bitdepth': (8,), + 'signed': (False,), + 'xyrsiz': [(1,), (1,)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) # no sop @@ -2606,9 +2555,10 @@ class TestSuite(MetadataBase): 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) # level - self.assertEqual(tuple(c.segment[2].code_block_size), (32, 32)) + self.assertEqual(tuple(c.segment[2].code_block_size), + (32, 32)) # cblk self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) @@ -2631,37 +2581,32 @@ class TestSuite(MetadataBase): self.verifySignatureBox(jp2.box[0]) self.verify_filetype_box(jp2.box[1], - FileTypeBox(compatibility_list=['jp2 ', - 'jpxb', - 'jpx '])) + FileTypeBox(compatibility_list=['jp2 ', 'jpxb', 'jpx '])) # Reader requirements talk. # unrestricted jpeg 2000 part 1 self.assertTrue(5 in jp2.box[2].standard_flag) ihdr = glymur.jp2box.ImageHeaderBox(326, 431, - num_components=3, - colorspace_unknown=True) + num_components=3, colorspace_unknown=True) self.verifyImageHeaderBox(jp2.box[3].box[0], ihdr) - colr = glymur.jp2box.ColourSpecificationBox(colorspace=SRGB, - approximation=1, - precedence=2) + colr = glymur.jp2box.ColourSpecificationBox( + colorspace=glymur.core.SRGB, + approximation=1, precedence=2) self.verifyColourSpecificationBox(jp2.box[3].box[1], colr) - c = jp2.box[4].codestream + c = jp2.box[4].main_header ids = [x.marker_id for x in c.segment] expected = ['SOC', 'SIZ', 'COD', 'QCD'] self.assertEqual(ids, expected) kwargs = {'rsiz': 0, 'xysiz': (431, 326), 'xyosiz': (0, 0), - 'xytsiz': (256, 256), 'xytosiz': (0, 0), - 'bitdepth': (8, 8, 8), - 'signed': (False, False, False), - 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (256, 256), 'xytosiz': (0, 0), 'bitdepth': (8, 8, 8), + 'signed': (False, False, False), + 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) # no sop @@ -2670,9 +2615,10 @@ class TestSuite(MetadataBase): 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) # level - self.assertEqual(tuple(c.segment[2].code_block_size), (32, 32)) + self.assertEqual(tuple(c.segment[2].code_block_size), + (32, 32)) # cblk self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_9X7_IRREVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) @@ -2700,10 +2646,11 @@ class TestSuite(MetadataBase): self.verify_filetype_box(jp2.box[1], FileTypeBox()) ihdr = glymur.jp2box.ImageHeaderBox(135, 135, num_components=2, - colorspace_unknown=True) + colorspace_unknown=True) self.verifyImageHeaderBox(jp2.box[2].box[0], ihdr) - colr = glymur.jp2box.ColourSpecificationBox(colorspace=GREYSCALE) + colr = glymur.jp2box.ColourSpecificationBox( + colorspace=glymur.core.GREYSCALE) self.verifyColourSpecificationBox(jp2.box[2].box[1], colr) # Jp2 Header @@ -2712,19 +2659,17 @@ class TestSuite(MetadataBase): self.assertEqual(jp2.box[2].box[2].channel_type, (0, 1)) # opacity self.assertEqual(jp2.box[2].box[2].association, (0, 0)) # both main - c = jp2.box[3].codestream + c = jp2.box[3].main_header ids = [x.marker_id for x in c.segment] expected = ['SOC', 'SIZ', 'COD', 'QCD', 'CME'] self.assertEqual(ids, expected) kwargs = {'rsiz': 0, 'xysiz': (135, 135), 'xyosiz': (0, 0), - 'xytsiz': (135, 135), 'xytosiz': (0, 0), - 'bitdepth': (8, 8), - 'signed': (False, False), - 'xyrsiz': [(1, 1), (1, 1)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (135, 135), 'xytosiz': (0, 0), 'bitdepth': (8, 8), + 'signed': (False, False), + 'xyrsiz': [(1, 1), (1, 1)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) # no sop @@ -2736,7 +2681,7 @@ class TestSuite(MetadataBase): self.assertEqual(tuple(c.segment[2].code_block_size), (64, 64)) # cblk self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_9X7_IRREVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) @@ -2765,23 +2710,20 @@ class TestSuite(MetadataBase): self.verifySignatureBox(jp2.box[0]) self.verify_filetype_box(jp2.box[1], - FileTypeBox(compatibility_list=['jp2 ', - 'jpxb', - 'jpx '])) + FileTypeBox(compatibility_list=['jp2 ', 'jpxb', 'jpx '])) # Reader requirements talk. # unrestricted jpeg 2000 part 1 self.assertTrue(5 in jp2.box[2].standard_flag) ihdr = glymur.jp2box.ImageHeaderBox(46, 124, bits_per_component=4, - colorspace_unknown=True) + colorspace_unknown=True) self.verifyImageHeaderBox(jp2.box[3].box[0], ihdr) - method = ENUMERATED_COLORSPACE - colr = glymur.jp2box.ColourSpecificationBox(colorspace=SRGB, - method=method, - approximation=1, - precedence=2) + colr = glymur.jp2box.ColourSpecificationBox( + colorspace=glymur.core.SRGB, + method=glymur.core.ENUMERATED_COLORSPACE, + approximation=1, precedence=2) self.verifyColourSpecificationBox(jp2.box[3].box[1], colr) # Jp2 Header @@ -2795,19 +2737,17 @@ class TestSuite(MetadataBase): self.assertEqual(jp2.box[3].box[3].mapping_type, (1, 1, 1)) self.assertEqual(jp2.box[3].box[3].palette_index, (0, 1, 2)) - c = jp2.box[4].codestream + c = jp2.box[4].main_header ids = [x.marker_id for x in c.segment] expected = ['SOC', 'SIZ', 'COD', 'QCD'] self.assertEqual(ids, expected) kwargs = {'rsiz': 0, 'xysiz': (124, 46), 'xyosiz': (0, 0), - 'xytsiz': (124, 46), 'xytosiz': (0, 0), - 'bitdepth': (4,), - 'signed': (False,), - 'xyrsiz': [(1,), (1,)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (124, 46), 'xytosiz': (0, 0), 'bitdepth': (4,), + 'signed': (False,), + 'xyrsiz': [(1,), (1,)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) # no sop @@ -2819,7 +2759,7 @@ class TestSuite(MetadataBase): self.assertEqual(tuple(c.segment[2].code_block_size), (32, 32)) # cblk self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) @@ -2849,19 +2789,17 @@ class TestSuite(MetadataBase): colr = glymur.jp2box.ColourSpecificationBox(colorspace=glymur.core.YCC) self.verifyColourSpecificationBox(jp2.box[2].box[1], colr) - c = jp2.box[3].codestream + c = jp2.box[3].main_header ids = [x.marker_id for x in c.segment] expected = ['SOC', 'SIZ', 'COD', 'QCD', 'POD'] self.assertEqual(ids, expected) kwargs = {'rsiz': 0, 'xysiz': (766, 576), 'xyosiz': (0, 0), - 'xytsiz': (766, 576), 'xytosiz': (0, 0), - 'bitdepth': (8, 8, 8), - 'signed': (False, False, False), - 'xyrsiz': [(1, 2, 2), (1, 1, 1)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (766, 576), 'xytosiz': (0, 0), 'bitdepth': (8, 8, 8), + 'signed': (False, False, False), + 'xyrsiz': [(1, 2, 2), (1, 1, 1)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) # no sop @@ -2873,7 +2811,7 @@ class TestSuite(MetadataBase): self.assertEqual(tuple(c.segment[2].code_block_size), (32, 128)) # cblk self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) @@ -2909,34 +2847,112 @@ class TestSuiteWarns(MetadataBase): relpath = 'input/nonregression/issue188_beach_64bitsbox.jp2' jfile = opj_data_file(relpath) with self.assertWarns(UserWarning): - Jp2k(jfile)[:] + j = Jp2k(jfile) + d = j.read() self.assertTrue(True) def test_NR_broken4_jp2_dump(self): jfile = opj_data_file('input/nonregression/broken4.jp2') - with warnings.catch_warnings(): - # Suppress a warning, all we really care is parsing the entire - # file. - warnings.simplefilter("ignore") - with self.assertWarns(UserWarning): - jp2 = Jp2k(jfile) - self.assertEqual(jp2.box[-1].codestream.segment[-1].marker_id, - 'QCC') + with self.assertWarns(UserWarning): + jp2 = Jp2k(jfile) + + self.assertEqual(jp2.box[-1].main_header.segment[-1].marker_id, 'QCC') + + @unittest.skipIf(sys.maxsize < 2**32, 'Do not run on 32-bit platforms') + def test_NR_broken3_jp2_dump(self): + """ + NR_broken3_jp2_dump + + The file in question here has a colr box with an erroneous box + length of over 1GB. Don't run it on 32-bit platforms. + """ + jfile = opj_data_file('input/nonregression/broken3.jp2') + with self.assertWarns(UserWarning): + # Bad box length. + jp2 = Jp2k(jfile) + + ids = [box.box_id for box in jp2.box] + self.assertEqual(ids, ['jP ', 'ftyp', 'jp2h', 'jp2c']) + + ids = [box.box_id for box in jp2.box[2].box] + self.assertEqual(ids, ['ihdr', 'colr']) + + self.verifySignatureBox(jp2.box[0]) + self.verify_filetype_box(jp2.box[1], FileTypeBox()) + + ihdr = glymur.jp2box.ImageHeaderBox(152, 203, num_components=3) + self.verifyImageHeaderBox(jp2.box[2].box[0], ihdr) + + colr = glymur.jp2box.ColourSpecificationBox(colorspace=glymur.core.SRGB) + self.verifyColourSpecificationBox(jp2.box[2].box[1], colr) + + c = jp2.box[3].main_header + + ids = [x.marker_id for x in c.segment] + expected = ['SOC', 'SIZ', 'CME', 'COD', 'QCD', 'QCC', 'QCC'] + self.assertEqual(ids, expected) + + kwargs = {'rsiz': 0, 'xysiz': (203, 152), 'xyosiz': (0, 0), + 'xytsiz': (203, 152), 'xytosiz': (0, 0), 'bitdepth': (8, 8, 8), + 'signed': (False, False, False), + 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) + + pargs = RCME_ISO_8859_1, "Creator: JasPer Vers)on 1.701.0".encode() + self.verifyCMEsegment(c.segment[2], CMEsegment(*pargs)) + + # COD: Coding style default + self.assertFalse(c.segment[3].scod & 2) # no sop + self.assertFalse(c.segment[3].scod & 4) # no eph + self.assertEqual(c.segment[3].spcod[0], glymur.core.LRCP) + self.assertEqual(c.segment[3].layers, 1) # layers = 1 + self.assertEqual(c.segment[3].spcod[3], 1) # mct + self.assertEqual(c.segment[3].spcod[4], 5) # level + self.assertEqual(tuple(c.segment[3].code_block_size), + (64, 64)) # cblk + self.verify_codeblock_style(c.segment[3].spcod[7], + [False, False, False, False, False, False]) + self.assertEqual(c.segment[3].spcod[8], + glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) + self.assertEqual(len(c.segment[3].spcod), 9) + + # QCD: Quantization default + self.assertEqual(c.segment[4].sqcd & 0x1f, 0) + self.assertEqual(c.segment[4].guard_bits, 2) + self.assertEqual(c.segment[4].mantissa, [0] * 16) + self.assertEqual(c.segment[4].exponent, + [8] + [9, 9, 10] * 5) + + # QCC: Quantization component + # associated component + self.assertEqual(c.segment[5].cqcc, 1) + self.assertEqual(c.segment[5].guard_bits, 2) + # quantization type + self.assertEqual(c.segment[5].sqcc & 0x1f, 0) # none + self.assertEqual(c.segment[5].mantissa, [0] * 16) + self.assertEqual(c.segment[5].exponent, + [8] + [9, 9, 10] * 5) + + # QCC: Quantization component + # associated component + self.assertEqual(c.segment[6].cqcc, 2) + self.assertEqual(c.segment[6].guard_bits, 2) + # quantization type + self.assertEqual(c.segment[6].sqcc & 0x1f, 0) # none + self.assertEqual(c.segment[6].mantissa, [0] * 16) + self.assertEqual(c.segment[6].exponent, + [8] + [9, 9, 10] * 5) def test_NR_broken2_jp2_dump(self): """ Invalid marker ID in the codestream. """ jfile = opj_data_file('input/nonregression/broken2.jp2') - with warnings.catch_warnings(): - # Suppress a warning, all we really care is parsing the entire - # file. - warnings.simplefilter("ignore") - with self.assertWarns(UserWarning): - # Invalid marker ID on codestream. - jp2 = Jp2k(jfile) - self.assertEqual(jp2.box[-1].codestream.segment[-1].marker_id, - 'QCC') + with self.assertWarns(UserWarning): + # Invalid marker ID on codestream. + jp2 = Jp2k(jfile) + + self.assertEqual(jp2.box[-1].main_header.segment[-1].marker_id, 'QCC') def test_NR_file1_dump(self): jfile = opj_data_file('input/conformance/file1.jp2') @@ -2963,8 +2979,8 @@ class TestSuiteWarns(MetadataBase): ihdr = glymur.jp2box.ImageHeaderBox(512, 768, num_components=3) self.verifyImageHeaderBox(jp2.box[3].box[0], ihdr) - colr = glymur.jp2box.ColourSpecificationBox(colorspace=SRGB, - approximation=1) + colr = glymur.jp2box.ColourSpecificationBox(colorspace=glymur.core.SRGB, + approximation=1) self.verifyColourSpecificationBox(jp2.box[3].box[1], colr) # XML box @@ -2991,7 +3007,7 @@ class TestSuiteWarns(MetadataBase): self.verifyImageHeaderBox(jp2.box[2].box[0], ihdr) colr = glymur.jp2box.ColourSpecificationBox(colorspace=glymur.core.YCC, - approximation=1) + approximation=1) self.verifyColourSpecificationBox(jp2.box[2].box[1], colr) # Jp2 Header @@ -3021,8 +3037,9 @@ class TestSuiteWarns(MetadataBase): ihdr = glymur.jp2box.ImageHeaderBox(640, 480, num_components=3) self.verifyImageHeaderBox(jp2.box[2].box[0], ihdr) - colr = glymur.jp2box.ColourSpecificationBox(colorspace=glymur.core.YCC, - approximation=1) + colr = glymur.jp2box.ColourSpecificationBox( + colorspace=glymur.core.YCC, + approximation=1) self.verifyColourSpecificationBox(jp2.box[2].box[1], colr) # sub-sampling @@ -3035,7 +3052,7 @@ class TestSuiteWarns(MetadataBase): self.assertEqual(codestream.segment[1].yrsiz[2], 2) def test_NR_file4_dump(self): - # One 8-bit component in the grey colourspace. + # One 8-bit component in the sRGB-grey colourspace. jfile = opj_data_file('input/conformance/file4.jp2') with self.assertWarns(UserWarning): jp2 = Jp2k(jfile) @@ -3052,8 +3069,8 @@ class TestSuiteWarns(MetadataBase): ihdr = glymur.jp2box.ImageHeaderBox(512, 768) self.verifyImageHeaderBox(jp2.box[2].box[0], ihdr) - colr = glymur.jp2box.ColourSpecificationBox(colorspace=GREYSCALE, - approximation=1) + colr = glymur.jp2box.ColourSpecificationBox( + colorspace=glymur.core.GREYSCALE, approximation=1) self.verifyColourSpecificationBox(jp2.box[2].box[1], colr) def test_NR_file5_dump(self): @@ -3075,18 +3092,16 @@ class TestSuiteWarns(MetadataBase): self.assertEqual(ids, ['ihdr', 'colr', 'colr']) self.verifySignatureBox(jp2.box[0]) - expected = FileTypeBox(brand='jpx ', - compatibility_list=['jp2 ', 'jpx ', 'jpxb']) + expected = FileTypeBox( + brand='jpx ', compatibility_list=['jp2 ', 'jpx ', 'jpxb']) self.verify_filetype_box(jp2.box[1], expected) ihdr = glymur.jp2box.ImageHeaderBox(512, 768, num_components=3) self.verifyImageHeaderBox(jp2.box[3].box[0], ihdr) - method = RESTRICTED_ICC_PROFILE - icc_profile = bytes([0] * 546) - colr = glymur.jp2box.ColourSpecificationBox(method=method, - approximation=1, - icc_profile=icc_profile) + colr = glymur.jp2box.ColourSpecificationBox( + method=glymur.core.RESTRICTED_ICC_PROFILE, + approximation=1, icc_profile=bytes([0] * 546)) self.verifyColourSpecificationBox(jp2.box[3].box[1], colr) self.assertEqual(jp2.box[3].box[1].icc_profile['Size'], 546) @@ -3107,10 +3122,10 @@ class TestSuiteWarns(MetadataBase): ihdr = glymur.jp2box.ImageHeaderBox(512, 768, bits_per_component=12) self.verifyImageHeaderBox(jp2.box[2].box[0], ihdr) - method = ENUMERATED_COLORSPACE - colr = glymur.jp2box.ColourSpecificationBox(colorspace=GREYSCALE, - method=method, - approximation=1) + colr = glymur.jp2box.ColourSpecificationBox( + colorspace=glymur.core.GREYSCALE, + method=glymur.core.ENUMERATED_COLORSPACE, + approximation=1) self.verifyColourSpecificationBox(jp2.box[2].box[1], colr) def test_NR_file7_dump(self): @@ -3136,13 +3151,12 @@ class TestSuiteWarns(MetadataBase): self.assertEqual(jp2.box[1].compatibility_list[1], 'jp2 ') ihdr = glymur.jp2box.ImageHeaderBox(640, 480, - num_components=3, - bits_per_component=16) + num_components=3, bits_per_component=16) self.verifyImageHeaderBox(jp2.box[3].box[0], ihdr) - method = RESTRICTED_ICC_PROFILE - colr = glymur.jp2box.ColourSpecificationBox(method=method, - approximation=1) + colr = glymur.jp2box.ColourSpecificationBox( + method=glymur.core.RESTRICTED_ICC_PROFILE, + approximation=1) self.verifyColourSpecificationBox(jp2.box[3].box[1], colr) self.assertEqual(jp2.box[3].box[1].icc_profile['Size'], 13332) @@ -3166,9 +3180,9 @@ class TestSuiteWarns(MetadataBase): ihdr = glymur.jp2box.ImageHeaderBox(400, 700) self.verifyImageHeaderBox(jp2.box[2].box[0], ihdr) - method = RESTRICTED_ICC_PROFILE - colr = glymur.jp2box.ColourSpecificationBox(method=method, - approximation=1) + colr = glymur.jp2box.ColourSpecificationBox( + method=glymur.core.RESTRICTED_ICC_PROFILE, + approximation=1) self.verifyColourSpecificationBox(jp2.box[2].box[1], colr) self.assertEqual(jp2.box[2].box[1].icc_profile['Size'], 414) @@ -3221,15 +3235,16 @@ class TestSuiteWarns(MetadataBase): self.assertEqual(jp2.box[2].box[2].mapping_type, (1, 1, 1)) self.assertEqual(jp2.box[2].box[2].palette_index, (0, 1, 2)) - colr = glymur.jp2box.ColourSpecificationBox(colorspace=SRGB, - approximation=1) + colr = glymur.jp2box.ColourSpecificationBox( + colorspace=glymur.core.SRGB, + approximation=1) self.verifyColourSpecificationBox(jp2.box[2].box[3], colr) def test_NR_issue188_beach_64bitsbox(self): lst = ['input', 'nonregression', 'issue188_beach_64bitsbox.jp2'] jfile = opj_data_file('/'.join(lst)) with self.assertWarns(UserWarning): - # There's a warning for an unknown box. + # There's a warning for an unknown box. jp2 = Jp2k(jfile) ids = [box.box_id for box in jp2.box] @@ -3242,29 +3257,25 @@ class TestSuiteWarns(MetadataBase): self.verify_filetype_box(jp2.box[1], FileTypeBox()) ihdr = glymur.jp2box.ImageHeaderBox(200, 200, - num_components=3, - colorspace_unknown=True) + num_components=3, colorspace_unknown=True) self.verifyImageHeaderBox(jp2.box[2].box[0], ihdr) - cspace = glymur.core.SRGB - colr = glymur.jp2box.ColourSpecificationBox(colorspace=cspace) + colr = glymur.jp2box.ColourSpecificationBox(colorspace=glymur.core.SRGB) self.verifyColourSpecificationBox(jp2.box[2].box[1], colr) # Skip the 4th box, it is uknown. - c = jp2.box[4].codestream + c = jp2.box[4].main_header ids = [x.marker_id for x in c.segment] expected = ['SOC', 'SIZ', 'COD', 'QCD', 'CME', 'CME'] self.assertEqual(ids, expected) kwargs = {'rsiz': 0, 'xysiz': (200, 200), 'xyosiz': (0, 0), - 'xytsiz': (200, 200), 'xytosiz': (0, 0), - 'bitdepth': (8, 8, 8), - 'signed': (False, False, False), - 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (200, 200), 'xytosiz': (0, 0), 'bitdepth': (8, 8, 8), + 'signed': (False, False, False), + 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) # no sop @@ -3276,7 +3287,7 @@ class TestSuiteWarns(MetadataBase): self.assertEqual(tuple(c.segment[2].code_block_size), (64, 64)) # cblk self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_9X7_IRREVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) @@ -3312,19 +3323,17 @@ class TestSuiteWarns(MetadataBase): self.assertIsNone(jp2.box[2].box[1].icc_profile) self.assertIsNone(jp2.box[2].box[1].colorspace) - c = jp2.box[3].codestream + c = jp2.box[3].main_header ids = [x.marker_id for x in c.segment] expected = ['SOC', 'SIZ', 'COD', 'QCD'] self.assertEqual(ids, expected) kwargs = {'rsiz': 0, 'xysiz': (117, 117), 'xyosiz': (0, 0), - 'xytsiz': (117, 117), 'xytosiz': (0, 0), - 'bitdepth': (8, 8, 8, 8), - 'signed': (False, False, False, False), - 'xyrsiz': [(1, 1, 1, 1), (1, 1, 1, 1)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (117, 117), 'xytosiz': (0, 0), 'bitdepth': (8, 8, 8, 8), + 'signed': (False, False, False, False), + 'xyrsiz': [(1, 1, 1, 1), (1, 1, 1, 1)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) # no sop @@ -3336,7 +3345,7 @@ class TestSuiteWarns(MetadataBase): self.assertEqual(tuple(c.segment[2].code_block_size), (64, 64)) # cblk self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) @@ -3375,19 +3384,17 @@ class TestSuiteWarns(MetadataBase): self.assertIsNone(jp2.box[2].box[1].icc_profile) self.assertIsNone(jp2.box[2].box[1].colorspace) - c = jp2.box[3].codestream + c = jp2.box[3].main_header ids = [x.marker_id for x in c.segment] expected = ['SOC', 'SIZ', 'COD', 'QCD'] self.assertEqual(ids, expected) kwargs = {'rsiz': 0, 'xysiz': (117, 117), 'xyosiz': (0, 0), - 'xytsiz': (117, 117), 'xytosiz': (0, 0), - 'bitdepth': (8, 8, 8, 8), - 'signed': (False, False, False, False), - 'xyrsiz': [(1, 1, 1, 1), (1, 1, 1, 1)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (117, 117), 'xytosiz': (0, 0), 'bitdepth': (8, 8, 8, 8), + 'signed': (False, False, False, False), + 'xyrsiz': [(1, 1, 1, 1), (1, 1, 1, 1)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) # no sop @@ -3399,7 +3406,7 @@ class TestSuiteWarns(MetadataBase): self.assertEqual(tuple(c.segment[2].code_block_size), (64, 64)) # cblk self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) diff --git a/glymur/test/test_opj_suite_neg.py b/glymur/test/test_opj_suite_neg.py index d2351a4..058be42 100644 --- a/glymur/test/test_opj_suite_neg.py +++ b/glymur/test/test_opj_suite_neg.py @@ -2,8 +2,15 @@ The tests here do not correspond directly to the OpenJPEG test suite, but seem like logical negative tests to add. """ +# E1101: assertWarns introduced in python 3.2 +# pylint: disable=E1101 + +# R0904: Not too many methods in unittest. +# pylint: disable=R0904 + import os import re +import sys import tempfile import unittest @@ -17,7 +24,6 @@ from .fixtures import OPJ_DATA_ROOT, opj_data_file, read_image from .fixtures import NO_READ_BACKEND, NO_READ_BACKEND_MSG from .fixtures import NO_SKIMAGE_FREEIMAGE_SUPPORT from .fixtures import WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG -from . import fixtures from glymur import Jp2k import glymur @@ -25,7 +31,7 @@ import glymur @unittest.skipIf(OPJ_DATA_ROOT is None, "OPJ_OPJ_DATA_ROOT environment variable not set") -class TestSuiteNegativeRead(unittest.TestCase): +class TestSuiteNegative(unittest.TestCase): """Test suite for certain negative tests from openjpeg suite.""" def setUp(self): @@ -35,6 +41,33 @@ class TestSuiteNegativeRead(unittest.TestCase): def tearDown(self): pass + + @unittest.skipIf(NO_SKIMAGE_FREEIMAGE_SUPPORT, + "Cannot read input image without scikit-image/freeimage") + @unittest.skipIf(os.name == "nt", "Temporary file issue on window.") + def test_cinema2K_bad_frame_rate(self): + """Cinema2k frame rate must be either 24 or 48.""" + relfile = 'input/nonregression/X_5_2K_24_235_CBR_STEM24_000.tif' + infile = opj_data_file(relfile) + data = skimage.io.imread(infile) + with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: + j = Jp2k(tfile.name, 'wb') + with self.assertRaises(IOError): + j.write(data, cinema2k=36) + + + @unittest.skipIf(NO_READ_BACKEND, NO_READ_BACKEND_MSG) + @unittest.skipIf(os.name == "nt", "Temporary file issue on window.") + def test_psnr_with_cratios(self): + """Using psnr with cratios options is not allowed.""" + # Not an OpenJPEG test, but close. + infile = opj_data_file('input/nonregression/Bretagne1.ppm') + data = read_image(infile) + with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: + j = Jp2k(tfile.name, 'wb') + with self.assertRaises(IOError): + j.write(data, psnr=[30, 35, 40], cratios=[2, 3, 4]) + def test_nr_marker_not_compliant(self): """non-compliant marker, should still be able to read""" relpath = 'input/nonregression/MarkerIsNotCompliant.j2k' @@ -65,80 +98,56 @@ class TestSuiteNegativeRead(unittest.TestCase): jp2k.get_codestream(header_only=False) self.assertTrue(True) - -@unittest.skipIf(re.match("1.5|2", glymur.version.openjpeg_version) is None, - "Must have openjpeg 1.5 or higher to run") -@unittest.skipIf(os.name == "nt", fixtures.WINDOWS_TMP_FILE_MSG) -@unittest.skipIf(OPJ_DATA_ROOT is None, - "OPJ_OPJ_DATA_ROOT environment variable not set") -class TestSuiteNegativeWrite(unittest.TestCase): - """Test suite for certain negative tests from openjpeg suite.""" - - def setUp(self): - self.jp2file = glymur.data.nemo() - self.j2kfile = glymur.data.goodstuff() - - def tearDown(self): - pass - - @unittest.skipIf(NO_SKIMAGE_FREEIMAGE_SUPPORT, - "Cannot read input image without scikit-image/freeimage") - def test_cinema2K_bad_frame_rate(self): - """Cinema2k frame rate must be either 24 or 48.""" - relfile = 'input/nonregression/X_5_2K_24_235_CBR_STEM24_000.tif' - infile = opj_data_file(relfile) - data = skimage.io.imread(infile) - with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: - with self.assertRaises(IOError): - Jp2k(tfile.name, data=data, cinema2k=36) - - @unittest.skipIf(NO_READ_BACKEND, NO_READ_BACKEND_MSG) - def test_psnr_with_cratios(self): - """Using psnr with cratios options is not allowed.""" - # Not an OpenJPEG test, but close. - infile = opj_data_file('input/nonregression/Bretagne1.ppm') - data = read_image(infile) - with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: - with self.assertRaises(IOError): - Jp2k(tfile.name, - data=data, psnr=[30, 35, 40], cratios=[2, 3, 4]) - + @unittest.skipIf(os.name == "nt", "Temporary file issue on window.") def test_code_block_dimensions(self): """don't allow extreme codeblock sizes""" # opj_compress doesn't allow the dimensions of a codeblock # to be too small or too big, so neither will we. data = np.zeros((256, 256), dtype=np.uint8) with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: + j = Jp2k(tfile.name, 'wb') + # opj_compress doesn't allow code block area to exceed 4096. with self.assertRaises(IOError): - Jp2k(tfile.name, data=data, cbsize=(256, 256)) + j.write(data, cbsize=(256, 256)) # opj_compress doesn't allow either dimension to be less than 4. with self.assertRaises(IOError): - Jp2k(tfile.name, data=data, cbsize=(2048, 2)) + j.write(data, cbsize=(2048, 2)) with self.assertRaises(IOError): - Jp2k(tfile.name, data=data, cbsize=(2, 2048)) + j.write(data, cbsize=(2, 2048)) + @unittest.skipIf(os.name == "nt", "Temporary file issue on window.") def test_precinct_size_not_p2(self): """precinct sizes should be powers of two.""" ifile = Jp2k(self.j2kfile) - data = ifile[::4, ::4] + data = ifile.read(rlevel=2) with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: + ofile = Jp2k(tfile.name, 'wb') with self.assertRaises(IOError): - Jp2k(tfile.name, data=data, psizes=[(13, 13)]) + ofile.write(data, psizes=[(13, 13)]) + @unittest.skipIf(os.name == "nt", "Temporary file issue on window.") def test_cblk_size_not_power_of_two(self): """code block sizes should be powers of two.""" ifile = Jp2k(self.j2kfile) - data = ifile[::4, ::4] + data = ifile.read(rlevel=2) with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: + ofile = Jp2k(tfile.name, 'wb') with self.assertRaises(IOError): - Jp2k(tfile.name, data=data, cbsize=(13, 12)) + ofile.write(data, cbsize=(13, 12)) + @unittest.skipIf(os.name == "nt", "Temporary file issue on window.") def test_cblk_size_precinct_size(self): """code block sizes should never exceed half that of precinct size.""" ifile = Jp2k(self.j2kfile) - data = ifile[::4, ::4] + data = ifile.read(rlevel=2) with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: + ofile = Jp2k(tfile.name, 'wb') with self.assertRaises(IOError): - Jp2k(tfile.name, data=data, cbsize=(64, 64), psizes=[(64, 64)]) + ofile.write(data, + cbsize=(64, 64), + psizes=[(64, 64)]) + +if __name__ == "__main__": + unittest.main() diff --git a/glymur/test/test_opj_suite_write.py b/glymur/test/test_opj_suite_write.py index f44c7b4..7e6357d 100644 --- a/glymur/test/test_opj_suite_write.py +++ b/glymur/test/test_opj_suite_write.py @@ -2,17 +2,16 @@ The tests defined here roughly correspond to what is in the OpenJPEG test suite. """ +# C0103: method names longer that 30 chars are ok in tests, IMHO +# R0904: Seems like pylint is fooled in this situation +# pylint: disable=R0904,C0103 + import os import re import sys import tempfile import unittest -if sys.hexversion <= 0x03030000: - from mock import patch -else: - from unittest.mock import patch - import numpy as np try: import skimage.io @@ -30,7 +29,6 @@ from glymur import Jp2k from glymur.codestream import SIZsegment from glymur.version import openjpeg_version - class CinemaBase(fixtures.MetadataBase): def verify_cinema_cod(self, cod_segment): @@ -41,14 +39,14 @@ class CinemaBase(fixtures.MetadataBase): self.assertEqual(cod_segment.layers, 1) self.assertEqual(cod_segment.spcod[3], 1) # mct self.assertEqual(cod_segment.spcod[4], 5) # levels - self.assertEqual(tuple(cod_segment.code_block_size), (32, 32)) + self.assertEqual(tuple(cod_segment.code_block_size), (32, 32)) # cblksz def check_cinema4k_codestream(self, codestream, image_size): kwargs = {'rsiz': 4, 'xysiz': image_size, 'xyosiz': (0, 0), - 'xytsiz': image_size, 'xytosiz': (0, 0), - 'bitdepth': (12, 12, 12), 'signed': (False, False, False), - 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} + 'xytsiz': image_size, 'xytosiz': (0, 0), + 'bitdepth': (12, 12, 12), 'signed': (False, False, False), + 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} self.verifySizSegment(codestream.segment[1], SIZsegment(**kwargs)) self.verify_cinema_cod(codestream.segment[2]) @@ -56,9 +54,9 @@ class CinemaBase(fixtures.MetadataBase): def check_cinema2k_codestream(self, codestream, image_size): kwargs = {'rsiz': 3, 'xysiz': image_size, 'xyosiz': (0, 0), - 'xytsiz': image_size, 'xytosiz': (0, 0), - 'bitdepth': (12, 12, 12), 'signed': (False, False, False), - 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} + 'xytsiz': image_size, 'xytosiz': (0, 0), + 'bitdepth': (12, 12, 12), 'signed': (False, False, False), + 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} self.verifySizSegment(codestream.segment[1], SIZsegment(**kwargs)) self.verify_cinema_cod(codestream.segment[2]) @@ -66,7 +64,7 @@ class CinemaBase(fixtures.MetadataBase): @unittest.skipIf(NO_SKIMAGE_FREEIMAGE_SUPPORT, "Cannot read input image without scikit-image/freeimage") -@unittest.skipIf(os.name == "nt", fixtures.WINDOWS_TMP_FILE_MSG) +@unittest.skipIf(os.name == "nt", "no write support on windows, period") @unittest.skipIf(re.match(r'''(1|2.0.0)''', glymur.version.openjpeg_version) is not None, "Uses features not supported until 2.0.1") @@ -84,9 +82,9 @@ class WriteCinema(CinemaBase): infile = opj_data_file(relfile) data = skimage.io.imread(infile) with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: + j = Jp2k(tfile.name, 'wb') with self.assertRaises(IOError): - Jp2k(tfile.name, data=data, - cinema2k=48, cratios=[200, 100, 50]) + j.write(data, cinema2k=48, cratios=[200, 100, 50]) def test_cinema4K_with_others(self): """Can't specify cinema4k with any other options.""" @@ -94,15 +92,15 @@ class WriteCinema(CinemaBase): infile = opj_data_file(relfile) data = skimage.io.imread(infile) with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: + j = Jp2k(tfile.name, 'wb') with self.assertRaises(IOError): - Jp2k(tfile.name, data=data, - cinema4k=True, cratios=[200, 100, 50]) + j.write(data, cinema4k=True, cratios=[200, 100, 50]) @unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG) @unittest.skipIf(NO_SKIMAGE_FREEIMAGE_SUPPORT, "Cannot read input image without scikit-image/freeimage") -@unittest.skipIf(os.name == "nt", fixtures.WINDOWS_TMP_FILE_MSG) +@unittest.skipIf(os.name == "nt", "no write support on windows, period") @unittest.skipIf(re.match(r'''(1|2.0.0)''', glymur.version.openjpeg_version) is not None, "Uses features not supported until 2.0.1") @@ -120,9 +118,10 @@ class WriteCinemaWarns(CinemaBase): infile = opj_data_file(relfile) data = skimage.io.imread(infile) with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: + j = Jp2k(tfile.name, 'wb') regex = 'OpenJPEG library warning:.*' with self.assertWarnsRegex(UserWarning, re.compile(regex)): - j = Jp2k(tfile.name, data=data, cinema4k=True) + j.write(data, cinema4k=True) codestream = j.get_codestream() self.check_cinema4k_codestream(codestream, (4096, 2160)) @@ -132,9 +131,9 @@ class WriteCinemaWarns(CinemaBase): infile = opj_data_file(relfile) data = skimage.io.imread(infile) with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: - with self.assertWarnsRegex(UserWarning, - 'OpenJPEG library warning'): - j = Jp2k(tfile.name, data=data, cinema2k=48) + j = Jp2k(tfile.name, 'wb') + with self.assertWarnsRegex(UserWarning, 'OpenJPEG library warning'): + j.write(data, cinema2k=48) codestream = j.get_codestream() self.check_cinema2k_codestream(codestream, (2048, 857)) @@ -144,9 +143,9 @@ class WriteCinemaWarns(CinemaBase): infile = opj_data_file(relfile) data = skimage.io.imread(infile) with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: - with self.assertWarnsRegex(UserWarning, - 'OpenJPEG library warning'): - j = Jp2k(tfile.name, data=data, cinema2k=48) + j = Jp2k(tfile.name, 'wb') + with self.assertWarnsRegex(UserWarning, 'OpenJPEG library warning'): + j.write(data, cinema2k=48) codestream = j.get_codestream() self.check_cinema2k_codestream(codestream, (2048, 1080)) @@ -156,9 +155,9 @@ class WriteCinemaWarns(CinemaBase): infile = opj_data_file(relfile) data = skimage.io.imread(infile) with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: - with self.assertWarnsRegex(UserWarning, - 'OpenJPEG library warning'): - j = Jp2k(tfile.name, data=data, cinema2k=24) + j = Jp2k(tfile.name, 'wb') + with self.assertWarnsRegex(UserWarning, 'OpenJPEG library warning'): + j.write(data, cinema2k=24) codestream = j.get_codestream() self.check_cinema2k_codestream(codestream, (2048, 1080)) @@ -168,11 +167,11 @@ class WriteCinemaWarns(CinemaBase): infile = opj_data_file(relfile) data = skimage.io.imread(infile) with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: - with self.assertWarnsRegex(UserWarning, - 'OpenJPEG library warning'): + j = Jp2k(tfile.name, 'wb') + with self.assertWarnsRegex(UserWarning, 'OpenJPEG library warning'): # OpenJPEG library warning: The desired maximum codestream # size has limited at least one of the desired quality layers - j = Jp2k(tfile.name, data=data, cinema2k=24) + j.write(data, cinema2k=24) codestream = j.get_codestream() self.check_cinema2k_codestream(codestream, (2048, 857)) @@ -182,11 +181,12 @@ class WriteCinemaWarns(CinemaBase): infile = opj_data_file(relfile) data = skimage.io.imread(infile) with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: + j = Jp2k(tfile.name, 'wb') regex = 'OpenJPEG library warning' with self.assertWarnsRegex(UserWarning, regex): # OpenJPEG library warning: The desired maximum codestream # size has limited at least one of the desired quality layers - j = Jp2k(tfile.name, data=data, cinema2k=48) + j.write(data, cinema2k=48) codestream = j.get_codestream() self.check_cinema2k_codestream(codestream, (1998, 1080)) @@ -194,11 +194,13 @@ class WriteCinemaWarns(CinemaBase): @unittest.skipIf(NO_SKIMAGE_FREEIMAGE_SUPPORT, "Cannot read input image without scikit-image/freeimage") -@unittest.skipIf(os.name == "nt", fixtures.WINDOWS_TMP_FILE_MSG) +@unittest.skipIf(os.name == "nt", "Temporary file issue on window.") +@unittest.skipIf(not re.match("(1.5|2.0.0)", glymur.version.openjpeg_version), + "Functionality implemented for 2.0.1") @unittest.skipIf(OPJ_DATA_ROOT is None, "OPJ_OPJ_DATA_ROOT environment variable not set") -class TestNegative2pointzero(unittest.TestCase): - """Feature set not supported for versions less than 2.0.1""" +class TestSuiteNegative2pointzero(unittest.TestCase): + """Feature set not supported for versions less than 2.0""" def setUp(self): self.jp2file = glymur.data.nemo() @@ -208,21 +210,18 @@ class TestNegative2pointzero(unittest.TestCase): pass def test_cinema_mode(self): - """Cinema mode not allowed for anything less than 2.0.1""" relfile = 'input/nonregression/X_4_2K_24_185_CBR_WB_000.tif' infile = opj_data_file(relfile) data = skimage.io.imread(infile) - versions = ["1.5.0", "2.0.0"] - for version in versions: - with patch('glymur.version.openjpeg_version', new=version): - with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: - with self.assertRaises(IOError): - Jp2k(tfile.name, data=data, cinema2k=48) + with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: + j = Jp2k(tfile.name, 'wb') + with self.assertRaises(IOError): + j.write(data, cinema2k=48) @unittest.skipIf(re.match(r'''1.[0-4]''', openjpeg_version) is not None, "Writing not supported until OpenJPEG 1.5") -@unittest.skipIf(os.name == "nt", fixtures.WINDOWS_TMP_FILE_MSG) +@unittest.skipIf(os.name == "nt", "no write support on windows, period") @unittest.skipIf(NO_READ_BACKEND, NO_READ_BACKEND_MSG) @unittest.skipIf(OPJ_DATA_ROOT is None, "OPJ_DATA_ROOT environment variable not set") @@ -243,28 +242,30 @@ class TestSuiteWrite(fixtures.MetadataBase): expdata = np.fromfile(filename, dtype=np.uint16) expdata.resize((2816, 2048)) with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: - j = Jp2k(tfile.name, data=expdata, irreversible=True) + j = Jp2k(tfile.name, 'wb') + j.write(expdata, irreversible=True) codestream = j.get_codestream() self.assertEqual(codestream.segment[2].spcod[8], glymur.core.WAVELET_XFORM_9X7_IRREVERSIBLE) + def test_NR_ENC_Bretagne1_ppm_1_encode(self): """NR-ENC-Bretagne1.ppm-1-encode""" infile = opj_data_file('input/nonregression/Bretagne1.ppm') data = read_image(infile) with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: - j = Jp2k(tfile.name, data=data, cratios=[200, 100, 50]) + j = Jp2k(tfile.name, 'wb') + j.write(data, cratios=[200, 100, 50]) # Should be three layers. c = j.get_codestream() kwargs = {'rsiz': 0, 'xysiz': (640, 480), 'xyosiz': (0, 0), - 'xytsiz': (640, 480), 'xytosiz': (0, 0), - 'bitdepth': (8, 8, 8), 'signed': (False, False, False), - 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} - self.verifySizSegment(c.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (640, 480), 'xytosiz': (0, 0), + 'bitdepth': (8, 8, 8), 'signed': (False, False, False), + 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} + self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(c.segment[2].scod & 2) # no sop @@ -276,7 +277,7 @@ class TestSuiteWrite(fixtures.MetadataBase): self.assertEqual(tuple(c.segment[2].code_block_size), (64, 64)) # cblksz self.verify_codeblock_style(c.segment[2].spcod[7], - [False, False, False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(c.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(len(c.segment[2].spcod), 9) @@ -286,17 +287,17 @@ class TestSuiteWrite(fixtures.MetadataBase): infile = opj_data_file('input/nonregression/Bretagne1.ppm') data = read_image(infile) with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: - j = Jp2k(tfile.name, data=data, psnr=[30, 35, 40], numres=2) + j = Jp2k(tfile.name, 'wb') + j.write(data, psnr=[30, 35, 40], numres=2) # Should be three layers. codestream = j.get_codestream() kwargs = {'rsiz': 0, 'xysiz': (640, 480), 'xyosiz': (0, 0), - 'xytsiz': (640, 480), 'xytosiz': (0, 0), - 'bitdepth': (8, 8, 8), 'signed': (False, False, False), - 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} - self.verifySizSegment(codestream.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (640, 480), 'xytosiz': (0, 0), + 'bitdepth': (8, 8, 8), 'signed': (False, False, False), + 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} + self.verifySizSegment(codestream.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(codestream.segment[2].scod & 2) # no sop @@ -308,8 +309,7 @@ class TestSuiteWrite(fixtures.MetadataBase): self.assertEqual(tuple(codestream.segment[2].code_block_size), (64, 64)) # cblksz self.verify_codeblock_style(codestream.segment[2].spcod[7], - [False, False, - False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(codestream.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(len(codestream.segment[2].spcod), 9) @@ -319,19 +319,18 @@ class TestSuiteWrite(fixtures.MetadataBase): infile = opj_data_file('input/nonregression/Bretagne1.ppm') data = read_image(infile) with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: - j = Jp2k(tfile.name, - data=data, - psnr=[30, 35, 40], cbsize=(16, 16), psizes=[(64, 64)]) + j = Jp2k(tfile.name, 'wb') + j.write(data, psnr=[30, 35, 40], cbsize=(16, 16), + psizes=[(64, 64)]) # Should be three layers. codestream = j.get_codestream() kwargs = {'rsiz': 0, 'xysiz': (640, 480), 'xyosiz': (0, 0), - 'xytsiz': (640, 480), 'xytosiz': (0, 0), - 'bitdepth': (8, 8, 8), 'signed': (False, False, False), - 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} - self.verifySizSegment(codestream.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (640, 480), 'xytosiz': (0, 0), + 'bitdepth': (8, 8, 8), 'signed': (False, False, False), + 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} + self.verifySizSegment(codestream.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(codestream.segment[2].scod & 2) # no sop @@ -343,8 +342,7 @@ class TestSuiteWrite(fixtures.MetadataBase): self.assertEqual(tuple(codestream.segment[2].code_block_size), (16, 16)) # cblksz self.verify_codeblock_style(codestream.segment[2].spcod[7], - [False, False, - False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(codestream.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(codestream.segment[2].precinct_size, @@ -356,22 +354,21 @@ class TestSuiteWrite(fixtures.MetadataBase): infile = opj_data_file('input/nonregression/Bretagne2.ppm') data = read_image(infile) with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: - j = Jp2k(tfile.name, - data=data, - psizes=[(128, 128)] * 3, - cratios=[100, 20, 2], - tilesize=(480, 640), - cbsize=(32, 32)) + j = Jp2k(tfile.name, 'wb') + j.write(data, + psizes=[(128, 128)] * 3, + cratios=[100, 20, 2], + tilesize=(480, 640), + cbsize=(32, 32)) # Should be three layers. codestream = j.get_codestream() kwargs = {'rsiz': 0, 'xysiz': (2592, 1944), 'xyosiz': (0, 0), - 'xytsiz': (640, 480), 'xytosiz': (0, 0), - 'bitdepth': (8, 8, 8), 'signed': (False, False, False), - 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} - self.verifySizSegment(codestream.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (640, 480), 'xytosiz': (0, 0), + 'bitdepth': (8, 8, 8), 'signed': (False, False, False), + 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} + self.verifySizSegment(codestream.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(codestream.segment[2].scod & 2) # no sop @@ -383,8 +380,7 @@ class TestSuiteWrite(fixtures.MetadataBase): self.assertEqual(tuple(codestream.segment[2].code_block_size), (32, 32)) # cblksz self.verify_codeblock_style(codestream.segment[2].spcod[7], - [False, False, - False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(codestream.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(codestream.segment[2].precinct_size, @@ -395,16 +391,16 @@ class TestSuiteWrite(fixtures.MetadataBase): infile = opj_data_file('input/nonregression/Bretagne2.ppm') data = read_image(infile) with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: - j = Jp2k(tfile.name, data=data, tilesize=(127, 127), prog="PCRL") + j = Jp2k(tfile.name, 'wb') + j.write(data, tilesize=(127, 127), prog="PCRL") codestream = j.get_codestream() kwargs = {'rsiz': 0, 'xysiz': (2592, 1944), 'xyosiz': (0, 0), - 'xytsiz': (127, 127), 'xytosiz': (0, 0), - 'bitdepth': (8, 8, 8), 'signed': (False, False, False), - 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} - self.verifySizSegment(codestream.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (127, 127), 'xytosiz': (0, 0), + 'bitdepth': (8, 8, 8), 'signed': (False, False, False), + 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} + self.verifySizSegment(codestream.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(codestream.segment[2].scod & 2) # no sop @@ -416,8 +412,7 @@ class TestSuiteWrite(fixtures.MetadataBase): self.assertEqual(tuple(codestream.segment[2].code_block_size), (64, 64)) # cblksz self.verify_codeblock_style(codestream.segment[2].spcod[7], - [False, False, - False, False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(codestream.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(len(codestream.segment[2].spcod), 9) @@ -427,16 +422,16 @@ class TestSuiteWrite(fixtures.MetadataBase): infile = opj_data_file('input/nonregression/Bretagne2.ppm') data = read_image(infile) with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: - j = Jp2k(tfile.name, data=data, subsam=(2, 2), sop=True) + j = Jp2k(tfile.name, 'wb') + j.write(data, subsam=(2, 2), sop=True) codestream = j.get_codestream(header_only=False) kwargs = {'rsiz': 0, 'xysiz': (5183, 3887), 'xyosiz': (0, 0), - 'xytsiz': (5183, 3887), 'xytosiz': (0, 0), - 'bitdepth': (8, 8, 8), 'signed': (False, False, False), - 'xyrsiz': [(2, 2, 2), (2, 2, 2)]} - self.verifySizSegment(codestream.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (5183, 3887), 'xytosiz': (0, 0), + 'bitdepth': (8, 8, 8), 'signed': (False, False, False), + 'xyrsiz': [(2, 2, 2), (2, 2, 2)]} + self.verifySizSegment(codestream.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertTrue(codestream.segment[2].scod & 2) # sop @@ -448,8 +443,7 @@ class TestSuiteWrite(fixtures.MetadataBase): self.assertEqual(tuple(codestream.segment[2].code_block_size), (64, 64)) # cblksz self.verify_codeblock_style(codestream.segment[2].spcod[7], - [False, False, False, - False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(codestream.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(len(codestream.segment[2].spcod), 9) @@ -464,16 +458,16 @@ class TestSuiteWrite(fixtures.MetadataBase): infile = opj_data_file('input/nonregression/Bretagne2.ppm') data = read_image(infile) with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: - j = Jp2k(tfile.name, data=data, modesw=38, eph=True) + j = Jp2k(tfile.name, 'wb') + j.write(data, modesw=38, eph=True) codestream = j.get_codestream(header_only=False) kwargs = {'rsiz': 0, 'xysiz': (2592, 1944), 'xyosiz': (0, 0), - 'xytsiz': (2592, 1944), 'xytosiz': (0, 0), - 'bitdepth': (8, 8, 8), 'signed': (False, False, False), - 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} - self.verifySizSegment(codestream.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (2592, 1944), 'xytosiz': (0, 0), + 'bitdepth': (8, 8, 8), 'signed': (False, False, False), + 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} + self.verifySizSegment(codestream.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(codestream.segment[2].scod & 2) # no sop @@ -483,10 +477,9 @@ class TestSuiteWrite(fixtures.MetadataBase): self.assertEqual(codestream.segment[2].spcod[3], 1) # mct self.assertEqual(codestream.segment[2].spcod[4], 5) # levels self.assertEqual(tuple(codestream.segment[2].code_block_size), - (64, 64)) # cblksz + (64, 64)) # cblksz self.verify_codeblock_style(codestream.segment[2].spcod[7], - [False, True, True, - False, False, True]) + [False, True, True, False, False, True]) self.assertEqual(codestream.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(len(codestream.segment[2].spcod), 9) @@ -500,17 +493,16 @@ class TestSuiteWrite(fixtures.MetadataBase): infile = opj_data_file('input/nonregression/Bretagne2.ppm') data = read_image(infile) with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: - j = Jp2k(tfile.name, - data=data, grid_offset=[300, 150], cratios=[800]) + j = Jp2k(tfile.name, 'wb') + j.write(data, grid_offset=[300, 150], cratios=[800]) codestream = j.get_codestream(header_only=False) kwargs = {'rsiz': 0, 'xysiz': (2742, 2244), 'xyosiz': (150, 300), - 'xytsiz': (2742, 2244), 'xytosiz': (0, 0), - 'bitdepth': (8, 8, 8), 'signed': (False, False, False), - 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} - self.verifySizSegment(codestream.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (2742, 2244), 'xytosiz': (0, 0), + 'bitdepth': (8, 8, 8), 'signed': (False, False, False), + 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} + self.verifySizSegment(codestream.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(codestream.segment[2].scod & 2) # no sop @@ -522,8 +514,7 @@ class TestSuiteWrite(fixtures.MetadataBase): self.assertEqual(tuple(codestream.segment[2].code_block_size), (64, 64)) # cblksz self.verify_codeblock_style(codestream.segment[2].spcod[7], - [False, False, False, - False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(codestream.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(len(codestream.segment[2].spcod), 9) @@ -533,16 +524,16 @@ class TestSuiteWrite(fixtures.MetadataBase): infile = opj_data_file('input/nonregression/Cevennes1.bmp') data = read_image(infile) with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: - j = Jp2k(tfile.name, data=data, cratios=[800]) + j = Jp2k(tfile.name, 'wb') + j.write(data, cratios=[800]) codestream = j.get_codestream(header_only=False) kwargs = {'rsiz': 0, 'xysiz': (2592, 1944), 'xyosiz': (0, 0), - 'xytsiz': (2592, 1944), 'xytosiz': (0, 0), - 'bitdepth': (8, 8, 8), 'signed': (False, False, False), - 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} - self.verifySizSegment(codestream.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (2592, 1944), 'xytosiz': (0, 0), + 'bitdepth': (8, 8, 8), 'signed': (False, False, False), + 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} + self.verifySizSegment(codestream.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(codestream.segment[2].scod & 2) # no sop @@ -554,8 +545,7 @@ class TestSuiteWrite(fixtures.MetadataBase): self.assertEqual(tuple(codestream.segment[2].code_block_size), (64, 64)) # cblksz self.verify_codeblock_style(codestream.segment[2].spcod[7], - [False, False, False, - False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(codestream.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(len(codestream.segment[2].spcod), 9) @@ -565,16 +555,16 @@ class TestSuiteWrite(fixtures.MetadataBase): infile = opj_data_file('input/nonregression/Cevennes2.ppm') data = read_image(infile) with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: - j = Jp2k(tfile.name, data=data, cratios=[50]) + j = Jp2k(tfile.name, 'wb') + j.write(data, cratios=[50]) codestream = j.get_codestream(header_only=False) kwargs = {'rsiz': 0, 'xysiz': (640, 480), 'xyosiz': (0, 0), - 'xytsiz': (640, 480), 'xytosiz': (0, 0), - 'bitdepth': (8, 8, 8), 'signed': (False, False, False), - 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} - self.verifySizSegment(codestream.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (640, 480), 'xytosiz': (0, 0), + 'bitdepth': (8, 8, 8), 'signed': (False, False, False), + 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} + self.verifySizSegment(codestream.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(codestream.segment[2].scod & 2) # no sop @@ -586,8 +576,7 @@ class TestSuiteWrite(fixtures.MetadataBase): self.assertEqual(tuple(codestream.segment[2].code_block_size), (64, 64)) # cblksz self.verify_codeblock_style(codestream.segment[2].spcod[7], - [False, False, False, - False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(codestream.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(len(codestream.segment[2].spcod), 9) @@ -596,8 +585,8 @@ class TestSuiteWrite(fixtures.MetadataBase): """NR-ENC-Rome.bmp-11-encode""" data = read_image(opj_data_file('input/nonregression/Rome.bmp')) with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: - jp2 = Jp2k(tfile.name, - data=data, psnr=[30, 35, 50], prog='LRCP', numres=3) + jp2 = Jp2k(tfile.name, 'wb') + jp2.write(data, psnr=[30, 35, 50], prog='LRCP', numres=3) ids = [box.box_id for box in jp2.box] self.assertEqual(ids, ['jP ', 'ftyp', 'jp2h', 'jp2c']) @@ -632,14 +621,13 @@ class TestSuiteWrite(fixtures.MetadataBase): self.assertIsNone(jp2.box[2].box[1].icc_profile) self.assertEqual(jp2.box[2].box[1].colorspace, glymur.core.SRGB) - codestream = jp2.box[3].codestream + codestream = jp2.box[3].main_header kwargs = {'rsiz': 0, 'xysiz': (640, 480), 'xyosiz': (0, 0), - 'xytsiz': (640, 480), 'xytosiz': (0, 0), - 'bitdepth': (8, 8, 8), 'signed': (False, False, False), - 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} - self.verifySizSegment(codestream.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (640, 480), 'xytosiz': (0, 0), + 'bitdepth': (8, 8, 8), 'signed': (False, False, False), + 'xyrsiz': [(1, 1, 1), (1, 1, 1)]} + self.verifySizSegment(codestream.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(codestream.segment[2].scod & 2) # no sop @@ -651,8 +639,7 @@ class TestSuiteWrite(fixtures.MetadataBase): self.assertEqual(tuple(codestream.segment[2].code_block_size), (64, 64)) # cblksz self.verify_codeblock_style(codestream.segment[2].spcod[7], - [False, False, False, - False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(codestream.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(len(codestream.segment[2].spcod), 9) @@ -664,16 +651,16 @@ class TestSuiteWrite(fixtures.MetadataBase): infile = opj_data_file('input/nonregression/random-issue-0005.tif') data = read_image(infile) with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: - j = Jp2k(tfile.name, data=data) + j = Jp2k(tfile.name, 'wb') + j.write(data) codestream = j.get_codestream(header_only=False) kwargs = {'rsiz': 0, 'xysiz': (1024, 1024), 'xyosiz': (0, 0), - 'xytsiz': (1024, 1024), 'xytosiz': (0, 0), - 'bitdepth': (16,), 'signed': (False,), - 'xyrsiz': [(1,), (1,)]} - self.verifySizSegment(codestream.segment[1], - glymur.codestream.SIZsegment(**kwargs)) + 'xytsiz': (1024, 1024), 'xytosiz': (0, 0), + 'bitdepth': (16,), 'signed': (False,), + 'xyrsiz': [(1,), (1,)]} + self.verifySizSegment(codestream.segment[1], glymur.codestream.SIZsegment(**kwargs)) # COD: Coding style default self.assertFalse(codestream.segment[2].scod & 2) # no sop @@ -685,8 +672,7 @@ class TestSuiteWrite(fixtures.MetadataBase): self.assertEqual(tuple(codestream.segment[2].code_block_size), (64, 64)) # cblksz self.verify_codeblock_style(codestream.segment[2].spcod[7], - [False, False, False, - False, False, False]) + [False, False, False, False, False, False]) self.assertEqual(codestream.segment[2].spcod[8], glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(len(codestream.segment[2].spcod), 9) diff --git a/glymur/test/test_printing.py b/glymur/test/test_printing.py index bccaa00..187a15b 100644 --- a/glymur/test/test_printing.py +++ b/glymur/test/test_printing.py @@ -1,6 +1,15 @@ # -*- coding: utf-8 -*- """Test suite for printing. """ +# C0302: don't care too much about having too many lines in a test module +# pylint: disable=C0302 + +# E061: unittest.mock introduced in 3.3 (python-2.7/pylint issue) +# pylint: disable=E0611,F0401 + +# R0904: Not too many methods in unittest. +# pylint: disable=R0904 + import os import re import struct @@ -22,15 +31,15 @@ import lxml.etree as ET import glymur from glymur import Jp2k, command_line +from glymur.version import openjpeg_version + from . import fixtures -from .fixtures import (OPJ_DATA_ROOT, opj_data_file, - WARNING_INFRASTRUCTURE_ISSUE, - WARNING_INFRASTRUCTURE_MSG, - WINDOWS_TMP_FILE_MSG, - text_gbr_27, text_gbr_33, text_gbr_34) +from .fixtures import OPJ_DATA_ROOT, opj_data_file +from .fixtures import WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG +from .fixtures import text_gbr_27, text_gbr_33, text_gbr_34 -@unittest.skipIf(os.name == "nt", WINDOWS_TMP_FILE_MSG) +@unittest.skipIf(os.name == "nt", "Temporary file issue on window.") class TestPrinting(unittest.TestCase): """Tests for verifying how printing works.""" def setUp(self): @@ -42,13 +51,25 @@ class TestPrinting(unittest.TestCase): glymur.set_printoptions(short=False, xml=True, codestream=True) def tearDown(self): - glymur.set_parseoptions(full_codestream=False) + pass + + def test_codestream(self): + """Should be able to print a raw codestream.""" + j = glymur.Jp2k(self.j2kfile) + with patch('sys.stdout', new=StringIO()) as fake_out: + print(j) + actual = fake_out.getvalue().strip() + # Remove the file line, as that is filesystem-dependent. + lines = actual.split('\n') + actual = '\n'.join(lines[1:]) + + self.assertEqual(actual, fixtures.codestream) def test_version_info(self): """Should be able to print(glymur.version.info)""" with patch('sys.stdout', new=StringIO()) as fake_out: print(glymur.version.info) - fake_out.getvalue().strip() + actual = fake_out.getvalue().strip() self.assertTrue(True) @@ -58,7 +79,7 @@ class TestPrinting(unittest.TestCase): with tempfile.NamedTemporaryFile(suffix='.jpx') as tfile: with open(self.jpxfile, 'rb') as ifile: tfile.write(ifile.read()) - + # Add the header for an unknown superbox. write_buffer = struct.pack('>I4s', 20, 'grp '.encode()) tfile.write(write_buffer) @@ -87,17 +108,18 @@ class TestPrinting(unittest.TestCase): with self.assertRaises(TypeError): glymur.set_printoptions(hi='low') - @unittest.skipIf(re.match("1.5|2", - glymur.version.openjpeg_version) is None, - "Must have openjpeg 1.5 or higher to run") + @unittest.skipIf(re.match('0|1.[0-4]', openjpeg_version) is not None, + "Not supported with OpenJPEG {0}".format(openjpeg_version)) def test_asoc_label_box(self): """verify printing of asoc, label boxes""" # Construct a fake file with an asoc and a label box, as # OpenJPEG doesn't have such a file. - data = glymur.Jp2k(self.jp2file)[::2, ::2] + data = glymur.Jp2k(self.jp2file).read(rlevel=1) with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: + j = glymur.Jp2k(tfile.name, 'wb') + j.write(data) + with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile2: - glymur.Jp2k(tfile.name, data=data) # Offset of the codestream is where we start. wbuffer = tfile.read(77) @@ -399,7 +421,7 @@ class TestPrinting(unittest.TestCase): @unittest.skipIf(sys.hexversion < 0x03000000, "Only trusting python3 for printing non-ascii chars") def test_xml_cyrrilic(self): - """Should be able to print XMLBox with utf-8 encoding (cyrrillic).""" + """Should be able to print an XMLBox with utf-8 encoding (cyrrillic).""" # Seems to be inconsistencies between different versions of python2.x # as to what gets printed. # @@ -417,8 +439,7 @@ class TestPrinting(unittest.TestCase): actual = fake_out.getvalue().strip() if sys.hexversion < 0x03000000: lines = ["XML Box (xml ) @ (-1, 0)", - (" Росс", - "ия")] + " Россия"] else: lines = ["XML Box (xml ) @ (-1, 0)", " Россия"] @@ -594,8 +615,7 @@ class TestPrinting(unittest.TestCase): lines = ["UUID Box (uuid) @ (1135519, 76)", " UUID: 4a706754-6966-6645-7869-662d3e4a5032 (EXIF)", - (" UUID Data: OrderedDict([('ImageWidth', 256)," - " ('ImageLength', 512), ('Make', 'HTC')])")] + " UUID Data: OrderedDict([('ImageWidth', 256), ('ImageLength', 512), ('Make', 'HTC')])"] expected = '\n'.join(lines) @@ -809,7 +829,6 @@ class TestPrintingOpjDataRoot(unittest.TestCase): expected = '\n'.join(lines) self.assertEqual(actual, expected) - @unittest.skipIf(OPJ_DATA_ROOT is None, "OPJ_DATA_ROOT environment variable not set") @unittest.skipIf(os.name == "nt", "Temporary file issue on window.") @@ -831,31 +850,20 @@ class TestPrintingOpjDataRootWarns(unittest.TestCase): def tearDown(self): pass - def test_invalid_colour_specification_method(self): - """should not error out with invalid colour specification method""" - # Don't care so much about what the output looks like, just that we - # do not error out. - filename = opj_data_file('input/nonregression/issue397.jp2') - with self.assertWarns(UserWarning): - jp2 = Jp2k(filename) - with patch('sys.stdout', new=StringIO()): - print(jp2) - self.assertTrue(True) - def test_invalid_colorspace(self): """An invalid colorspace shouldn't cause an error.""" filename = opj_data_file('input/nonregression/edf_c2_1103421.jp2') with self.assertWarns(UserWarning): jp2 = Jp2k(filename) - with patch('sys.stdout', new=StringIO()): + with patch('sys.stdout', new=StringIO()) as fake_out: print(jp2) def test_bad_rsiz(self): """Should still be able to print if rsiz is bad, issue196""" filename = opj_data_file('input/nonregression/edf_c2_1002767.jp2') with self.assertWarns(UserWarning): - j = Jp2k(filename).get_codestream() - with patch('sys.stdout', new=StringIO()): + j = Jp2k(filename) + with patch('sys.stdout', new=StringIO()) as fake_out: print(j) def test_bad_wavelet_transform(self): @@ -863,7 +871,7 @@ class TestPrintingOpjDataRootWarns(unittest.TestCase): filename = opj_data_file('input/nonregression/edf_c2_10025.jp2') with self.assertWarns(UserWarning): jp2 = Jp2k(filename) - with patch('sys.stdout', new=StringIO()): + with patch('sys.stdout', new=StringIO()) as fake_out: print(jp2) def test_invalid_progression_order(self): @@ -1024,7 +1032,7 @@ class TestPrintingOpjDataRootWarns(unittest.TestCase): 'issue171.jp2')) with self.assertWarns(UserWarning): jp2 = Jp2k(filename) - with patch('sys.stdout', new=StringIO()): + with patch('sys.stdout', new=StringIO()) as fake_out: # No need to verify, it's enough that we don't error out. print(jp2) @@ -1040,10 +1048,9 @@ class TestJp2dump(unittest.TestCase): # Reset printoptions for every test. glymur.set_printoptions(short=False, xml=True, codestream=True) - glymur.set_parseoptions(full_codestream=False) def tearDown(self): - glymur.set_parseoptions(full_codestream=False) + pass def run_jp2dump(self, args): sys.argv = args @@ -1056,53 +1063,24 @@ class TestJp2dump(unittest.TestCase): return actual def test_default_nemo(self): - """by default one should get the main header""" + """Should be able to dump a JP2 file's metadata with no codestream.""" actual = self.run_jp2dump(['', self.jp2file]) - # shave off the non-main-header segments - lines = fixtures.nemo.split('\n') - expected = lines[0:140] - expected = '\n'.join(expected) - self.assertEqual(actual, expected) + self.assertEqual(actual, fixtures.nemo_dump_no_codestream) - def test_jp2_codestream_0(self): + def test_codestream_0(self): """Verify dumping with -c 0, supressing all codestream details.""" actual = self.run_jp2dump(['', '-c', '0', self.jp2file]) - # shave off the codestream details - lines = fixtures.nemo.split('\n') - expected = lines[0:105] - expected = '\n'.join(expected) - self.assertEqual(actual, expected) + self.assertEqual(actual, fixtures.nemo_dump_no_codestream) - def test_jp2_codestream_1(self): + def test_codestream_1(self): """Verify dumping with -c 1, print just the header.""" actual = self.run_jp2dump(['', '-c', '1', self.jp2file]) - # shave off the non-main-header segments - lines = fixtures.nemo.split('\n') - expected = lines[0:140] - expected = '\n'.join(expected) - self.assertEqual(actual, expected) + self.assertEqual(actual, fixtures.nemo_with_codestream_header) - def test_jp2_codestream_2(self): - """Verify dumping with -c 2, print entire jp2 jacket, codestream.""" - actual = self.run_jp2dump(['', '-c', '2', self.jp2file]) - - # shave off the non-main-header segments - expected = fixtures.nemo - self.assertEqual(actual, expected) - - @unittest.skipIf(sys.hexversion < 0x03000000, "assertRegex not in 2.7") - def test_j2k_codestream_0(self): - """-c 0 should print just a single line when used on a codestream.""" - sys.argv = ['', '-c', '0', self.j2kfile] - with patch('sys.stdout', new=StringIO()) as fake_out: - command_line.main() - actual = fake_out.getvalue().strip() - self.assertRegex(actual, "File: .*") - - def test_j2k_codestream_2(self): + def test_codestream_2(self): """Verify dumping with -c 2, full details.""" with patch('sys.stdout', new=StringIO()) as fake_out: sys.argv = ['', '-c', '2', self.j2kfile] @@ -1127,9 +1105,4 @@ class TestJp2dump(unittest.TestCase): """Verify dumping with -x, suppress XML.""" actual = self.run_jp2dump(['', '-x', self.jp2file]) - # shave off the XML and non-main-header segments - lines = fixtures.nemo.split('\n') - expected = lines[0:18] - expected.extend(lines[104:140]) - expected = '\n'.join(expected) - self.assertEqual(actual, expected) + self.assertEqual(actual, fixtures.nemo_dump_no_codestream_no_xml) diff --git a/glymur/version.py b/glymur/version.py index 4096ee5..1c1279f 100644 --- a/glymur/version.py +++ b/glymur/version.py @@ -14,11 +14,12 @@ from distutils.version import LooseVersion import lxml.etree import numpy as np -from .lib import openjpeg as opj, openjp2 as opj2 +from .lib import openjpeg as opj +from .lib import openjp2 as opj2 # Do not change the format of this next line! Doing so risks breaking # setup.py -version = "0.8.0" +version = "0.7.3" _sv = LooseVersion(version) version_tuple = _sv.version diff --git a/setup.py b/setup.py index b49ed69..1a61dc3 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,4 @@ -from setuptools import setup +from setuptools import setup, find_packages import os import re import sys @@ -11,20 +11,18 @@ kwargs = {'name': 'Glymur', 'url': 'https://github.com/quintusdias/glymur', 'packages': ['glymur', 'glymur.data', 'glymur.test', 'glymur.lib', 'glymur.lib.test'], - 'package_data': {'glymur': ['data/*.jp2', - 'data/*.j2k', - 'data/*.jpx']}, + 'package_data': {'glymur': ['data/*.jp2', 'data/*.j2k', 'data/*.jpx']}, 'entry_points': { 'console_scripts': ['jp2dump=glymur.command_line:main'], }, 'license': 'MIT', 'test_suite': 'glymur.test'} -install_requires = ['numpy>=1.7.0', 'lxml>=3.0.0'] +instllrqrs = ['numpy>=1.4.1', 'lxml>=2.3.2'] if sys.hexversion < 0x03030000: - install_requires.append('contextlib2>=0.4') - install_requires.append('mock>=1.0.1') -kwargs['install_requires'] = install_requires + instllrqrs.append('contextlib2>=0.4') + instllrqrs.append('mock>=1.0.1') +kwargs['install_requires'] = instllrqrs clssfrs = ["Programming Language :: Python", "Programming Language :: Python :: 2.7",