Compare commits

..

7 commits

45 changed files with 3163 additions and 2908 deletions

View file

@ -1,8 +1,3 @@
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.
Oct 06, 2014 - v0.7.2 Added ellipsis support in array-style slicing. Oct 06, 2014 - v0.7.2 Added ellipsis support in array-style slicing.
Oct 02, 2014 - v0.7.1 Fixed README to mention Python 3.4 Oct 02, 2014 - v0.7.1 Fixed README to mention Python 3.4

116
docs/source/api.rst Normal file
View file

@ -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:

View file

@ -13,6 +13,7 @@
# serve to show the default. # serve to show the default.
import sys import sys
import os
class Mock(object): 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, # 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 # 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. # 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 ---------------------------------------------------- # -- General configuration ----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here. # 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 # Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
@ -61,7 +62,7 @@ templates_path = ['_templates']
source_suffix = '.rst' source_suffix = '.rst'
# The encoding of source files. # The encoding of source files.
# source_encoding = 'utf-8-sig' #source_encoding = 'utf-8-sig'
# The master toctree document. # The master toctree document.
master_doc = 'index' master_doc = 'index'
@ -75,19 +76,19 @@ copyright = u'2013, John Evans'
# built documents. # built documents.
# #
# The short X.Y version. # The short X.Y version.
version = '0.8' version = '0.7'
# The full version, including alpha/beta/rc tags. # The full version, including alpha/beta/rc tags.
release = '0.8.0' release = '0.7.2'
# The language for content autogenerated by Sphinx. Refer to documentation # The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages. # for a list of supported languages.
# language = None #language = None
# There are two options for replacing |today|: either, you set today to some # There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used: # non-false value, then it is used:
# today = '' #today = ''
# Else, today_fmt is used as the format for a strftime call. # 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 # List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files. # 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 # The reST default role (used for this markup: `text`) to use for all
# documents. # documents.
# default_role = None #default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text. # 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 # If true, the current module name will be prepended to all description
# unit titles (such as .. function::). # unit titles (such as .. function::).
# add_module_names = True #add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the # If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default. # output. They are ignored by default.
# show_authors = False #show_authors = False
# The name of the Pygments (syntax highlighting) style to use. # The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx' pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting. # A list of ignored prefixes for module index sorting.
# modindex_common_prefix = [] #modindex_common_prefix = []
# -- Options for HTML output -------------------------------------------------- # -- 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 # 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 # further. For a list of options available for each theme, see the
# documentation. # documentation.
# html_theme_options = {} #html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory. # 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 # The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation". # "<project> v<release> documentation".
# html_title = None #html_title = None
# A shorter title for the navigation bar. Default is the same as html_title. # 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 # The name of an image file (relative to this directory) to place at the top
# of the sidebar. # 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 # 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 # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large. # pixels large.
# html_favicon = None #html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here, # 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, # 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, # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format. # 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 # If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities. # typographically correct entities.
# html_use_smartypants = True #html_use_smartypants = True
# Custom sidebar templates, maps document names to template names. # 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 # Additional templates that should be rendered to pages, maps page names to
# template names. # template names.
# html_additional_pages = {} #html_additional_pages = {}
# If false, no module index is generated. # If false, no module index is generated.
# html_domain_indices = True #html_domain_indices = True
# If false, no index is generated. # 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. # 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. # If true, links to the reST sources are added to the pages.
html_show_sourcelink = True html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is 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. # 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 # If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the # contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served. # 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"). # 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. # Output file base name for HTML help builder.
htmlhelp_basename = 'glymurdoc' htmlhelp_basename = 'glymurdoc'
@ -198,13 +199,13 @@ htmlhelp_basename = 'glymurdoc'
# -- Options for LaTeX output ------------------------------------------------- # -- Options for LaTeX output -------------------------------------------------
# The paper size ('letterpaper' or 'a4paper'). # The paper size ('letterpaper' or 'a4paper').
# 'papersize': 'letterpaper', #'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt'). # The font size ('10pt', '11pt' or '12pt').
# 'pointsize': '10pt', #'pointsize': '10pt',
# Additional stuff for the LaTeX preamble. # Additional stuff for the LaTeX preamble.
# 'preamble': '', #'preamble': '',
latex_elements = {} latex_elements = {}
# Grouping the document tree into LaTeX files. List of tuples # 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 name of an image file (relative to this directory) to place at the top of
# the title page. # the title page.
# latex_logo = None #latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts, # For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters. # not chapters.
# latex_use_parts = False #latex_use_parts = False
# If true, show page references after internal links. # If true, show page references after internal links.
# latex_show_pagerefs = False #latex_show_pagerefs = False
# If true, show URL addresses after external links. # 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. # Documents to append as an appendix to all manuals.
# latex_appendices = [] #latex_appendices = []
# If false, no module index is generated. # If false, no module index is generated.
# latex_domain_indices = True #latex_domain_indices = True
# -- Options for manual page output ------------------------------------------- # -- Options for manual page output -------------------------------------------
@ -244,7 +245,7 @@ man_pages = [
] ]
# If true, show URL addresses after external links. # If true, show URL addresses after external links.
# man_show_urls = False #man_show_urls = False
# -- Options for Texinfo output ----------------------------------------------- # -- Options for Texinfo output -----------------------------------------------
@ -257,13 +258,13 @@ texinfo_documents = [('index', 'glymur', u'glymur Documentation',
'One line description of project.', 'Miscellaneous'), ] 'One line description of project.', 'Miscellaneous'), ]
# Documents to append as an appendix to all manuals. # Documents to append as an appendix to all manuals.
# texinfo_appendices = [] #texinfo_appendices = []
# If false, no module index is generated. # If false, no module index is generated.
# texinfo_domain_indices = True #texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'. # 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. # Example configuration for intersphinx: refer to the Python standard library.

View file

@ -15,7 +15,7 @@ or if you use windows, then read on.
Glymur uses ctypes to access the openjp2/openjpeg libraries, and Glymur uses ctypes to access the openjp2/openjpeg libraries, and
because ctypes accesses libraries in a platform-dependent manner, 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 non-standard location, you should then create a configuration file
to help Glymur properly find the openjpeg or openjp2 libraries to help Glymur properly find the openjpeg or openjp2 libraries
(linux users or macports users dont need to bother with this if (linux users or macports users dont need to bother with this if
@ -50,9 +50,6 @@ installed in a non-standard place, i.e. ::
[library] [library]
openjpeg: /somewhere/lib/libopenjpeg.so 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.
''''''' '''''''
Testing Testing
''''''' '''''''

View file

@ -14,22 +14,27 @@ retrieve a full resolution and first lower-resolution image ::
>>> jp2file = glymur.data.nemo() # just a path to a JPEG2000 file >>> jp2file = glymur.data.nemo() # just a path to a JPEG2000 file
>>> jp2 = glymur.Jp2k(jp2file) >>> jp2 = glymur.Jp2k(jp2file)
>>> fullres = jp2[:] >>> fullres = jp2[:]
>>> fullres.shape >>> print(fullres.shape)
(1456, 2592, 3) (1456, 2592, 3)
>>> thumbnail = jp2[::2, ::2] >>> thumbnail = jp2[::2, ::2]
>>> thumbnail.shape >>> print(thumbnail.shape)
(728, 1296, 3) (728, 1296, 3)
The :py:meth:`read` method exposes many more options for other JPEG 2000
features such as quality layers.
... write images? ... write images?
================= =================
It's pretty simple, just supply the image data as the 2nd argument to the Jp2k So long as the image data can fit entirely into memory, array-style slicing may
constructor. also be used to write JPEG 2000 files.
>>> import glymur, numpy as np >>> 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 The :py:meth:`write` method exposes many more options for other JPEG 2000
images with glymur. features. You should have OpenJPEG version 1.5 or more recent before writing
JPEG 2000 images.
... display metadata? ... display metadata?
===================== =====================
@ -214,9 +219,9 @@ making use of the :py:meth:`set_printoptions` function::
UUID: be7acfcb-97a9-42e8-9c71-999491e3afac (XMP) UUID: be7acfcb-97a9-42e8-9c71-999491e3afac (XMP)
Contiguous Codestream Box (jp2c) @ (3223, 1132296) 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? ... add XML metadata?
===================== =====================
@ -349,14 +354,15 @@ image isn't square. ::
>>> import numpy as np >>> import numpy as np
>>> import glymur >>> import glymur
>>> from glymur import Jp2k >>> from glymur import Jp2k
>>> rgb = Jp2k(glymur.data.goodstuff())[:] >>> rgb = Jp2k(glymur.data.goodstuff()).read()
>>> lx, ly = rgb.shape[0:2] >>> lx, ly = rgb.shape[0:2]
>>> X, Y = np.ogrid[0:lx, 0:ly] >>> 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 >>> 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 = 255 * np.ones((lx, ly, 1), dtype=np.uint8)
>>> alpha[mask] = 0 >>> alpha[mask] = 0
>>> rgba = np.concatenate((rgb, alpha), axis=2) >>> 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. Next we need to specify what types of channels we have.
The first three channels are color channels, but we identify the fourth as The first three channels are color channels, but we identify the fourth as
@ -442,7 +448,7 @@ following
'Google' 'Google'
But that would be painful. A better solution is to install the Python XMP 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 import XMPMeta
>>> from libxmp.consts import XMP_NS_XMP as NS_XAP >>> 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 >>> import skimage.io
>>> image = skimage.io.imread('PIA17145.tif') >>> image = skimage.io.imread('PIA17145.tif')
>>> from glymur import Jp2k >>> from glymur import Jp2k
>>> jp2 = Jp2k('PIA17145.jp2', data=image) >>> jp2 = Jp2k('PIA17145.jp2', 'wb')
>>> jp2[:] = image
Next you can extract the XMP metadata. Next you can extract the XMP metadata.

View file

@ -17,6 +17,7 @@ Contents:
how_do_i how_do_i
whatsnew/index whatsnew/index
roadmap roadmap
api
------------------ ------------------
Indices and tables Indices and tables

View file

@ -1,3 +1,12 @@
-------------------------------------
Platforms Tested (0.7.0 release only)
-------------------------------------
* Linux Mint 17 / Python 3.4.0 and 2.7.6 / OpenJPEG 2.1.0 and 1.3.0
* MacOS 10.6.8 / MacPorts Python 3.4.1, 3.3.5,and 2.7.8 / OpenJPEG 2.1.0
* CentOS 6.5 / Anaconda Python 3.4.1 / OpenJPEG 1.3.0
* Fedora 20 i386 / Python 2.7.5 and 3.3.2 / OpenJPEG 1.5.1
* Windows 7 32bit / Anaconda Python 2.7.6 and 3.4.1 / OpenJPEG 2.1.0
------------ ------------
Known Issues Known Issues
------------ ------------

View file

@ -2,26 +2,10 @@
Changes in glymur 0.7 Changes in glymur 0.7
===================== =====================
Changes in 0.7.3
=================
* 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 Changes in 0.7.0
================= =================
* implemented :py:meth:`__getitem__`, :py:meth:`__setitem__` support * implemented :py:meth:`__getitem__`, :py:meth:`__setitem__` support
* added back windows support * added back windows support
* box_id and longname are class attributes now instead of instance * box_id and longname are class attributes now instead of instance
attributes attributes (see issue 248)

View file

@ -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

View file

@ -8,7 +8,6 @@ These document the changes between minor (or major) versions of glymur.
.. toctree:: .. toctree::
0.8
0.7
0.6
0.5 0.5
0.6
0.7

View file

@ -1,25 +1,21 @@
"""glymur - read, write, and interrogate JPEG 2000 files """glymur - read, write, and interrogate JPEG 2000 files
""" """
import sys
import unittest import unittest
from glymur import version from glymur import version
__version__ = version.version __version__ = version.version
from .jp2k import Jp2k from .jp2k import Jp2k
from .jp2box import (get_printoptions, from .jp2box import (
set_printoptions, get_printoptions, set_printoptions,
get_parseoptions, get_parseoptions, set_parseoptions
set_parseoptions) )
from . import data from . import data
def runtests(): def runtests():
"""Discover and run all tests for the glymur package. """Discover and run all tests for the glymur package.
""" """
suite = unittest.defaultTestLoader.discover(__path__[0]) suite = unittest.defaultTestLoader.discover(__path__[0])
unittest.TextTestRunner(verbosity=2).run(suite) unittest.TextTestRunner(verbosity=2).run(suite)
__all__ = [__version__, Jp2k, get_printoptions, set_printoptions,
get_parseoptions, set_parseoptions, data, runtests]

View file

@ -3,13 +3,14 @@
Part of glymur. Part of glymur.
""" """
from collections import OrderedDict from collections import OrderedDict
import pprint
import re
import struct import struct
import sys import sys
import warnings import warnings
import lxml.etree as ET import lxml.etree as ET
def xml(raw_data): def xml(raw_data):
""" """
XMP data to be parsed as XML. XMP data to be parsed as XML.
@ -22,7 +23,6 @@ def xml(raw_data):
return ET.ElementTree(elt) return ET.ElementTree(elt)
def tiff_header(read_buffer): def tiff_header(read_buffer):
""" """
Interpret the uuid raw data as a tiff header. Interpret the uuid raw data as a tiff header.
@ -37,8 +37,8 @@ def tiff_header(read_buffer):
# big endian # big endian
endian = '>' endian = '>'
else: else:
msg = "The byte order indication in the TIFF header ({0}) is " msg = "The byte order indication in the TIFF header ({0}) is invalid. "
msg += "invalid. It should be either {1} or {2}." msg += "It should be either {1} or {2}."
msg = msg.format(read_buffer[6:8], bytes([73, 73]), bytes([77, 77])) msg = msg.format(read_buffer[6:8], bytes([73, 73]), bytes([77, 77]))
raise IOError(msg) raise IOError(msg)
@ -503,3 +503,6 @@ class _ExifInteroperabilityIfd(_Ifd):
def __init__(self, endian, read_buffer, offset): def __init__(self, endian, read_buffer, offset):
_Ifd.__init__(self, endian, read_buffer, offset) _Ifd.__init__(self, endian, read_buffer, offset)
self.post_process(self.tagnum2name) self.post_process(self.tagnum2name)

View file

@ -6,13 +6,16 @@ codestreams.
# The number of lines in the module is long and that's ok. It would not help # 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. # matters to move anything out to another file.
# pylint: disable=C0302
# "Too many instance attributes", "Too many arguments" # "Too many instance attributes", "Too many arguments"
# Some segments just have a lot of information. # Some segments just have a lot of information.
# It doesn't make sense to subclass just for that. # 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 # "Too few public methods" Some segments don't define any new methods from
# the base Segment class. # the base Segment class.
# pylint: disable=R0903
import math import math
import struct import struct
@ -21,22 +24,24 @@ import warnings
import numpy as np import numpy as np
from .core import (LRCP, RLCP, RPCL, PCRL, CPRL, from .core import (
WAVELET_XFORM_9X7_IRREVERSIBLE, LRCP, RLCP, RPCL, PCRL, CPRL,
WAVELET_XFORM_5X3_REVERSIBLE, WAVELET_XFORM_9X7_IRREVERSIBLE, WAVELET_XFORM_5X3_REVERSIBLE,
_Keydefaultdict) _Keydefaultdict
)
from .lib import openjp2 as opj2 from .lib import openjp2 as opj2
_factory = lambda x: '{0} (invalid)'.format(x) _factory = lambda x: '{0} (invalid)'.format(x)
_PROGRESSION_ORDER_DISPLAY = _Keydefaultdict(_factory, {LRCP: 'LRCP', _PROGRESSION_ORDER_DISPLAY = _Keydefaultdict(_factory,
RLCP: 'RLCP', { LRCP: 'LRCP',
RPCL: 'RPCL', RLCP: 'RLCP',
PCRL: 'PCRL', RPCL: 'RPCL',
CPRL: 'CPRL'}) PCRL: 'PCRL',
CPRL: 'CPRL'})
_keysvalues = {WAVELET_XFORM_9X7_IRREVERSIBLE: '9-7 irreversible', _WAVELET_TRANSFORM_DISPLAY = _Keydefaultdict(_factory,
WAVELET_XFORM_5X3_REVERSIBLE: '5-3 reversible'} { WAVELET_XFORM_9X7_IRREVERSIBLE: '9-7 irreversible',
_WAVELET_TRANSFORM_DISPLAY = _Keydefaultdict(_factory, _keysvalues) WAVELET_XFORM_5X3_REVERSIBLE: '5-3 reversible'})
_NO_PROFILE = 0 _NO_PROFILE = 0
_PROFILE_0 = 1 _PROFILE_0 = 1
@ -47,11 +52,12 @@ _PROFILE_4 = 4
_KNOWN_PROFILES = [_NO_PROFILE, _PROFILE_0, _PROFILE_1, _PROFILE_3, _PROFILE_4] _KNOWN_PROFILES = [_NO_PROFILE, _PROFILE_0, _PROFILE_1, _PROFILE_3, _PROFILE_4]
# How to display the codestream profile. # How to display the codestream profile.
_CAPABILITIES_DISPLAY = _Keydefaultdict(_factory, {_NO_PROFILE: 'no profile', _CAPABILITIES_DISPLAY = _Keydefaultdict(_factory,
_PROFILE_0: '0', { _NO_PROFILE: 'no profile',
_PROFILE_1: '1', _PROFILE_0: '0',
_PROFILE_3: 'Cinema 2K', _PROFILE_1: '1',
_PROFILE_4: 'Cinema 4K'}) _PROFILE_3: 'Cinema 2K',
_PROFILE_4: 'Cinema 4K'} )
# Need a catch-all list of valid markers. # Need a catch-all list of valid markers.
# See table A-1 in ISO/IEC FCD15444-1. # See table A-1 in ISO/IEC FCD15444-1.
@ -293,6 +299,7 @@ class Codestream(object):
msg += ''.join(strs) msg += ''.join(strs)
return msg return msg
# pylint: disable=R0201
def _parse_cme_segment(self, fptr): def _parse_cme_segment(self, fptr):
"""Parse the CME marker segment. """Parse the CME marker segment.
@ -688,7 +695,7 @@ class Codestream(object):
try: try:
num_tiles_x = (xysiz[0] - xyosiz[0]) / (xytsiz[0] - xytosiz[0]) num_tiles_x = (xysiz[0] - xyosiz[0]) / (xytsiz[0] - xytosiz[0])
num_tiles_y = (xysiz[1] - xyosiz[1]) / (xytsiz[1] - xytosiz[1]) num_tiles_y = (xysiz[1] - xyosiz[1]) / (xytsiz[1] - xytosiz[1])
except ZeroDivisionError: except ZeroDivisionError as err:
warnings.warn("Invalid tile dimensions.") warnings.warn("Invalid tile dimensions.")
else: else:
numtiles = math.ceil(num_tiles_x) * math.ceil(num_tiles_y) numtiles = math.ceil(num_tiles_x) * math.ceil(num_tiles_y)
@ -696,6 +703,7 @@ class Codestream(object):
msg = "Invalid number of tiles ({0}).".format(numtiles) msg = "Invalid number of tiles ({0}).".format(numtiles)
warnings.warn(msg) warnings.warn(msg)
kwargs = {'rsiz': rsiz, kwargs = {'rsiz': rsiz,
'xysiz': xysiz, 'xysiz': xysiz,
'xyosiz': xyosiz, 'xyosiz': xyosiz,
@ -822,6 +830,7 @@ class Codestream(object):
return TLMsegment(length, offset, ztlm, ttlm, ptlm) return TLMsegment(length, offset, ztlm, ttlm, ptlm)
# pylint: disable=W0613
def _parse_reserved_marker(self, fptr): def _parse_reserved_marker(self, fptr):
"""Marker range between 0xff30 and 0xff39. """Marker range between 0xff30 and 0xff39.
""" """
@ -1605,7 +1614,6 @@ class SOCsegment(Segment):
msg = "glymur.codestream.SOCsegment()" msg = "glymur.codestream.SOCsegment()"
return msg return msg
class SODsegment(Segment): class SODsegment(Segment):
"""Container for Start of Data (SOD) segment information. """Container for Start of Data (SOD) segment information.

View file

@ -2,36 +2,32 @@
Entry point for console script jp2dump. Entry point for console script jp2dump.
""" """
import argparse import argparse
import os import sys
import warnings import warnings
from . import Jp2k, set_printoptions
from . import Jp2k, set_printoptions, set_parseoptions, lib
def main(): def main():
""" """
Entry point for console script jp2dump. Entry point for console script jp2dump.
""" """
kwargs = {'description': 'Print JPEG2000 metadata.', description='Print JPEG2000 metadata.'
'formatter_class': argparse.ArgumentDefaultsHelpFormatter} parser = argparse.ArgumentParser(description=description)
parser = argparse.ArgumentParser(**kwargs)
parser.add_argument('-x', '--noxml', parser.add_argument('-x', '--noxml',
help='suppress XML', help='Suppress XML.',
action='store_true') action='store_true')
parser.add_argument('-s', '--short', parser.add_argument('-s', '--short',
help='only print box id, offset, and length', help='Only print box id, offset, and length.',
action='store_true') action='store_true')
chelp = 'Level of codestream information. 0 suppresses all details, ' chelp = 'Level of codestream information. 0 suppressed all details, '
chelp += '1 prints the main header, 2 prints the full codestream.' chelp += '1 prints headers, 2 prints the full codestream'
parser.add_argument('-c', '--codestream', parser.add_argument('-c', '--codestream',
help=chelp, help=chelp,
metavar='LEVEL', nargs=1,
nargs=1, type=int,
type=int, default=[0])
default=[1])
parser.add_argument('filename') parser.add_argument('filename')
@ -40,33 +36,30 @@ def main():
set_printoptions(xml=False) set_printoptions(xml=False)
if args.short: if args.short:
set_printoptions(short=True) set_printoptions(short=True)
codestream_level = args.codestream[0] codestream_level = args.codestream[0]
if codestream_level not in [0, 1, 2]: if codestream_level not in [0, 1, 2]:
raise ValueError("Invalid level of codestream information specified.") raise ValueError("Invalid level of codestream information specified.")
if codestream_level == 0: if codestream_level == 0:
set_printoptions(codestream=False) set_printoptions(codestream=False)
elif codestream_level == 2: print_full_codestream = False
set_parseoptions(full_codestream=True) elif codestream_level == 1:
print_full_codestream = False
else:
print_full_codestream = True
filename = args.filename filename = args.filename
with warnings.catch_warnings(record=True) as wctx: with warnings.catch_warnings(record=True) as wctx:
# JP2 metadata can be extensive, so don't print any warnings until we # JP2 metadata can be extensive, so don't print any warnings until we
# are done with the metadata. # are done with the metadata.
jp2 = Jp2k(filename) j = Jp2k(filename)
if jp2._codec_format == lib.openjp2.CODEC_J2K: if print_full_codestream:
if codestream_level == 0: print(j.get_codestream(header_only=False))
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))
else: else:
print(jp2) print(j)
# Re-emit any warnings that may have been suppressed. # Re-emit any warnings that may have been suppressed.
if len(wctx) > 0: if len(wctx) > 0:

View file

@ -1,7 +1,8 @@
"""Core definitions to be shared amongst the modules. """Core definitions to be shared amongst the modules.
""" """
import collections import collections
import copy
import lxml.etree as ET
class _Keydefaultdict(collections.defaultdict): class _Keydefaultdict(collections.defaultdict):
"""Unlisted keys help form their own error message. """Unlisted keys help form their own error message.
@ -120,12 +121,12 @@ ROMM_RGB = 21
_factory = lambda x: '{0} (unrecognized)'.format(x) _factory = lambda x: '{0} (unrecognized)'.format(x)
_COLORSPACE_MAP_DISPLAY = _Keydefaultdict(_factory, _COLORSPACE_MAP_DISPLAY = _Keydefaultdict(_factory,
{CMYK: 'CMYK', { CMYK: 'CMYK',
SRGB: 'sRGB', SRGB: 'sRGB',
GREYSCALE: 'greyscale', GREYSCALE: 'greyscale',
YCC: 'YCC', YCC: 'YCC',
E_SRGB: 'e-sRGB', E_SRGB: 'e-sRGB',
ROMM_RGB: 'ROMM-RGB'}) ROMM_RGB: 'ROMM-RGB'} )
# enumerated color channel types # enumerated color channel types
COLOR = 0 COLOR = 0
@ -133,11 +134,11 @@ OPACITY = 1
PRE_MULTIPLIED_OPACITY = 2 PRE_MULTIPLIED_OPACITY = 2
_UNSPECIFIED = 65535 _UNSPECIFIED = 65535
_factory = lambda x: '{0} (invalid)'.format(x) _factory = lambda x: '{0} (invalid)'.format(x)
_dict = {COLOR: 'color', _COLOR_TYPE_MAP_DISPLAY = _Keydefaultdict(_factory,
OPACITY: 'opacity', { COLOR: 'color',
PRE_MULTIPLIED_OPACITY: 'pre-multiplied opacity', OPACITY: 'opacity',
_UNSPECIFIED: 'unspecified'} PRE_MULTIPLIED_OPACITY: 'pre-multiplied opacity',
_COLOR_TYPE_MAP_DISPLAY = _Keydefaultdict(_factory, _dict) _UNSPECIFIED: 'unspecified'})
# color channel definitions. # color channel definitions.
RED = 1 RED = 1
@ -152,3 +153,4 @@ _COLORSPACE = {SRGB: {"R": 1, "G": 2, "B": 3},
YCC: {"Y": 1, "Cb": 2, "Cr": 3}, YCC: {"Y": 1, "Cb": 2, "Cr": 3},
E_SRGB: {"R": 1, "G": 2, "B": 3}, E_SRGB: {"R": 1, "G": 2, "B": 3},
ROMM_RGB: {"R": 1, "G": 2, "B": 3}} ROMM_RGB: {"R": 1, "G": 2, "B": 3}}

View file

@ -43,3 +43,4 @@ def jpxfile():
""" """
filename = pkg_resources.resource_filename(__name__, "heliov.jpx") filename = pkg_resources.resource_filename(__name__, "heliov.jpx")
return filename return filename

View file

@ -11,6 +11,8 @@ References
Extensions Extensions
""" """
# pylint: disable=C0302,R0903,R0913,W0142
from collections import OrderedDict from collections import OrderedDict
import datetime import datetime
import io import io
@ -20,37 +22,35 @@ import pprint
import struct import struct
import sys import sys
import textwrap import textwrap
from uuid import UUID import uuid
import warnings import warnings
import lxml.etree as ET import lxml.etree as ET
import numpy as np import numpy as np
from .codestream import Codestream from .codestream import Codestream
from .core import (_COLORSPACE_MAP_DISPLAY, _COLOR_TYPE_MAP_DISPLAY, from .core import (
SRGB, GREYSCALE, YCC, _COLORSPACE_MAP_DISPLAY, _COLOR_TYPE_MAP_DISPLAY,
ENUMERATED_COLORSPACE, RESTRICTED_ICC_PROFILE, SRGB, GREYSCALE, YCC,
ANY_ICC_PROFILE, VENDOR_COLOR_METHOD, ENUMERATED_COLORSPACE, RESTRICTED_ICC_PROFILE,
_Keydefaultdict) ANY_ICC_PROFILE, VENDOR_COLOR_METHOD,
_Keydefaultdict
)
from . import _uuid_io from . import _uuid_io
_factory = lambda x: '{0} (invalid)'.format(x) _METHOD_DISPLAY = {
_keysvalues = {ENUMERATED_COLORSPACE: 'enumerated colorspace', ENUMERATED_COLORSPACE: 'enumerated colorspace',
RESTRICTED_ICC_PROFILE: 'restricted ICC profile', RESTRICTED_ICC_PROFILE: 'restricted ICC profile',
ANY_ICC_PROFILE: 'any ICC profile', ANY_ICC_PROFILE: 'any ICC profile',
VENDOR_COLOR_METHOD: 'vendor color method'} VENDOR_COLOR_METHOD: 'vendor color method'}
_METHOD_DISPLAY = _Keydefaultdict(_factory, _keysvalues)
_factory = lambda x: '{0} (invalid)'.format(x) _factory = lambda x: '{0} (invalid)'.format(x)
_keysvalues = {1: 'accurately represents correct colorspace definition', _APPROX_DISPLAY = _Keydefaultdict(_factory,
2: ('approximates correct colorspace definition, ' {1: 'accurately represents correct colorspace definition',
'exceptional quality'), 2: 'approximates correct colorspace definition, exceptional quality',
3: ('approximates correct colorspace definition, ' 3: 'approximates correct colorspace definition, reasonable quality',
'reasonable quality'), 4: 'approximates correct colorspace definition, poor quality'})
4: 'approximates correct colorspace definition, poor quality'}
_APPROX_DISPLAY = _Keydefaultdict(_factory, _keysvalues)
class Jp2kBox(object): class Jp2kBox(object):
"""Superclass for JPEG 2000 boxes. """Superclass for JPEG 2000 boxes.
@ -109,6 +109,7 @@ class Jp2kBox(object):
msg += '\n' + self._indent(boxstr) msg += '\n' + self._indent(boxstr)
return msg return msg
def _indent(self, textstr, indent_level=4): def _indent(self, textstr, indent_level=4):
""" """
Indent a string. Indent a string.
@ -134,6 +135,7 @@ class Jp2kBox(object):
lst = [(' ' * indent_level + x) for x in textstr.split('\n')] lst = [(' ' * indent_level + x) for x in textstr.split('\n')]
return '\n'.join(lst) return '\n'.join(lst)
def _write_superbox(self, fptr, box_id): def _write_superbox(self, fptr, box_id):
"""Write a superbox. """Write a superbox.
@ -189,14 +191,13 @@ class Jp2kBox(object):
try: try:
box = parser(fptr, start, num_bytes) box = parser(fptr, start, num_bytes)
except ValueError as err: except ValueError as err:
msg = "Encountered an unrecoverable ValueError while parsing a " msg = "Encountered an unrecoverable ValueError while parsing a {0} "
msg += "{0} box at byte offset {1}. The original error message " msg += "box at byte offset {1}. The original error message was "
msg += "was \"{2}\"" msg += "\"{2}\""
msg = msg.format(_BOX_WITH_ID[box_id].longname, start, str(err)) msg = msg.format(_BOX_WITH_ID[box_id].longname, start, str(err))
warnings.warn(msg, UserWarning) warnings.warn(msg, UserWarning)
box = UnknownBox(box_id.decode('utf-8'), box = UnknownBox(box_id.decode('utf-8'),
length=num_bytes, length=num_bytes, offset=start, longname='Unknown')
offset=start, longname='Unknown')
return box return box
@ -298,7 +299,6 @@ class ColourSpecificationBox(Jp2kBox):
""" """
longname = 'Colour Specification' longname = 'Colour Specification'
box_id = 'colr' box_id = 'colr'
def __init__(self, method=ENUMERATED_COLORSPACE, precedence=0, def __init__(self, method=ENUMERATED_COLORSPACE, precedence=0,
approximation=0, colorspace=None, icc_profile=None, approximation=0, colorspace=None, icc_profile=None,
length=0, offset=-1): length=0, offset=-1):
@ -337,16 +337,16 @@ class ColourSpecificationBox(Jp2kBox):
if self.icc_profile is None: if self.icc_profile is None:
if self.colorspace not in [SRGB, GREYSCALE, YCC]: if self.colorspace not in [SRGB, GREYSCALE, YCC]:
msg = "Colorspace should correspond to one of SRGB, " msg = "Colorspace should correspond to one of SRGB, GREYSCALE, "
msg += "GREYSCALE, or YCC." msg += "or YCC."
self._dispatch_validation_error(msg, writing=True) self._dispatch_validation_error(msg, writing=True)
self._validate(writing=True) self._validate(writing=True)
def __repr__(self): def __repr__(self):
msg = "glymur.jp2box.ColourSpecificationBox(" msg = "glymur.jp2box.ColourSpecificationBox("
msg += "method={0}, precedence={1}, approximation={2}, " msg += "method={0}, precedence={1}, approximation={2}, colorspace={3}, "
msg += "colorspace={3}, "
msg += "icc_profile={4})" msg += "icc_profile={4})"
msg = msg.format(self.method, msg = msg.format(self.method,
self.precedence, self.precedence,
@ -357,7 +357,7 @@ class ColourSpecificationBox(Jp2kBox):
def __str__(self): def __str__(self):
msg = Jp2kBox.__str__(self) msg = Jp2kBox.__str__(self)
if _printoptions['short'] is True: if _printoptions['short'] == True:
return msg return msg
msg += '\n Method: {0}'.format(_METHOD_DISPLAY[self.method]) msg += '\n Method: {0}'.format(_METHOD_DISPLAY[self.method])
@ -619,9 +619,10 @@ class ChannelDefinitionBox(Jp2kBox):
msg += " 65535 - unspecified" msg += " 65535 - unspecified"
self._dispatch_validation_error(msg, writing=writing) self._dispatch_validation_error(msg, writing=writing)
def __str__(self): def __str__(self):
msg = Jp2kBox.__str__(self) msg = Jp2kBox.__str__(self)
if _printoptions['short'] is True: if _printoptions['short'] == True:
return msg return msg
for j in range(len(self.association)): for j in range(len(self.association)):
@ -841,7 +842,7 @@ class CompositingLayerHeaderBox(Jp2kBox):
List of boxes contained in this superbox. List of boxes contained in this superbox.
""" """
box_id = 'jplh' box_id = 'jplh'
longname = 'Compositing Layer Header' longname='Compositing Layer Header'
def __init__(self, box=None, length=0, offset=-1): def __init__(self, box=None, length=0, offset=-1):
Jp2kBox.__init__(self) Jp2kBox.__init__(self)
@ -930,7 +931,7 @@ class ComponentMappingBox(Jp2kBox):
def __str__(self): def __str__(self):
msg = Jp2kBox.__str__(self) msg = Jp2kBox.__str__(self)
if _printoptions['short'] is True: if _printoptions['short'] == True:
return msg return msg
for k in range(len(self.component_index)): for k in range(len(self.component_index)):
@ -1001,19 +1002,18 @@ class ContiguousCodestreamBox(Jp2kBox):
offset of the box from the start of the file. offset of the box from the start of the file.
longname : str longname : str
more verbose description of the box. more verbose description of the box.
codestream : Codestream object main_header : Codestream object
Contains list of codestream marker/segments. By default, only the main contains list of main header marker/segments
header is retrieved.
main_header_offset : int main_header_offset : int
offset of main header from start of file offset of main header from start of file
""" """
box_id = 'jp2c' box_id = 'jp2c'
longname = 'Contiguous Codestream' 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): offset=-1):
Jp2kBox.__init__(self) Jp2kBox.__init__(self)
self._codestream = codestream self._main_header = main_header
self.length = length self.length = length
self.offset = offset self.offset = offset
self.main_header_offset = main_header_offset self.main_header_offset = main_header_offset
@ -1022,33 +1022,29 @@ class ContiguousCodestreamBox(Jp2kBox):
self._filename = None self._filename = None
@property @property
def codestream(self): def main_header(self):
if _parseoptions['full_codestream'] is True: if self._main_header is None:
header_only = False
else:
header_only = True
if self._codestream is None:
if self._filename is not None: if self._filename is not None:
with open(self._filename, 'rb') as fptr: with open(self._filename, 'rb') as fptr:
fptr.seek(self.main_header_offset) fptr.seek(self.main_header_offset)
codestream = Codestream(fptr, self._length, main_header = Codestream(fptr, self._length, header_only=True)
header_only=header_only) self._main_header = main_header
self._codestream = codestream return self._main_header
return self._codestream
def __repr__(self): def __repr__(self):
msg = "glymur.jp2box.ContiguousCodeStreamBox(codestream={0})" msg = "glymur.jp2box.ContiguousCodeStreamBox(main_header={0})"
return msg.format(repr(self.codestream)) return msg.format(repr(self.main_header))
def __str__(self): def __str__(self):
msg = Jp2kBox.__str__(self) msg = Jp2kBox.__str__(self)
if _printoptions['short'] is True: if _printoptions['short'] == True:
return msg return msg
if _printoptions['codestream'] is False: if _printoptions['codestream'] == False:
return msg return msg
for segment in self.codestream.segment: msg += '\n Main header:'
msg += '\n' + self._indent(str(segment), indent_level=4) for segment in self.main_header.segment:
msg += '\n' + self._indent(str(segment), indent_level=8)
return msg return msg
@ -1070,11 +1066,11 @@ class ContiguousCodestreamBox(Jp2kBox):
ContiguousCodestreamBox instance ContiguousCodestreamBox instance
""" """
main_header_offset = fptr.tell() main_header_offset = fptr.tell()
if _parseoptions['full_codestream'] is True: if _parseoptions['codestream'] is True:
codestream = Codestream(fptr, length, header_only=False) main_header = Codestream(fptr, length, header_only=True)
else: else:
codestream = None main_header = None
box = cls(codestream, main_header_offset=main_header_offset, box = cls(main_header, main_header_offset=main_header_offset,
length=length, offset=offset) length=length, offset=offset)
box._filename = fptr.name box._filename = fptr.name
box._length = length box._length = length
@ -1122,8 +1118,7 @@ class DataReferenceBox(Jp2kBox):
"""Verify that the box obeys the specifications for writing. """Verify that the box obeys the specifications for writing.
""" """
if len(self.DR) == 0: if len(self.DR) == 0:
msg = "A data reference box cannot be empty when written to a " msg = "A data reference box cannot be empty when written to a file."
msg += "file."
self._dispatch_validation_error(msg, writing=True) self._dispatch_validation_error(msg, writing=True)
self._validate(writing=True) self._validate(writing=True)
@ -1150,7 +1145,7 @@ class DataReferenceBox(Jp2kBox):
def __str__(self): def __str__(self):
msg = Jp2kBox.__str__(self) msg = Jp2kBox.__str__(self)
if _printoptions['short'] is True: if _printoptions['short'] == True:
return msg return msg
for box in self.DR: for box in self.DR:
@ -1253,7 +1248,7 @@ class FileTypeBox(Jp2kBox):
def __str__(self): def __str__(self):
msg = Jp2kBox.__str__(self) msg = Jp2kBox.__str__(self)
if _printoptions['short'] is True: if _printoptions['short'] == True:
return msg return msg
lst = [msg, lst = [msg,
@ -1316,18 +1311,12 @@ class FileTypeBox(Jp2kBox):
brand = brand.decode('utf-8') brand = brand.decode('utf-8')
# Extract the compatibility list. Each entry has 4 bytes. # Extract the compatibility list. Each entry has 4 bytes.
num_entries = int((length - 16) / 4) num_entries = int((length - 16)/ 4)
compatibility_list = [] compatibility_list = []
for j in range(int(num_entries)): for j in range(int(num_entries)):
entry, = struct.unpack_from('>4s', read_buffer, 8 + j * 4) entry, = struct.unpack_from('>4s', read_buffer, 8 + j * 4)
if sys.hexversion >= 0x03000000: if sys.hexversion >= 0x03000000:
try: entry = entry.decode('utf-8')
entry = entry.decode('utf-8')
except UnicodeDecodeError:
# The entry is invalid, but we've got code to catch this
# later on.
pass
compatibility_list.append(entry) compatibility_list.append(entry)
return cls(brand=brand, minor_version=minor_version, return cls(brand=brand, minor_version=minor_version,
@ -1385,7 +1374,7 @@ class FragmentListBox(Jp2kBox):
def __str__(self): def __str__(self):
msg = Jp2kBox.__str__(self) msg = Jp2kBox.__str__(self)
if _printoptions['short'] is True: if _printoptions['short'] == True:
return msg return msg
for j in range(len(self.fragment_offset)): for j in range(len(self.fragment_offset)):
@ -1469,10 +1458,7 @@ class FragmentTableBox(Jp2kBox):
def __repr__(self): def __repr__(self):
msg = "glymur.jp2box.FragmentTableBox(box={0})" msg = "glymur.jp2box.FragmentTableBox(box={0})"
if len(self.box) == 0: msg = msg.format(None) if (len(self.box) == 0) else msg.format(self.box)
msg = msg.format(None)
else:
msg = msg.format(self.box)
return msg return msg
def __str__(self): def __str__(self):
@ -1519,6 +1505,7 @@ class FragmentTableBox(Jp2kBox):
self._write_superbox(fptr, b'ftbl') self._write_superbox(fptr, b'ftbl')
class FreeBox(Jp2kBox): class FreeBox(Jp2kBox):
"""Container for JPX free box information. """Container for JPX free box information.
@ -1547,7 +1534,7 @@ class FreeBox(Jp2kBox):
def __str__(self): def __str__(self):
msg = Jp2kBox.__str__(self) msg = Jp2kBox.__str__(self)
if _printoptions['short'] is True: if _printoptions['short'] == True:
return msg return msg
return msg return msg
@ -1643,7 +1630,7 @@ class ImageHeaderBox(Jp2kBox):
def __str__(self): def __str__(self):
msg = Jp2kBox.__str__(self) msg = Jp2kBox.__str__(self)
if _printoptions['short'] is True: if _printoptions['short'] == True:
return msg return msg
msg = "{0}" msg = "{0}"
@ -1874,7 +1861,7 @@ class JPEG2000SignatureBox(Jp2kBox):
def __str__(self): def __str__(self):
msg = Jp2kBox.__str__(self) msg = Jp2kBox.__str__(self)
if _printoptions['short'] is True: if _printoptions['short'] == True:
return msg return msg
msg += '\n Signature: {0:02x}{1:02x}{2:02x}{3:02x}' msg += '\n Signature: {0:02x}{1:02x}{2:02x}{3:02x}'
@ -1963,7 +1950,7 @@ class PaletteBox(Jp2kBox):
def __str__(self): def __str__(self):
msg = Jp2kBox.__str__(self) msg = Jp2kBox.__str__(self)
if _printoptions['short'] is True: if _printoptions['short'] == True:
return msg return msg
msg += '\n Size: ({0} x {1})'.format(*self.palette.shape) msg += '\n Size: ({0} x {1})'.format(*self.palette.shape)
@ -1993,6 +1980,7 @@ class PaletteBox(Jp2kBox):
*bps_signed) *bps_signed)
fptr.write(write_buffer) fptr.write(write_buffer)
bps = self.bits_per_component
# All components are the same. Writing is straightforward. # All components are the same. Writing is straightforward.
if self.bits_per_component[0] <= 8: if self.bits_per_component[0] <= 8:
write_buffer = memoryview(self.palette.astype(np.uint8)) write_buffer = memoryview(self.palette.astype(np.uint8))
@ -2032,10 +2020,13 @@ class PaletteBox(Jp2kBox):
# Ok the palette has the same datatype for all columns. We should # Ok the palette has the same datatype for all columns. We should
# be able to efficiently read it. # be able to efficiently read it.
if bps[0] <= 8: if bps[0] <= 8:
nbytes_per_row = ncols
dtype = np.uint8 dtype = np.uint8
elif bps[0] <= 16: elif bps[0] <= 16:
nbytes_per_row = 2 * ncols
dtype = np.uint16 dtype = np.uint16
elif bps[0] <= 32: elif bps[0] <= 32:
nbytes_per_row = 3 * ncols
dtype = np.uint32 dtype = np.uint32
palette = np.frombuffer(read_buffer[3 + ncols:], dtype=dtype) palette = np.frombuffer(read_buffer[3 + ncols:], dtype=dtype)
@ -2079,80 +2070,80 @@ _READER_REQUIREMENTS_DISPLAY = {
7: 'JPEG codestream as defined in ISO/IEC 10918-1', 7: 'JPEG codestream as defined in ISO/IEC 10918-1',
8: 'Deprecated - does not contain opacity', 8: 'Deprecated - does not contain opacity',
9: 'Non-premultiplied opacity channel', 9: 'Non-premultiplied opacity channel',
10: 'Premultiplied opacity channel', 10: 'Premultiplied opacity channel',
11: 'Chroma-key based opacity', 11: 'Chroma-key based opacity',
12: 'Deprecated - codestream is contiguous', 12: 'Deprecated - codestream is contiguous',
13: 'Fragmented codestream where all fragments are in file and in order', 13: 'Fragmented codestream where all fragments are in file and in order',
14: ('Fragmented codestream where all fragments are in file ' 14: 'Fragmented codestream where all fragments are in file '
'but are out of order'), + 'but are out of order',
15: ('Fragmented codestream where not all fragments are within the file ' 15: 'Fragmented codestream where not all fragments are within the file '
'but are all in locally accessible files'), + 'but are all in locally accessible files',
16: ('Fragmented codestream where some fragments may be accessible ' 16: 'Fragmented codestream where some fragments may be accessible '
'only through a URL specified network connection'), + 'only through a URL specified network connection',
17: ('Compositing required to produce rendered result from multiple ' 17: 'Compositing required to produce rendered result from multiple '
'compositing layers'), + 'compositing layers',
18: 'Deprecated - support for compositing is not required', 18: 'Deprecated - support for compositing is not required',
19: ('Deprecated - contains multiple, discrete layers that should not ' 19: 'Deprecated - contains multiple, discrete layers that should not '
'be combined through either animation or compositing'), + 'be combined through either animation or compositing',
20: ('Deprecated - compositing layers each contain only a single ' 20: 'Deprecated - compositing layers each contain only a single '
'codestream'), + 'codestream',
21: 'At least one compositing layer consists of multiple codestreams', 21: 'At least one compositing layer consists of multiple codestreams',
22: 'Deprecated - all compositing layers are in the same colourspace', 22: 'Deprecated - all compositing layers are in the same colourspace',
23: ('Colourspace transformations are required to combine compositing ' 23: 'Colourspace transformations are required to combine compositing '
'layers; not all compositing layers are in the same colourspace'), + 'layers; not all compositing layers are in the same colourspace',
24: 'Deprecated - rendered result created without using animation', 24: 'Deprecated - rendered result created without using animation',
25: ('Deprecated - animated, but first layer covers entire area and is ' 25: 'Deprecated - animated, but first layer covers entire area and is '
'opaque'), + 'opaque',
26: 'First animation layer does not cover entire rendered result', 26: 'First animation layer does not cover entire rendered result',
27: 'Deprecated - animated, and no layer is reused', 27: 'Deprecated - animated, and no layer is reused',
28: 'Reuse of animation layers', 28: 'Reuse of animation layers',
29: 'Deprecated - animated, but layers are reused', 29: 'Deprecated - animated, but layers are reused',
30: 'Some animated frames are non-persistent', 30: 'Some animated frames are non-persistent',
31: 'Deprecated - rendered result created without using scaling', 31: 'Deprecated - rendered result created without using scaling',
32: 'Rendered result involves scaling within a layer', 32: 'Rendered result involves scaling within a layer',
33: 'Rendered result involves scaling between layers', 33: 'Rendered result involves scaling between layers',
34: 'ROI metadata', 34: 'ROI metadata',
35: 'IPR metadata', 35: 'IPR metadata',
36: 'Content metadata', 36: 'Content metadata',
37: 'History metadata', 37: 'History metadata',
38: 'Creation metadata', 38: 'Creation metadata',
39: 'JPX digital signatures', 39: 'JPX digital signatures',
40: 'JPX checksums', 40: 'JPX checksums',
41: 'Desires Graphics Arts Reproduction specified', 41: 'Desires Graphics Arts Reproduction specified',
42: 'Deprecated - compositing layer uses palettized colour', 42: 'Deprecated - compositing layer uses palettized colour',
43: 'Deprecated - compositing layer uses restricted ICC profile', 43: 'Deprecated - compositing layer uses restricted ICC profile',
44: 'Compositing layer uses Any ICC profile', 44: 'Compositing layer uses Any ICC profile',
45: 'Deprecated - compositing layer uses sRGB enumerated colourspace', 45: 'Deprecated - compositing layer uses sRGB enumerated colourspace',
46: 'Deprecated - compositing layer uses sRGB-grey enumerated colourspace', 46: 'Deprecated - compositing layer uses sRGB-grey enumerated colourspace',
47: 'BiLevel 1 enumerated colourspace', 47: 'BiLevel 1 enumerated colourspace',
48: 'BiLevel 2 enumerated colourspace', 48: 'BiLevel 2 enumerated colourspace',
49: 'YCbCr 1 enumerated colourspace', 49: 'YCbCr 1 enumerated colourspace',
50: 'YCbCr 2 enumerated colourspace', 50: 'YCbCr 2 enumerated colourspace',
51: 'YCbCr 3 enumerated colourspace', 51: 'YCbCr 3 enumerated colourspace',
52: 'PhotoYCC enumerated colourspace', 52: 'PhotoYCC enumerated colourspace',
53: 'YCCK enumerated colourspace', 53: 'YCCK enumerated colourspace',
54: 'CMY enumerated colourspace', 54: 'CMY enumerated colourspace',
55: 'CMYK enumerated colorspace', 55: 'CMYK enumerated colorspace',
56: 'CIELab enumerated colourspace with default parameters', 56: 'CIELab enumerated colourspace with default parameters',
57: 'CIELab enumerated colourspace with non-default parameters', 57: 'CIELab enumerated colourspace with non-default parameters',
58: 'CIEJab enumerated colourspace with default parameters', 58: 'CIEJab enumerated colourspace with default parameters',
59: 'CIEJab enumerated colourspace with non-default parameters', 59: 'CIEJab enumerated colourspace with non-default parameters',
60: 'e-sRGB enumerated colorspace', 60: 'e-sRGB enumerated colorspace',
61: 'ROMM_RGB enumerated colorspace', 61: 'ROMM_RGB enumerated colorspace',
62: 'Non-square samples', 62: 'Non-square samples',
63: 'Deprecated - compositing layers have labels', 63: 'Deprecated - compositing layers have labels',
64: 'Deprecated - codestreams have labels', 64: 'Deprecated - codestreams have labels',
65: 'Deprecated - compositing layers have different colour spaces', 65: 'Deprecated - compositing layers have different colour spaces',
66: 'Deprecated - compositing layers have different metadata', 66: 'Deprecated - compositing layers have different metadata',
67: 'GIS metadata XML box', 67: 'GIS metadata XML box',
68: 'JPSEC extensions in codestream as specified by ISO/IEC 15444-8', 68: 'JPSEC extensions in codestream as specified by ISO/IEC 15444-8',
69: 'JP3D extensions in codestream as specified by ISO/IEC 15444-10', 69: 'JP3D extensions in codestream as specified by ISO/IEC 15444-10',
70: 'Deprecated - compositing layer uses sYCC enumerated colour space', 70: 'Deprecated - compositing layer uses sYCC enumerated colour space',
71: 'e-sYCC enumerated colourspace', 71: 'e-sYCC enumerated colourspace',
72: ('JPEG 2000 Part 2 codestream as restricted by baseline conformance ' 72: 'JPEG 2000 Part 2 codestream as restricted by baseline conformance '
'requirements in M.9.2.3'), + 'requirements in M.9.2.3',
73: 'YPbPr(1125/60) enumerated colourspace', 73: 'YPbPr(1125/60) enumerated colourspace',
74: 'YPbPr(1250/50) enumerated colourspace'} 74: 'YPbPr(1250/50) enumerated colourspace'}
class ReaderRequirementsBox(Jp2kBox): class ReaderRequirementsBox(Jp2kBox):
@ -2212,11 +2203,10 @@ class ReaderRequirementsBox(Jp2kBox):
def __str__(self): def __str__(self):
msg = Jp2kBox.__str__(self) msg = Jp2kBox.__str__(self)
if _printoptions['short'] is True: if _printoptions['short'] == True:
return msg return msg
msg += '\n Fully Understands Aspect Mask: 0x{0:x}' msg += '\n Fully Understands Aspect Mask: 0x{0:x}'.format(self.fuam)
msg = msg.format(self.fuam)
msg += '\n Display Completely Mask: 0x{0:x}'.format(self.dcm) msg += '\n Display Completely Mask: 0x{0:x}'.format(self.dcm)
msg += '\n Standard Features and Masks:' msg += '\n Standard Features and Masks:'
@ -2272,8 +2262,7 @@ class ReaderRequirementsBox(Jp2kBox):
standard_flag, standard_mask = data standard_flag, standard_mask = data
nflags = len(standard_flag) nflags = len(standard_flag)
vendor_offset = (1 + 2 * mask_length + 2 vendor_offset = 1 + 2 * mask_length + 2 + (2 + mask_length) * nflags
+ (2 + mask_length) * nflags)
data = _parse_vendor_features(read_buffer[vendor_offset:], data = _parse_vendor_features(read_buffer[vendor_offset:],
mask_length) mask_length)
vendor_feature, vendor_mask = data vendor_feature, vendor_mask = data
@ -2330,8 +2319,8 @@ def _parse_rreq3(read_buffer, length, offset):
read_buffer = read_buffer[9 + num_standard_features * 10:] read_buffer = read_buffer[9 + num_standard_features * 10:]
for j in range(num_vendor_features): for j in range(num_vendor_features):
uslice = slice(j * entry_length, (j + 1) * entry_length) uslice = slice(j * entry_length, (j + 1) * entry_length)
ubuffer = read_buffer[uslice] ubuffer = read_buffer[slice]
vendor_feature.append(UUID(bytes=ubuffer[0:16])) vendor_feature.append(uuid.UUID(bytes=ubuffer[0:16]))
lst = struct.unpack('>BBB', ubuffer[16:]) lst = struct.unpack('>BBB', ubuffer[16:])
vmask = lst[0] << 16 | lst[1] << 8 | lst[2] vmask = lst[0] << 16 | lst[1] << 8 | lst[2]
@ -2359,11 +2348,14 @@ def _parse_standard_flag(read_buffer, mask_length):
# from the buffer read from file. # from the buffer read from file.
mask_format = {1: 'B', 2: 'H', 4: 'I'}[mask_length] 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) num_standard_flags, = struct.unpack_from('>H', read_buffer, offset=0)
# Read in standard flags and standard masks. Each standard flag should # 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 # be two bytes, but the standard mask flag is as long as specified by
# the mask length. # the mask length.
#read_buffer = fptr.read(num_standard_flags * (2 + mask_length))
fmt = '>' + ('H' + mask_format) * num_standard_flags fmt = '>' + ('H' + mask_format) * num_standard_flags
data = struct.unpack_from(fmt, read_buffer, offset=2) data = struct.unpack_from(fmt, read_buffer, offset=2)
@ -2394,12 +2386,13 @@ def _parse_vendor_features(read_buffer, mask_length):
# Each vendor feature consists of a 16-byte UUID plus a mask whose # Each vendor feature consists of a 16-byte UUID plus a mask whose
# length is specified by, you guessed it, "mask_length". # length is specified by, you guessed it, "mask_length".
entry_length = 16 + mask_length entry_length = 16 + mask_length
#read_buffer = fptr.read(num_vendor_features * entry_length)
vendor_feature = [] vendor_feature = []
vendor_mask = [] vendor_mask = []
for j in range(num_vendor_features): for j in range(num_vendor_features):
uslice = slice(2 + j * entry_length, 2 + (j + 1) * entry_length) uslice = slice(2 + j * entry_length, 2 + (j + 1) * entry_length)
ubuffer = read_buffer[uslice] 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:]) vmask = struct.unpack('>' + mask_format, ubuffer[16:])
vendor_mask.append(vmask) vendor_mask.append(vmask)
@ -2501,7 +2494,7 @@ class CaptureResolutionBox(Jp2kBox):
def __str__(self): def __str__(self):
msg = Jp2kBox.__str__(self) msg = Jp2kBox.__str__(self)
if _printoptions['short'] is True: if _printoptions['short'] == True:
return msg return msg
msg += '\n VCR: {0}'.format(self.vertical_resolution) msg += '\n VCR: {0}'.format(self.vertical_resolution)
@ -2567,7 +2560,7 @@ class DisplayResolutionBox(Jp2kBox):
def __str__(self): def __str__(self):
msg = Jp2kBox.__str__(self) msg = Jp2kBox.__str__(self)
if _printoptions['short'] is True: if _printoptions['short'] == True:
return msg return msg
msg += '\n VDR: {0}'.format(self.vertical_resolution) msg += '\n VDR: {0}'.format(self.vertical_resolution)
@ -2627,7 +2620,7 @@ class LabelBox(Jp2kBox):
def __str__(self): def __str__(self):
msg = Jp2kBox.__str__(self) msg = Jp2kBox.__str__(self)
if _printoptions['short'] is True: if _printoptions['short'] == True:
return msg return msg
msg += '\n Label: {0}'.format(self.label) msg += '\n Label: {0}'.format(self.label)
@ -2695,7 +2688,7 @@ class NumberListBox(Jp2kBox):
def __str__(self): def __str__(self):
msg = Jp2kBox.__str__(self) msg = Jp2kBox.__str__(self)
if _printoptions['short'] is True: if _printoptions['short'] == True:
return msg return msg
for j, association in enumerate(self.associations): for j, association in enumerate(self.associations):
@ -2745,8 +2738,7 @@ class NumberListBox(Jp2kBox):
def write(self, fptr): def write(self, fptr):
"""Write a NumberList box to file. """Write a NumberList box to file.
""" """
fptr.write(struct.pack('>I4s', fptr.write(struct.pack('>I4s', len(self.associations) * 4 + 8, b'nlst'))
len(self.associations) * 4 + 8, b'nlst'))
fmt = '>' + 'I' * len(self.associations) fmt = '>' + 'I' * len(self.associations)
write_buffer = struct.pack(fmt, *self.associations) write_buffer = struct.pack(fmt, *self.associations)
@ -2798,9 +2790,9 @@ class XMLBox(Jp2kBox):
def __str__(self): def __str__(self):
msg = Jp2kBox.__str__(self) msg = Jp2kBox.__str__(self)
if _printoptions['short'] is True: if _printoptions['short'] == True:
return msg return msg
if _printoptions['xml'] is False: if _printoptions['xml'] == False:
return msg return msg
msg += '\n' msg += '\n'
@ -2919,7 +2911,7 @@ class UUIDListBox(Jp2kBox):
def __str__(self): def __str__(self):
msg = Jp2kBox.__str__(self) msg = Jp2kBox.__str__(self)
if _printoptions['short'] is True: if _printoptions['short'] == True:
return msg return msg
for j, uuid_item in enumerate(self.ulst): for j, uuid_item in enumerate(self.ulst):
@ -2950,8 +2942,8 @@ class UUIDListBox(Jp2kBox):
ulst = [] ulst = []
for j in range(num_uuids): for j in range(num_uuids):
uuid_buffer = read_buffer[2 + j * 16:2 + (j + 1) * 16] uuid_buffer = read_buffer[2 + j * 16 : 2 + (j + 1) * 16]
ulst.append(UUID(bytes=uuid_buffer)) ulst.append(uuid.UUID(bytes=uuid_buffer))
return cls(ulst, length=length, offset=offset) return cls(ulst, length=length, offset=offset)
@ -3064,6 +3056,7 @@ class DataEntryURLBox(Jp2kBox):
fptr.write(write_buffer) fptr.write(write_buffer)
fptr.write(url) fptr.write(url)
def __repr__(self): def __repr__(self):
msg = "glymur.jp2box.DataEntryURLBox({0}, {1}, '{2}')" msg = "glymur.jp2box.DataEntryURLBox({0}, {1}, '{2}')"
msg = msg.format(self.version, self.flag, self.url) msg = msg.format(self.version, self.flag, self.url)
@ -3071,7 +3064,7 @@ class DataEntryURLBox(Jp2kBox):
def __str__(self): def __str__(self):
msg = Jp2kBox.__str__(self) msg = Jp2kBox.__str__(self)
if _printoptions['short'] is True: if _printoptions['short'] == True:
return msg return msg
msg += '\n ' msg += '\n '
@ -3209,7 +3202,7 @@ class UUIDBox(Jp2kBox):
""" """
Private function for parsing UUID payloads if possible. 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) self.data = _uuid_io.xml(self.raw_data)
elif self.uuid.bytes == b'JpgTiffExif->JP2': elif self.uuid.bytes == b'JpgTiffExif->JP2':
self.data = _uuid_io.tiff_header(self.raw_data) self.data = _uuid_io.tiff_header(self.raw_data)
@ -3223,23 +3216,23 @@ class UUIDBox(Jp2kBox):
def __str__(self): def __str__(self):
msg = Jp2kBox.__str__(self) msg = Jp2kBox.__str__(self)
if _printoptions['short'] is True: if _printoptions['short'] == True:
return msg return msg
msg = '{0}\n UUID: {1}'.format(msg, self.uuid) 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)' msg += ' (XMP)'
elif self.uuid.bytes == b'JpgTiffExif->JP2': elif self.uuid.bytes == b'JpgTiffExif->JP2':
msg += ' (EXIF)' msg += ' (EXIF)'
else: else:
msg += ' (unknown)' msg += ' (unknown)'
if (((_printoptions['xml'] is False) and if (((_printoptions['xml'] == False) and
(self.uuid == UUID('be7acfcb-97a9-42e8-9c71-999491e3afac')))): (self.uuid == uuid.UUID('be7acfcb-97a9-42e8-9c71-999491e3afac')))):
# If it's an XMP UUID, don't print the XML contents. # If it's an XMP UUID, don't print the XML contents.
return msg 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}' line = '\n UUID Data:\n{0}'
xmlstring = ET.tostring(self.data, xmlstring = ET.tostring(self.data,
encoding='utf-8', encoding='utf-8',
@ -3282,7 +3275,7 @@ class UUIDBox(Jp2kBox):
""" """
num_bytes = offset + length - fptr.tell() num_bytes = offset + length - fptr.tell()
read_buffer = fptr.read(num_bytes) 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) return cls(the_uuid, read_buffer[16:], length=length, offset=offset)
@ -3317,20 +3310,18 @@ _BOX_WITH_ID = {
b'uuid': UUIDBox, b'uuid': UUIDBox,
b'xml ': XMLBox} b'xml ': XMLBox}
_parseoptions = {'full_codestream': False} _parseoptions = {'codestream': True}
def set_parseoptions(codestream=True):
def set_parseoptions(full_codestream=True):
"""Set parsing options. """Set parsing options.
These options determine the way JPEG 2000 boxes are parsed. These options determine the way JPEG 2000 boxes are parsed.
Parameters Parameters
---------- ----------
full_codestream : bool, defaults to True codestream : bool, defaults to True
When False, only the codestream header is parsed for metadata. This When False, the codestream header is only parsed when accessed. This
can results in faster JP2/JPX parsing. When True, the entire can results in faster JP2/JPX parsing.
codestream is parsed for metadata.
See also See also
-------- --------
@ -3341,10 +3332,9 @@ def set_parseoptions(full_codestream=True):
To put back the default options, you can use: To put back the default options, you can use:
>>> import glymur >>> import glymur
>>> glymur.set_parseoptions(full_codestream=True) >>> glymur.set_parseoptions(codestream=True)
""" """
_parseoptions['full_codestream'] = full_codestream _parseoptions['codestream'] = codestream
def get_parseoptions(): def get_parseoptions():
"""Return the current parsing options. """Return the current parsing options.
@ -3366,7 +3356,6 @@ def get_parseoptions():
_printoptions = {'short': False, 'xml': True, 'codestream': True} _printoptions = {'short': False, 'xml': True, 'codestream': True}
def set_printoptions(**kwargs): def set_printoptions(**kwargs):
"""Set printing options. """Set printing options.
@ -3376,15 +3365,12 @@ def set_printoptions(**kwargs):
---------- ----------
short : bool, optional short : bool, optional
When True, only the box ID, offset, and length are displayed. Useful When True, only the box ID, offset, and length are displayed. Useful
for displaying only the basic structure or skeleton of a JPEG 2000 for displaying only the basic structure or skeleton of a JPEG 2000 file.
file.
xml : bool, optional xml : bool, optional
When False, printing of the XML contents of any XML boxes or UUID XMP When False, printing of the XML contents of any XML boxes or UUID XMP
boxes is suppressed. boxes is suppressed.
codestream : bool, optional codestream : bool, optional
When False, only the codestream header is printed. When True, the When False, printing of the codestream contents is suppressed.
entire codestream is printed. This option has no effect when the
'short' option is set to True.
See also See also
-------- --------
@ -3402,7 +3388,6 @@ def set_printoptions(**kwargs):
raise TypeError('"{0}" not a valid keyword parameter.'.format(key)) raise TypeError('"{0}" not a valid keyword parameter.'.format(key))
_printoptions[key] = value _printoptions[key] = value
def get_printoptions(): def get_printoptions():
"""Return the current print options. """Return the current print options.
@ -3422,3 +3407,5 @@ def get_printoptions():
set_printoptions set_printoptions
""" """
return _printoptions return _printoptions

View file

@ -10,12 +10,13 @@ License: MIT
import sys import sys
# Exitstack not found in contextlib in 2.7 # Exitstack not found in contextlib in 2.7
# pylint: disable=E0611
if sys.hexversion >= 0x03030000: if sys.hexversion >= 0x03030000:
from contextlib import ExitStack from contextlib import ExitStack
from itertools import filterfalse from itertools import compress, filterfalse
else: else:
from contextlib2 import ExitStack from contextlib2 import ExitStack
from itertools import ifilterfalse as filterfalse from itertools import compress, ifilterfalse as filterfalse
from collections import Counter from collections import Counter
import ctypes import ctypes
@ -30,11 +31,21 @@ import numpy as np
from .codestream import Codestream from .codestream import Codestream
from . import core, version from . import core, version
from .jp2box import (Jp2kBox, JPEG2000SignatureBox, FileTypeBox, from .jp2box import (
JP2HeaderBox, ColourSpecificationBox, Jp2kBox, JPEG2000SignatureBox, FileTypeBox, JP2HeaderBox,
ContiguousCodestreamBox, ImageHeaderBox) ColourSpecificationBox, ContiguousCodestreamBox, ImageHeaderBox
)
from .lib import openjpeg as opj, openjp2 as opj2, c as libc from .lib import openjpeg as opj, openjp2 as opj2, 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']
_COLORSPACE_MAP = {'rgb': opj2.CLRSPC_SRGB,
'gray': opj2.CLRSPC_GRAY,
'grey': opj2.CLRSPC_GRAY,
'ycc': opj2.CLRSPC_YCC}
class Jp2k(Jp2kBox): class Jp2k(Jp2kBox):
"""JPEG 2000 file. """JPEG 2000 file.
@ -43,200 +54,33 @@ class Jp2k(Jp2kBox):
---------- ----------
filename : str filename : str
The path to the JPEG 2000 file. The path to the JPEG 2000 file.
mode : str
The mode used to open the file.
box : sequence box : sequence
List of top-level boxes in the file. Each box may in turn contain 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 its own list of boxes. Will be empty if the file consists only of a
raw codestream. 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 Parameters
---------- ----------
filename : str or file filename : str or file
the path to JPEG 2000 file The path to JPEG 2000 file.
image_data : ndarray, optional mode : str, optional
image data to be written The mode used to open the file.
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
""" """
Jp2kBox.__init__(self) Jp2kBox.__init__(self)
self.filename = filename self.filename = filename
self.mode = mode
self.box = [] self.box = []
self._codec_format = None self._codec_format = None
self._colorspace = 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. # 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() 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): def __repr__(self):
msg = "glymur.Jp2k('{0}')".format(self.filename) msg = "glymur.Jp2k('{0}')".format(self.filename)
@ -248,7 +92,8 @@ class Jp2k(Jp2kBox):
for box in self.box: for box in self.box:
metadata.append(str(box)) metadata.append(str(box))
else: else:
metadata.append(str(self.codestream)) codestream = self.get_codestream()
metadata.append(str(codestream))
return '\n'.join(metadata) return '\n'.join(metadata)
def parse(self): def parse(self):
@ -373,8 +218,8 @@ class Jp2k(Jp2kBox):
kwargs : dictionary kwargs : dictionary
non-image keyword inputs provided to write method non-image keyword inputs provided to write method
""" """
if ((('cinema2k' in kwargs or 'cinema4k' in kwargs) and if (('cinema2k' in kwargs or 'cinema4k' in kwargs) and
(len(set(kwargs)) > 1))): (len(set(kwargs)) > 1)):
msg = "Cannot specify cinema2k/cinema4k along with other options." msg = "Cannot specify cinema2k/cinema4k along with other options."
raise IOError(msg) raise IOError(msg)
@ -489,19 +334,79 @@ class Jp2k(Jp2kBox):
self._cparams = cparams 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 """Write image data to a JP2/JPX/J2k file. Intended usage of the
various parameters follows that of OpenJPEG's opj_compress utility. various parameters follows that of OpenJPEG's opj_compress utility.
This method can only be used to create JPEG 2000 images that can fit This method can only be used to create JPEG 2000 images that can fit
in memory. in memory.
"""
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) 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))
"""
if re.match("1.[0-4]", version.openjpeg_version) is not None:
raise RuntimeError("You must have at least version 1.5 of OpenJPEG "
"in order to write images.")
self._determine_colorspace(img_array, **kwargs)
self._populate_cparams(img_array, **kwargs) self._populate_cparams(img_array, **kwargs)
if opj2.OPENJP2 is not None: if opj2.OPENJP2 is not None:
@ -530,12 +435,10 @@ class Jp2k(Jp2kBox):
# set image offset and reference grid # set image offset and reference grid
image.contents.x0 = self._cparams.image_offset_x0 image.contents.x0 = self._cparams.image_offset_x0
image.contents.y0 = self._cparams.image_offset_y0 image.contents.y0 = self._cparams.image_offset_y0
image.contents.x1 = (image.contents.x0 image.contents.x1 = image.contents.x0 \
+ (numcols - 1) * self._cparams.subsampling_dx + (numcols - 1) * self._cparams.subsampling_dx + 1
+ 1) image.contents.y1 = image.contents.y0 \
image.contents.y1 = (image.contents.y0 + (numrows - 1) * self._cparams.subsampling_dy + 1
+ (numrows - 1) * self._cparams.subsampling_dy
+ 1)
# Stage the image data to the openjpeg data structure. # Stage the image data to the openjpeg data structure.
for k in range(0, numlayers): for k in range(0, numlayers):
@ -642,24 +545,25 @@ class Jp2k(Jp2kBox):
raise IOError(msg) raise IOError(msg)
if img_array.dtype != np.uint8 and img_array.dtype != np.uint16: if img_array.dtype != np.uint8 and img_array.dtype != np.uint16:
msg = "Only uint8 and uint16 datatypes are currently supported " msg = "Only uint8 and uint16 images are currently supported."
msg += "when writing."
raise RuntimeError(msg) raise RuntimeError(msg)
def _determine_colorspace(self, colorspace=None, **kwargs): def _determine_colorspace(self, img_array, colorspace=None, **kwargs):
"""Determine the colorspace from the supplied inputs. """Determine the colorspace from the supplied inputs.
Parameters Parameters
---------- ----------
img_array : ndarray
Image data to be written to file.
colorspace : str, optional colorspace : str, optional
Either 'rgb' or 'gray'. Either 'rgb' or 'gray'.
""" """
if colorspace is None: if colorspace is None:
# Must infer the colorspace from the image dimensions. # Must infer the colorspace from the image dimensions.
if len(self.shape) < 3: if img_array.ndim < 3:
# A single channel image is grayscale. # A single channel image is grayscale.
self._colorspace = opj2.CLRSPC_GRAY self._colorspace = opj2.CLRSPC_GRAY
elif self.shape[2] == 1 or self.shape[2] == 2: elif img_array.shape[2] == 1 or img_array.shape[2] == 2:
# A single channel image or an image with two channels is going # A single channel image or an image with two channels is going
# to be greyscale. # to be greyscale.
self._colorspace = opj2.CLRSPC_GRAY self._colorspace = opj2.CLRSPC_GRAY
@ -670,19 +574,15 @@ class Jp2k(Jp2kBox):
if colorspace.lower() not in ('rgb', 'grey', 'gray'): if colorspace.lower() not in ('rgb', 'grey', 'gray'):
msg = 'Invalid colorspace "{0}"'.format(colorspace) msg = 'Invalid colorspace "{0}"'.format(colorspace)
raise IOError(msg) raise IOError(msg)
elif colorspace.lower() == 'rgb' and self.shape[2] < 3: elif colorspace.lower() == 'rgb' and img_array.shape[2] < 3:
msg = 'RGB colorspace requires at least 3 components.' msg = 'RGB colorspace requires at least 3 components.'
raise IOError(msg) raise IOError(msg)
# Turn the colorspace from a string to the enumerated value that # Turn the colorspace from a string to the enumerated value that
# the library expects. # the library expects.
COLORSPACE_MAP = {'rgb': opj2.CLRSPC_SRGB, self._colorspace = _COLORSPACE_MAP[colorspace.lower()]
'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): def _write_openjp2(self, img_array, verbose=False):
""" """
Write JPEG 2000 file using OpenJPEG 2.x interface. Write JPEG 2000 file using OpenJPEG 2.x interface.
@ -703,11 +603,7 @@ class Jp2k(Jp2kBox):
codec = opj2.create_compress(self._cparams.codec_fmt) codec = opj2.create_compress(self._cparams.codec_fmt)
stack.callback(opj2.destroy_codec, codec) stack.callback(opj2.destroy_codec, codec)
if self._verbose or verbose: info_handler = _INFO_CALLBACK if verbose else None
info_handler = _INFO_CALLBACK
else:
info_handler = None
opj2.set_info_handler(codec, info_handler) opj2.set_info_handler(codec, info_handler)
opj2.set_warning_handler(codec, _WARNING_CALLBACK) opj2.set_warning_handler(codec, _WARNING_CALLBACK)
opj2.set_error_handler(codec, _ERROR_CALLBACK) opj2.set_error_handler(codec, _ERROR_CALLBACK)
@ -748,8 +644,7 @@ class Jp2k(Jp2kBox):
if not ((box.box_id == 'xml ') or if not ((box.box_id == 'xml ') or
(box.box_id == 'uuid' and (box.box_id == 'uuid' and
box.uuid == UUID('be7acfcb-97a9-42e8-9c71-999491e3afac'))): box.uuid == UUID('be7acfcb-97a9-42e8-9c71-999491e3afac'))):
msg = "Only XML boxes and XMP UUID boxes can currently be " msg = "Only XML boxes and XMP UUID boxes can currently be appended."
msg += "appended."
raise IOError(msg) raise IOError(msg)
# Check the last box. If the length field is zero, then rewrite # Check the last box. If the length field is zero, then rewrite
@ -845,7 +740,7 @@ class Jp2k(Jp2kBox):
raise IOError(msg) raise IOError(msg)
# Find the first codestream in the file. # 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 offset = jp2c[0].offset
# Ready to write the codestream. # Ready to write the codestream.
@ -879,9 +774,10 @@ class Jp2k(Jp2kBox):
FileTypeBox(), FileTypeBox(),
JP2HeaderBox(), JP2HeaderBox(),
ContiguousCodestreamBox()] ContiguousCodestreamBox()]
height = self.codestream.segment[1].ysiz codestream = self.get_codestream()
width = self.codestream.segment[1].xsiz height = codestream.segment[1].ysiz
num_components = len(self.codestream.segment[1].xrsiz) width = codestream.segment[1].xsiz
num_components = len(codestream.segment[1].xrsiz)
if num_components < 3: if num_components < 3:
colorspace = core.GREYSCALE colorspace = core.GREYSCALE
else: else:
@ -905,13 +801,13 @@ class Jp2k(Jp2kBox):
Slicing protocol. Slicing protocol.
""" """
if ((isinstance(index, slice) and if ((isinstance(index, slice) and
(index.start is None and (index.start == None and
index.stop is None and index.stop == None and
index.step is None)) or (index is Ellipsis)): index.step == None)) or (index is Ellipsis)):
# Case of jp2[:] = data, i.e. write the entire image. # Case of jp2[:] = data, i.e. write the entire image.
# #
# Should have a slice object where start = stop = step = None # Should have a slice object where start = stop = step = None
self._write(data) self.write(data)
else: else:
msg = "Partial write operations are currently not allowed." msg = "Partial write operations are currently not allowed."
raise TypeError(msg) raise TypeError(msg)
@ -920,32 +816,29 @@ class Jp2k(Jp2kBox):
""" """
Slicing protocol. Slicing protocol.
""" """
if len(self.shape) == 2: codestream = self.get_codestream(header_only=True)
numrows, numcols = self.shape numrows = codestream.segment[1].ysiz
numbands = 1 numcols = codestream.segment[1].xsiz
else: numbands = codestream.segment[1].Csiz
numrows, numcols, numbands = self.shape
if isinstance(pargs, int): if isinstance(pargs, int):
# Not a very good use of this protocol, but technically legal. # Not a very good use of this protocol, but technically legal.
# This retrieves a single row. # This retrieves a single row.
row = pargs row = pargs
area = (row, 0, row + 1, numcols) area = (row, 0, row + 1, numcols)
return self._read(area=area).squeeze() return self.read(area=area).squeeze()
if pargs is Ellipsis: if pargs is Ellipsis:
# Case of jp2[...] # Case of jp2[...]
return self._read() return self.read()
if isinstance(pargs, slice): if isinstance(pargs, slice):
if (((pargs.start is None) and if pargs.start is None and pargs.stop is None and pargs.step is None:
(pargs.stop is None) and
(pargs.step is None))):
# Case of jp2[:] # Case of jp2[:]
return self._read() return self.read()
# Corner case of jp2[x] where x is a slice object with non-null # 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. # below handle it.
pargs = (pargs, Ellipsis) pargs = (pargs, Ellipsis)
@ -988,7 +881,8 @@ class Jp2k(Jp2kBox):
# Reduce dimensionality in the scalar dimension. # Reduce dimensionality in the scalar dimension.
return np.squeeze(data, axis=idx) 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] rows = pargs[0]
cols = pargs[1] cols = pargs[1]
if len(pargs) == 2: if len(pargs) == 2:
@ -1005,28 +899,49 @@ class Jp2k(Jp2kBox):
# Ok, reduce layer step is the same in both xy directions, so just take # Ok, reduce layer step is the same in both xy directions, so just take
# one of them. # one of them.
step = rows_step step = rows_step
# Check if the step size is a power of 2. # Check if the step size is a power of 2.
if np.abs(np.log2(step) - np.round(np.log2(step))) > 1e-6: if np.abs(np.log2(step) - np.round(np.log2(step))) > 1e-6:
msg = "Row and column strides must be powers of 2." msg = "Row and column strides must be powers of 2."
raise IndexError(msg) raise IndexError(msg)
rlevel = np.int(np.round(np.log2(step))) 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, 0 if cols.start is None else cols.start,
numrows if rows.stop is None else rows.stop, numrows if rows.stop is None else rows.stop,
numcols if cols.stop is None else cols.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: if len(pargs) == 2:
return data return data
# Ok, 3 arguments in pargs. # Ok, 3 arguments in pargs.
return data[:, :, bands] return data[:, :, bands]
def _read(self, **kwargs):
def read(self, **kwargs):
"""Read a JPEG 2000 image. """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 Returns
------- -------
img_array : ndarray img_array : ndarray
@ -1036,65 +951,42 @@ class Jp2k(Jp2kBox):
------ ------
IOError IOError
If the image has differing subsample factors. If the image has differing subsample factors.
"""
if version.openjpeg_version_tuple[0] < 2:
img = self._read_openjpeg(**kwargs)
else:
img = self._read_openjp2(**kwargs)
return img
def read(self, **kwargs): Examples
""" --------
""" >>> import glymur
# Read a JPEG 2000 image. >>> jfile = glymur.data.nemo()
# >>> jp = glymur.Jp2k(jfile)
# Parameters >>> image = jp.read()
# ---------- >>> image.shape
# rlevel : int, optional (1456, 2592, 3)
# 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: Read the lowest resolution thumbnail.
self.ignore_pclr_cmap_cdef = kwargs['ignore_pclr_cmap_cdef']
warnings.warn("Use array-style slicing instead.", DeprecationWarning) >>> thumbnail = jp.read(rlevel=-1)
if version.openjpeg_version_tuple[0] < 2: >>> thumbnail.shape
img = self._read_openjpeg(**kwargs) (728, 1296, 3)
else: """
if opj2.OPENJP2 is not None:
img = self._read_openjp2(**kwargs) img = self._read_openjp2(**kwargs)
else:
img = self._read_openjpeg(**kwargs)
return img return img
def _subsampling_sanity_check(self): def _subsampling_sanity_check(self):
"""Check for differing subsample factors. """Check for differing subsample factors.
""" """
dxs = np.array(self.codestream.segment[1].xrsiz) codestream = self.get_codestream(header_only=True)
dys = np.array(self.codestream.segment[1].yrsiz) dxs = np.array(codestream.segment[1].xrsiz)
dys = np.array(codestream.segment[1].yrsiz)
if np.any(dxs - dxs[0]) or np.any(dys - dys[0]): if np.any(dxs - dxs[0]) or np.any(dys - dys[0]):
msg = "Components must all have the same subsampling factors " msg = "Components must all have the same subsampling factors "
msg += "to use this method. Please consider using OPENJP2 and " msg += "to use this method. Please consider using OPENJP2 and "
msg += "the read_bands method instead." msg += "the read_bands method instead."
raise RuntimeError(msg) 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. """Read a JPEG 2000 image using libopenjpeg.
Parameters Parameters
@ -1102,6 +994,9 @@ class Jp2k(Jp2kBox):
rlevel : int, optional rlevel : int, optional
Factor by which to rlevel output resolution. Use -1 to get the Factor by which to rlevel output resolution. Use -1 to get the
lowest resolution thumbnail. 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 verbose : bool, optional
Print informational messages produced by the OpenJPEG library. Print informational messages produced by the OpenJPEG library.
area : tuple, optional area : tuple, optional
@ -1120,7 +1015,7 @@ class Jp2k(Jp2kBox):
""" """
self._subsampling_sanity_check() self._subsampling_sanity_check()
self._populate_dparams(rlevel) self._populate_dparams(rlevel, ignore_pclr_cmap_cdef)
with ExitStack() as stack: with ExitStack() as stack:
try: try:
@ -1130,10 +1025,7 @@ class Jp2k(Jp2kBox):
event_mgr = opj.EventMgrType() event_mgr = opj.EventMgrType()
info_handler = ctypes.cast(_INFO_CALLBACK, ctypes.c_void_p) info_handler = ctypes.cast(_INFO_CALLBACK, ctypes.c_void_p)
if verbose or self._verbose: event_mgr.info_handler = info_handler if verbose else None
event_mgr.info_handler = info_handler
else:
event_mgr.info_handler = None
event_mgr.warning_handler = ctypes.cast(_WARNING_CALLBACK, event_mgr.warning_handler = ctypes.cast(_WARNING_CALLBACK,
ctypes.c_void_p) ctypes.c_void_p)
event_mgr.error_handler = ctypes.cast(_ERROR_CALLBACK, event_mgr.error_handler = ctypes.cast(_ERROR_CALLBACK,
@ -1176,8 +1068,8 @@ class Jp2k(Jp2kBox):
return data return data
def _read_openjp2(self, rlevel=0, layer=None, area=None, tile=None, def _read_openjp2(self, rlevel=0, layer=0, area=None, tile=None,
verbose=False): verbose=False, ignore_pclr_cmap_cdef=False):
"""Read a JPEG 2000 image using libopenjp2. """Read a JPEG 2000 image using libopenjp2.
Parameters Parameters
@ -1205,12 +1097,10 @@ class Jp2k(Jp2kBox):
RuntimeError RuntimeError
If the image has differing subsample factors. If the image has differing subsample factors.
""" """
if layer is not None:
self._layer = layer
self._subsampling_sanity_check() self._subsampling_sanity_check()
self._populate_dparams(rlevel, tile=tile, area=area) self._populate_dparams(rlevel, ignore_pclr_cmap_cdef,
layer=layer, tile=tile, area=area)
with ExitStack() as stack: with ExitStack() as stack:
if re.match("2.1", version.openjpeg_version): if re.match("2.1", version.openjpeg_version):
@ -1227,8 +1117,7 @@ class Jp2k(Jp2kBox):
opj2.set_error_handler(codec, _ERROR_CALLBACK) opj2.set_error_handler(codec, _ERROR_CALLBACK)
opj2.set_warning_handler(codec, _WARNING_CALLBACK) opj2.set_warning_handler(codec, _WARNING_CALLBACK)
if verbose:
if self._verbose or verbose:
opj2.set_info_handler(codec, _INFO_CALLBACK) opj2.set_info_handler(codec, _INFO_CALLBACK)
else: else:
opj2.set_info_handler(codec, None) opj2.set_info_handler(codec, None)
@ -1245,8 +1134,7 @@ class Jp2k(Jp2kBox):
self._dparams.DA_x0, self._dparams.DA_y0, self._dparams.DA_x0, self._dparams.DA_y0,
self._dparams.DA_x1, self._dparams.DA_y1) self._dparams.DA_x1, self._dparams.DA_y1)
opj2.decode(codec, stream, image) opj2.decode(codec, stream, image)
opj2.end_decompress(codec, stream)
opj2.end_decompress(codec, stream)
img_array = extract_image_cube(image) img_array = extract_image_cube(image)
@ -1255,11 +1143,14 @@ class Jp2k(Jp2kBox):
return img_array return img_array
def _populate_dparams(self, rlevel, tile=None, area=None): def _populate_dparams(self, rlevel, ignore_pclr_cmap_cdef, tile=None,
layer=None, area=None):
"""Populate decompression structure with appropriate input parameters. """Populate decompression structure with appropriate input parameters.
Parameters Parameters
---------- ----------
layer : int
Number of quality layer to decode.
rlevel : int rlevel : int
Factor by which to rlevel output resolution. Factor by which to rlevel output resolution.
area : tuple area : tuple
@ -1267,6 +1158,9 @@ class Jp2k(Jp2kBox):
(first_row, first_col, last_row, last_col) (first_row, first_col, last_row, last_col)
tile : int tile : int
Number of tile to decode. 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.
""" """
if opj2.OPENJP2 is not None: if opj2.OPENJP2 is not None:
dparam = opj2.set_default_decoder_parameters() dparam = opj2.set_default_decoder_parameters()
@ -1279,18 +1173,16 @@ class Jp2k(Jp2kBox):
infile += b'0' * nelts infile += b'0' * nelts
dparam.infile = infile dparam.infile = infile
if self.ignore_pclr_cmap_cdef:
# Return raw codestream components.
dparam.flags |= 1
dparam.decod_format = self._codec_format 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. # Must check the specified rlevel against the maximum.
if rlevel != 0: if rlevel != 0:
# Must check the specified rlevel against the maximum. # 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: if rlevel == -1:
# -1 is shorthand for the largest rlevel # -1 is shorthand for the largest rlevel
rlevel = max_rlevel rlevel = max_rlevel
@ -1315,13 +1207,13 @@ class Jp2k(Jp2kBox):
dparam.tile_index = tile dparam.tile_index = tile
dparam.nb_tile_to_decode = 1 dparam.nb_tile_to_decode = 1
if self.ignore_pclr_cmap_cdef: if ignore_pclr_cmap_cdef is True:
# Return raw codestream components. # Return raw codestream components.
dparam.flags |= 1 dparam.flags |= 1
self._dparams = dparam self._dparams = 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): verbose=False, ignore_pclr_cmap_cdef=False):
"""Read a JPEG 2000 image. """Read a JPEG 2000 image.
@ -1367,10 +1259,8 @@ class Jp2k(Jp2kBox):
"OpenJPEG installed before using this " "OpenJPEG installed before using this "
"functionality.") "functionality.")
self.ignore_pclr_cmap_cdef = ignore_pclr_cmap_cdef self._populate_dparams(rlevel, ignore_pclr_cmap_cdef,
if layer is not None: layer=layer, tile=tile, area=area)
self._layer = layer
self._populate_dparams(rlevel, tile=tile, area=area)
with ExitStack() as stack: with ExitStack() as stack:
if re.match("2.1", version.openjpeg_version): if re.match("2.1", version.openjpeg_version):
@ -1446,6 +1336,7 @@ class Jp2k(Jp2kBox):
codestream = Codestream(fptr, self.length, codestream = Codestream(fptr, self.length,
header_only=header_only) header_only=header_only)
else: else:
ftyp = self.box[1]
box = [x for x in self.box if x.box_id == 'jp2c'] box = [x for x in self.box if x.box_id == 'jp2c']
fptr.seek(box[0].offset) fptr.seek(box[0].offset)
read_buffer = fptr.read(8) read_buffer = fptr.read(8)
@ -1465,7 +1356,7 @@ class Jp2k(Jp2kBox):
def _populate_image_struct(self, image, imgdata): def _populate_image_struct(self, image, imgdata):
"""Populates image struct needed for compression. """Populates image struct needed for compression.
Parameters Parameters
---------- ----------
image : ImageType(ctypes.Structure) image : ImageType(ctypes.Structure)
@ -1473,9 +1364,9 @@ class Jp2k(Jp2kBox):
img_array : ndarray img_array : ndarray
Image data to be written to file. Image data to be written to file.
""" """
numrows, numcols, num_comps = imgdata.shape numrows, numcols, num_comps = imgdata.shape
# set image offset and reference grid # set image offset and reference grid
image.contents.x0 = self._cparams.image_offset_x0 image.contents.x0 = self._cparams.image_offset_x0
image.contents.y0 = self._cparams.image_offset_y0 image.contents.y0 = self._cparams.image_offset_y0
@ -1483,7 +1374,7 @@ class Jp2k(Jp2kBox):
(numcols - 1) * self._cparams.subsampling_dx + 1) (numcols - 1) * self._cparams.subsampling_dx + 1)
image.contents.y1 = (image.contents.y0 + image.contents.y1 = (image.contents.y0 +
(numrows - 1) * self._cparams.subsampling_dy + 1) (numrows - 1) * self._cparams.subsampling_dy + 1)
# Stage the image data to the openjpeg data structure. # Stage the image data to the openjpeg data structure.
for k in range(0, num_comps): for k in range(0, num_comps):
if re.match("2.0", version.openjpeg_version) is not None: if re.match("2.0", version.openjpeg_version) is not None:
@ -1497,19 +1388,19 @@ class Jp2k(Jp2kBox):
core.OPJ_PROFILE_CINEMA_4K): core.OPJ_PROFILE_CINEMA_4K):
image.contents.comps[k].prec = 12 image.contents.comps[k].prec = 12
image.contents.comps[k].bpp = 12 image.contents.comps[k].bpp = 12
layer = np.ascontiguousarray(imgdata[:, :, k], dtype=np.int32) layer = np.ascontiguousarray(imgdata[:, :, k], dtype=np.int32)
dest = image.contents.comps[k].data dest = image.contents.comps[k].data
src = layer.ctypes.data src = layer.ctypes.data
ctypes.memmove(dest, src, layer.nbytes) ctypes.memmove(dest, src, layer.nbytes)
return image return image
def _populate_comptparms(self, img_array): def _populate_comptparms(self, img_array):
"""Instantiate and populate comptparms structure. """Instantiate and populate comptparms structure.
This structure defines the image components. This structure defines the image components.
Parameters Parameters
---------- ----------
img_array : ndarray img_array : ndarray
@ -1538,8 +1429,8 @@ class Jp2k(Jp2kBox):
comptparms[j].sgnd = 0 comptparms[j].sgnd = 0
self._comptparms = comptparms self._comptparms = comptparms
def _component2dtype(component): def _component2dtype(component):
"""Take an OpenJPEG component structure and determine the numpy datatype. """Take an OpenJPEG component structure and determine the numpy datatype.
@ -1585,7 +1476,6 @@ JP2_IDS = ['colr', 'cdef', 'cmap', 'jp2c', 'ftyp', 'ihdr', 'jp2h', 'jP ',
'pclr', 'res ', 'resc', 'resd', 'xml ', 'ulst', 'uinf', 'url ', 'pclr', 'res ', 'resc', 'resd', 'xml ', 'ulst', 'uinf', 'url ',
'uuid'] 'uuid']
def _validate_jp2_box_sequence(boxes): def _validate_jp2_box_sequence(boxes):
"""Run through series of tests for JP2 box legality. """Run through series of tests for JP2 box legality.
@ -1601,13 +1491,12 @@ def _validate_jp2_box_sequence(boxes):
count = _collect_box_count(boxes) count = _collect_box_count(boxes)
for box_id in count.keys(): for box_id in count.keys():
if box_id not in JP2_IDS: if box_id not in JP2_IDS:
msg = "The presence of a '{0}' box requires that the file " msg = "The presence of a '{0}' box requires that the file type "
msg += "type brand be set to 'jpx '." msg += "brand be set to 'jpx '."
raise IOError(msg.format(box_id)) raise IOError(msg.format(box_id))
_validate_jp2_colr(boxes) _validate_jp2_colr(boxes)
def _validate_jp2_colr(boxes): def _validate_jp2_colr(boxes):
""" """
Validate JP2 requirements on colour specification boxes. Validate JP2 requirements on colour specification boxes.
@ -1619,7 +1508,6 @@ def _validate_jp2_colr(boxes):
msg = "A JP2 colr box cannot have a non-zero approximation field." msg = "A JP2 colr box cannot have a non-zero approximation field."
raise IOError(msg) raise IOError(msg)
def _validate_jpx_box_sequence(boxes): def _validate_jpx_box_sequence(boxes):
"""Run through series of tests for JPX box legality.""" """Run through series of tests for JPX box legality."""
_validate_label(boxes) _validate_label(boxes)
@ -1628,7 +1516,6 @@ def _validate_jpx_box_sequence(boxes):
_validate_singletons(boxes) _validate_singletons(boxes)
_validate_top_level(boxes) _validate_top_level(boxes)
def _validate_signature_compatibility(boxes): def _validate_signature_compatibility(boxes):
"""Validate the file signature and compatibility status.""" """Validate the file signature and compatibility status."""
# Check for a bad sequence of boxes. # Check for a bad sequence of boxes.
@ -1716,8 +1603,6 @@ def _validate_channel_definition(jp2h, colr):
JP2H_CHILDREN = set(['bpcc', 'cdef', 'cmap', 'ihdr', 'pclr']) JP2H_CHILDREN = set(['bpcc', 'cdef', 'cmap', 'ihdr', 'pclr'])
def _check_jp2h_child_boxes(boxes, parent_box_name): def _check_jp2h_child_boxes(boxes, parent_box_name):
"""Certain boxes can only reside in the JP2 header.""" """Certain boxes can only reside in the JP2 header."""
box_ids = set([box.box_id for box in boxes]) box_ids = set([box.box_id for box in boxes])
@ -1745,7 +1630,6 @@ def _collect_box_count(boxes):
TOP_LEVEL_ONLY_BOXES = set(['dtbl']) TOP_LEVEL_ONLY_BOXES = set(['dtbl'])
def _check_superbox_for_top_levels(boxes): def _check_superbox_for_top_levels(boxes):
"""Several boxes can only occur at the top level.""" """Several boxes can only occur at the top level."""
# We are only looking at the boxes contained in a superbox, so if any of # We are only looking at the boxes contained in a superbox, so if any of
@ -1761,7 +1645,6 @@ def _check_superbox_for_top_levels(boxes):
if hasattr(box, 'box'): if hasattr(box, 'box'):
_check_superbox_for_top_levels(box.box) _check_superbox_for_top_levels(box.box)
def _validate_top_level(boxes): def _validate_top_level(boxes):
"""Several boxes can only occur at the top level.""" """Several boxes can only occur at the top level."""
# Add the counts in the superboxes. # Add the counts in the superboxes.
@ -1781,7 +1664,6 @@ def _validate_top_level(boxes):
msg += 'a fragment table box as well.' msg += 'a fragment table box as well.'
raise IOError(msg) raise IOError(msg)
def _validate_singletons(boxes): def _validate_singletons(boxes):
"""Several boxes can only occur once.""" """Several boxes can only occur once."""
count = _collect_box_count(boxes) count = _collect_box_count(boxes)
@ -1790,9 +1672,6 @@ def _validate_singletons(boxes):
if 'dtbl' in multiples: if 'dtbl' in multiples:
raise IOError('There can only be one dtbl box in a file.') raise IOError('There can only be one dtbl box in a file.')
JPX_IDS = ['asoc', 'nlst']
def _validate_jpx_brand(boxes, brand): def _validate_jpx_brand(boxes, brand):
""" """
If there is a JPX box then the brand must be 'jpx '. If there is a JPX box then the brand must be 'jpx '.
@ -1807,7 +1686,6 @@ def _validate_jpx_brand(boxes, brand):
# Same set of checks on any child boxes. # Same set of checks on any child boxes.
_validate_jpx_brand(box.box, brand) _validate_jpx_brand(box.box, brand)
def _validate_jpx_compatibility(boxes, compatibility_list): def _validate_jpx_compatibility(boxes, compatibility_list):
""" """
If there is a JPX box then the compatibility list must also contain 'jpx '. If there is a JPX box then the compatibility list must also contain 'jpx '.
@ -1823,7 +1701,6 @@ def _validate_jpx_compatibility(boxes, compatibility_list):
# Same set of checks on any child boxes. # Same set of checks on any child boxes.
_validate_jpx_compatibility(box.box, compatibility_list) _validate_jpx_compatibility(box.box, compatibility_list)
def _validate_label(boxes): def _validate_label(boxes):
""" """
Label boxes can only be inside association, codestream headers, or Label boxes can only be inside association, codestream headers, or
@ -1840,7 +1717,6 @@ def _validate_label(boxes):
# Same set of checks on any child boxes. # Same set of checks on any child boxes.
_validate_label(box.box) _validate_label(box.box)
def extract_image_cube(image): def extract_image_cube(image):
"""Extract 3D image from openjpeg data structure. """Extract 3D image from openjpeg data structure.
""" """
@ -1896,6 +1772,7 @@ def extract_image_bands(image):
return data return data
# Setup the default callback handlers. See the callback functions subsection # Setup the default callback handlers. See the callback functions subsection
# in the ctypes section of the Python documentation for a solid explanation of # in the ctypes section of the Python documentation for a solid explanation of
# what's going on here. # what's going on here.

View file

@ -2,5 +2,3 @@
from . import openjp2 as openjp2 from . import openjp2 as openjp2
from . import openjpeg as openjpeg from . import openjpeg as openjpeg
from . import c from . import c
__all__ = [openjp2, openjpeg, c]

View file

@ -1,6 +1,9 @@
""" """
Configure glymur to use installed libraries if possible. Configure glymur to use installed libraries if possible.
""" """
# configparser is new in python3 (pylint/python-2.7)
# pylint: disable=F0401
import ctypes import ctypes
from ctypes.util import find_library from ctypes.util import find_library
import os import os
@ -16,21 +19,18 @@ else:
from configparser import NoOptionError from configparser import NoOptionError
# default library locations for MacPorts # default library locations for MacPorts
_macports_default_location = {'openjp2': '/opt/local/lib/libopenjp2.dylib', _macports_default_location = {
'openjpeg': '/opt/local/lib/libopenjpeg.dylib'} 'openjp2': '/opt/local/lib/libopenjp2.dylib',
'openjpeg': '/opt/local/lib/libopenjpeg.dylib'
}
# default library locations on Windows # default library locations on Windows
_windows_default_location = {'openjp2': os.path.join('C:\\', _windows_default_location = {
'Program files', 'openjp2': os.path.join('C:\\', 'Program files', 'OpenJPEG 2.0',
'OpenJPEG 2.0', 'bin', 'openjp2.dll'),
'bin', 'openjpeg': os.path.join('C:\\', 'Program files', 'OpenJPEG 1.5',
'openjp2.dll'), 'bin', 'openjpeg.dll')
'openjpeg': os.path.join('C:\\', }
'Program files',
'OpenJPEG 1.5',
'bin',
'openjpeg.dll')}
def glymurrc_fname(): def glymurrc_fname():
"""Return the path to the configuration file. """Return the path to the configuration file.
@ -55,9 +55,8 @@ def glymurrc_fname():
# didn't find a configuration file. # didn't find a configuration file.
return None return None
def load_openjpeg_library(libname): def load_openjpeg_library(libname):
path = read_config_file(libname) path = read_config_file(libname)
if path is not None: if path is not None:
return load_library_handle(path) return load_library_handle(path)
@ -80,15 +79,13 @@ def load_openjpeg_library(libname):
return load_library_handle(path) return load_library_handle(path)
def load_library_handle(path): def load_library_handle(path):
"""Load the library, return the ctypes handle.""" """Load the library, return the ctypes handle."""
if path is None or path in ['None', 'none']: if path is None or path in ['None', 'none']:
# Either could not find a library via ctypes or # Either could not find a library via ctypes or user-configuration-file,
# user-configuration-file, or we could not find it in any of the # or we could not find it in any of the default locations.
# default locations, or possibly the user intentionally does not want # This is probably a very old linux.
# one of the libraries to load.
return None return None
try: try:
@ -97,10 +94,10 @@ def load_library_handle(path):
else: else:
opj_lib = ctypes.CDLL(path) opj_lib = ctypes.CDLL(path)
except (TypeError, OSError): except (TypeError, OSError):
msg = 'The library specified by configuration file at {0} could not ' msg = '"Library {0}" could not be loaded. Operating in degraded mode.'
msg += 'be loaded.' msg = msg.format(path)
warnings.warn(msg.format(path), UserWarning) warnings.warn(msg, UserWarning)
opj_lib = None opj_lib = None
return opj_lib return opj_lib
@ -133,7 +130,6 @@ def read_config_file(libname):
path = None path = None
return path return path
def glymur_config(): def glymur_config():
""" """
Try to ascertain locations of openjp2, openjpeg libraries. Try to ascertain locations of openjp2, openjpeg libraries.
@ -148,10 +144,9 @@ def glymur_config():
lst.append(load_openjpeg_library(libname)) lst.append(load_openjpeg_library(libname))
if all(handle is None for handle in lst): if all(handle is None for handle in lst):
msg = "Neither the openjp2 nor the openjpeg library could be loaded. " msg = "Neither the openjp2 nor the openjpeg library could be loaded. "
warnings.warn(msg) raise IOError(msg)
return tuple(lst) return tuple(lst)
def get_configdir(): def get_configdir():
"""Return string representing the configuration directory. """Return string representing the configuration directory.

View file

@ -2,6 +2,8 @@
Wraps individual functions in openjp2 library. Wraps individual functions in openjp2 library.
""" """
# pylint: disable=C0302,R0903,W0201
import ctypes import ctypes
import re import re
import sys import sys
@ -11,7 +13,6 @@ from .config import glymur_config
OPENJP2, OPENJPEG = glymur_config() OPENJP2, OPENJPEG = glymur_config()
def version(): def version():
"""Wrapper for opj_version library routine.""" """Wrapper for opj_version library routine."""
try: try:
@ -49,6 +50,13 @@ JPWL_MAX_NO_TILESPECS = 16
TRUE = 1 TRUE = 1
FALSE = 0 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 # supported color spaces
CLRSPC_UNKNOWN = -1 CLRSPC_UNKNOWN = -1
CLRSPC_UNSPECIFIED = 0 CLRSPC_UNSPECIFIED = 0
@ -408,6 +416,7 @@ class CompressionParametersType(ctypes.Structure):
for j in range(self.numpocs): for j in range(self.numpocs):
msg += " [#{0}]:".format(j) msg += " [#{0}]:".format(j)
msg += " {0}".format(str(self.poc[j])) msg += " {0}".format(str(self.poc[j]))
msg += textwrap.indent(textstr, ' ' * 12)
elif field_name in ['tcp_rates', 'tcp_distoratio']: elif field_name in ['tcp_rates', 'tcp_distoratio']:
lst = [] lst = []
@ -480,14 +489,6 @@ class ImageCompType(ctypes.Structure):
if _MINOR == '1': if _MINOR == '1':
_fields_.append(("alpha", ctypes.c_uint16)) _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): class ImageType(ctypes.Structure):
"""Defines image data and characteristics. """Defines image data and characteristics.
@ -518,26 +519,6 @@ class ImageType(ctypes.Structure):
# restricted ICC profile buffer length # restricted ICC profile buffer length
("icc_profile_len", ctypes.c_uint32)] ("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): class ImageComptParmType(ctypes.Structure):
"""Component parameters structure used by image_create function. """Component parameters structure used by image_create function.
@ -575,6 +556,107 @@ class ImageComptParmType(ctypes.Structure):
return msg 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): def check_error(status):
"""Set a generic function as the restype attribute of all OpenJPEG """Set a generic function as the restype attribute of all OpenJPEG
functions that return a BOOL_TYPE value. This way we do not have to check functions that return a BOOL_TYPE value. This way we do not have to check
@ -737,6 +819,28 @@ def encode(codec, stream):
OPENJP2.opj_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): def get_decoded_tile(codec, stream, imagep, tile_index):
"""get the decoded tile from the codec """get the decoded tile from the codec
@ -767,6 +871,23 @@ def get_decoded_tile(codec, stream, imagep, tile_index):
OPENJP2.opj_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): def end_compress(codec, stream):
"""End of compressing the current image. """End of compressing the current image.
@ -909,7 +1030,7 @@ def read_header(stream, codec):
ARGTYPES = [STREAM_TYPE_P, CODEC_TYPE, ARGTYPES = [STREAM_TYPE_P, CODEC_TYPE,
ctypes.POINTER(ctypes.POINTER(ImageType))] ctypes.POINTER(ctypes.POINTER(ImageType))]
OPENJP2.opj_read_header.argtypes = ARGTYPES OPENJP2.opj_read_header.argtypes = ARGTYPES
OPENJP2.opj_read_header.restype = check_error OPENJP2.opj_read_header.restype = check_error
imagep = ctypes.POINTER(ImageType)() imagep = ctypes.POINTER(ImageType)()
OPENJP2.opj_read_header(stream, codec, ctypes.byref(imagep)) OPENJP2.opj_read_header(stream, codec, ctypes.byref(imagep))
@ -1268,7 +1389,6 @@ def _stream_create_default_file_stream_2p0(fptr, isa_read_stream):
stream = OPENJP2.opj_stream_create_default_file_stream(fptr, read_stream) stream = OPENJP2.opj_stream_create_default_file_stream(fptr, read_stream)
return stream return stream
def _stream_create_default_file_stream_2p1(fname, isa_read_stream): def _stream_create_default_file_stream_2p1(fname, isa_read_stream):
"""Wraps openjp2 library function opj_stream_create_default_vile_stream. """Wraps openjp2 library function opj_stream_create_default_vile_stream.
@ -1295,7 +1415,7 @@ def _stream_create_default_file_stream_2p1(fname, isa_read_stream):
stream = OPENJP2.opj_stream_create_default_file_stream(file_argument, stream = OPENJP2.opj_stream_create_default_file_stream(file_argument,
read_stream) read_stream)
return stream return stream
if re.match(r'''2.0''', version()): if re.match(r'''2.0''', version()):
stream_create_default_file_stream = _stream_create_default_file_stream_2p0 stream_create_default_file_stream = _stream_create_default_file_stream_2p0
else: else:

View file

@ -1,9 +1,13 @@
"""Wraps library calls to openjpeg. """Wraps library calls to openjpeg.
""" """
# pylint: disable=R0903
import ctypes import ctypes
import sys import sys
import numpy as np
from .config import glymur_config from .config import glymur_config
_, OPENJPEG = glymur_config() _, OPENJPEG = glymur_config()
@ -55,10 +59,8 @@ class CommonStructType(ctypes.Structure):
("mj2_handle", ctypes.c_void_p)] ("mj2_handle", ctypes.c_void_p)]
STREAM_READ = 0x0001 # The stream was opened for reading. STREAM_READ = 0x0001 # The stream was opened for reading.
STREAM_WRITE = 0x0002 # The stream was opened for writing. STREAM_WRITE = 0x0002 # The stream was opened for writing.
class CioType(ctypes.Structure): class CioType(ctypes.Structure):
"""Byte input-output stream (CIO) """Byte input-output stream (CIO)
@ -89,57 +91,70 @@ class CompressionInfoType(CommonStructType):
class PocType(ctypes.Structure): class PocType(ctypes.Structure):
"""Progression order changes.""" """Progression order changes."""
_fields_ = [("resno", ctypes.c_int), _fields_ = [("resno", ctypes.c_int),
# Resolution num start, Component num start, given by POC # Resolution num start, Component num start, given by POC
("compno0", ctypes.c_int), ("compno0", ctypes.c_int),
# Layer num end,Resolution num end, Component num end, given # Layer num end,Resolution num end, Component num end, given by POC
# by POC ("layno1", ctypes.c_int),
("layno1", ctypes.c_int), ("resno1", ctypes.c_int),
("resno1", ctypes.c_int), ("compno1", ctypes.c_int),
("compno1", ctypes.c_int),
# Layer num start,Precinct num start, Precinct num end # Layer num start,Precinct num start, Precinct num end
("layno0", ctypes.c_int), ("layno0", ctypes.c_int),
("precno0", ctypes.c_int), ("precno0", ctypes.c_int),
("precno1", ctypes.c_int), ("precno1", ctypes.c_int),
# Progression order enum # Progression order enum
# OPJ_PROG_ORDER prg1,prg; # OPJ_PROG_ORDER prg1,prg;
("prg1", ctypes.c_int), ("prg1", ctypes.c_int),
("prg", ctypes.c_int), ("prg", ctypes.c_int),
# Progression order string # Progression order string
# char progorder[5]; # char progorder[5];
("progorder", ctypes.c_char * 5), ("progorder", ctypes.c_char * 5),
# Tile number # Tile number
# int tile; # int tile;
("tile", ctypes.c_int), ("tile", ctypes.c_int),
("tx0", ctypes.c_int), # /** Start and end values for Tile width and height*/
("tx1", ctypes.c_int), # int tx0,tx1,ty0,ty1;
("ty0", ctypes.c_int), ("tx0", ctypes.c_int),
("ty1", ctypes.c_int), ("tx1", ctypes.c_int),
("layS", ctypes.c_int), ("ty0", ctypes.c_int),
("resS", ctypes.c_int), ("ty1", ctypes.c_int),
("compS", ctypes.c_int),
("prcS", ctypes.c_int), # /** Start value, initialised in pi_initialise_encode*/
("layE", ctypes.c_int), # int layS, resS, compS, prcS;
("resE", ctypes.c_int), ("layS", ctypes.c_int),
("compE", ctypes.c_int), ("resS", ctypes.c_int),
("prcE", ctypes.c_int), ("compS", ctypes.c_int),
("txS", ctypes.c_int), ("prcS", ctypes.c_int),
("txE", ctypes.c_int),
("tyS", ctypes.c_int), # /** End value, initialised in pi_initialise_encode */
("tyE", ctypes.c_int), # int layE, resE, compE, prcE;
("dx", ctypes.c_int), ("layE", ctypes.c_int),
("dy", ctypes.c_int), ("resE", ctypes.c_int),
("lay_t", ctypes.c_int), ("compE", ctypes.c_int),
("res_t", ctypes.c_int), ("prcE", ctypes.c_int),
("comp_t", ctypes.c_int),
("prc_t", ctypes.c_int), # Start and end values of Tile width and height, initialised in
("tx0_t", ctypes.c_int), # pi_initialise_encode int txS,txE,tyS,tyE,dx,dy;
("ty0_t", 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),
# 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): class CompressionParametersType(ctypes.Structure):
@ -360,47 +375,48 @@ class DecompressionParametersType(ctypes.Structure):
class ImageComptParmType(ctypes.Structure): class ImageComptParmType(ctypes.Structure):
"""Component parameters structure used by the opj_image_create function. """Component parameters structure used by the opj_image_create function.
""" """
_fields_ = [("dx", ctypes.c_int), _fields_ = [
# XRsiz: horizontal separation of a sample of ith component # XRsiz: horizontal separation of a sample of ith component with
# with respect to the reference grid # respect to the reference grid
("dx", ctypes.c_int),
# YRsiz: vertical separation of a sample of ith component with # YRsiz: vertical separation of a sample of ith component with
# respect to the reference grid */ # respect to the reference grid */
("dy", ctypes.c_int), ("dy", ctypes.c_int),
# data width, height # data width, height
("w", ctypes.c_int), ("w", ctypes.c_int),
("h", ctypes.c_int), ("h", ctypes.c_int),
# x component offset compared to the whole image # x component offset compared to the whole image
# y component offset compared to the whole image # y component offset compared to the whole image
("x0", ctypes.c_int), ("x0", ctypes.c_int),
("y0", ctypes.c_int), ("y0", ctypes.c_int),
# precision # precision
('prec', ctypes.c_int), ('prec', ctypes.c_int),
# image depth in bits # image depth in bits
('bpp', ctypes.c_int), ('bpp', ctypes.c_int),
# signed (1) / unsigned (0) # signed (1) / unsigned (0)
('sgnd', ctypes.c_int)] ('sgnd', ctypes.c_int)]
class ImageCompType(ctypes.Structure): class ImageCompType(ctypes.Structure):
"""Defines a single image component. """ """Defines a single image component. """
_fields_ = [("dx", ctypes.c_int), _fields_ = [("dx", ctypes.c_int),
("dy", ctypes.c_int), ("dy", ctypes.c_int),
("w", ctypes.c_int), ("w", ctypes.c_int),
("h", ctypes.c_int), ("h", ctypes.c_int),
("x0", ctypes.c_int), ("x0", ctypes.c_int),
("y0", ctypes.c_int), ("y0", ctypes.c_int),
("prec", ctypes.c_int), ("prec", ctypes.c_int),
("bpp", ctypes.c_int), ("bpp", ctypes.c_int),
("sgnd", ctypes.c_int), ("sgnd", ctypes.c_int),
("resno_decoded", ctypes.c_int), ("resno_decoded", ctypes.c_int),
("factor", ctypes.c_int), ("factor", ctypes.c_int),
("data", ctypes.POINTER(ctypes.c_int))] ("data", ctypes.POINTER(ctypes.c_int))]
class ImageType(ctypes.Structure): class ImageType(ctypes.Structure):
@ -452,7 +468,6 @@ def cio_tell(cio):
pos = OPENJPEG.cio_tell(cio) pos = OPENJPEG.cio_tell(cio)
return pos return pos
def create_compress(fmt): def create_compress(fmt):
"""Wrapper for openjpeg library function opj_create_compress. """Wrapper for openjpeg library function opj_create_compress.
@ -523,11 +538,56 @@ def destroy_decompress(dinfo):
OPENJPEG.opj_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): def image_create(cmptparms, cspace):
"""Wrapper for openjpeg library function opj_image_create. """Wrapper for openjpeg library function opj_image_create.
""" """
lst = [ctypes.c_int, ctypes.POINTER(ImageComptParmType), ctypes.c_int] OPENJPEG.opj_image_create.argtypes = [ctypes.c_int,
OPENJPEG.opj_image_create.argtypes = lst ctypes.POINTER(ImageComptParmType),
ctypes.c_int]
OPENJPEG.opj_image_create.restype = ctypes.POINTER(ImageType) OPENJPEG.opj_image_create.restype = ctypes.POINTER(ImageType)
image = OPENJPEG.opj_image_create(len(cmptparms), cmptparms, cspace) image = OPENJPEG.opj_image_create(len(cmptparms), cmptparms, cspace)

View file

@ -127,18 +127,3 @@ default_image_component_parameters = """<class 'glymur.lib.openjp2.ImageComptPar
prec: 0 prec: 0
bpp: 0 bpp: 0
sgnd: 0""" sgnd: 0"""
# The "icc_profile_buf" field is problematic as it is a pointer value, i.e.
#
# icc_profile_buf: <glymur.lib.openjp2.LP_c_ubyte object at 0x7f28cd5d5d90>
#
# Have to treat it as a regular expression.
default_image_type = """<class 'glymur.lib.openjp2.ImageType'>:
x0: 0
y0: 0
x1: 0
y1: 0
numcomps: 0
color_space: 0
icc_profile_buf: <glymur.lib.openjp2.LP_c_ubyte object at 0x[0-9A-Fa-f]*>
icc_profile_len: 0"""

View file

@ -1,8 +1,13 @@
""" """
Tests for libopenjp2 wrapping functions. 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 os
import re import re
import sys
import tempfile import tempfile
import unittest import unittest
@ -16,8 +21,8 @@ from glymur.lib import openjp2
@unittest.skipIf(openjp2.OPENJP2 is None, @unittest.skipIf(openjp2.OPENJP2 is None,
"Missing openjp2 library.") "Missing openjp2 library.")
@unittest.skipIf(re.match(r'''(1|2.0)''', @unittest.skipIf(re.match(r'''(1|2.0)''',
glymur.version.openjpeg_version) is not None, glymur.version.openjpeg_version) is not None,
"Not to be run until 2.1.0") "Not to be run until 2.1.0")
class TestOpenJP2(unittest.TestCase): class TestOpenJP2(unittest.TestCase):
"""Test openjp2 library functionality. """Test openjp2 library functionality.
@ -51,6 +56,53 @@ class TestOpenJP2(unittest.TestCase):
self.assertEqual(dparams.DA_x1, 0) self.assertEqual(dparams.DA_x1, 0)
self.assertEqual(dparams.DA_y1, 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): def test_tte0(self):
"""Runs test designated tte0 in OpenJPEG test suite.""" """Runs test designated tte0 in OpenJPEG test suite."""
with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile: with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile:
@ -108,6 +160,15 @@ class TestOpenJP2(unittest.TestCase):
tile_decoder(**kwargs) tile_decoder(**kwargs)
self.assertTrue(True) 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): def test_tte2(self):
"""Runs test designated tte2 in OpenJPEG test suite.""" """Runs test designated tte2 in OpenJPEG test suite."""
with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile: with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile:
@ -129,25 +190,62 @@ class TestOpenJP2(unittest.TestCase):
tile_decoder(**kwargs) tile_decoder(**kwargs)
self.assertTrue(True) 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): def test_tte3(self):
"""Runs test designated tte3 in OpenJPEG test suite.""" """Runs test designated tte3 in OpenJPEG test suite."""
with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile: with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile:
xtx3_setup(tfile.name) xtx3_setup(tfile.name)
self.assertTrue(True) 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): def test_tte4(self):
"""Runs test designated tte4 in OpenJPEG test suite.""" """Runs test designated tte4 in OpenJPEG test suite."""
with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile: with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile:
xtx4_setup(tfile.name) xtx4_setup(tfile.name)
self.assertTrue(True) 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): def test_tte5(self):
"""Runs test designated tte5 in OpenJPEG test suite.""" """Runs test designated tte5 in OpenJPEG test suite."""
with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile: with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile:
xtx5_setup(tfile.name) xtx5_setup(tfile.name)
self.assertTrue(True) 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): def tile_encoder(**kwargs):
"""Fixture used by many tests.""" """Fixture used by many tests."""
num_tiles = ((kwargs['image_width'] / kwargs['tile_width']) * num_tiles = ((kwargs['image_width'] / kwargs['tile_width']) *
@ -211,7 +309,7 @@ def tile_encoder(**kwargs):
openjp2.setup_encoder(codec, l_param, l_image) openjp2.setup_encoder(codec, l_param, l_image)
stream = openjp2.stream_create_default_file_stream(kwargs['filename'], stream = openjp2.stream_create_default_file_stream(kwargs['filename'],
False) False)
openjp2.start_compress(codec, l_image, stream) openjp2.start_compress(codec, l_image, stream)
for j in np.arange(num_tiles): for j in np.arange(num_tiles):
@ -222,14 +320,13 @@ def tile_encoder(**kwargs):
openjp2.destroy_codec(codec) openjp2.destroy_codec(codec)
openjp2.image_destroy(l_image) openjp2.image_destroy(l_image)
def tile_decoder(**kwargs): def tile_decoder(**kwargs):
"""Fixture called with various configurations by many tests. """Fixture called with various configurations by many tests.
Reads a tile. That's all it does. Reads a tile. That's all it does.
""" """
stream = openjp2.stream_create_default_file_stream(kwargs['filename'], stream = openjp2.stream_create_default_file_stream(kwargs['filename'],
True) True)
dparam = openjp2.set_default_decoder_parameters() dparam = openjp2.set_default_decoder_parameters()
dparam.decod_format = kwargs['codec_format'] dparam.decod_format = kwargs['codec_format']
@ -267,7 +364,6 @@ def tile_decoder(**kwargs):
openjp2.stream_destroy(stream) openjp2.stream_destroy(stream)
openjp2.image_destroy(image) openjp2.image_destroy(image)
def ttx0_setup(filename): def ttx0_setup(filename):
"""Runs tests tte0, tte0.""" """Runs tests tte0, tte0."""
kwargs = {'filename': filename, kwargs = {'filename': filename,
@ -281,7 +377,6 @@ def ttx0_setup(filename):
'tile_width': 100} 'tile_width': 100}
tile_encoder(**kwargs) tile_encoder(**kwargs)
def xtx2_setup(filename): def xtx2_setup(filename):
"""Runs tests rta2, tte2, ttd2.""" """Runs tests rta2, tte2, ttd2."""
kwargs = {'filename': filename, kwargs = {'filename': filename,
@ -295,7 +390,6 @@ def xtx2_setup(filename):
'tile_width': 128} 'tile_width': 128}
tile_encoder(**kwargs) tile_encoder(**kwargs)
def xtx3_setup(filename): def xtx3_setup(filename):
"""Runs tests tte3, rta3.""" """Runs tests tte3, rta3."""
kwargs = {'filename': filename, kwargs = {'filename': filename,
@ -309,7 +403,6 @@ def xtx3_setup(filename):
'tile_width': 128} 'tile_width': 128}
tile_encoder(**kwargs) tile_encoder(**kwargs)
def xtx4_setup(filename): def xtx4_setup(filename):
"""Runs tests rta4, tte4.""" """Runs tests rta4, tte4."""
kwargs = {'filename': filename, kwargs = {'filename': filename,
@ -323,7 +416,6 @@ def xtx4_setup(filename):
'tile_width': 128} 'tile_width': 128}
tile_encoder(**kwargs) tile_encoder(**kwargs)
def xtx5_setup(filename): def xtx5_setup(filename):
"""Runs tests rta5, tte5.""" """Runs tests rta5, tte5."""
kwargs = {'filename': filename, kwargs = {'filename': filename,
@ -336,3 +428,6 @@ def xtx5_setup(filename):
'tile_height': 256, 'tile_height': 256,
'tile_width': 256} 'tile_width': 256}
tile_encoder(**kwargs) tile_encoder(**kwargs)
if __name__ == "__main__":
unittest.main()

View file

@ -1,6 +1,8 @@
""" """
Tests for OpenJPEG module. Tests for OpenJPEG module.
""" """
# pylint: disable=E1101,R0904
import ctypes import ctypes
import re import re
import sys import sys
@ -8,7 +10,6 @@ import unittest
import glymur import glymur
@unittest.skipIf(glymur.lib.openjpeg.OPENJPEG is None, @unittest.skipIf(glymur.lib.openjpeg.OPENJPEG is None,
"Missing openjpeg library.") "Missing openjpeg library.")
class TestOpenJPEG(unittest.TestCase): class TestOpenJPEG(unittest.TestCase):

View file

@ -16,9 +16,8 @@ else:
import glymur import glymur
from . import fixtures from . import fixtures
@unittest.skipIf(sys.hexversion < 0x03000000, "do not care about 2.7 here") @unittest.skipIf(sys.hexversion < 0x03000000, "do not care about 2.7 here")
@unittest.skipIf(re.match('0|1|2.0', glymur.version.openjpeg_version), @unittest.skipIf(re.match('1|2.0', glymur.version.openjpeg_version),
"Requires openjpeg 2.1.0 or higher") "Requires openjpeg 2.1.0 or higher")
class TestPrintingOpenjp2(unittest.TestCase): class TestPrintingOpenjp2(unittest.TestCase):
"""Tests for verifying how printing works on openjp2 library structures.""" """Tests for verifying how printing works on openjp2 library structures."""
@ -64,12 +63,5 @@ class TestPrintingOpenjp2(unittest.TestCase):
expected = fixtures.default_image_component_parameters expected = fixtures.default_image_component_parameters
self.assertEqual(actual, expected) 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)

View file

@ -13,14 +13,6 @@ import six
import glymur import glymur
# If openjpeg is not installed, many tests cannot be run.
if glymur.version.openjpeg_version == '0.0.0':
OPENJPEG_NOT_AVAILABLE = True
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. # Some versions of "six" on python3 cause problems when verifying warnings.
# Only use when the version is 1.7 or higher. # Only use when the version is 1.7 or higher.
# And moreover, we only test using the 3.x infrastructure, never on 2.x. # And moreover, we only test using the 3.x infrastructure, never on 2.x.
@ -37,7 +29,6 @@ elif re.match('1.[0-6]', six.__version__) is not None:
# Cannot reopen a named temporary file in windows. # Cannot reopen a named temporary file in windows.
WINDOWS_TMP_FILE_MSG = "cannot use NamedTemporaryFile like this in windows" WINDOWS_TMP_FILE_MSG = "cannot use NamedTemporaryFile like this in windows"
class MetadataBase(unittest.TestCase): class MetadataBase(unittest.TestCase):
""" """
Base class for testing metadata. Base class for testing metadata.
@ -102,8 +93,8 @@ class MetadataBase(unittest.TestCase):
""" """
verify the fields of a RGN segment verify the fields of a RGN segment
""" """
self.assertEqual(actual.crgn, expected.crgn) # 0 = component self.assertEqual(actual.crgn, expected.crgn) # 0 = component
self.assertEqual(actual.srgn, expected.srgn) # 0 = implicit self.assertEqual(actual.srgn, expected.srgn) # 0 = implicit
self.assertEqual(actual.sprgn, expected.sprgn) self.assertEqual(actual.sprgn, expected.sprgn)
def verifySOTsegment(self, actual, expected): def verifySOTsegment(self, actual, expected):
@ -126,9 +117,8 @@ class MetadataBase(unittest.TestCase):
""" """
Verify the fields of the SIZ segment. Verify the fields of the SIZ segment.
""" """
for field in ['rsiz', 'xsiz', 'ysiz', 'xosiz', 'yosiz', 'xtsiz', for field in ['rsiz', 'xsiz', 'ysiz', 'xosiz', 'yosiz', 'xtsiz',
'ytsiz', 'xtosiz', 'ytosiz', 'bitdepth', 'ytsiz', 'xtosiz', 'ytosiz', 'bitdepth', 'xrsiz', 'yrsiz']:
'xrsiz', 'yrsiz']:
self.assertEqual(getattr(actual, field), getattr(expected, field)) self.assertEqual(getattr(actual, field), getattr(expected, field))
def verifyImageHeaderBox(self, box1, box2): def verifyImageHeaderBox(self, box1, box2):
@ -155,7 +145,7 @@ class MetadataBase(unittest.TestCase):
else: else:
self.assertEqual(actual.colorspace, expected.colorspace) self.assertEqual(actual.colorspace, expected.colorspace)
self.assertIsNone(actual.icc_profile) self.assertIsNone(actual.icc_profile)
# The Python XMP Toolkit may be used for XMP UUIDs, but only if available and # The Python XMP Toolkit may be used for XMP UUIDs, but only if available and
# if the version is at least 2.0.0. # if the version is at least 2.0.0.
@ -185,7 +175,7 @@ except:
# The Cinema2K/4K tests seem to need the freeimage backend to skimage.io # 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 # 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 # 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 NO_SKIMAGE_FREEIMAGE_SUPPORT = False
try: try:
import skimage import skimage
@ -213,7 +203,7 @@ def _indent(textstr):
String to be indented. String to be indented.
indent_level : str indent_level : str
Number of spaces of indentation to add. Number of spaces of indentation to add.
Returns Returns
------- -------
indented_string : str indented_string : str
@ -236,6 +226,7 @@ try:
# The whole point of trying to import PIL is to determine if it's there # The whole point of trying to import PIL is to determine if it's there
# or not. We won't use it directly. # or not. We won't use it directly.
# pylint: disable=F0401,W0611
import PIL import PIL
NO_READ_BACKEND = False NO_READ_BACKEND = False
@ -545,7 +536,7 @@ text_gbr_34 = """Colour Specification Box (colr) @ (179, 1339)
dump = r'''JPEG 2000 Signature Box (jP ) @ (0, 12) dump = r'''JPEG 2000 Signature Box (jP ) @ (0, 12)
Signature: 0d0a870a Signature: 0d0a870a
File Type Box (ftyp) @ (12, 20) File Type Box (ftyp) @ (12, 20)
Brand: jp2 Brand: jp2
Compatibility: ['jp2 '] Compatibility: ['jp2 ']
JP2 Header Box (jp2h) @ (32, 45) JP2 Header Box (jp2h) @ (32, 45)
Image Header Box (ihdr) @ (40, 22) Image Header Box (ihdr) @ (40, 22)
@ -601,6 +592,7 @@ Contiguous Codestream Box (jp2c) @ (3223, 1132296)
"Created by OpenJPEG version 2.0.0"''' "Created by OpenJPEG version 2.0.0"'''
nemo_with_codestream_header = dump.format(_indent(nemo_xmp)) 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) nemo_dump_short = r"""JPEG 2000 Signature Box (jP ) @ (0, 12)
File Type Box (ftyp) @ (12, 20) File Type Box (ftyp) @ (12, 20)
@ -613,7 +605,7 @@ Contiguous Codestream Box (jp2c) @ (3223, 1132296)"""
nemo_dump_no_xml = '''JPEG 2000 Signature Box (jP ) @ (0, 12) nemo_dump_no_xml = '''JPEG 2000 Signature Box (jP ) @ (0, 12)
Signature: 0d0a870a Signature: 0d0a870a
File Type Box (ftyp) @ (12, 20) File Type Box (ftyp) @ (12, 20)
Brand: jp2 Brand: jp2
Compatibility: ['jp2 '] Compatibility: ['jp2 ']
JP2 Header Box (jp2h) @ (32, 45) JP2 Header Box (jp2h) @ (32, 45)
Image Header Box (ihdr) @ (40, 22) Image Header Box (ihdr) @ (40, 22)
@ -669,7 +661,7 @@ Contiguous Codestream Box (jp2c) @ (3223, 1132296)
dump = r"""JPEG 2000 Signature Box (jP ) @ (0, 12) dump = r"""JPEG 2000 Signature Box (jP ) @ (0, 12)
Signature: 0d0a870a Signature: 0d0a870a
File Type Box (ftyp) @ (12, 20) File Type Box (ftyp) @ (12, 20)
Brand: jp2 Brand: jp2
Compatibility: ['jp2 '] Compatibility: ['jp2 ']
JP2 Header Box (jp2h) @ (32, 45) JP2 Header Box (jp2h) @ (32, 45)
Image Header Box (ihdr) @ (40, 22) Image Header Box (ihdr) @ (40, 22)
@ -691,26 +683,6 @@ nemo_dump_no_codestream = dump.format(_indent(nemo_xmp))
nemo_dump_no_codestream_no_xml = r"""JPEG 2000 Signature Box (jP ) @ (0, 12) nemo_dump_no_codestream_no_xml = r"""JPEG 2000 Signature Box (jP ) @ (0, 12)
Signature: 0d0a870a 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) File Type Box (ftyp) @ (12, 20)
Brand: jp2 Brand: jp2
Compatibility: ['jp2 '] Compatibility: ['jp2 ']
@ -727,171 +699,7 @@ JP2 Header Box (jp2h) @ (32, 45)
Colorspace: sRGB Colorspace: sRGB
UUID Box (uuid) @ (77, 3146) UUID Box (uuid) @ (77, 3146)
UUID: be7acfcb-97a9-42e8-9c71-999491e3afac (XMP) UUID: be7acfcb-97a9-42e8-9c71-999491e3afac (XMP)
UUID Data: Contiguous Codestream Box (jp2c) @ (3223, 1132296)"""
<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
<ns0:xmpmeta xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:ns0="adobe:ns:meta/" xmlns:ns2="http://ns.adobe.com/xap/1.0/" xmlns:ns3="http://ns.adobe.com/tiff/1.0/" xmlns:ns4="http://ns.adobe.com/exif/1.0/" xmlns:ns5="http://ns.adobe.com/photoshop/1.0/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" ns0:xmptk="Exempi + XMP Core 5.1.2">
<rdf:RDF>
<rdf:Description rdf:about="">
<ns2:CreatorTool>Google</ns2:CreatorTool>
<ns2:CreateDate>2013-02-09T14:47:53</ns2:CreateDate>
</rdf:Description>
<rdf:Description rdf:about="">
<ns3:YCbCrPositioning>1</ns3:YCbCrPositioning>
<ns3:XResolution>72/1</ns3:XResolution>
<ns3:YResolution>72/1</ns3:YResolution>
<ns3:ResolutionUnit>2</ns3:ResolutionUnit>
<ns3:Make>HTC</ns3:Make>
<ns3:Model>HTC Glacier</ns3:Model>
<ns3:ImageWidth>2592</ns3:ImageWidth>
<ns3:ImageLength>1456</ns3:ImageLength>
<ns3:BitsPerSample>
<rdf:Seq>
<rdf:li>8</rdf:li>
<rdf:li>8</rdf:li>
<rdf:li>8</rdf:li>
</rdf:Seq>
</ns3:BitsPerSample>
<ns3:PhotometricInterpretation>2</ns3:PhotometricInterpretation>
<ns3:SamplesPerPixel>3</ns3:SamplesPerPixel>
<ns3:WhitePoint>
<rdf:Seq>
<rdf:li>1343036288/4294967295</rdf:li>
<rdf:li>1413044224/4294967295</rdf:li>
</rdf:Seq>
</ns3:WhitePoint>
<ns3:PrimaryChromaticities>
<rdf:Seq>
<rdf:li>2748779008/4294967295</rdf:li>
<rdf:li>1417339264/4294967295</rdf:li>
<rdf:li>1288490240/4294967295</rdf:li>
<rdf:li>2576980480/4294967295</rdf:li>
<rdf:li>644245120/4294967295</rdf:li>
<rdf:li>257698032/4294967295</rdf:li>
</rdf:Seq>
</ns3:PrimaryChromaticities>
</rdf:Description>
<rdf:Description rdf:about="">
<ns4:ColorSpace>1</ns4:ColorSpace>
<ns4:PixelXDimension>2528</ns4:PixelXDimension>
<ns4:PixelYDimension>1424</ns4:PixelYDimension>
<ns4:FocalLength>353/100</ns4:FocalLength>
<ns4:GPSAltitudeRef>0</ns4:GPSAltitudeRef>
<ns4:GPSAltitude>0/1</ns4:GPSAltitude>
<ns4:GPSMapDatum>WGS-84</ns4:GPSMapDatum>
<ns4:DateTimeOriginal>2013-02-09T14:47:53</ns4:DateTimeOriginal>
<ns4:ISOSpeedRatings>
<rdf:Seq>
<rdf:li>76</rdf:li>
</rdf:Seq>
</ns4:ISOSpeedRatings>
<ns4:ExifVersion>0220</ns4:ExifVersion>
<ns4:FlashpixVersion>0100</ns4:FlashpixVersion>
<ns4:ComponentsConfiguration>
<rdf:Seq>
<rdf:li>1</rdf:li>
<rdf:li>2</rdf:li>
<rdf:li>3</rdf:li>
<rdf:li>0</rdf:li>
</rdf:Seq>
</ns4:ComponentsConfiguration>
<ns4:GPSLatitude>42,20.56N</ns4:GPSLatitude>
<ns4:GPSLongitude>71,5.29W</ns4:GPSLongitude>
<ns4:GPSTimeStamp>2013-02-09T19:47:53Z</ns4:GPSTimeStamp>
<ns4:GPSProcessingMethod>NETWORK</ns4:GPSProcessingMethod>
</rdf:Description>
<rdf:Description rdf:about="">
<ns5:DateCreated>2013-02-09T14:47:53</ns5:DateCreated>
</rdf:Description>
<rdf:Description rdf:about="">
<dc:Creator>
<rdf:Seq>
<rdf:li>Glymur</rdf:li>
<rdf:li>Python XMP Toolkit</rdf:li>
</rdf:Seq>
</dc:Creator>
</rdf:Description>
</rdf:RDF>
</ns0:xmpmeta>
<?xpacket end="w"?>
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)"""
# Output of reader requirements printing for text_GBR.jp2 # Output of reader requirements printing for text_GBR.jp2
text_GBR_rreq = r"""Reader Requirements Box (rreq) @ (40, 109) text_GBR_rreq = r"""Reader Requirements Box (rreq) @ (40, 109)
@ -927,7 +735,7 @@ issue_183_colr = """Colour Specification Box (colr) @ (62, 12)
Method: restricted ICC profile Method: restricted ICC profile
Precedence: 0 Precedence: 0
ICC Profile: None""" ICC Profile: None"""
# Progression order is invalid. # Progression order is invalid.
issue_186_progression_order = """COD marker segment @ (174, 12) issue_186_progression_order = """COD marker segment @ (174, 12)
@ -1092,3 +900,4 @@ goodstuff_with_full_header = r"""Codestream:
Step size: [(0, 8), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), (0, 10)] Step size: [(0, 8), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), (0, 10)]
SOD marker segment @ (164, 0) SOD marker segment @ (164, 0)
EOC marker segment @ (115218, 0)""" EOC marker segment @ (115218, 0)"""

View file

@ -1,6 +1,12 @@
""" """
Test suite for openjpeg's callback functions. 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 os
import re import re
import sys import sys
@ -19,7 +25,8 @@ import glymur
from .fixtures import WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG from .fixtures import WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG
@unittest.skipIf(glymur.lib.openjp2.OPENJP2 is None,
"Missing openjp2 library.")
class TestCallbacks(unittest.TestCase): class TestCallbacks(unittest.TestCase):
"""Test suite for callbacks.""" """Test suite for callbacks."""
@ -30,60 +37,68 @@ class TestCallbacks(unittest.TestCase):
def tearDown(self): def tearDown(self):
pass pass
@unittest.skipIf(glymur.version.openjpeg_version[0] != '2',
"Missing openjp2 library.")
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG) @unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.") @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.""" """Verify messages printed when writing an image in verbose mode."""
j = glymur.Jp2k(self.jp2file) j = glymur.Jp2k(self.jp2file)
with self.assertWarns(UserWarning): with self.assertWarns(UserWarning):
tiledata = j.read(tile=0) tiledata = j.read(tile=0)
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
j = glymur.Jp2k(tfile.name, 'wb')
with patch('sys.stdout', new=StringIO()) as fake_out: 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] != '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)
actual = fake_out.getvalue().strip() actual = fake_out.getvalue().strip()
expected = '[INFO] tile number 1 / 1' expected = '[INFO] tile number 1 / 1'
self.assertEqual(actual, expected) self.assertEqual(actual, expected)
@unittest.skipIf(glymur.version.openjpeg_version[0] == '0',
"Missing openjpeg/openjp2 library.")
def test_info_callbacks_on_read(self): def test_info_callbacks_on_read(self):
"""stdio output when info callback handler is enabled""" """stdio output when info callback handler is enabled"""
# Verify that we get the expected stdio output when our internal info # Verify that we get the expected stdio output when our internal info
# callback handler is enabled. # callback handler is enabled.
jp2 = glymur.Jp2k(self.j2kfile) j = glymur.Jp2k(self.j2kfile)
with patch('sys.stdout', new=StringIO()) as fake_out: with patch('sys.stdout', new=StringIO()) as fake_out:
jp2.verbose = True j.read(rlevel=1, verbose=True, area=(0, 0, 200, 150))
jp2[::2, ::2]
actual = fake_out.getvalue().strip() actual = fake_out.getvalue().strip()
if glymur.version.openjpeg_version[0] == '2': lines = ['[INFO] Start to read j2k main header (0).',
lines = ['[INFO] Start to read j2k main header (0).', '[INFO] Main header has been correctly decoded.',
'[INFO] Main header has been correctly decoded.', '[INFO] Setting decoding area to 0,0,150,200',
'[INFO] Setting decoding area to 0,0,480,800', '[INFO] Header of tile 0 / 0 has been read.',
'[INFO] Header of tile 0 / 0 has been read.', '[INFO] Tile 1/1 has been decoded.',
'[INFO] Tile 1/1 has been decoded.', '[INFO] Image data has been updated with tile 1.']
'[INFO] Image data has been updated with tile 1.']
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+ regex = re.compile(r"""\[INFO\]\stile\s1\sof\s1\s+
\[INFO\]\s-\stiers-1\stook\s \[INFO\]\s-\stiers-1\stook\s
[0-9]+\.[0-9]+\ss\s+ [0-9]+\.[0-9]+\ss\s+
@ -93,7 +108,13 @@ class TestCallbacks(unittest.TestCase):
[0-9]+\.[0-9]+\ss""", [0-9]+\.[0-9]+\ss""",
re.VERBOSE) re.VERBOSE)
# assertRegex in Python 3.3 (python2.7/pylint issue)
# pylint: disable=E1101
if sys.hexversion <= 0x03020000: if sys.hexversion <= 0x03020000:
self.assertRegexpMatches(actual, regex) self.assertRegexpMatches(actual, regex)
else: else:
self.assertRegex(actual, regex) self.assertRegex(actual, regex)
if __name__ == "__main__":
unittest.main()

View file

@ -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()

View file

@ -1,8 +1,16 @@
"""These tests are for edge cases where OPENJPEG does not exist, but """These tests are for edge cases where OPENJPEG does not exist, but
OPENJP2 may be present in some form or other. OPENJP2 may be present in some form or other.
""" """
import contextlib # unittest doesn't work well with R0904.
import ctypes # 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 imp
import os import os
import sys import sys
@ -17,42 +25,10 @@ else:
import glymur import glymur
from glymur import Jp2k from glymur import Jp2k
from .fixtures import (WARNING_INFRASTRUCTURE_ISSUE, from .fixtures import (
WARNING_INFRASTRUCTURE_MSG, WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG,
WINDOWS_TMP_FILE_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)
@unittest.skipIf(sys.hexversion < 0x03020000, @unittest.skipIf(sys.hexversion < 0x03020000,
@ -89,6 +65,7 @@ class TestSuite(unittest.TestCase):
# Need to reliably recover the location of the openjp2 library, # Need to reliably recover the location of the openjp2 library,
# so using '_name' appears to be the only way to do it. # so using '_name' appears to be the only way to do it.
# pylint: disable=W0212
libloc = glymur.lib.openjp2.OPENJP2._name libloc = glymur.lib.openjp2.OPENJP2._name
line = 'openjp2: {0}\n'.format(libloc) line = 'openjp2: {0}\n'.format(libloc)
tfile.write(line) tfile.write(line)
@ -117,56 +94,17 @@ class TestSuite(unittest.TestCase):
with self.assertWarnsRegex(UserWarning, regex): with self.assertWarnsRegex(UserWarning, regex):
imp.reload(glymur.lib.openjp2) 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, @unittest.skipIf(glymur.lib.openjp2.OPENJP2 is None and
"Needs openjpeg before this test make sense.") glymur.lib.openjpeg.OPENJPEG is None,
@unittest.skipIf(openjpeg_not_found_by_ctypes(), "Missing openjp2 library.")
"OpenJPEG must be found before this test can work.") class TestConfig(unittest.TestCase):
@unittest.skipIf(os.name == "nt", WINDOWS_TMP_FILE_MSG) """Test suite for reading without proper library in place."""
def test_config_dir_but_no_config_file(self):
with tempfile.TemporaryDirectory() as tdir: def setUp(self):
configdir = os.path.join(tdir, 'glymur') self.jp2file = glymur.data.nemo()
os.mkdir(configdir) self.j2kfile = glymur.data.goodstuff()
with patch.dict('os.environ', {'XDG_CONFIG_HOME': tdir}):
# Should still be able to load openjpeg, despite the def tearDown(self):
# configuration file not being there pass
imp.reload(glymur.lib.openjpeg)
self.assertIsNotNone(glymur.lib.openjp2.OPENJPEG)
@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)

View file

@ -1,32 +1,34 @@
""" """
Test suite for warnings issued by glymur. Test suite for warnings issued by glymur.
""" """
# unittest doesn't work well with R0904.
# pylint: disable=R0904
import platform
import os import os
import re import re
import struct import struct
import sys
import tempfile import tempfile
import unittest import unittest
import six
from glymur import Jp2k from glymur import Jp2k
import glymur import glymur
from .fixtures import opj_data_file, OPJ_DATA_ROOT from .fixtures import opj_data_file, OPJ_DATA_ROOT
from .fixtures import WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG 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, @unittest.skipIf(OPJ_DATA_ROOT is None,
"OPJ_DATA_ROOT environment variable not set") "OPJ_DATA_ROOT environment variable not set")
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG) @unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
class TestWarnings(unittest.TestCase): class TestWarnings(unittest.TestCase):
"""Test suite for warnings issued by glymur.""" """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): def test_exceeded_box_length(self):
""" """
should warn if reading past end of a box should warn if reading past end of a box
@ -53,11 +55,12 @@ class TestWarnings(unittest.TestCase):
""" """
relpath = 'input/nonregression/issue188_beach_64bitsbox.jp2' relpath = 'input/nonregression/issue188_beach_64bitsbox.jp2'
jfile = opj_data_file(relpath) jfile = opj_data_file(relpath)
pattern = r"""Unrecognized\sbox\s\(b'XML\s'\)\sencountered.""" regex = re.compile(r"""Unrecognized\sbox\s\(b'XML\s'\)\sencountered.""",
regex = re.compile(pattern, re.VERBOSE) re.VERBOSE)
with self.assertWarnsRegex(UserWarning, regex): with self.assertWarnsRegex(UserWarning, regex):
Jp2k(jfile) Jp2k(jfile)
def test_NR_gdal_fuzzer_unchecked_numresolutions_dump(self): def test_NR_gdal_fuzzer_unchecked_numresolutions_dump(self):
""" """
Has an invalid number of resolutions. Has an invalid number of resolutions.
@ -69,7 +72,7 @@ class TestWarnings(unittest.TestCase):
\(\d+\)\.""", \(\d+\)\.""",
re.VERBOSE) re.VERBOSE)
with self.assertWarnsRegex(UserWarning, regex): with self.assertWarnsRegex(UserWarning, regex):
Jp2k(jfile).get_codestream() Jp2k(jfile)
@unittest.skipIf(re.match("1.5|2.0.0", glymur.version.openjpeg_version), @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") "Test not passing on 1.5.x, not introduced until 2.x")
@ -84,7 +87,7 @@ class TestWarnings(unittest.TestCase):
\(\d+\)\.""", \(\d+\)\.""",
re.VERBOSE) re.VERBOSE)
with self.assertWarnsRegex(UserWarning, regex): with self.assertWarnsRegex(UserWarning, regex):
Jp2k(jfile).get_codestream() Jp2k(jfile)
def test_NR_gdal_fuzzer_check_comp_dx_dy_jp2_dump(self): 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+""", dx=\d+,\s*dy=\d+""",
re.VERBOSE) re.VERBOSE)
with self.assertWarnsRegex(UserWarning, regex): 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): def test_NR_gdal_fuzzer_assert_in_opj_j2k_read_SQcd_SQcc_patch_jp2(self):
lst = ['input', 'nonregression', lst = ['input', 'nonregression',
@ -107,32 +110,53 @@ class TestWarnings(unittest.TestCase):
number\sof\scomponents\sis\sonly\s\d+""", number\sof\scomponents\sis\sonly\s\d+""",
re.VERBOSE) re.VERBOSE)
with self.assertWarnsRegex(UserWarning, regex): 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): def test_bad_rsiz(self):
"""Should warn if RSIZ is bad. Issue196""" """Should warn if RSIZ is bad. Issue196"""
filename = opj_data_file('input/nonregression/edf_c2_1002767.jp2') filename = opj_data_file('input/nonregression/edf_c2_1002767.jp2')
with self.assertWarnsRegex(UserWarning, 'Invalid profile'): with self.assertWarnsRegex(UserWarning, 'Invalid profile'):
Jp2k(filename).get_codestream() Jp2k(filename)
def test_bad_wavelet_transform(self): def test_bad_wavelet_transform(self):
"""Should warn if wavelet transform is bad. Issue195""" """Should warn if wavelet transform is bad. Issue195"""
filename = opj_data_file('input/nonregression/edf_c2_10025.jp2') filename = opj_data_file('input/nonregression/edf_c2_10025.jp2')
with self.assertWarnsRegex(UserWarning, 'Invalid wavelet transform'): with self.assertWarnsRegex(UserWarning, 'Invalid wavelet transform'):
Jp2k(filename).get_codestream() Jp2k(filename)
def test_invalid_progression_order(self): def test_invalid_progression_order(self):
"""Should still be able to parse even if prog order is invalid.""" """Should still be able to parse even if prog order is invalid."""
jfile = opj_data_file('input/nonregression/2977.pdf.asan.67.2198.jp2') jfile = opj_data_file('input/nonregression/2977.pdf.asan.67.2198.jp2')
with self.assertWarnsRegex(UserWarning, 'Invalid progression order'): with self.assertWarnsRegex(UserWarning, 'Invalid progression order'):
Jp2k(jfile).get_codestream() Jp2k(jfile)
def test_tile_height_is_zero(self): def test_tile_height_is_zero(self):
"""Zero tile height should not cause an exception.""" """Zero tile height should not cause an exception."""
filename = 'input/nonregression/2539.pdf.SIGFPE.706.1712.jp2' filename = opj_data_file('input/nonregression/2539.pdf.SIGFPE.706.1712.jp2')
filename = opj_data_file(filename)
with self.assertWarnsRegex(UserWarning, 'Invalid tile dimensions'): with self.assertWarnsRegex(UserWarning, 'Invalid tile dimensions'):
Jp2k(filename).get_codestream() Jp2k(filename)
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.") @unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
def test_unknown_marker_segment(self): def test_unknown_marker_segment(self):
@ -155,9 +179,9 @@ class TestWarnings(unittest.TestCase):
read_buffer = ifile.read() read_buffer = ifile.read()
tfile.write(read_buffer) tfile.write(read_buffer)
tfile.flush() tfile.flush()
with self.assertWarnsRegex(UserWarning, 'Unrecognized marker'): with self.assertWarnsRegex(UserWarning, 'Unrecognized marker'):
Jp2k(tfile.name).get_codestream() codestream = Jp2k(tfile.name).get_codestream()
if __name__ == "__main__": if __name__ == "__main__":

View file

@ -1,7 +1,13 @@
""" """
ICC profile tests. ICC profile tests.
""" """
# unittest doesn't work well with R0904.
# pylint: disable=R0904
import datetime import datetime
import os
import sys
import unittest import unittest
import numpy as np import numpy as np
@ -60,6 +66,11 @@ class TestICC(unittest.TestCase):
"""invalid ICC header data should cause UserWarning""" """invalid ICC header data should cause UserWarning"""
jfile = opj_data_file('input/nonregression/orb-blue10-lin-jp2.jp2') 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' regex = 'ICC profile header is corrupt'
with self.assertWarnsRegex(UserWarning, regex): with self.assertWarnsRegex(UserWarning, regex):
Jp2k(jfile) Jp2k(jfile)
if __name__ == "__main__":
unittest.main()

View file

@ -1,6 +1,18 @@
""" """
Test suite specifically targeting JP2 box layout. 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 doctest
import os import os
import re import re
@ -8,6 +20,7 @@ import shutil
import struct import struct
import sys import sys
import tempfile import tempfile
import uuid
from uuid import UUID from uuid import UUID
import unittest import unittest
@ -22,13 +35,15 @@ from glymur.jp2box import JPEG2000SignatureBox
from glymur.core import COLOR, OPACITY from glymur.core import COLOR, OPACITY
from glymur.core import RED, GREEN, BLUE, GREY, WHOLE_IMAGE from glymur.core import RED, GREEN, BLUE, GREY, WHOLE_IMAGE
from .fixtures import (WARNING_INFRASTRUCTURE_ISSUE, from .fixtures import (
WARNING_INFRASTRUCTURE_MSG, WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG,
WINDOWS_TMP_FILE_MSG, MetadataBase) WINDOWS_TMP_FILE_MSG, MetadataBase
)
try:
def docTearDown(doctest_obj): FORMAT_CORPUS_DATA_ROOT = os.environ['FORMAT_CORPUS_DATA_ROOT']
glymur.set_parseoptions(full_codestream=False) except KeyError:
FORMAT_CORPUS_DATA_ROOT = None
def load_tests(loader, tests, ignore): def load_tests(loader, tests, ignore):
@ -36,35 +51,31 @@ def load_tests(loader, tests, ignore):
if os.name == "nt": if os.name == "nt":
# Can't do it on windows, temporary file issue. # Can't do it on windows, temporary file issue.
return tests return tests
tests.addTests(doctest.DocTestSuite('glymur.jp2box', tests.addTests(doctest.DocTestSuite('glymur.jp2box'))
tearDown=docTearDown))
return tests return tests
@unittest.skipIf(os.name == "nt", WINDOWS_TMP_FILE_MSG) @unittest.skipIf(os.name == "nt", WINDOWS_TMP_FILE_MSG)
class TestDataEntryURL(unittest.TestCase): class TestDataEntryURL(unittest.TestCase):
"""Test suite for DataEntryURL boxes.""" """Test suite for DataEntryURL boxes."""
def setUp(self): def setUp(self):
self.jp2file = glymur.data.nemo() 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): def test_wrap_greyscale(self):
"""A single component should be wrapped as GREYSCALE.""" """A single component should be wrapped as GREYSCALE."""
j = Jp2k(self.jp2file) j = Jp2k(self.jp2file)
data = j[:] data = j.read()
red = data[:, :, 0] red = data[:, :, 0]
# Write it back out as a raw codestream. # Write it back out as a raw codestream.
with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile1: 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. # Ok, now rewrap it as JP2. The colorspace should be GREYSCALE.
with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile2: with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile2:
jp2 = j2k.wrap(tfile2.name) jp2 = j2k.wrap(tfile2.name)
self.assertEqual(jp2.box[2].box[1].colorspace, self.assertEqual(jp2.box[2].box[1].colorspace,
glymur.core.GREYSCALE) glymur.core.GREYSCALE)
def test_basic_url(self): def test_basic_url(self):
"""Just your most basic URL box.""" """Just your most basic URL box."""
@ -86,7 +97,7 @@ class TestDataEntryURL(unittest.TestCase):
self.assertEqual(jp22.box[4].url, url) self.assertEqual(jp22.box[4].url, url)
def test_null_termination(self): 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) jp2 = Jp2k(self.jp2file)
url = 'http://glymur.readthedocs.org' url = 'http://glymur.readthedocs.org'
@ -97,21 +108,18 @@ class TestDataEntryURL(unittest.TestCase):
jp22 = jp2.wrap(tfile.name, boxes=boxes) jp22 = jp2.wrap(tfile.name, boxes=boxes)
self.assertEqual(jp22.box[-1].length, 42) self.assertEqual(jp22.box[-1].length, 42)
# Go to the last box. Seek past the L, T, version, # Go to the last box. Seek past the L, T, version, and flag fields.
# and flag fields.
with open(tfile.name, 'rb') as fptr: with open(tfile.name, 'rb') as fptr:
fptr.seek(jp22.box[-1].offset + 4 + 4 + 1 + 3) fptr.seek(jp22.box[-1].offset + 4 + 4 + 1 + 3)
nbytes = (jp22.box[-1].offset + nbytes = jp22.box[-1].offset + jp22.box[-1].length - fptr.tell()
jp22.box[-1].length -
fptr.tell())
read_buffer = fptr.read(nbytes) read_buffer = fptr.read(nbytes)
read_url = read_buffer.decode('utf-8') read_url = read_buffer.decode('utf-8')
self.assertEqual(url + chr(0), read_url) self.assertEqual(url + chr(0), read_url)
@unittest.skipIf(re.match(r'''0|1|2.0.0''', @unittest.skipIf(re.match(r'''(1|2.0.0)''',
glymur.version.openjpeg_version) is not None, glymur.version.openjpeg_version) is not None,
"Not supported until 2.1") "Not supported until 2.1")
@unittest.skipIf(os.name == "nt", WINDOWS_TMP_FILE_MSG) @unittest.skipIf(os.name == "nt", WINDOWS_TMP_FILE_MSG)
@ -122,21 +130,24 @@ class TestChannelDefinition(unittest.TestCase):
def setUpClass(cls): def setUpClass(cls):
"""Need a one_plane plane image for greyscale testing.""" """Need a one_plane plane image for greyscale testing."""
j2k = Jp2k(glymur.data.goodstuff()) j2k = Jp2k(glymur.data.goodstuff())
data = j2k[:] data = j2k.read()
# Write the first component back out to file. # Write the first component back out to file.
with tempfile.NamedTemporaryFile(suffix=".j2k", delete=False) as tfile: 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 cls.one_plane = tfile.name
# Write the first two components back out to file. # Write the first two components back out to file.
with tempfile.NamedTemporaryFile(suffix=".j2k", delete=False) as tfile: 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 cls.two_planes = tfile.name
# Write four components back out to file. # Write four components back out to file.
with tempfile.NamedTemporaryFile(suffix=".j2k", delete=False) as tfile: with tempfile.NamedTemporaryFile(suffix=".j2k", delete=False) as tfile:
rgba_jp2 = Jp2k(tfile.name, 'wb')
shape = (data.shape[0], data.shape[1], 1) shape = (data.shape[0], data.shape[1], 1)
alpha = np.zeros((shape), dtype=data.dtype) alpha = np.zeros((shape), dtype=data.dtype)
data4 = np.concatenate((data, alpha), axis=2) data4 = np.concatenate((data, alpha), axis=2)
Jp2k(tfile.name, data=data4) rgba_jp2.write(data4)
cls.four_planes = tfile.name cls.four_planes = tfile.name
@classmethod @classmethod
@ -389,7 +400,7 @@ class TestFileTypeBox(unittest.TestCase):
ftyp = glymur.jp2box.FileTypeBox(brand='jp3') ftyp = glymur.jp2box.FileTypeBox(brand='jp3')
with self.assertRaises(IOError): with self.assertRaises(IOError):
with tempfile.TemporaryFile() as tfile: with tempfile.TemporaryFile() as tfile:
ftyp.write(tfile) ftyp.write(tfile)
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG) @unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_cl_entry_unknown(self): def test_cl_entry_unknown(self):
@ -399,8 +410,7 @@ class TestFileTypeBox(unittest.TestCase):
ftyp = glymur.jp2box.FileTypeBox(compatibility_list=['jp3']) ftyp = glymur.jp2box.FileTypeBox(compatibility_list=['jp3'])
with self.assertRaises(IOError): with self.assertRaises(IOError):
with tempfile.TemporaryFile() as tfile: with tempfile.TemporaryFile() as tfile:
ftyp.write(tfile) ftyp.write(tfile)
class TestColourSpecificationBox(unittest.TestCase): class TestColourSpecificationBox(unittest.TestCase):
"""Test suite for colr box instantiation.""" """Test suite for colr box instantiation."""
@ -523,8 +533,8 @@ class TestPaletteBox(unittest.TestCase):
bps = (8, 8, 8) bps = (8, 8, 8)
signed = (False, False) signed = (False, False)
with self.assertWarns(UserWarning): with self.assertWarns(UserWarning):
glymur.jp2box.PaletteBox(palette, bits_per_component=bps, pclr = glymur.jp2box.PaletteBox(palette, bits_per_component=bps,
signed=signed) signed=signed)
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG) @unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_mismatched_signed_palette(self): def test_mismatched_signed_palette(self):
@ -533,8 +543,8 @@ class TestPaletteBox(unittest.TestCase):
bps = (8, 8, 8, 8) bps = (8, 8, 8, 8)
signed = (False, False, False, False) signed = (False, False, False, False)
with self.assertWarns(UserWarning): with self.assertWarns(UserWarning):
glymur.jp2box.PaletteBox(palette, bits_per_component=bps, pclr = glymur.jp2box.PaletteBox(palette, bits_per_component=bps,
signed=signed) signed=signed)
def test_writing_with_different_bitdepths(self): def test_writing_with_different_bitdepths(self):
"""Bitdepths must be the same when writing.""" """Bitdepths must be the same when writing."""
@ -634,7 +644,7 @@ class TestAppend(unittest.TestCase):
jp2 = Jp2k(tfile.name) jp2 = Jp2k(tfile.name)
# Make a UUID box. Only XMP UUID boxes can currently be appended. # 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' data = b'0123456789'
uuidbox = glymur.jp2box.UUIDBox(uuid_instance, data) uuidbox = glymur.jp2box.UUIDBox(uuid_instance, data)
with self.assertRaises(IOError): with self.assertRaises(IOError):
@ -790,7 +800,7 @@ class TestWrap(unittest.TestCase):
# list to trigger the error. # list to trigger the error.
boxes[2].box = [] boxes[2].box = []
with self.assertRaises(IOError): with self.assertRaises(IOError):
jp2.wrap(tfile.name, boxes=boxes) jp22 = jp2.wrap(tfile.name, boxes=boxes)
def test_default_layout_with_boxes(self): def test_default_layout_with_boxes(self):
"""basic test for rewrapping a jp2 file, boxes specified""" """basic test for rewrapping a jp2 file, boxes specified"""
@ -855,8 +865,8 @@ class TestWrap(unittest.TestCase):
"""A palette box must reside in a JP2 header box.""" """A palette box must reside in a JP2 header box."""
palette = np.array([[255, 0, 255], [0, 255, 0]], dtype=np.int32) palette = np.array([[255, 0, 255], [0, 255, 0]], dtype=np.int32)
bps = (8, 8, 8) bps = (8, 8, 8)
pclr = glymur.jp2box.PaletteBox(palette=palette, signed = (True, False, True)
bits_per_component=bps, pclr = glymur.jp2box.PaletteBox(palette=palette, bits_per_component=bps,
signed=(True, False, True)) signed=(True, False, True))
j2k = Jp2k(self.j2kfile) j2k = Jp2k(self.j2kfile)
@ -968,8 +978,7 @@ class TestWrap(unittest.TestCase):
"""Rewrap a jpx file.""" """Rewrap a jpx file."""
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile1: with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile1:
jpx = Jp2k(self.jpxfile) jpx = Jp2k(self.jpxfile)
idx = (list(range(5)) + idx = list(range(5)) + list(range(9, 12)) + list(range(6, 9)) + [12]
list(range(9, 12)) + list(range(6, 9))) + [12]
boxes = [jpx.box[j] for j in idx] boxes = [jpx.box[j] for j in idx]
jpx2 = jpx.wrap(tfile1.name, boxes=boxes) jpx2 = jpx.wrap(tfile1.name, boxes=boxes)
exp_ids = [box.box_id for box in boxes] exp_ids = [box.box_id for box in boxes]
@ -1021,11 +1030,11 @@ class TestJp2Boxes(unittest.TestCase):
"""Raw instantiation should not produce a main_header.""" """Raw instantiation should not produce a main_header."""
box = ContiguousCodestreamBox() box = ContiguousCodestreamBox()
self.assertEqual(box.box_id, 'jp2c') self.assertEqual(box.box_id, 'jp2c')
self.assertIsNone(box.codestream) self.assertIsNone(box.main_header)
def test_codestream_main_header_offset(self): def test_codestream_main_header_offset(self):
"""main_header_offset is an attribute of the CCS box""" """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, self.assertEqual(j.box[5].main_header_offset,
j.box[5].offset + 8) j.box[5].offset + 8)
@ -1226,8 +1235,8 @@ class TestRepr(MetadataBase):
def test_uuidlist_box(self): def test_uuidlist_box(self):
"""Verify __repr__ method on ulst box.""" """Verify __repr__ method on ulst box."""
uuid1 = UUID('00000000-0000-0000-0000-000000000001') uuid1 = uuid.UUID('00000000-0000-0000-0000-000000000001')
uuid2 = UUID('00000000-0000-0000-0000-000000000002') uuid2 = uuid.UUID('00000000-0000-0000-0000-000000000002')
uuids = [uuid1, uuid2] uuids = [uuid1, uuid2]
ulst = glymur.jp2box.UUIDListBox(ulst=uuids) ulst = glymur.jp2box.UUIDListBox(ulst=uuids)
newbox = eval(repr(ulst)) newbox = eval(repr(ulst))
@ -1239,6 +1248,7 @@ class TestRepr(MetadataBase):
"""Verify Palette box repr.""" """Verify Palette box repr."""
palette = np.array([[255, 0, 1000], [0, 255, 0]], dtype=np.int32) palette = np.array([[255, 0, 1000], [0, 255, 0]], dtype=np.int32)
bps = (8, 8, 16) bps = (8, 8, 16)
signed = (True, False, True)
box = glymur.jp2box.PaletteBox(palette=palette, bits_per_component=bps, box = glymur.jp2box.PaletteBox(palette=palette, bits_per_component=bps,
signed=(True, False, True)) signed=(True, False, True))
@ -1281,15 +1291,14 @@ class TestRepr(MetadataBase):
def test_uuid_box_generic(self): def test_uuid_box_generic(self):
"""Verify uuid repr method.""" """Verify uuid repr method."""
uuid_instance = UUID('00000000-0000-0000-0000-000000000000') uuid_instance = uuid.UUID('00000000-0000-0000-0000-000000000000')
data = b'0123456789' data = b'0123456789'
box = glymur.jp2box.UUIDBox(the_uuid=uuid_instance, raw_data=data) box = glymur.jp2box.UUIDBox(the_uuid=uuid_instance, raw_data=data)
# Since the raw_data parameter is a sequence of bytes which could be # 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()). # quite long, don't bother trying to make it conform to eval(repr()).
regexp = r"""glymur.jp2box.UUIDBox\(""" regexp = r"""glymur.jp2box.UUIDBox\("""
regexp += """the_uuid=""" regexp += """the_uuid=UUID\('00000000-0000-0000-0000-000000000000'\),\s"""
regexp += """UUID\('00000000-0000-0000-0000-000000000000'\),\s"""
regexp += """raw_data=<byte\sarray\s10\selements>\)""" regexp += """raw_data=<byte\sarray\s10\selements>\)"""
if sys.hexversion < 0x03000000: if sys.hexversion < 0x03000000:
@ -1306,8 +1315,7 @@ class TestRepr(MetadataBase):
# Since the raw_data parameter is a sequence of bytes which could be # 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()). # quite long, don't bother trying to make it conform to eval(repr()).
regexp = r"""glymur.jp2box.UUIDBox\(""" regexp = r"""glymur.jp2box.UUIDBox\("""
regexp += """the_uuid=""" regexp += """the_uuid=UUID\('be7acfcb-97a9-42e8-9c71-999491e3afac'\),\s"""
regexp += """UUID\('be7acfcb-97a9-42e8-9c71-999491e3afac'\),\s"""
regexp += """raw_data=<byte\sarray\s3122\selements>\)""" regexp += """raw_data=<byte\sarray\s3122\selements>\)"""
if sys.hexversion < 0x03000000: if sys.hexversion < 0x03000000:
@ -1323,10 +1331,49 @@ class TestRepr(MetadataBase):
# Difficult to eval(repr()) this, so just match the general pattern. # Difficult to eval(repr()) this, so just match the general pattern.
regexp = "glymur.jp2box.ContiguousCodeStreamBox" regexp = "glymur.jp2box.ContiguousCodeStreamBox"
regexp += "[(]codestream=<glymur.codestream.Codestream\sobject\s" regexp += "[(]main_header=<glymur.codestream.Codestream\sobject\s"
regexp += "at\s0x([a-fA-F0-9]*)>[)]" regexp += "at\s0x([a-fA-F0-9]*)>[)]"
if sys.hexversion < 0x03000000: if sys.hexversion < 0x03000000:
self.assertRegexpMatches(repr(box), regexp) self.assertRegexpMatches(repr(box), regexp)
else: else:
self.assertRegex(repr(box), regexp) self.assertRegex(repr(box), regexp)
class TestJpxBoxes(unittest.TestCase):
"""Tests for JPX boxes."""
def setUp(self):
pass
def tearDown(self):
pass
@unittest.skipIf(FORMAT_CORPUS_DATA_ROOT is None,
"FORMAT_CORPUS_DATA_ROOT environment variable not set")
def test_codestream_header(self):
"""Should recognize codestream header box."""
jfile = os.path.join(FORMAT_CORPUS_DATA_ROOT,
'jp2k-formats/balloon.jpf')
jpx = Jp2k(jfile)
# This superbox just happens to be empty.
self.assertEqual(jpx.box[4].box_id, 'jpch')
self.assertEqual(len(jpx.box[4].box), 0)
@unittest.skipIf(FORMAT_CORPUS_DATA_ROOT is None,
"FORMAT_CORPUS_DATA_ROOT environment variable not set")
def test_compositing_layer_header(self):
"""Should recognize compositing layer header box."""
jfile = os.path.join(FORMAT_CORPUS_DATA_ROOT,
'jp2k-formats/balloon.jpf')
jpx = Jp2k(jfile)
# This superbox just happens to be empty.
self.assertEqual(jpx.box[5].box_id, 'jplh')
self.assertEqual(len(jpx.box[5].box), 0)
if __name__ == "__main__":
unittest.main()

View file

@ -6,6 +6,7 @@ Test suite specifically targeting JPX box layout.
import ctypes import ctypes
import os import os
import struct import struct
import sys
import tempfile import tempfile
import unittest import unittest
@ -19,7 +20,6 @@ from glymur.jp2box import ColourSpecificationBox
from .fixtures import WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG from .fixtures import WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.") @unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
class TestJPXWrap(unittest.TestCase): class TestJPXWrap(unittest.TestCase):
"""Test suite for wrapping JPX files.""" """Test suite for wrapping JPX files."""
@ -184,7 +184,7 @@ class TestJPXWrap(unittest.TestCase):
with tempfile.NamedTemporaryFile(suffix=".jpx") as tfile: with tempfile.NamedTemporaryFile(suffix=".jpx") as tfile:
with self.assertRaises(IOError): with self.assertRaises(IOError):
jp2.wrap(tfile.name, boxes=boxes) jpx = jp2.wrap(tfile.name, boxes=boxes)
def test_cgrp_neg(self): def test_cgrp_neg(self):
"""Can't write a cgrp with anything but colr sub boxes""" """Can't write a cgrp with anything but colr sub boxes"""
@ -204,7 +204,7 @@ class TestJPXWrap(unittest.TestCase):
with tempfile.NamedTemporaryFile(suffix=".jpx") as tfile: with tempfile.NamedTemporaryFile(suffix=".jpx") as tfile:
with self.assertRaises(IOError): with self.assertRaises(IOError):
jp2.wrap(tfile.name, boxes=boxes) jpx = jp2.wrap(tfile.name, boxes=boxes)
def test_ftbl(self): def test_ftbl(self):
"""Write a fragment table box.""" """Write a fragment table box."""
@ -484,7 +484,7 @@ class TestJPX(unittest.TestCase):
ftbl.write(tfile) ftbl.write(tfile)
def test_data_reference_requires_dtbl(self): def test_data_reference_requires_dtbl(self):
"""The existance of data reference box requires a ftbl box as well.""" """The existance of a data reference box requires a ftbl box as well."""
flag = 0 flag = 0
version = (0, 0, 0) version = (0, 0, 0)
url1 = 'file:////usr/local/bin' url1 = 'file:////usr/local/bin'
@ -574,7 +574,7 @@ class TestJPX(unittest.TestCase):
131072, 65536, 32768, 16384, 8192] 131072, 65536, 32768, 16384, 8192]
for j in range(len(standard_flags)): for j in range(len(standard_flags)):
mask = (standard_masks[j] >> 16, mask = (standard_masks[j] >> 16,
standard_masks[j] & 0x0000ffff >> 8, standard_masks[j] & 0x0000ffff>> 8,
standard_masks[j] & 0x000000ff) standard_masks[j] & 0x000000ff)
struct.pack_into('>HBBB', rreq_buffer, 17 + j * 5, struct.pack_into('>HBBB', rreq_buffer, 17 + j * 5,
standard_flags[j], *mask) standard_flags[j], *mask)
@ -599,6 +599,7 @@ class TestJPX(unittest.TestCase):
self.assertEqual(jpx.box[2].standard_flag, self.assertEqual(jpx.box[2].standard_flag,
(5, 42, 45, 2, 18, 19, 1, 8, 12, 31, 20)) (5, 42, 45, 2, 18, 19, 1, 8, 12, 31, 20))
def test_nlst(self): def test_nlst(self):
"""Verify that we can handle a number list box.""" """Verify that we can handle a number list box."""
j = Jp2k(self.jpxfile) j = Jp2k(self.jpxfile)

View file

@ -1,7 +1,17 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""Test suite for printing. """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 os
import re
import shutil import shutil
import struct import struct
import sys import sys
@ -13,18 +23,30 @@ if sys.hexversion < 0x02070000:
else: else:
import unittest 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 import lxml.etree
from .fixtures import (WARNING_INFRASTRUCTURE_ISSUE, from .fixtures import HAS_PYTHON_XMP_TOOLKIT, OPJ_DATA_ROOT
WARNING_INFRASTRUCTURE_MSG, from .fixtures import WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG
WINDOWS_TMP_FILE_MSG)
if HAS_PYTHON_XMP_TOOLKIT:
from libxmp import XMPMeta
import glymur import glymur
from glymur import Jp2k 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): class TestSuite(unittest.TestCase):
"""Tests for XMP, Exif UUIDs.""" """Tests for XMP, Exif UUIDs."""
@ -79,9 +101,8 @@ class TestSuite(unittest.TestCase):
jp2 = glymur.Jp2k(tfile.name) jp2 = glymur.Jp2k(tfile.name)
self.assertEqual(jp2.box[-1].data['Make'], "HTC") self.assertEqual(jp2.box[-1].data['Make'], "HTC")
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG) @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): class TestSuiteWarns(unittest.TestCase):
"""Tests for XMP, Exif UUIDs, issues warnings.""" """Tests for XMP, Exif UUIDs, issues warnings."""
@ -90,7 +111,7 @@ class TestSuiteWarns(unittest.TestCase):
def tearDown(self): def tearDown(self):
pass pass
def test_unrecognized_exif_tag(self): def test_unrecognized_exif_tag(self):
"""Verify warning in case of unrecognized tag.""" """Verify warning in case of unrecognized tag."""
with tempfile.NamedTemporaryFile(suffix='.jp2', mode='wb') as tfile: with tempfile.NamedTemporaryFile(suffix='.jp2', mode='wb') as tfile:
@ -114,7 +135,7 @@ class TestSuiteWarns(unittest.TestCase):
tfile.flush() tfile.flush()
with self.assertWarnsRegex(UserWarning, 'Unrecognized Exif tag'): with self.assertWarnsRegex(UserWarning, 'Unrecognized Exif tag'):
glymur.Jp2k(tfile.name) j = glymur.Jp2k(tfile.name)
def test_bad_tag_datatype(self): def test_bad_tag_datatype(self):
"""Only certain datatypes are allowable""" """Only certain datatypes are allowable"""
@ -170,3 +191,6 @@ class TestSuiteWarns(unittest.TestCase):
jp2 = glymur.Jp2k(tfile.name) jp2 = glymur.Jp2k(tfile.name)
self.assertEqual(jp2.box[-1].box_id, 'uuid') self.assertEqual(jp2.box[-1].box_id, 'uuid')
if __name__ == "__main__":
unittest.main()

View file

@ -2,12 +2,35 @@
""" """
Test suite specifically targeting JP2 box layout. 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 os
import re import re
import struct import struct
import sys
import tempfile import tempfile
import unittest 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 lxml.etree as ET
import glymur import glymur
@ -20,7 +43,6 @@ from .fixtures import OPJ_DATA_ROOT, opj_data_file
from .fixtures import WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG from .fixtures import WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG
from . import fixtures from . import fixtures
@unittest.skipIf(os.name == "nt", fixtures.WINDOWS_TMP_FILE_MSG) @unittest.skipIf(os.name == "nt", fixtures.WINDOWS_TMP_FILE_MSG)
class TestXML(unittest.TestCase): class TestXML(unittest.TestCase):
"""Test suite for XML boxes.""" """Test suite for XML boxes."""
@ -145,6 +167,7 @@ class TestXML(unittest.TestCase):
u'<country>Россия</country>') u'<country>Россия</country>')
class TestJp2kBadXmlFile(unittest.TestCase): class TestJp2kBadXmlFile(unittest.TestCase):
"""Test suite for bad XML box situations""" """Test suite for bad XML box situations"""
@ -270,19 +293,22 @@ class TestXML_OpjDataRoot(unittest.TestCase):
'nonregression', 'nonregression',
'issue171.jp2')) 'issue171.jp2'))
msg = 'An illegal BOM \(byte order marker\) was detected and removed ' 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)): with self.assertWarnsRegex(UserWarning, re.compile(msg)):
jp2 = Jp2k(filename) jp2 = Jp2k(filename)
self.assertIsNotNone(jp2.box[3].xml) self.assertIsNotNone(jp2.box[3].xml)
def test_invalid_utf8(self): def test_invalid_utf8(self):
"""Bad byte sequence that cannot be parsed.""" """Bad byte sequence that cannot be parsed."""
relname = '26ccf3651020967f7778238ef5af08af.SIGFPE.d25.527.jp2'
filename = opj_data_file(os.path.join('input', filename = opj_data_file(os.path.join('input',
'nonregression', 'nonregression',
relname)) '26ccf3651020967f7778238ef5af08af.SIGFPE.d25.527.jp2'))
with self.assertWarns((UserWarning, UserWarning)): with self.assertWarns((UserWarning, UserWarning)):
jp2 = Jp2k(filename) jp2 = Jp2k(filename)
self.assertIsNone(jp2.box[3].box[1].box[1].xml) self.assertIsNone(jp2.box[3].box[1].box[1].xml)

File diff suppressed because it is too large Load diff

View file

@ -2,10 +2,34 @@
The tests defined here roughly correspond to what is in the OpenJPEG test The tests defined here roughly correspond to what is in the OpenJPEG test
suite. suite.
""" """
# 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 re
import sys import sys
import unittest import unittest
import warnings
import numpy as np import numpy as np
@ -13,14 +37,13 @@ import glymur
from glymur import Jp2k from glymur import Jp2k
from glymur.jp2box import FileTypeBox, ImageHeaderBox, ColourSpecificationBox from glymur.jp2box import FileTypeBox, ImageHeaderBox, ColourSpecificationBox
from .fixtures import (OPJ_DATA_ROOT, MetadataBase, from .fixtures import (
WARNING_INFRASTRUCTURE_ISSUE, OPJ_DATA_ROOT, MetadataBase,
WARNING_INFRASTRUCTURE_MSG, WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG,
mse, peak_tolerance, read_pgx, opj_data_file, mse, peak_tolerance, read_pgx, opj_data_file
OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG) )
@unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG)
@unittest.skipIf(OPJ_DATA_ROOT is None, @unittest.skipIf(OPJ_DATA_ROOT is None,
"OPJ_DATA_ROOT environment variable not set") "OPJ_DATA_ROOT environment variable not set")
class TestSuite(unittest.TestCase): class TestSuite(unittest.TestCase):
@ -34,7 +57,7 @@ class TestSuite(unittest.TestCase):
def test_ETS_C1P0_p0_01_j2k(self): def test_ETS_C1P0_p0_01_j2k(self):
jfile = opj_data_file('input/conformance/p0_01.j2k') jfile = opj_data_file('input/conformance/p0_01.j2k')
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
jpdata = jp2k[:] jpdata = jp2k.read(rlevel=0)
pgxfile = opj_data_file('baseline/conformance/c1p0_01_0.pgx') pgxfile = opj_data_file('baseline/conformance/c1p0_01_0.pgx')
pgxdata = read_pgx(pgxfile) pgxdata = read_pgx(pgxfile)
@ -44,7 +67,7 @@ class TestSuite(unittest.TestCase):
def test_ETS_C1P0_p0_03_j2k(self): def test_ETS_C1P0_p0_03_j2k(self):
jfile = opj_data_file('input/conformance/p0_03.j2k') jfile = opj_data_file('input/conformance/p0_03.j2k')
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
jpdata = jp2k[:] jpdata = jp2k.read(rlevel=0)
pgxfile = opj_data_file('baseline/conformance/c1p0_03_0.pgx') pgxfile = opj_data_file('baseline/conformance/c1p0_03_0.pgx')
pgxdata = read_pgx(pgxfile) pgxdata = read_pgx(pgxfile)
@ -54,7 +77,7 @@ class TestSuite(unittest.TestCase):
def test_ETS_C1P0_p0_04_j2k(self): def test_ETS_C1P0_p0_04_j2k(self):
jfile = opj_data_file('input/conformance/p0_04.j2k') jfile = opj_data_file('input/conformance/p0_04.j2k')
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
jpdata = jp2k[:] jpdata = jp2k.read(rlevel=0)
pgxfile = opj_data_file('baseline/conformance/c1p0_04_0.pgx') pgxfile = opj_data_file('baseline/conformance/c1p0_04_0.pgx')
pgxdata = read_pgx(pgxfile) pgxdata = read_pgx(pgxfile)
@ -74,7 +97,7 @@ class TestSuite(unittest.TestCase):
def test_ETS_C1P0_p0_08_j2k(self): def test_ETS_C1P0_p0_08_j2k(self):
jfile = opj_data_file('input/conformance/p0_08.j2k') jfile = opj_data_file('input/conformance/p0_08.j2k')
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
jpdata = jp2k[::2, ::2] jpdata = jp2k.read(rlevel=1)
pgxfile = opj_data_file('baseline/conformance/c1p0_08_0.pgx') pgxfile = opj_data_file('baseline/conformance/c1p0_08_0.pgx')
pgxdata = read_pgx(pgxfile) pgxdata = read_pgx(pgxfile)
@ -91,7 +114,7 @@ class TestSuite(unittest.TestCase):
def test_ETS_C1P0_p0_09_j2k(self): def test_ETS_C1P0_p0_09_j2k(self):
jfile = opj_data_file('input/conformance/p0_09.j2k') jfile = opj_data_file('input/conformance/p0_09.j2k')
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
jpdata = jp2k[:] jpdata = jp2k.read(rlevel=0)
pgxfile = opj_data_file('baseline/conformance/c1p0_09_0.pgx') pgxfile = opj_data_file('baseline/conformance/c1p0_09_0.pgx')
pgxdata = read_pgx(pgxfile) pgxdata = read_pgx(pgxfile)
@ -100,7 +123,7 @@ class TestSuite(unittest.TestCase):
def test_ETS_C1P0_p0_11_j2k(self): def test_ETS_C1P0_p0_11_j2k(self):
jfile = opj_data_file('input/conformance/p0_11.j2k') jfile = opj_data_file('input/conformance/p0_11.j2k')
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
jpdata = jp2k[:] jpdata = jp2k.read(rlevel=0)
pgxfile = opj_data_file('baseline/conformance/c1p0_11_0.pgx') pgxfile = opj_data_file('baseline/conformance/c1p0_11_0.pgx')
pgxdata = read_pgx(pgxfile) pgxdata = read_pgx(pgxfile)
@ -109,7 +132,7 @@ class TestSuite(unittest.TestCase):
def test_ETS_C1P0_p0_14_j2k(self): def test_ETS_C1P0_p0_14_j2k(self):
jfile = opj_data_file('input/conformance/p0_14.j2k') jfile = opj_data_file('input/conformance/p0_14.j2k')
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
jpdata = jp2k[:] jpdata = jp2k.read(rlevel=0)
pgxfile = opj_data_file('baseline/conformance/c1p0_14_0.pgx') pgxfile = opj_data_file('baseline/conformance/c1p0_14_0.pgx')
pgxdata = read_pgx(pgxfile) pgxdata = read_pgx(pgxfile)
@ -126,7 +149,7 @@ class TestSuite(unittest.TestCase):
def test_ETS_C1P0_p0_15_j2k(self): def test_ETS_C1P0_p0_15_j2k(self):
jfile = opj_data_file('input/conformance/p0_15.j2k') jfile = opj_data_file('input/conformance/p0_15.j2k')
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
jpdata = jp2k[:] jpdata = jp2k.read(rlevel=0)
pgxfile = opj_data_file('baseline/conformance/c1p0_15_0.pgx') pgxfile = opj_data_file('baseline/conformance/c1p0_15_0.pgx')
pgxdata = read_pgx(pgxfile) pgxdata = read_pgx(pgxfile)
@ -135,7 +158,7 @@ class TestSuite(unittest.TestCase):
def test_ETS_C1P0_p0_16_j2k(self): def test_ETS_C1P0_p0_16_j2k(self):
jfile = opj_data_file('input/conformance/p0_16.j2k') jfile = opj_data_file('input/conformance/p0_16.j2k')
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
jpdata = jp2k[:] jpdata = jp2k.read(rlevel=0)
pgxfile = opj_data_file('baseline/conformance/c1p0_16_0.pgx') pgxfile = opj_data_file('baseline/conformance/c1p0_16_0.pgx')
pgxdata = read_pgx(pgxfile) pgxdata = read_pgx(pgxfile)
@ -144,7 +167,7 @@ class TestSuite(unittest.TestCase):
def test_ETS_C1P1_p1_01_j2k(self): def test_ETS_C1P1_p1_01_j2k(self):
jfile = opj_data_file('input/conformance/p1_01.j2k') jfile = opj_data_file('input/conformance/p1_01.j2k')
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
jpdata = jp2k[:] jpdata = jp2k.read(rlevel=0)
pgxfile = opj_data_file('baseline/conformance/c1p1_01_0.pgx') pgxfile = opj_data_file('baseline/conformance/c1p1_01_0.pgx')
pgxdata = read_pgx(pgxfile) pgxdata = read_pgx(pgxfile)
@ -153,7 +176,7 @@ class TestSuite(unittest.TestCase):
def test_ETS_C1P1_p1_02_j2k(self): def test_ETS_C1P1_p1_02_j2k(self):
jfile = opj_data_file('input/conformance/p1_02.j2k') jfile = opj_data_file('input/conformance/p1_02.j2k')
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
jpdata = jp2k[:] jpdata = jp2k.read(rlevel=0)
pgxfile = opj_data_file('baseline/conformance/c1p1_02_0.pgx') pgxfile = opj_data_file('baseline/conformance/c1p1_02_0.pgx')
pgxdata = read_pgx(pgxfile) pgxdata = read_pgx(pgxfile)
@ -173,7 +196,7 @@ class TestSuite(unittest.TestCase):
def test_ETS_C1P1_p1_04_j2k(self): def test_ETS_C1P1_p1_04_j2k(self):
jfile = opj_data_file('input/conformance/p1_04.j2k') jfile = opj_data_file('input/conformance/p1_04.j2k')
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
jpdata = jp2k[:] jpdata = jp2k.read()
pgxfile = opj_data_file('baseline/conformance/c1p1_04_0.pgx') pgxfile = opj_data_file('baseline/conformance/c1p1_04_0.pgx')
pgxdata = read_pgx(pgxfile) pgxdata = read_pgx(pgxfile)
@ -183,95 +206,95 @@ class TestSuite(unittest.TestCase):
def test_NR_DEC_Bretagne2_j2k_1_decode(self): def test_NR_DEC_Bretagne2_j2k_1_decode(self):
jfile = opj_data_file('input/nonregression/Bretagne2.j2k') jfile = opj_data_file('input/nonregression/Bretagne2.j2k')
jp2 = Jp2k(jfile) jp2 = Jp2k(jfile)
jp2[:] jp2.read()
self.assertTrue(True) self.assertTrue(True)
def test_NR_DEC__00042_j2k_2_decode(self): def test_NR_DEC__00042_j2k_2_decode(self):
jfile = opj_data_file('input/nonregression/_00042.j2k') jfile = opj_data_file('input/nonregression/_00042.j2k')
jp2 = Jp2k(jfile) jp2 = Jp2k(jfile)
jp2[:] jp2.read()
self.assertTrue(True) self.assertTrue(True)
def test_NR_DEC_buxI_j2k_9_decode(self): def test_NR_DEC_buxI_j2k_9_decode(self):
jfile = opj_data_file('input/nonregression/buxI.j2k') jfile = opj_data_file('input/nonregression/buxI.j2k')
Jp2k(jfile)[:] Jp2k(jfile).read()
self.assertTrue(True) self.assertTrue(True)
def test_NR_DEC_buxR_j2k_10_decode(self): def test_NR_DEC_buxR_j2k_10_decode(self):
jfile = opj_data_file('input/nonregression/buxR.j2k') jfile = opj_data_file('input/nonregression/buxR.j2k')
Jp2k(jfile)[:] Jp2k(jfile).read()
self.assertTrue(True) self.assertTrue(True)
def test_NR_DEC_Cannotreaddatawithnosizeknown_j2k_11_decode(self): def test_NR_DEC_Cannotreaddatawithnosizeknown_j2k_11_decode(self):
relpath = 'input/nonregression/Cannotreaddatawithnosizeknown.j2k' relpath = 'input/nonregression/Cannotreaddatawithnosizeknown.j2k'
jfile = opj_data_file(relpath) jfile = opj_data_file(relpath)
Jp2k(jfile)[:] Jp2k(jfile).read()
self.assertTrue(True) self.assertTrue(True)
def test_NR_DEC_cthead1_j2k_12_decode(self): def test_NR_DEC_cthead1_j2k_12_decode(self):
jfile = opj_data_file('input/nonregression/cthead1.j2k') jfile = opj_data_file('input/nonregression/cthead1.j2k')
Jp2k(jfile)[:] Jp2k(jfile).read()
self.assertTrue(True) self.assertTrue(True)
def test_NR_DEC_CT_Phillips_JPEG2K_Decompr_Problem_j2k_13_decode(self): def test_NR_DEC_CT_Phillips_JPEG2K_Decompr_Problem_j2k_13_decode(self):
relpath = 'input/nonregression/CT_Phillips_JPEG2K_Decompr_Problem.j2k' relpath = 'input/nonregression/CT_Phillips_JPEG2K_Decompr_Problem.j2k'
jfile = opj_data_file(relpath) jfile = opj_data_file(relpath)
Jp2k(jfile)[:] Jp2k(jfile).read()
self.assertTrue(True) self.assertTrue(True)
def test_NR_DEC_j2k32_j2k_15_decode(self): def test_NR_DEC_j2k32_j2k_15_decode(self):
jfile = opj_data_file('input/nonregression/j2k32.j2k') jfile = opj_data_file('input/nonregression/j2k32.j2k')
Jp2k(jfile)[:] Jp2k(jfile).read()
self.assertTrue(True) self.assertTrue(True)
def test_NR_DEC_MarkerIsNotCompliant_j2k_17_decode(self): def test_NR_DEC_MarkerIsNotCompliant_j2k_17_decode(self):
jfile = opj_data_file('input/nonregression/MarkerIsNotCompliant.j2k') jfile = opj_data_file('input/nonregression/MarkerIsNotCompliant.j2k')
Jp2k(jfile)[:] Jp2k(jfile).read()
self.assertTrue(True) self.assertTrue(True)
def test_NR_DEC_Marrin_jp2_18_decode(self): def test_NR_DEC_Marrin_jp2_18_decode(self):
jfile = opj_data_file('input/nonregression/Marrin.jp2') jfile = opj_data_file('input/nonregression/Marrin.jp2')
Jp2k(jfile)[:] Jp2k(jfile).read()
self.assertTrue(True) self.assertTrue(True)
def test_NR_DEC_movie_00000_j2k_20_decode(self): def test_NR_DEC_movie_00000_j2k_20_decode(self):
jfile = opj_data_file('input/nonregression/movie_00000.j2k') jfile = opj_data_file('input/nonregression/movie_00000.j2k')
Jp2k(jfile)[:] Jp2k(jfile).read()
self.assertTrue(True) self.assertTrue(True)
def test_NR_DEC_movie_00001_j2k_21_decode(self): def test_NR_DEC_movie_00001_j2k_21_decode(self):
jfile = opj_data_file('input/nonregression/movie_00001.j2k') jfile = opj_data_file('input/nonregression/movie_00001.j2k')
Jp2k(jfile)[:] Jp2k(jfile).read()
self.assertTrue(True) self.assertTrue(True)
def test_NR_DEC_movie_00002_j2k_22_decode(self): def test_NR_DEC_movie_00002_j2k_22_decode(self):
jfile = opj_data_file('input/nonregression/movie_00002.j2k') jfile = opj_data_file('input/nonregression/movie_00002.j2k')
Jp2k(jfile)[:] Jp2k(jfile).read()
self.assertTrue(True) self.assertTrue(True)
def test_NR_DEC_orb_blue_lin_j2k_j2k_23_decode(self): def test_NR_DEC_orb_blue_lin_j2k_j2k_23_decode(self):
jfile = opj_data_file('input/nonregression/orb-blue10-lin-j2k.j2k') jfile = opj_data_file('input/nonregression/orb-blue10-lin-j2k.j2k')
Jp2k(jfile)[:] Jp2k(jfile).read()
self.assertTrue(True) self.assertTrue(True)
def test_NR_DEC_orb_blue_win_j2k_j2k_24_decode(self): def test_NR_DEC_orb_blue_win_j2k_j2k_24_decode(self):
jfile = opj_data_file('input/nonregression/orb-blue10-win-j2k.j2k') jfile = opj_data_file('input/nonregression/orb-blue10-win-j2k.j2k')
Jp2k(jfile)[:] Jp2k(jfile).read()
self.assertTrue(True) self.assertTrue(True)
def test_NR_DEC_relax_jp2_27_decode(self): def test_NR_DEC_relax_jp2_27_decode(self):
jfile = opj_data_file('input/nonregression/relax.jp2') jfile = opj_data_file('input/nonregression/relax.jp2')
Jp2k(jfile)[:] Jp2k(jfile).read()
self.assertTrue(True) self.assertTrue(True)
def test_NR_DEC_test_lossless_j2k_28_decode(self): def test_NR_DEC_test_lossless_j2k_28_decode(self):
jfile = opj_data_file('input/nonregression/test_lossless.j2k') jfile = opj_data_file('input/nonregression/test_lossless.j2k')
Jp2k(jfile)[:] Jp2k(jfile).read()
self.assertTrue(True) self.assertTrue(True)
def test_NR_DEC_pacs_ge_j2k_30_decode(self): def test_NR_DEC_pacs_ge_j2k_30_decode(self):
jfile = opj_data_file('input/nonregression/pacs.ge.j2k') jfile = opj_data_file('input/nonregression/pacs.ge.j2k')
Jp2k(jfile)[:] Jp2k(jfile).read()
self.assertTrue(True) self.assertTrue(True)
@ -294,14 +317,14 @@ class TestSuiteWarns(MetadataBase):
with self.assertWarns(UserWarning): with self.assertWarns(UserWarning):
# Bad compatibility list item. # Bad compatibility list item.
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
jpdata = jp2k[:] jpdata = jp2k.read()
self.assertEqual(jpdata.shape, (512, 768, 3)) self.assertEqual(jpdata.shape, (512, 768, 3))
def test_ETS_JP2_file2(self): def test_ETS_JP2_file2(self):
jfile = opj_data_file('input/conformance/file2.jp2') jfile = opj_data_file('input/conformance/file2.jp2')
with self.assertWarns(UserWarning): with self.assertWarns(UserWarning):
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
jpdata = jp2k[:] jpdata = jp2k.read()
self.assertEqual(jpdata.shape, (640, 480, 3)) self.assertEqual(jpdata.shape, (640, 480, 3))
@unittest.skipIf(glymur.version.openjpeg_version_tuple[0] < 2, @unittest.skipIf(glymur.version.openjpeg_version_tuple[0] < 2,
@ -319,7 +342,7 @@ class TestSuiteWarns(MetadataBase):
jfile = opj_data_file('input/conformance/file4.jp2') jfile = opj_data_file('input/conformance/file4.jp2')
with self.assertWarns(UserWarning): with self.assertWarns(UserWarning):
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
jpdata = jp2k[:] jpdata = jp2k.read()
self.assertEqual(jpdata.shape, (512, 768)) self.assertEqual(jpdata.shape, (512, 768))
def test_ETS_JP2_file5(self): def test_ETS_JP2_file5(self):
@ -328,45 +351,42 @@ class TestSuiteWarns(MetadataBase):
# There's a warning for an unknown compatibility entry. # There's a warning for an unknown compatibility entry.
# Ignore it here. # Ignore it here.
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
jpdata = jp2k[:] jpdata = jp2k.read()
self.assertEqual(jpdata.shape, (512, 768, 3)) self.assertEqual(jpdata.shape, (512, 768, 3))
def test_ETS_JP2_file6(self): def test_ETS_JP2_file6(self):
jfile = opj_data_file('input/conformance/file6.jp2') jfile = opj_data_file('input/conformance/file6.jp2')
with self.assertWarns(UserWarning): with self.assertWarns(UserWarning):
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
jpdata = jp2k[:] jpdata = jp2k.read()
self.assertEqual(jpdata.shape, (512, 768)) self.assertEqual(jpdata.shape, (512, 768))
def test_ETS_JP2_file7(self): def test_ETS_JP2_file7(self):
jfile = opj_data_file('input/conformance/file7.jp2') jfile = opj_data_file('input/conformance/file7.jp2')
with self.assertWarns(UserWarning): with self.assertWarns(UserWarning):
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
jpdata = jp2k[:] jpdata = jp2k.read()
self.assertEqual(jpdata.shape, (640, 480, 3)) self.assertEqual(jpdata.shape, (640, 480, 3))
def test_ETS_JP2_file8(self): def test_ETS_JP2_file8(self):
jfile = opj_data_file('input/conformance/file8.jp2') jfile = opj_data_file('input/conformance/file8.jp2')
with self.assertWarns(UserWarning): with self.assertWarns(UserWarning):
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
jpdata = jp2k[:] jpdata = jp2k.read()
self.assertEqual(jpdata.shape, (400, 700)) self.assertEqual(jpdata.shape, (400, 700))
def test_ETS_JP2_file9(self): def test_ETS_JP2_file9(self):
jfile = opj_data_file('input/conformance/file9.jp2') jfile = opj_data_file('input/conformance/file9.jp2')
with self.assertWarns(UserWarning): with self.assertWarns(UserWarning):
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
jpdata = jp2k[:] jpdata = jp2k.read()
self.assertEqual(jpdata.shape, (512, 768, 3)) self.assertEqual(jpdata.shape, (512, 768, 3))
def test_NR_broken1_jp2_dump(self): def test_NR_broken_jp2_dump(self):
jfile = opj_data_file('input/nonregression/broken1.jp2') jfile = opj_data_file('input/nonregression/broken.jp2')
# The colr box has a ridiculously incorrect box length. with self.assertWarns(UserWarning):
regex = re.compile(r'''b'colr'\sbox\shas\sincorrect\sbox\slength\s # colr box has bad length.
\(\d+\)''',
re.VERBOSE)
with self.assertWarnsRegex(UserWarning, regex):
jp2 = Jp2k(jfile) jp2 = Jp2k(jfile)
ids = [box.box_id for box in jp2.box] ids = [box.box_id for box in jp2.box]
@ -385,24 +405,23 @@ class TestSuiteWarns(MetadataBase):
expected = ColourSpecificationBox(colorspace=glymur.core.SRGB) expected = ColourSpecificationBox(colorspace=glymur.core.SRGB)
self.verifyColourSpecificationBox(jp2.box[2].box[1], expected) 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] ids = [x.marker_id for x in c.segment]
expected = ['SOC', 'SIZ', 'CME', 'COD', 'QCD', 'QCC', 'QCC'] expected = ['SOC', 'SIZ', 'CME', 'COD', 'QCD', 'QCC', 'QCC']
self.assertEqual(ids, expected) self.assertEqual(ids, expected)
kwargs = {'rsiz': 0, 'xysiz': (203, 152), 'xyosiz': (0, 0), kwargs = {'rsiz': 0, 'xysiz': (203, 152), 'xyosiz': (0, 0),
'xytsiz': (203, 152), 'xytosiz': (0, 0), 'xytsiz': (203, 152), 'xytosiz': (0, 0), 'bitdepth': (8, 8, 8),
'bitdepth': (8, 8, 8), 'signed': (False, False, False),
'signed': (False, False, False), 'xyrsiz': [(1, 1, 1), (1, 1, 1)]}
'xyrsiz': [(1, 1, 1), (1, 1, 1)]}
self.verifySizSegment(c.segment[1], self.verifySizSegment(c.segment[1],
glymur.codestream.SIZsegment(**kwargs)) glymur.codestream.SIZsegment(**kwargs))
pargs = (glymur.core.RCME_ISO_8859_1, 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], self.verifyCMEsegment(c.segment[2],
glymur.codestream.CMEsegment(*pargs)) glymur.codestream.CMEsegment(*pargs))
# COD: Coding style default # COD: Coding style default
self.assertFalse(c.segment[3].scod & 2) # no sop self.assertFalse(c.segment[3].scod & 2) # no sop
@ -414,7 +433,7 @@ class TestSuiteWarns(MetadataBase):
self.assertEqual(tuple(c.segment[3].code_block_size), self.assertEqual(tuple(c.segment[3].code_block_size),
(64, 64)) # cblk (64, 64)) # cblk
self.verify_codeblock_style(c.segment[3].spcod[7], 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], self.assertEqual(c.segment[3].spcod[8],
glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) glymur.core.WAVELET_XFORM_5X3_REVERSIBLE)
self.assertEqual(len(c.segment[3].spcod), 9) self.assertEqual(len(c.segment[3].spcod), 9)
@ -450,19 +469,19 @@ class TestSuiteWarns(MetadataBase):
jfile = opj_data_file('input/nonregression/orb-blue10-lin-jp2.jp2') jfile = opj_data_file('input/nonregression/orb-blue10-lin-jp2.jp2')
with self.assertWarns(UserWarning): with self.assertWarns(UserWarning):
# This file has an invalid ICC profile # This file has an invalid ICC profile
Jp2k(jfile)[:] Jp2k(jfile).read()
self.assertTrue(True) self.assertTrue(True)
def test_NR_DEC_orb_blue_win_jp2_26_decode(self): def test_NR_DEC_orb_blue_win_jp2_26_decode(self):
jfile = opj_data_file('input/nonregression/orb-blue10-win-jp2.jp2') jfile = opj_data_file('input/nonregression/orb-blue10-win-jp2.jp2')
with self.assertWarns(UserWarning): with self.assertWarns(UserWarning):
Jp2k(jfile)[:] Jp2k(jfile).read()
self.assertTrue(True) self.assertTrue(True)
@unittest.skipIf(OPJ_DATA_ROOT is None, @unittest.skipIf(OPJ_DATA_ROOT is None,
"OPJ_DATA_ROOT environment variable not set") "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") "Feature not supported in glymur until openjpeg 2.0")
class TestSuiteBands(unittest.TestCase): class TestSuiteBands(unittest.TestCase):
""" """
@ -557,7 +576,7 @@ class TestSuiteBands(unittest.TestCase):
@unittest.skipIf(OPJ_DATA_ROOT is None, @unittest.skipIf(OPJ_DATA_ROOT is None,
"OPJ_DATA_ROOT environment variable not set") "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") "Tests not passing until 2.0")
class TestSuite2point0(unittest.TestCase): class TestSuite2point0(unittest.TestCase):
"""Runs tests introduced in version 2.0 or that pass only in 2.0""" """Runs tests introduced in version 2.0 or that pass only in 2.0"""
@ -571,7 +590,7 @@ class TestSuite2point0(unittest.TestCase):
def test_ETS_C1P0_p0_10_j2k(self): def test_ETS_C1P0_p0_10_j2k(self):
jfile = opj_data_file('input/conformance/p0_10.j2k') jfile = opj_data_file('input/conformance/p0_10.j2k')
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
jpdata = jp2k[:] jpdata = jp2k.read(rlevel=0)
pgxfile = opj_data_file('baseline/conformance/c1p0_10_0.pgx') pgxfile = opj_data_file('baseline/conformance/c1p0_10_0.pgx')
pgxdata = read_pgx(pgxfile) pgxdata = read_pgx(pgxfile)
@ -587,16 +606,13 @@ class TestSuite2point0(unittest.TestCase):
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG) @unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_NR_DEC_broken2_jp2_5_decode(self): def test_NR_DEC_broken2_jp2_5_decode(self):
""" # Null pointer access
Invalid marker ID on codestream, Null pointer access upon read.
"""
jfile = opj_data_file('input/nonregression/broken2.jp2') 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.assertRaises(IOError):
with self.assertWarnsRegex(UserWarning, regex): with self.assertWarns(UserWarning):
Jp2k(jfile)[:] # Invalid marker ID.
Jp2k(jfile).read()
self.assertTrue(True)
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG) @unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_NR_DEC_broken4_jp2_7_decode(self): def test_NR_DEC_broken4_jp2_7_decode(self):
@ -604,7 +620,7 @@ class TestSuite2point0(unittest.TestCase):
with self.assertRaises(IOError): with self.assertRaises(IOError):
with self.assertWarns(UserWarning): with self.assertWarns(UserWarning):
# invalid number of subbands, bad marker ID # invalid number of subbands, bad marker ID
Jp2k(jfile)[:] Jp2k(jfile).read()
self.assertTrue(True) self.assertTrue(True)
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG) @unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
@ -616,344 +632,11 @@ class TestSuite2point0(unittest.TestCase):
if glymur.version.openjpeg_version_tuple[0] < 2: if glymur.version.openjpeg_version_tuple[0] < 2:
with self.assertWarns(UserWarning): with self.assertWarns(UserWarning):
# Incorrect warning issued about tile parts. # Incorrect warning issued about tile parts.
Jp2k(jfile)[:] Jp2k(jfile).read()
else: else:
Jp2k(jfile)[:] Jp2k(jfile).read()
self.assertTrue(True) self.assertTrue(True)
@unittest.skipIf(OPJ_DATA_ROOT is None, if __name__ == "__main__":
"OPJ_DATA_ROOT environment variable not set") unittest.main()
@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)

View file

@ -0,0 +1,342 @@
"""
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 .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(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(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)

File diff suppressed because it is too large Load diff

View file

@ -2,8 +2,15 @@
The tests here do not correspond directly to the OpenJPEG test suite, but The tests here do not correspond directly to the OpenJPEG test suite, but
seem like logical negative tests to add. seem like logical negative tests to add.
""" """
# E1101: assertWarns introduced in python 3.2
# pylint: disable=E1101
# R0904: Not too many methods in unittest.
# pylint: disable=R0904
import os import os
import re import re
import sys
import tempfile import tempfile
import unittest 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_READ_BACKEND, NO_READ_BACKEND_MSG
from .fixtures import NO_SKIMAGE_FREEIMAGE_SUPPORT from .fixtures import NO_SKIMAGE_FREEIMAGE_SUPPORT
from .fixtures import WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG from .fixtures import WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG
from . import fixtures
from glymur import Jp2k from glymur import Jp2k
import glymur import glymur
@ -25,7 +31,7 @@ import glymur
@unittest.skipIf(OPJ_DATA_ROOT is None, @unittest.skipIf(OPJ_DATA_ROOT is None,
"OPJ_OPJ_DATA_ROOT environment variable not set") "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.""" """Test suite for certain negative tests from openjpeg suite."""
def setUp(self): def setUp(self):
@ -35,6 +41,33 @@ class TestSuiteNegativeRead(unittest.TestCase):
def tearDown(self): def tearDown(self):
pass 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): def test_nr_marker_not_compliant(self):
"""non-compliant marker, should still be able to read""" """non-compliant marker, should still be able to read"""
relpath = 'input/nonregression/MarkerIsNotCompliant.j2k' relpath = 'input/nonregression/MarkerIsNotCompliant.j2k'
@ -65,80 +98,56 @@ class TestSuiteNegativeRead(unittest.TestCase):
jp2k.get_codestream(header_only=False) jp2k.get_codestream(header_only=False)
self.assertTrue(True) self.assertTrue(True)
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
@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])
def test_code_block_dimensions(self): def test_code_block_dimensions(self):
"""don't allow extreme codeblock sizes""" """don't allow extreme codeblock sizes"""
# opj_compress doesn't allow the dimensions of a codeblock # opj_compress doesn't allow the dimensions of a codeblock
# to be too small or too big, so neither will we. # to be too small or too big, so neither will we.
data = np.zeros((256, 256), dtype=np.uint8) data = np.zeros((256, 256), dtype=np.uint8)
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
j = Jp2k(tfile.name, 'wb')
# opj_compress doesn't allow code block area to exceed 4096. # opj_compress doesn't allow code block area to exceed 4096.
with self.assertRaises(IOError): 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. # opj_compress doesn't allow either dimension to be less than 4.
with self.assertRaises(IOError): with self.assertRaises(IOError):
Jp2k(tfile.name, data=data, cbsize=(2048, 2)) j.write(data, cbsize=(2048, 2))
with self.assertRaises(IOError): 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): def test_precinct_size_not_p2(self):
"""precinct sizes should be powers of two.""" """precinct sizes should be powers of two."""
ifile = Jp2k(self.j2kfile) ifile = Jp2k(self.j2kfile)
data = ifile[::4, ::4] data = ifile.read(rlevel=2)
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
ofile = Jp2k(tfile.name, 'wb')
with self.assertRaises(IOError): 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): def test_cblk_size_not_power_of_two(self):
"""code block sizes should be powers of two.""" """code block sizes should be powers of two."""
ifile = Jp2k(self.j2kfile) ifile = Jp2k(self.j2kfile)
data = ifile[::4, ::4] data = ifile.read(rlevel=2)
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
ofile = Jp2k(tfile.name, 'wb')
with self.assertRaises(IOError): 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): def test_cblk_size_precinct_size(self):
"""code block sizes should never exceed half that of precinct size.""" """code block sizes should never exceed half that of precinct size."""
ifile = Jp2k(self.j2kfile) ifile = Jp2k(self.j2kfile)
data = ifile[::4, ::4] data = ifile.read(rlevel=2)
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
ofile = Jp2k(tfile.name, 'wb')
with self.assertRaises(IOError): 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()

View file

@ -2,17 +2,16 @@
The tests defined here roughly correspond to what is in the OpenJPEG test The tests defined here roughly correspond to what is in the OpenJPEG test
suite. suite.
""" """
# 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 os
import re import re
import sys import sys
import tempfile import tempfile
import unittest import unittest
if sys.hexversion <= 0x03030000:
from mock import patch
else:
from unittest.mock import patch
import numpy as np import numpy as np
try: try:
import skimage.io import skimage.io
@ -30,7 +29,6 @@ from glymur import Jp2k
from glymur.codestream import SIZsegment from glymur.codestream import SIZsegment
from glymur.version import openjpeg_version from glymur.version import openjpeg_version
class CinemaBase(fixtures.MetadataBase): class CinemaBase(fixtures.MetadataBase):
def verify_cinema_cod(self, cod_segment): 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.layers, 1)
self.assertEqual(cod_segment.spcod[3], 1) # mct self.assertEqual(cod_segment.spcod[3], 1) # mct
self.assertEqual(cod_segment.spcod[4], 5) # levels 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): def check_cinema4k_codestream(self, codestream, image_size):
kwargs = {'rsiz': 4, 'xysiz': image_size, 'xyosiz': (0, 0), kwargs = {'rsiz': 4, 'xysiz': image_size, 'xyosiz': (0, 0),
'xytsiz': image_size, 'xytosiz': (0, 0), 'xytsiz': image_size, 'xytosiz': (0, 0),
'bitdepth': (12, 12, 12), 'signed': (False, False, False), 'bitdepth': (12, 12, 12), 'signed': (False, False, False),
'xyrsiz': [(1, 1, 1), (1, 1, 1)]} 'xyrsiz': [(1, 1, 1), (1, 1, 1)]}
self.verifySizSegment(codestream.segment[1], SIZsegment(**kwargs)) self.verifySizSegment(codestream.segment[1], SIZsegment(**kwargs))
self.verify_cinema_cod(codestream.segment[2]) self.verify_cinema_cod(codestream.segment[2])
@ -56,9 +54,9 @@ class CinemaBase(fixtures.MetadataBase):
def check_cinema2k_codestream(self, codestream, image_size): def check_cinema2k_codestream(self, codestream, image_size):
kwargs = {'rsiz': 3, 'xysiz': image_size, 'xyosiz': (0, 0), kwargs = {'rsiz': 3, 'xysiz': image_size, 'xyosiz': (0, 0),
'xytsiz': image_size, 'xytosiz': (0, 0), 'xytsiz': image_size, 'xytosiz': (0, 0),
'bitdepth': (12, 12, 12), 'signed': (False, False, False), 'bitdepth': (12, 12, 12), 'signed': (False, False, False),
'xyrsiz': [(1, 1, 1), (1, 1, 1)]} 'xyrsiz': [(1, 1, 1), (1, 1, 1)]}
self.verifySizSegment(codestream.segment[1], SIZsegment(**kwargs)) self.verifySizSegment(codestream.segment[1], SIZsegment(**kwargs))
self.verify_cinema_cod(codestream.segment[2]) self.verify_cinema_cod(codestream.segment[2])
@ -84,9 +82,9 @@ class WriteCinema(CinemaBase):
infile = opj_data_file(relfile) infile = opj_data_file(relfile)
data = skimage.io.imread(infile) data = skimage.io.imread(infile)
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
j = Jp2k(tfile.name, 'wb')
with self.assertRaises(IOError): with self.assertRaises(IOError):
Jp2k(tfile.name, data=data, j.write(data, cinema2k=48, cratios=[200, 100, 50])
cinema2k=48, cratios=[200, 100, 50])
def test_cinema4K_with_others(self): def test_cinema4K_with_others(self):
"""Can't specify cinema4k with any other options.""" """Can't specify cinema4k with any other options."""
@ -94,9 +92,9 @@ class WriteCinema(CinemaBase):
infile = opj_data_file(relfile) infile = opj_data_file(relfile)
data = skimage.io.imread(infile) data = skimage.io.imread(infile)
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
j = Jp2k(tfile.name, 'wb')
with self.assertRaises(IOError): with self.assertRaises(IOError):
Jp2k(tfile.name, data=data, j.write(data, cinema4k=True, cratios=[200, 100, 50])
cinema4k=True, cratios=[200, 100, 50])
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG) @unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
@ -120,9 +118,10 @@ class WriteCinemaWarns(CinemaBase):
infile = opj_data_file(relfile) infile = opj_data_file(relfile)
data = skimage.io.imread(infile) data = skimage.io.imread(infile)
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
j = Jp2k(tfile.name, 'wb')
regex = 'OpenJPEG library warning:.*' regex = 'OpenJPEG library warning:.*'
with self.assertWarnsRegex(UserWarning, re.compile(regex)): with self.assertWarnsRegex(UserWarning, re.compile(regex)):
j = Jp2k(tfile.name, data=data, cinema4k=True) j.write(data, cinema4k=True)
codestream = j.get_codestream() codestream = j.get_codestream()
self.check_cinema4k_codestream(codestream, (4096, 2160)) self.check_cinema4k_codestream(codestream, (4096, 2160))
@ -132,9 +131,9 @@ class WriteCinemaWarns(CinemaBase):
infile = opj_data_file(relfile) infile = opj_data_file(relfile)
data = skimage.io.imread(infile) data = skimage.io.imread(infile)
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
with self.assertWarnsRegex(UserWarning, j = Jp2k(tfile.name, 'wb')
'OpenJPEG library warning'): with self.assertWarnsRegex(UserWarning, 'OpenJPEG library warning'):
j = Jp2k(tfile.name, data=data, cinema2k=48) j.write(data, cinema2k=48)
codestream = j.get_codestream() codestream = j.get_codestream()
self.check_cinema2k_codestream(codestream, (2048, 857)) self.check_cinema2k_codestream(codestream, (2048, 857))
@ -144,9 +143,9 @@ class WriteCinemaWarns(CinemaBase):
infile = opj_data_file(relfile) infile = opj_data_file(relfile)
data = skimage.io.imread(infile) data = skimage.io.imread(infile)
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
with self.assertWarnsRegex(UserWarning, j = Jp2k(tfile.name, 'wb')
'OpenJPEG library warning'): with self.assertWarnsRegex(UserWarning, 'OpenJPEG library warning'):
j = Jp2k(tfile.name, data=data, cinema2k=48) j.write(data, cinema2k=48)
codestream = j.get_codestream() codestream = j.get_codestream()
self.check_cinema2k_codestream(codestream, (2048, 1080)) self.check_cinema2k_codestream(codestream, (2048, 1080))
@ -156,9 +155,9 @@ class WriteCinemaWarns(CinemaBase):
infile = opj_data_file(relfile) infile = opj_data_file(relfile)
data = skimage.io.imread(infile) data = skimage.io.imread(infile)
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
with self.assertWarnsRegex(UserWarning, j = Jp2k(tfile.name, 'wb')
'OpenJPEG library warning'): with self.assertWarnsRegex(UserWarning, 'OpenJPEG library warning'):
j = Jp2k(tfile.name, data=data, cinema2k=24) j.write(data, cinema2k=24)
codestream = j.get_codestream() codestream = j.get_codestream()
self.check_cinema2k_codestream(codestream, (2048, 1080)) self.check_cinema2k_codestream(codestream, (2048, 1080))
@ -168,11 +167,11 @@ class WriteCinemaWarns(CinemaBase):
infile = opj_data_file(relfile) infile = opj_data_file(relfile)
data = skimage.io.imread(infile) data = skimage.io.imread(infile)
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
with self.assertWarnsRegex(UserWarning, j = Jp2k(tfile.name, 'wb')
'OpenJPEG library warning'): with self.assertWarnsRegex(UserWarning, 'OpenJPEG library warning'):
# OpenJPEG library warning: The desired maximum codestream # OpenJPEG library warning: The desired maximum codestream
# size has limited at least one of the desired quality layers # 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() codestream = j.get_codestream()
self.check_cinema2k_codestream(codestream, (2048, 857)) self.check_cinema2k_codestream(codestream, (2048, 857))
@ -182,11 +181,12 @@ class WriteCinemaWarns(CinemaBase):
infile = opj_data_file(relfile) infile = opj_data_file(relfile)
data = skimage.io.imread(infile) data = skimage.io.imread(infile)
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
j = Jp2k(tfile.name, 'wb')
regex = 'OpenJPEG library warning' regex = 'OpenJPEG library warning'
with self.assertWarnsRegex(UserWarning, regex): with self.assertWarnsRegex(UserWarning, regex):
# OpenJPEG library warning: The desired maximum codestream # OpenJPEG library warning: The desired maximum codestream
# size has limited at least one of the desired quality layers # 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() codestream = j.get_codestream()
self.check_cinema2k_codestream(codestream, (1998, 1080)) self.check_cinema2k_codestream(codestream, (1998, 1080))
@ -195,10 +195,12 @@ class WriteCinemaWarns(CinemaBase):
@unittest.skipIf(NO_SKIMAGE_FREEIMAGE_SUPPORT, @unittest.skipIf(NO_SKIMAGE_FREEIMAGE_SUPPORT,
"Cannot read input image without scikit-image/freeimage") "Cannot read input image without scikit-image/freeimage")
@unittest.skipIf(os.name == "nt", fixtures.WINDOWS_TMP_FILE_MSG) @unittest.skipIf(os.name == "nt", fixtures.WINDOWS_TMP_FILE_MSG)
@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, @unittest.skipIf(OPJ_DATA_ROOT is None,
"OPJ_OPJ_DATA_ROOT environment variable not set") "OPJ_OPJ_DATA_ROOT environment variable not set")
class TestNegative2pointzero(unittest.TestCase): class TestSuiteNegative2pointzero(unittest.TestCase):
"""Feature set not supported for versions less than 2.0.1""" """Feature set not supported for versions less than 2.0"""
def setUp(self): def setUp(self):
self.jp2file = glymur.data.nemo() self.jp2file = glymur.data.nemo()
@ -208,16 +210,13 @@ class TestNegative2pointzero(unittest.TestCase):
pass pass
def test_cinema_mode(self): 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' relfile = 'input/nonregression/X_4_2K_24_185_CBR_WB_000.tif'
infile = opj_data_file(relfile) infile = opj_data_file(relfile)
data = skimage.io.imread(infile) data = skimage.io.imread(infile)
versions = ["1.5.0", "2.0.0"] with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
for version in versions: j = Jp2k(tfile.name, 'wb')
with patch('glymur.version.openjpeg_version', new=version): with self.assertRaises(IOError):
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: j.write(data, cinema2k=48)
with self.assertRaises(IOError):
Jp2k(tfile.name, data=data, cinema2k=48)
@unittest.skipIf(re.match(r'''1.[0-4]''', openjpeg_version) is not None, @unittest.skipIf(re.match(r'''1.[0-4]''', openjpeg_version) is not None,
@ -243,28 +242,30 @@ class TestSuiteWrite(fixtures.MetadataBase):
expdata = np.fromfile(filename, dtype=np.uint16) expdata = np.fromfile(filename, dtype=np.uint16)
expdata.resize((2816, 2048)) expdata.resize((2816, 2048))
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: 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() codestream = j.get_codestream()
self.assertEqual(codestream.segment[2].spcod[8], self.assertEqual(codestream.segment[2].spcod[8],
glymur.core.WAVELET_XFORM_9X7_IRREVERSIBLE) glymur.core.WAVELET_XFORM_9X7_IRREVERSIBLE)
def test_NR_ENC_Bretagne1_ppm_1_encode(self): def test_NR_ENC_Bretagne1_ppm_1_encode(self):
"""NR-ENC-Bretagne1.ppm-1-encode""" """NR-ENC-Bretagne1.ppm-1-encode"""
infile = opj_data_file('input/nonregression/Bretagne1.ppm') infile = opj_data_file('input/nonregression/Bretagne1.ppm')
data = read_image(infile) data = read_image(infile)
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: 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. # Should be three layers.
c = j.get_codestream() c = j.get_codestream()
kwargs = {'rsiz': 0, 'xysiz': (640, 480), 'xyosiz': (0, 0), kwargs = {'rsiz': 0, 'xysiz': (640, 480), 'xyosiz': (0, 0),
'xytsiz': (640, 480), 'xytosiz': (0, 0), 'xytsiz': (640, 480), 'xytosiz': (0, 0),
'bitdepth': (8, 8, 8), 'signed': (False, False, False), 'bitdepth': (8, 8, 8), 'signed': (False, False, False),
'xyrsiz': [(1, 1, 1), (1, 1, 1)]} 'xyrsiz': [(1, 1, 1), (1, 1, 1)]}
self.verifySizSegment(c.segment[1], self.verifySizSegment(c.segment[1], glymur.codestream.SIZsegment(**kwargs))
glymur.codestream.SIZsegment(**kwargs))
# COD: Coding style default # COD: Coding style default
self.assertFalse(c.segment[2].scod & 2) # no sop self.assertFalse(c.segment[2].scod & 2) # no sop
@ -276,7 +277,7 @@ class TestSuiteWrite(fixtures.MetadataBase):
self.assertEqual(tuple(c.segment[2].code_block_size), self.assertEqual(tuple(c.segment[2].code_block_size),
(64, 64)) # cblksz (64, 64)) # cblksz
self.verify_codeblock_style(c.segment[2].spcod[7], 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], self.assertEqual(c.segment[2].spcod[8],
glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) glymur.core.WAVELET_XFORM_5X3_REVERSIBLE)
self.assertEqual(len(c.segment[2].spcod), 9) self.assertEqual(len(c.segment[2].spcod), 9)
@ -286,17 +287,17 @@ class TestSuiteWrite(fixtures.MetadataBase):
infile = opj_data_file('input/nonregression/Bretagne1.ppm') infile = opj_data_file('input/nonregression/Bretagne1.ppm')
data = read_image(infile) data = read_image(infile)
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: 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. # Should be three layers.
codestream = j.get_codestream() codestream = j.get_codestream()
kwargs = {'rsiz': 0, 'xysiz': (640, 480), 'xyosiz': (0, 0), kwargs = {'rsiz': 0, 'xysiz': (640, 480), 'xyosiz': (0, 0),
'xytsiz': (640, 480), 'xytosiz': (0, 0), 'xytsiz': (640, 480), 'xytosiz': (0, 0),
'bitdepth': (8, 8, 8), 'signed': (False, False, False), 'bitdepth': (8, 8, 8), 'signed': (False, False, False),
'xyrsiz': [(1, 1, 1), (1, 1, 1)]} 'xyrsiz': [(1, 1, 1), (1, 1, 1)]}
self.verifySizSegment(codestream.segment[1], self.verifySizSegment(codestream.segment[1], glymur.codestream.SIZsegment(**kwargs))
glymur.codestream.SIZsegment(**kwargs))
# COD: Coding style default # COD: Coding style default
self.assertFalse(codestream.segment[2].scod & 2) # no sop 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), self.assertEqual(tuple(codestream.segment[2].code_block_size),
(64, 64)) # cblksz (64, 64)) # cblksz
self.verify_codeblock_style(codestream.segment[2].spcod[7], 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], self.assertEqual(codestream.segment[2].spcod[8],
glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) glymur.core.WAVELET_XFORM_5X3_REVERSIBLE)
self.assertEqual(len(codestream.segment[2].spcod), 9) self.assertEqual(len(codestream.segment[2].spcod), 9)
@ -319,19 +319,18 @@ class TestSuiteWrite(fixtures.MetadataBase):
infile = opj_data_file('input/nonregression/Bretagne1.ppm') infile = opj_data_file('input/nonregression/Bretagne1.ppm')
data = read_image(infile) data = read_image(infile)
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
j = Jp2k(tfile.name, j = Jp2k(tfile.name, 'wb')
data=data, j.write(data, psnr=[30, 35, 40], cbsize=(16, 16),
psnr=[30, 35, 40], cbsize=(16, 16), psizes=[(64, 64)]) psizes=[(64, 64)])
# Should be three layers. # Should be three layers.
codestream = j.get_codestream() codestream = j.get_codestream()
kwargs = {'rsiz': 0, 'xysiz': (640, 480), 'xyosiz': (0, 0), kwargs = {'rsiz': 0, 'xysiz': (640, 480), 'xyosiz': (0, 0),
'xytsiz': (640, 480), 'xytosiz': (0, 0), 'xytsiz': (640, 480), 'xytosiz': (0, 0),
'bitdepth': (8, 8, 8), 'signed': (False, False, False), 'bitdepth': (8, 8, 8), 'signed': (False, False, False),
'xyrsiz': [(1, 1, 1), (1, 1, 1)]} 'xyrsiz': [(1, 1, 1), (1, 1, 1)]}
self.verifySizSegment(codestream.segment[1], self.verifySizSegment(codestream.segment[1], glymur.codestream.SIZsegment(**kwargs))
glymur.codestream.SIZsegment(**kwargs))
# COD: Coding style default # COD: Coding style default
self.assertFalse(codestream.segment[2].scod & 2) # no sop 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), self.assertEqual(tuple(codestream.segment[2].code_block_size),
(16, 16)) # cblksz (16, 16)) # cblksz
self.verify_codeblock_style(codestream.segment[2].spcod[7], 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], self.assertEqual(codestream.segment[2].spcod[8],
glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) glymur.core.WAVELET_XFORM_5X3_REVERSIBLE)
self.assertEqual(codestream.segment[2].precinct_size, self.assertEqual(codestream.segment[2].precinct_size,
@ -356,22 +354,21 @@ class TestSuiteWrite(fixtures.MetadataBase):
infile = opj_data_file('input/nonregression/Bretagne2.ppm') infile = opj_data_file('input/nonregression/Bretagne2.ppm')
data = read_image(infile) data = read_image(infile)
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
j = Jp2k(tfile.name, j = Jp2k(tfile.name, 'wb')
data=data, j.write(data,
psizes=[(128, 128)] * 3, psizes=[(128, 128)] * 3,
cratios=[100, 20, 2], cratios=[100, 20, 2],
tilesize=(480, 640), tilesize=(480, 640),
cbsize=(32, 32)) cbsize=(32, 32))
# Should be three layers. # Should be three layers.
codestream = j.get_codestream() codestream = j.get_codestream()
kwargs = {'rsiz': 0, 'xysiz': (2592, 1944), 'xyosiz': (0, 0), kwargs = {'rsiz': 0, 'xysiz': (2592, 1944), 'xyosiz': (0, 0),
'xytsiz': (640, 480), 'xytosiz': (0, 0), 'xytsiz': (640, 480), 'xytosiz': (0, 0),
'bitdepth': (8, 8, 8), 'signed': (False, False, False), 'bitdepth': (8, 8, 8), 'signed': (False, False, False),
'xyrsiz': [(1, 1, 1), (1, 1, 1)]} 'xyrsiz': [(1, 1, 1), (1, 1, 1)]}
self.verifySizSegment(codestream.segment[1], self.verifySizSegment(codestream.segment[1], glymur.codestream.SIZsegment(**kwargs))
glymur.codestream.SIZsegment(**kwargs))
# COD: Coding style default # COD: Coding style default
self.assertFalse(codestream.segment[2].scod & 2) # no sop 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), self.assertEqual(tuple(codestream.segment[2].code_block_size),
(32, 32)) # cblksz (32, 32)) # cblksz
self.verify_codeblock_style(codestream.segment[2].spcod[7], 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], self.assertEqual(codestream.segment[2].spcod[8],
glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) glymur.core.WAVELET_XFORM_5X3_REVERSIBLE)
self.assertEqual(codestream.segment[2].precinct_size, self.assertEqual(codestream.segment[2].precinct_size,
@ -395,16 +391,16 @@ class TestSuiteWrite(fixtures.MetadataBase):
infile = opj_data_file('input/nonregression/Bretagne2.ppm') infile = opj_data_file('input/nonregression/Bretagne2.ppm')
data = read_image(infile) data = read_image(infile)
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: 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() codestream = j.get_codestream()
kwargs = {'rsiz': 0, 'xysiz': (2592, 1944), 'xyosiz': (0, 0), kwargs = {'rsiz': 0, 'xysiz': (2592, 1944), 'xyosiz': (0, 0),
'xytsiz': (127, 127), 'xytosiz': (0, 0), 'xytsiz': (127, 127), 'xytosiz': (0, 0),
'bitdepth': (8, 8, 8), 'signed': (False, False, False), 'bitdepth': (8, 8, 8), 'signed': (False, False, False),
'xyrsiz': [(1, 1, 1), (1, 1, 1)]} 'xyrsiz': [(1, 1, 1), (1, 1, 1)]}
self.verifySizSegment(codestream.segment[1], self.verifySizSegment(codestream.segment[1], glymur.codestream.SIZsegment(**kwargs))
glymur.codestream.SIZsegment(**kwargs))
# COD: Coding style default # COD: Coding style default
self.assertFalse(codestream.segment[2].scod & 2) # no sop 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), self.assertEqual(tuple(codestream.segment[2].code_block_size),
(64, 64)) # cblksz (64, 64)) # cblksz
self.verify_codeblock_style(codestream.segment[2].spcod[7], 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], self.assertEqual(codestream.segment[2].spcod[8],
glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) glymur.core.WAVELET_XFORM_5X3_REVERSIBLE)
self.assertEqual(len(codestream.segment[2].spcod), 9) self.assertEqual(len(codestream.segment[2].spcod), 9)
@ -427,16 +422,16 @@ class TestSuiteWrite(fixtures.MetadataBase):
infile = opj_data_file('input/nonregression/Bretagne2.ppm') infile = opj_data_file('input/nonregression/Bretagne2.ppm')
data = read_image(infile) data = read_image(infile)
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: 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) codestream = j.get_codestream(header_only=False)
kwargs = {'rsiz': 0, 'xysiz': (5183, 3887), 'xyosiz': (0, 0), kwargs = {'rsiz': 0, 'xysiz': (5183, 3887), 'xyosiz': (0, 0),
'xytsiz': (5183, 3887), 'xytosiz': (0, 0), 'xytsiz': (5183, 3887), 'xytosiz': (0, 0),
'bitdepth': (8, 8, 8), 'signed': (False, False, False), 'bitdepth': (8, 8, 8), 'signed': (False, False, False),
'xyrsiz': [(2, 2, 2), (2, 2, 2)]} 'xyrsiz': [(2, 2, 2), (2, 2, 2)]}
self.verifySizSegment(codestream.segment[1], self.verifySizSegment(codestream.segment[1], glymur.codestream.SIZsegment(**kwargs))
glymur.codestream.SIZsegment(**kwargs))
# COD: Coding style default # COD: Coding style default
self.assertTrue(codestream.segment[2].scod & 2) # sop 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), self.assertEqual(tuple(codestream.segment[2].code_block_size),
(64, 64)) # cblksz (64, 64)) # cblksz
self.verify_codeblock_style(codestream.segment[2].spcod[7], 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], self.assertEqual(codestream.segment[2].spcod[8],
glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) glymur.core.WAVELET_XFORM_5X3_REVERSIBLE)
self.assertEqual(len(codestream.segment[2].spcod), 9) self.assertEqual(len(codestream.segment[2].spcod), 9)
@ -464,16 +458,16 @@ class TestSuiteWrite(fixtures.MetadataBase):
infile = opj_data_file('input/nonregression/Bretagne2.ppm') infile = opj_data_file('input/nonregression/Bretagne2.ppm')
data = read_image(infile) data = read_image(infile)
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: 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) codestream = j.get_codestream(header_only=False)
kwargs = {'rsiz': 0, 'xysiz': (2592, 1944), 'xyosiz': (0, 0), kwargs = {'rsiz': 0, 'xysiz': (2592, 1944), 'xyosiz': (0, 0),
'xytsiz': (2592, 1944), 'xytosiz': (0, 0), 'xytsiz': (2592, 1944), 'xytosiz': (0, 0),
'bitdepth': (8, 8, 8), 'signed': (False, False, False), 'bitdepth': (8, 8, 8), 'signed': (False, False, False),
'xyrsiz': [(1, 1, 1), (1, 1, 1)]} 'xyrsiz': [(1, 1, 1), (1, 1, 1)]}
self.verifySizSegment(codestream.segment[1], self.verifySizSegment(codestream.segment[1], glymur.codestream.SIZsegment(**kwargs))
glymur.codestream.SIZsegment(**kwargs))
# COD: Coding style default # COD: Coding style default
self.assertFalse(codestream.segment[2].scod & 2) # no sop 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[3], 1) # mct
self.assertEqual(codestream.segment[2].spcod[4], 5) # levels self.assertEqual(codestream.segment[2].spcod[4], 5) # levels
self.assertEqual(tuple(codestream.segment[2].code_block_size), self.assertEqual(tuple(codestream.segment[2].code_block_size),
(64, 64)) # cblksz (64, 64)) # cblksz
self.verify_codeblock_style(codestream.segment[2].spcod[7], self.verify_codeblock_style(codestream.segment[2].spcod[7],
[False, True, True, [False, True, True, False, False, True])
False, False, True])
self.assertEqual(codestream.segment[2].spcod[8], self.assertEqual(codestream.segment[2].spcod[8],
glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) glymur.core.WAVELET_XFORM_5X3_REVERSIBLE)
self.assertEqual(len(codestream.segment[2].spcod), 9) self.assertEqual(len(codestream.segment[2].spcod), 9)
@ -500,17 +493,16 @@ class TestSuiteWrite(fixtures.MetadataBase):
infile = opj_data_file('input/nonregression/Bretagne2.ppm') infile = opj_data_file('input/nonregression/Bretagne2.ppm')
data = read_image(infile) data = read_image(infile)
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
j = Jp2k(tfile.name, j = Jp2k(tfile.name, 'wb')
data=data, grid_offset=[300, 150], cratios=[800]) j.write(data, grid_offset=[300, 150], cratios=[800])
codestream = j.get_codestream(header_only=False) codestream = j.get_codestream(header_only=False)
kwargs = {'rsiz': 0, 'xysiz': (2742, 2244), 'xyosiz': (150, 300), kwargs = {'rsiz': 0, 'xysiz': (2742, 2244), 'xyosiz': (150, 300),
'xytsiz': (2742, 2244), 'xytosiz': (0, 0), 'xytsiz': (2742, 2244), 'xytosiz': (0, 0),
'bitdepth': (8, 8, 8), 'signed': (False, False, False), 'bitdepth': (8, 8, 8), 'signed': (False, False, False),
'xyrsiz': [(1, 1, 1), (1, 1, 1)]} 'xyrsiz': [(1, 1, 1), (1, 1, 1)]}
self.verifySizSegment(codestream.segment[1], self.verifySizSegment(codestream.segment[1], glymur.codestream.SIZsegment(**kwargs))
glymur.codestream.SIZsegment(**kwargs))
# COD: Coding style default # COD: Coding style default
self.assertFalse(codestream.segment[2].scod & 2) # no sop 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), self.assertEqual(tuple(codestream.segment[2].code_block_size),
(64, 64)) # cblksz (64, 64)) # cblksz
self.verify_codeblock_style(codestream.segment[2].spcod[7], 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], self.assertEqual(codestream.segment[2].spcod[8],
glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) glymur.core.WAVELET_XFORM_5X3_REVERSIBLE)
self.assertEqual(len(codestream.segment[2].spcod), 9) self.assertEqual(len(codestream.segment[2].spcod), 9)
@ -533,16 +524,16 @@ class TestSuiteWrite(fixtures.MetadataBase):
infile = opj_data_file('input/nonregression/Cevennes1.bmp') infile = opj_data_file('input/nonregression/Cevennes1.bmp')
data = read_image(infile) data = read_image(infile)
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: 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) codestream = j.get_codestream(header_only=False)
kwargs = {'rsiz': 0, 'xysiz': (2592, 1944), 'xyosiz': (0, 0), kwargs = {'rsiz': 0, 'xysiz': (2592, 1944), 'xyosiz': (0, 0),
'xytsiz': (2592, 1944), 'xytosiz': (0, 0), 'xytsiz': (2592, 1944), 'xytosiz': (0, 0),
'bitdepth': (8, 8, 8), 'signed': (False, False, False), 'bitdepth': (8, 8, 8), 'signed': (False, False, False),
'xyrsiz': [(1, 1, 1), (1, 1, 1)]} 'xyrsiz': [(1, 1, 1), (1, 1, 1)]}
self.verifySizSegment(codestream.segment[1], self.verifySizSegment(codestream.segment[1], glymur.codestream.SIZsegment(**kwargs))
glymur.codestream.SIZsegment(**kwargs))
# COD: Coding style default # COD: Coding style default
self.assertFalse(codestream.segment[2].scod & 2) # no sop 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), self.assertEqual(tuple(codestream.segment[2].code_block_size),
(64, 64)) # cblksz (64, 64)) # cblksz
self.verify_codeblock_style(codestream.segment[2].spcod[7], 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], self.assertEqual(codestream.segment[2].spcod[8],
glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) glymur.core.WAVELET_XFORM_5X3_REVERSIBLE)
self.assertEqual(len(codestream.segment[2].spcod), 9) self.assertEqual(len(codestream.segment[2].spcod), 9)
@ -565,16 +555,16 @@ class TestSuiteWrite(fixtures.MetadataBase):
infile = opj_data_file('input/nonregression/Cevennes2.ppm') infile = opj_data_file('input/nonregression/Cevennes2.ppm')
data = read_image(infile) data = read_image(infile)
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: 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) codestream = j.get_codestream(header_only=False)
kwargs = {'rsiz': 0, 'xysiz': (640, 480), 'xyosiz': (0, 0), kwargs = {'rsiz': 0, 'xysiz': (640, 480), 'xyosiz': (0, 0),
'xytsiz': (640, 480), 'xytosiz': (0, 0), 'xytsiz': (640, 480), 'xytosiz': (0, 0),
'bitdepth': (8, 8, 8), 'signed': (False, False, False), 'bitdepth': (8, 8, 8), 'signed': (False, False, False),
'xyrsiz': [(1, 1, 1), (1, 1, 1)]} 'xyrsiz': [(1, 1, 1), (1, 1, 1)]}
self.verifySizSegment(codestream.segment[1], self.verifySizSegment(codestream.segment[1], glymur.codestream.SIZsegment(**kwargs))
glymur.codestream.SIZsegment(**kwargs))
# COD: Coding style default # COD: Coding style default
self.assertFalse(codestream.segment[2].scod & 2) # no sop 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), self.assertEqual(tuple(codestream.segment[2].code_block_size),
(64, 64)) # cblksz (64, 64)) # cblksz
self.verify_codeblock_style(codestream.segment[2].spcod[7], 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], self.assertEqual(codestream.segment[2].spcod[8],
glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) glymur.core.WAVELET_XFORM_5X3_REVERSIBLE)
self.assertEqual(len(codestream.segment[2].spcod), 9) self.assertEqual(len(codestream.segment[2].spcod), 9)
@ -596,8 +585,8 @@ class TestSuiteWrite(fixtures.MetadataBase):
"""NR-ENC-Rome.bmp-11-encode""" """NR-ENC-Rome.bmp-11-encode"""
data = read_image(opj_data_file('input/nonregression/Rome.bmp')) data = read_image(opj_data_file('input/nonregression/Rome.bmp'))
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
jp2 = Jp2k(tfile.name, jp2 = Jp2k(tfile.name, 'wb')
data=data, psnr=[30, 35, 50], prog='LRCP', numres=3) jp2.write(data, psnr=[30, 35, 50], prog='LRCP', numres=3)
ids = [box.box_id for box in jp2.box] ids = [box.box_id for box in jp2.box]
self.assertEqual(ids, ['jP ', 'ftyp', 'jp2h', 'jp2c']) 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.assertIsNone(jp2.box[2].box[1].icc_profile)
self.assertEqual(jp2.box[2].box[1].colorspace, glymur.core.SRGB) 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), kwargs = {'rsiz': 0, 'xysiz': (640, 480), 'xyosiz': (0, 0),
'xytsiz': (640, 480), 'xytosiz': (0, 0), 'xytsiz': (640, 480), 'xytosiz': (0, 0),
'bitdepth': (8, 8, 8), 'signed': (False, False, False), 'bitdepth': (8, 8, 8), 'signed': (False, False, False),
'xyrsiz': [(1, 1, 1), (1, 1, 1)]} 'xyrsiz': [(1, 1, 1), (1, 1, 1)]}
self.verifySizSegment(codestream.segment[1], self.verifySizSegment(codestream.segment[1], glymur.codestream.SIZsegment(**kwargs))
glymur.codestream.SIZsegment(**kwargs))
# COD: Coding style default # COD: Coding style default
self.assertFalse(codestream.segment[2].scod & 2) # no sop 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), self.assertEqual(tuple(codestream.segment[2].code_block_size),
(64, 64)) # cblksz (64, 64)) # cblksz
self.verify_codeblock_style(codestream.segment[2].spcod[7], 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], self.assertEqual(codestream.segment[2].spcod[8],
glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) glymur.core.WAVELET_XFORM_5X3_REVERSIBLE)
self.assertEqual(len(codestream.segment[2].spcod), 9) 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') infile = opj_data_file('input/nonregression/random-issue-0005.tif')
data = read_image(infile) data = read_image(infile)
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile: 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) codestream = j.get_codestream(header_only=False)
kwargs = {'rsiz': 0, 'xysiz': (1024, 1024), 'xyosiz': (0, 0), kwargs = {'rsiz': 0, 'xysiz': (1024, 1024), 'xyosiz': (0, 0),
'xytsiz': (1024, 1024), 'xytosiz': (0, 0), 'xytsiz': (1024, 1024), 'xytosiz': (0, 0),
'bitdepth': (16,), 'signed': (False,), 'bitdepth': (16,), 'signed': (False,),
'xyrsiz': [(1,), (1,)]} 'xyrsiz': [(1,), (1,)]}
self.verifySizSegment(codestream.segment[1], self.verifySizSegment(codestream.segment[1], glymur.codestream.SIZsegment(**kwargs))
glymur.codestream.SIZsegment(**kwargs))
# COD: Coding style default # COD: Coding style default
self.assertFalse(codestream.segment[2].scod & 2) # no sop 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), self.assertEqual(tuple(codestream.segment[2].code_block_size),
(64, 64)) # cblksz (64, 64)) # cblksz
self.verify_codeblock_style(codestream.segment[2].spcod[7], 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], self.assertEqual(codestream.segment[2].spcod[8],
glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) glymur.core.WAVELET_XFORM_5X3_REVERSIBLE)
self.assertEqual(len(codestream.segment[2].spcod), 9) self.assertEqual(len(codestream.segment[2].spcod), 9)

View file

@ -1,6 +1,15 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""Test suite for printing. """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 os
import re import re
import struct import struct
@ -23,12 +32,11 @@ import lxml.etree as ET
import glymur import glymur
from glymur import Jp2k, command_line from glymur import Jp2k, command_line
from . import fixtures from . import fixtures
from .fixtures import (OPJ_DATA_ROOT, opj_data_file, from .fixtures import (
WARNING_INFRASTRUCTURE_ISSUE, OPJ_DATA_ROOT, opj_data_file,
WARNING_INFRASTRUCTURE_MSG, WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG,
WINDOWS_TMP_FILE_MSG, WINDOWS_TMP_FILE_MSG, text_gbr_27, text_gbr_33, text_gbr_34
text_gbr_27, text_gbr_33, text_gbr_34) )
@unittest.skipIf(os.name == "nt", WINDOWS_TMP_FILE_MSG) @unittest.skipIf(os.name == "nt", WINDOWS_TMP_FILE_MSG)
class TestPrinting(unittest.TestCase): class TestPrinting(unittest.TestCase):
@ -42,13 +50,25 @@ class TestPrinting(unittest.TestCase):
glymur.set_printoptions(short=False, xml=True, codestream=True) glymur.set_printoptions(short=False, xml=True, codestream=True)
def tearDown(self): 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): def test_version_info(self):
"""Should be able to print(glymur.version.info)""" """Should be able to print(glymur.version.info)"""
with patch('sys.stdout', new=StringIO()) as fake_out: with patch('sys.stdout', new=StringIO()) as fake_out:
print(glymur.version.info) print(glymur.version.info)
fake_out.getvalue().strip() actual = fake_out.getvalue().strip()
self.assertTrue(True) self.assertTrue(True)
@ -58,7 +78,7 @@ class TestPrinting(unittest.TestCase):
with tempfile.NamedTemporaryFile(suffix='.jpx') as tfile: with tempfile.NamedTemporaryFile(suffix='.jpx') as tfile:
with open(self.jpxfile, 'rb') as ifile: with open(self.jpxfile, 'rb') as ifile:
tfile.write(ifile.read()) tfile.write(ifile.read())
# Add the header for an unknown superbox. # Add the header for an unknown superbox.
write_buffer = struct.pack('>I4s', 20, 'grp '.encode()) write_buffer = struct.pack('>I4s', 20, 'grp '.encode())
tfile.write(write_buffer) tfile.write(write_buffer)
@ -87,17 +107,16 @@ class TestPrinting(unittest.TestCase):
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
glymur.set_printoptions(hi='low') 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")
def test_asoc_label_box(self): def test_asoc_label_box(self):
"""verify printing of asoc, label boxes""" """verify printing of asoc, label boxes"""
# Construct a fake file with an asoc and a label box, as # Construct a fake file with an asoc and a label box, as
# OpenJPEG doesn't have such a file. # OpenJPEG doesn't have such a file.
data = glymur.Jp2k(self.jp2file)[::2, ::2] data = glymur.Jp2k(self.jp2file).read(rlevel=1)
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
j = glymur.Jp2k(tfile.name, 'wb')
j.write(data)
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile2: with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile2:
glymur.Jp2k(tfile.name, data=data)
# Offset of the codestream is where we start. # Offset of the codestream is where we start.
wbuffer = tfile.read(77) wbuffer = tfile.read(77)
@ -399,7 +418,7 @@ class TestPrinting(unittest.TestCase):
@unittest.skipIf(sys.hexversion < 0x03000000, @unittest.skipIf(sys.hexversion < 0x03000000,
"Only trusting python3 for printing non-ascii chars") "Only trusting python3 for printing non-ascii chars")
def test_xml_cyrrilic(self): 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 # Seems to be inconsistencies between different versions of python2.x
# as to what gets printed. # as to what gets printed.
# #
@ -417,8 +436,7 @@ class TestPrinting(unittest.TestCase):
actual = fake_out.getvalue().strip() actual = fake_out.getvalue().strip()
if sys.hexversion < 0x03000000: if sys.hexversion < 0x03000000:
lines = ["XML Box (xml ) @ (-1, 0)", lines = ["XML Box (xml ) @ (-1, 0)",
(" <country>&#1056;&#1086;&#1089;&#1089;", " <country>&#1056;&#1086;&#1089;&#1089;&#1080;&#1103;</country>"]
"&#1080;&#1103;</country>")]
else: else:
lines = ["XML Box (xml ) @ (-1, 0)", lines = ["XML Box (xml ) @ (-1, 0)",
" <country>Россия</country>"] " <country>Россия</country>"]
@ -594,14 +612,12 @@ class TestPrinting(unittest.TestCase):
lines = ["UUID Box (uuid) @ (1135519, 76)", lines = ["UUID Box (uuid) @ (1135519, 76)",
" UUID: 4a706754-6966-6645-7869-662d3e4a5032 (EXIF)", " UUID: 4a706754-6966-6645-7869-662d3e4a5032 (EXIF)",
(" UUID Data: OrderedDict([('ImageWidth', 256)," " UUID Data: OrderedDict([('ImageWidth', 256), ('ImageLength', 512), ('Make', 'HTC')])"]
" ('ImageLength', 512), ('Make', 'HTC')])")]
expected = '\n'.join(lines) expected = '\n'.join(lines)
self.assertEqual(actual, expected) self.assertEqual(actual, expected)
@unittest.skipIf(OPJ_DATA_ROOT is None, @unittest.skipIf(OPJ_DATA_ROOT is None,
"OPJ_DATA_ROOT environment variable not set") "OPJ_DATA_ROOT environment variable not set")
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.") @unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
@ -809,7 +825,6 @@ class TestPrintingOpjDataRoot(unittest.TestCase):
expected = '\n'.join(lines) expected = '\n'.join(lines)
self.assertEqual(actual, expected) self.assertEqual(actual, expected)
@unittest.skipIf(OPJ_DATA_ROOT is None, @unittest.skipIf(OPJ_DATA_ROOT is None,
"OPJ_DATA_ROOT environment variable not set") "OPJ_DATA_ROOT environment variable not set")
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.") @unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
@ -831,31 +846,20 @@ class TestPrintingOpjDataRootWarns(unittest.TestCase):
def tearDown(self): def tearDown(self):
pass 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): def test_invalid_colorspace(self):
"""An invalid colorspace shouldn't cause an error.""" """An invalid colorspace shouldn't cause an error."""
filename = opj_data_file('input/nonregression/edf_c2_1103421.jp2') filename = opj_data_file('input/nonregression/edf_c2_1103421.jp2')
with self.assertWarns(UserWarning): with self.assertWarns(UserWarning):
jp2 = Jp2k(filename) jp2 = Jp2k(filename)
with patch('sys.stdout', new=StringIO()): with patch('sys.stdout', new=StringIO()) as fake_out:
print(jp2) print(jp2)
def test_bad_rsiz(self): def test_bad_rsiz(self):
"""Should still be able to print if rsiz is bad, issue196""" """Should still be able to print if rsiz is bad, issue196"""
filename = opj_data_file('input/nonregression/edf_c2_1002767.jp2') filename = opj_data_file('input/nonregression/edf_c2_1002767.jp2')
with self.assertWarns(UserWarning): with self.assertWarns(UserWarning):
j = Jp2k(filename).get_codestream() j = Jp2k(filename)
with patch('sys.stdout', new=StringIO()): with patch('sys.stdout', new=StringIO()) as fake_out:
print(j) print(j)
def test_bad_wavelet_transform(self): def test_bad_wavelet_transform(self):
@ -863,7 +867,7 @@ class TestPrintingOpjDataRootWarns(unittest.TestCase):
filename = opj_data_file('input/nonregression/edf_c2_10025.jp2') filename = opj_data_file('input/nonregression/edf_c2_10025.jp2')
with self.assertWarns(UserWarning): with self.assertWarns(UserWarning):
jp2 = Jp2k(filename) jp2 = Jp2k(filename)
with patch('sys.stdout', new=StringIO()): with patch('sys.stdout', new=StringIO()) as fake_out:
print(jp2) print(jp2)
def test_invalid_progression_order(self): def test_invalid_progression_order(self):
@ -1024,7 +1028,7 @@ class TestPrintingOpjDataRootWarns(unittest.TestCase):
'issue171.jp2')) 'issue171.jp2'))
with self.assertWarns(UserWarning): with self.assertWarns(UserWarning):
jp2 = Jp2k(filename) 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. # No need to verify, it's enough that we don't error out.
print(jp2) print(jp2)
@ -1040,10 +1044,9 @@ class TestJp2dump(unittest.TestCase):
# Reset printoptions for every test. # Reset printoptions for every test.
glymur.set_printoptions(short=False, xml=True, codestream=True) glymur.set_printoptions(short=False, xml=True, codestream=True)
glymur.set_parseoptions(full_codestream=False)
def tearDown(self): def tearDown(self):
glymur.set_parseoptions(full_codestream=False) pass
def run_jp2dump(self, args): def run_jp2dump(self, args):
sys.argv = args sys.argv = args
@ -1056,53 +1059,24 @@ class TestJp2dump(unittest.TestCase):
return actual return actual
def test_default_nemo(self): 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]) actual = self.run_jp2dump(['', self.jp2file])
# shave off the non-main-header segments self.assertEqual(actual, fixtures.nemo_dump_no_codestream)
lines = fixtures.nemo.split('\n')
expected = lines[0:140]
expected = '\n'.join(expected)
self.assertEqual(actual, expected)
def test_jp2_codestream_0(self): def test_codestream_0(self):
"""Verify dumping with -c 0, supressing all codestream details.""" """Verify dumping with -c 0, supressing all codestream details."""
actual = self.run_jp2dump(['', '-c', '0', self.jp2file]) actual = self.run_jp2dump(['', '-c', '0', self.jp2file])
# shave off the codestream details self.assertEqual(actual, fixtures.nemo_dump_no_codestream)
lines = fixtures.nemo.split('\n')
expected = lines[0:105]
expected = '\n'.join(expected)
self.assertEqual(actual, expected)
def test_jp2_codestream_1(self): def test_codestream_1(self):
"""Verify dumping with -c 1, print just the header.""" """Verify dumping with -c 1, print just the header."""
actual = self.run_jp2dump(['', '-c', '1', self.jp2file]) actual = self.run_jp2dump(['', '-c', '1', self.jp2file])
# shave off the non-main-header segments self.assertEqual(actual, fixtures.nemo_with_codestream_header)
lines = fixtures.nemo.split('\n')
expected = lines[0:140]
expected = '\n'.join(expected)
self.assertEqual(actual, expected)
def test_jp2_codestream_2(self): def test_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):
"""Verify dumping with -c 2, full details.""" """Verify dumping with -c 2, full details."""
with patch('sys.stdout', new=StringIO()) as fake_out: with patch('sys.stdout', new=StringIO()) as fake_out:
sys.argv = ['', '-c', '2', self.j2kfile] sys.argv = ['', '-c', '2', self.j2kfile]
@ -1127,9 +1101,4 @@ class TestJp2dump(unittest.TestCase):
"""Verify dumping with -x, suppress XML.""" """Verify dumping with -x, suppress XML."""
actual = self.run_jp2dump(['', '-x', self.jp2file]) actual = self.run_jp2dump(['', '-x', self.jp2file])
# shave off the XML and non-main-header segments self.assertEqual(actual, fixtures.nemo_dump_no_codestream_no_xml)
lines = fixtures.nemo.split('\n')
expected = lines[0:18]
expected.extend(lines[104:140])
expected = '\n'.join(expected)
self.assertEqual(actual, expected)

View file

@ -18,7 +18,7 @@ from .lib import openjpeg as opj, openjp2 as opj2
# Do not change the format of this next line! Doing so risks breaking # Do not change the format of this next line! Doing so risks breaking
# setup.py # setup.py
version = "0.8.0" version = "0.7.2"
_sv = LooseVersion(version) _sv = LooseVersion(version)
version_tuple = _sv.version version_tuple = _sv.version

View file

@ -1,4 +1,4 @@
from setuptools import setup from setuptools import setup, find_packages
import os import os
import re import re
import sys import sys
@ -11,20 +11,18 @@ kwargs = {'name': 'Glymur',
'url': 'https://github.com/quintusdias/glymur', 'url': 'https://github.com/quintusdias/glymur',
'packages': ['glymur', 'glymur.data', 'glymur.test', 'glymur.lib', 'packages': ['glymur', 'glymur.data', 'glymur.test', 'glymur.lib',
'glymur.lib.test'], 'glymur.lib.test'],
'package_data': {'glymur': ['data/*.jp2', 'package_data': {'glymur': ['data/*.jp2', 'data/*.j2k', 'data/*.jpx']},
'data/*.j2k',
'data/*.jpx']},
'entry_points': { 'entry_points': {
'console_scripts': ['jp2dump=glymur.command_line:main'], 'console_scripts': ['jp2dump=glymur.command_line:main'],
}, },
'license': 'MIT', 'license': 'MIT',
'test_suite': 'glymur.test'} '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: if sys.hexversion < 0x03030000:
install_requires.append('contextlib2>=0.4') instllrqrs.append('contextlib2>=0.4')
install_requires.append('mock>=1.0.1') instllrqrs.append('mock>=1.0.1')
kwargs['install_requires'] = install_requires kwargs['install_requires'] = instllrqrs
clssfrs = ["Programming Language :: Python", clssfrs = ["Programming Language :: Python",
"Programming Language :: Python :: 2.7", "Programming Language :: Python :: 2.7",