Compare commits

..

1 commit

Author SHA1 Message Date
John Evans
64809a9024 refactor RGN, SOT testing 2014-09-19 13:59:45 -04:00
46 changed files with 5085 additions and 4050 deletions

View file

@ -1,15 +1,4 @@
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 02, 2014 - v0.7.1 Fixed README to mention Python 3.4
Oct 01, 2014 - v0.7.0 Added array-style slicing.
August 03, 2014 - v0.6.0 Added Cinema2K, Cinema4K write support.
Mar 06, 2014 - Added Cinema2K, Cinema4K write support.
Changed constructor for ChannelDefinition box. Removed support
for Python 2.6. Added write support for JP2 UUID, DataEntryURL,
Palette and Component Mapping boxes, JPX Association, NumberList

View file

@ -3,6 +3,6 @@ glymur: a Python interface for JPEG 2000
**glymur** contains a Python interface to the OpenJPEG library which
allows one to read and write JPEG 2000 files. **glymur** works on
Python 2.7, 3.3, and 3.4.
Python 2.7 and 3.3. Python 3.3 is strongly recommended.
Please read the docs, https://glymur.readthedocs.org/en/latest/

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

View file

@ -15,7 +15,7 @@ or if you use windows, then read on.
Glymur uses ctypes to access the openjp2/openjpeg libraries, and
because ctypes accesses libraries in a platform-dependent manner,
it is recommended that **if** you compile and install OpenJPEG into a
it is recommended that if you compile and install OpenJPEG into a
non-standard location, you should then create a configuration file
to help Glymur properly find the openjpeg or openjp2 libraries
(linux users or macports users dont need to bother with this if
@ -24,11 +24,11 @@ configuration format is the same as used by Pythons configparser
module, i.e. ::
[library]
openjp2: /somewhere/lib/libopenjp2.so
openjp2: /opt/openjp2-svn/lib/libopenjp2.so
This assumes, of course, that you've installed OpenJPEG into
/opt/openjpeg on a linux system. The location of the configuration file
can vary as well. If you use either linux or mac, the path
/opt/openjp2-svn on a linux system. The location of the configuration file
can vary as well (of course). If you use either linux or mac, the path
to the configuration file would normally be ::
$HOME/.config/glymur/glymurrc
@ -48,10 +48,7 @@ You may also include a line for the version 1.x openjpeg library if you have it
installed in a non-standard place, i.e. ::
[library]
openjpeg: /somewhere/lib/libopenjpeg.so
Once again, you should not have to bother with a configuration file if you use
mac or linux and OpenJPEG is provided by your package manager.
openjpeg: /not/the/usual/location/lib/libopenjpeg.so
'''''''
Testing

View file

@ -3,46 +3,36 @@ How do I...?
------------
... read images?
================
Jp2k implements slicing via the :py:meth:`__getitem__` method, meaning that
multiple resolution imagery in a JPEG 2000 file can
easily be accessed via array-style slicing. For example here's how to
retrieve a full resolution and first lower-resolution image ::
... read the lower resolution images?
=====================================
Jp2k implements slicing via the :py:meth:`__getitem__` method so
any lower resolution images in a JPEG 2000 file can easily be
accessed, for example here's how to retrieve the first sub-image ::
>>> import glymur
>>> jp2file = glymur.data.nemo() # just a path to a JPEG2000 file
>>> jp2file = glymur.data.nemo()
>>> jp2 = glymur.Jp2k(jp2file)
>>> fullres = jp2[:]
>>> fullres.shape
>>> print(fullres.shape)
(1456, 2592, 3)
>>> thumbnail = jp2[::2, ::2]
>>> thumbnail.shape
>>> print(thumbnail.shape)
(728, 1296, 3)
... write images?
=================
It's pretty simple, just supply the image data as the 2nd argument to the Jp2k
constructor.
>>> import glymur, numpy as np
>>> jp2 = glymur.Jp2k('zeros.jp2', data=np.zeros((640, 480), dtype=np.uint8)
You must have OpenJPEG version 1.5 or more recent in order to write JPEG 2000
images with glymur.
The :py:meth:`read` method gives many more options for other JPEG 2000 features
such as quality layers.
... display metadata?
=====================
There are two ways. From the command line, the console script **jp2dump** is
There are two ways. From the command line, the script **jp2dump** is
available. ::
$ jp2dump /path/to/glymur/installation/data/nemo.jp2
From within Python, the same result is obtained simply by printing the Jp2k
object, i.e. ::
From within Python, it is as simple as printing the Jp2k object, i.e. ::
>>> import glymur
>>> jp2file = glymur.data.nemo() # just a path to a JP2 file
>>> jp2file = glymur.data.nemo()
>>> jp2 = glymur.Jp2k(jp2file)
>>> print(jp2)
File: nemo.jp2
@ -214,9 +204,9 @@ making use of the :py:meth:`set_printoptions` function::
UUID: be7acfcb-97a9-42e8-9c71-999491e3afac (XMP)
Contiguous Codestream Box (jp2c) @ (3223, 1132296)
It is possible to easily print the codestream header details as well, i.e. ::
It is possible to print all the gory codestream details as well, i.e. ::
>>> print(j.codestream) # details not show
>>> print(j.get_codestream()) # details not shown
... add XML metadata?
=====================
@ -336,10 +326,10 @@ is currently limited to XML and UUID boxes.
... create an image with an alpha layer?
========================================
OpenJPEG can create JP2 files with more than 3 components (use version 2.1.0+
for this), but by default, any extra components are not described
as such. In order to do so, we need to rewrap such an image in a
set of boxes that includes a channel definition box.
OpenJPEG can create JP2 files with more than 3 components (requires
the development version of OpenJPEG), but by default, any extra components are
not described as such. In order to do so, we need to rewrap such
an image in a set of boxes that includes a channel definition box.
This example is based on SciPy example code found at
http://scipy-lectures.github.io/advanced/image_processing/#basic-manipulations .
@ -349,14 +339,15 @@ image isn't square. ::
>>> import numpy as np
>>> import glymur
>>> from glymur import Jp2k
>>> rgb = Jp2k(glymur.data.goodstuff())[:]
>>> rgb = Jp2k(glymur.data.goodstuff()).read()
>>> lx, ly = rgb.shape[0:2]
>>> X, Y = np.ogrid[0:lx, 0:ly]
>>> mask = ly**2*(X - lx / 2) ** 2 + lx**2*(Y - ly / 2) ** 2 > (lx * ly / 2)**2
>>> alpha = 255 * np.ones((lx, ly, 1), dtype=np.uint8)
>>> alpha[mask] = 0
>>> rgba = np.concatenate((rgb, alpha), axis=2)
>>> jp2 = Jp2k('tmp.jp2', data=rgba)
>>> jp2 = Jp2k('tmp.jp2', 'wb')
>>> jp2[:] = rgba
Next we need to specify what types of channels we have.
The first three channels are color channels, but we identify the fourth as
@ -396,11 +387,7 @@ Here's how the Preview application on the mac shows the RGBA image.
... work with XMP UUIDs?
========================
`Wikipedia <http://en.wikipedia.org/wiki/Extensible_Metadata_Platform>`_ states
that "The Extensible Metadata Platform (XMP) is an ISO standard,
originally created by Adobe Systems Inc., for the creation, processing
and interchange of standardized and custom metadata for all kinds
of resources."
XMP is metadata on steroids.
The example JP2 file shipped with glymur has an XMP UUID. ::
@ -442,7 +429,7 @@ following
'Google'
But that would be painful. A better solution is to install the Python XMP
Toolkit (make sure it is at least version 2.0)::
Toolkit (make sure it is version 2.0)::
>>> from libxmp import XMPMeta
>>> from libxmp.consts import XMP_NS_XMP as NS_XAP
@ -459,7 +446,8 @@ http://photojournal.jpl.nasa.gov/tiff/PIA17145.tif info JPEG 2000::
>>> import skimage.io
>>> image = skimage.io.imread('PIA17145.tif')
>>> from glymur import Jp2k
>>> jp2 = Jp2k('PIA17145.jp2', data=image)
>>> jp2 = Jp2k('PIA17145.jp2', 'wb')
>>> jp2[:] = image
Next you can extract the XMP metadata.

View file

@ -15,6 +15,7 @@ Contents:
introduction
detailed_installation
how_do_i
api
whatsnew/index
roadmap

View file

@ -5,16 +5,13 @@ Glymur: a Python interface for JPEG 2000
**Glymur** is an interface to the OpenJPEG library
which allows one to read and write JPEG 2000 files from Python.
Glymur supports both reading and writing of JPEG 2000 images, but writing
JPEG 2000 images is currently limited to images that can fit in memory.
**Glymur** can read images using OpenJPEG library versions as far back as 1.3,
but it is strongly recommended to use version 2.1.0, which is the most recently
released version of OpenJPEG at this time.
JPEG 2000 images is currently limited to images that can fit in memory
In regards to metadata, most JP2 boxes are properly interpreted.
Certain optional JP2 boxes can also be written, including XML boxes and
XMP UUIDs. There is incomplete support for reading JPX metadata.
Glymur works on Python versions 2.7, 3.3 and 3.4. If you have Python 2.6,
Glymur 0.6 works on Python versions 2.7, 3.3 and 3.4. If you have Python 2.6,
you should use the 0.5 series of Glymur.
For more information about OpenJPEG, please consult http://www.openjpeg.org.
@ -30,6 +27,5 @@ but you should also be able to install Glymur via pip ::
$ pip install glymur
In addition to the package, this also gives you a command line script
**jp2dump** that can be used from the command line line to print JPEG 2000
metadata.
In addition to the package, this also gives you a script **jp2dump** that can
be used from the command line line to print JPEG 2000 metadata.

View file

@ -1,27 +0,0 @@
=====================
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
=================
* implemented :py:meth:`__getitem__`, :py:meth:`__setitem__` support
* added back windows support
* box_id and longname are class attributes now instead of instance
attributes

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,5 @@ These document the changes between minor (or major) versions of glymur.
.. toctree::
0.8
0.7
0.6
0.5
0.6

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

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

View file

@ -1,6 +1,9 @@
"""
Configure glymur to use installed libraries if possible.
"""
# configparser is new in python3 (pylint/python-2.7)
# pylint: disable=F0401
import ctypes
from ctypes.util import find_library
import os
@ -15,22 +18,6 @@ else:
from configparser import ConfigParser
from configparser import NoOptionError
# default library locations for MacPorts
_macports_default_location = {'openjp2': '/opt/local/lib/libopenjp2.dylib',
'openjpeg': '/opt/local/lib/libopenjpeg.dylib'}
# default library locations on Windows
_windows_default_location = {'openjp2': os.path.join('C:\\',
'Program files',
'OpenJPEG 2.0',
'bin',
'openjp2.dll'),
'openjpeg': os.path.join('C:\\',
'Program files',
'OpenJPEG 1.5',
'bin',
'openjpeg.dll')}
def glymurrc_fname():
"""Return the path to the configuration file.
@ -56,23 +43,52 @@ def glymurrc_fname():
return None
def load_openjpeg_library(libname):
def load_openjpeg(path):
"""Load the openjpeg library, falling back on defaults if necessary.
path = read_config_file(libname)
if path is not None:
return load_library_handle(path)
Parameters
----------
path : str
Path to openjpeg 1.5 library as specified by configuration file. Will
be None if no configuration file specified.
"""
if path is None:
# Let ctypes try to find it.
path = find_library('openjpeg')
# No location specified by the configuration file, must look for it
# elsewhere.
path = find_library(libname)
# If we could not find it, then look in some likely locations on mac
# and win.
if path is None:
# Could not find a library via ctypes
if platform.system() == 'Darwin':
# MacPorts
path = '/opt/local/lib/libopenjpeg.dylib'
elif os.name == 'nt':
path = os.path.join('C:\\', 'Program files', 'OpenJPEG 1.5',
'bin', 'openjpeg.dll')
if path is not None and not os.path.exists(path):
# the mac/win default location does not exist.
return None
return load_library_handle(path)
def load_openjp2(path):
"""Load the openjp2 library, falling back on defaults if necessary.
"""
if path is None:
# No help from the config file, try to find it via ctypes.
path = find_library('openjp2')
if path is None:
# Could not find a library via ctypes
if platform.system() == 'Darwin':
# MacPorts
path = _macports_default_location[libname]
path = '/opt/local/lib/libopenjp2.dylib'
elif os.name == 'nt':
path = _windows_default_location[libname]
path = os.path.join('C:\\', 'Program files', 'OpenJPEG 2.0',
'bin', 'openjp2.dll')
if path is not None and not os.path.exists(path):
# the mac/win default location does not exist.
@ -84,11 +100,10 @@ def load_openjpeg_library(libname):
def load_library_handle(path):
"""Load the library, return the ctypes handle."""
if path is None or path in ['None', 'none']:
# Either could not find a library via ctypes or
# user-configuration-file, or we could not find it in any of the
# default locations, or possibly the user intentionally does not want
# one of the libraries to load.
if path is None:
# Either could not find a library via ctypes or user-configuration-file,
# or we could not find it in any of the default locations.
# This is probably a very old linux.
return None
try:
@ -97,59 +112,47 @@ def load_library_handle(path):
else:
opj_lib = ctypes.CDLL(path)
except (TypeError, OSError):
msg = 'The library specified by configuration file at {0} could not '
msg += 'be loaded.'
warnings.warn(msg.format(path), UserWarning)
opj_lib = None
msg = '"Library {0}" could not be loaded. Operating in degraded mode.'
msg = msg.format(path)
warnings.warn(msg, UserWarning)
opj_lib = None
return opj_lib
def read_config_file(libname):
def read_config_file():
"""
Extract library locations from a configuration file.
Parameters
----------
libname : str
One of either 'openjp2' or 'openjpeg'
Returns
-------
path : None or str
None if no location is specified, otherwise a path to the library
We must use a configuration file that the user must write.
"""
lib = {'openjp2': None, 'openjpeg': None}
filename = glymurrc_fname()
if filename is None:
# There's no library file path to return in this case.
return None
if filename is not None:
# Read the configuration file for the library location.
parser = ConfigParser()
parser.read(filename)
try:
lib['openjp2'] = parser.get('library', 'openjp2')
except NoOptionError:
pass
try:
lib['openjpeg'] = parser.get('library', 'openjpeg')
except NoOptionError:
pass
# Read the configuration file for the library location.
parser = ConfigParser()
parser.read(filename)
try:
path = parser.get('library', libname)
except NoOptionError:
path = None
return path
return lib
def glymur_config():
"""Try to ascertain locations of openjp2, openjpeg libraries.
"""
Try to ascertain locations of openjp2, openjpeg libraries.
Returns
-------
tpl : tuple
tuple of library handles
"""
lst = []
for libname in ['openjp2', 'openjpeg']:
lst.append(load_openjpeg_library(libname))
if all(handle is None for handle in lst):
libs = read_config_file()
libopenjp2_handle = load_openjp2(libs['openjp2'])
libopenjpeg_handle = load_openjpeg(libs['openjpeg'])
if libopenjp2_handle is None and libopenjpeg_handle is None:
msg = "Neither the openjp2 nor the openjpeg library could be loaded. "
warnings.warn(msg)
return tuple(lst)
msg += "Operating in severely degraded mode."
warnings.warn(msg, UserWarning)
return libopenjp2_handle, libopenjpeg_handle
def get_configdir():

View file

@ -2,16 +2,15 @@
Wraps individual functions in openjp2 library.
"""
# pylint: disable=C0302,R0903,W0201
import ctypes
import re
import sys
import textwrap
from .config import glymur_config
OPENJP2, OPENJPEG = glymur_config()
def version():
"""Wrapper for opj_version library routine."""
try:
@ -49,6 +48,13 @@ JPWL_MAX_NO_TILESPECS = 16
TRUE = 1
FALSE = 0
#PROFILE = {'none': 0, # No profile
# 0: 1, # Profile 0
# 1: 2, # Profile 1
# 'part2': 0x8000, # At least one extension
# 'Cinema2K': 0x0003, # 2K cinema profile
# 'Cinema4K': 0x0004, # 4K cinema profile
# supported color spaces
CLRSPC_UNKNOWN = -1
CLRSPC_UNSPECIFIED = 0
@ -131,13 +137,6 @@ class PocType(ctypes.Structure):
("tx0_t", ctypes.c_uint32),
("ty0_t", ctypes.c_uint32)]
def __str__(self):
msg = "{0}:\n".format(self.__class__)
for field_name, _ in self._fields_:
msg += " {0}: {1}\n".format(
field_name, getattr(self, field_name))
return msg
class DecompressionParametersType(ctypes.Structure):
"""Decompression parameters.
@ -201,13 +200,6 @@ class DecompressionParametersType(ctypes.Structure):
# maximum number of tiles
("flags", ctypes.c_uint32)]
def __str__(self):
msg = "{0}:\n".format(self.__class__)
for field_name, _ in self._fields_:
msg += " {0}: {1}\n".format(
field_name, getattr(self, field_name))
return msg
class CompressionParametersType(ctypes.Structure):
"""Compression parameters.
@ -399,46 +391,6 @@ class CompressionParametersType(ctypes.Structure):
# values.
_fields_.append(("rsiz", ctypes.c_uint16))
def __str__(self):
msg = "{0}:\n".format(self.__class__)
for field_name, _ in self._fields_:
if field_name == 'poc':
msg += " numpocs: {0}\n".format(self.numpocs)
for j in range(self.numpocs):
msg += " [#{0}]:".format(j)
msg += " {0}".format(str(self.poc[j]))
elif field_name in ['tcp_rates', 'tcp_distoratio']:
lst = []
arr = getattr(self, field_name)
lst = [arr[j] for j in range(self.tcp_numlayers)]
msg += " {0}: {1}\n".format(field_name, lst)
elif field_name in ['prcw_init', 'prch_init']:
pass
elif field_name == 'res_spec':
prcw_init = [self.prcw_init[j] for j in range(self.res_spec)]
prch_init = [self.prch_init[j] for j in range(self.res_spec)]
msg += " res_spec: {0}\n".format(self.res_spec)
msg += " prch_init: {0}\n".format(prch_init)
msg += " prcw_init: {0}\n".format(prcw_init)
elif field_name in [
'jpwl_hprot_tph_tileno', 'jpwl_hprot_tph',
'jpwl_pprot_tileno', 'jpwl_pprot_packno', 'jpwl_pprot',
'jpwl_sens_tph_tileno', 'jpwl_sens_tph']:
arr = getattr(self, field_name)
lst = [arr[j] for j in range(JPWL_MAX_NO_TILESPECS)]
msg += " {0}: {1}\n".format(field_name, lst)
else:
msg += " {0}: {1}\n".format(
field_name, getattr(self, field_name))
return msg
class ImageCompType(ctypes.Structure):
"""Defines a single image component.
@ -480,14 +432,6 @@ class ImageCompType(ctypes.Structure):
if _MINOR == '1':
_fields_.append(("alpha", ctypes.c_uint16))
def __str__(self):
msg = "{0}:\n".format(self.__class__)
for field_name, _ in self._fields_:
msg += " {0}: {1}\n".format(
field_name, getattr(self, field_name))
return msg
class ImageType(ctypes.Structure):
"""Defines image data and characteristics.
@ -518,26 +462,6 @@ class ImageType(ctypes.Structure):
# restricted ICC profile buffer length
("icc_profile_len", ctypes.c_uint32)]
def __str__(self):
msg = "{0}:\n".format(self.__class__)
for field_name, _ in self._fields_:
if field_name == "numcomps":
msg += " numcomps: {0}\n".format(self.numcomps)
for j in range(self.numcomps):
msg += " comps[#{0}]:\n".format(j)
msg += textwrap.indent(str(self.comps[j]), ' ' * 12)
elif field_name == "comps":
# handled above
pass
else:
msg += " {0}: {1}\n".format(
field_name, getattr(self, field_name))
return msg
class ImageComptParmType(ctypes.Structure):
"""Component parameters structure used by image_create function.
@ -567,12 +491,106 @@ class ImageComptParmType(ctypes.Structure):
# signed (1) / unsigned (0)
("sgnd", ctypes.c_uint32)]
def __str__(self):
msg = "{0}:\n".format(self.__class__)
for field_name, _ in self._fields_:
msg += " {0}: {1}\n".format(
field_name, getattr(self, field_name))
return msg
class TccpInfo(ctypes.Structure):
"""Tile-component coding parameters information.
Corresponds to tccp_info_t type in openjp2 header file.
"""
_fields_ = [
# component index
("compno", ctypes.c_uint32),
# coding style
("csty", ctypes.c_uint32),
# number of resolutions
("numresolutions", ctypes.c_uint32),
# code-blocks width
("cblkw", ctypes.c_uint32),
# code-blocks height
("cblkh", ctypes.c_uint32),
# code-block coding style
("cblksty", ctypes.c_uint32),
# discrete wavelet transform identifier
("qmfbid", ctypes.c_uint32),
# quantization style
("qntsty", ctypes.c_uint32),
# stepsizes used for quantization
("stepsizes_mant", ctypes.c_uint32 * J2K_MAXBANDS),
("stepsizes_expn", ctypes.c_uint32 * J2K_MAXBANDS),
# stepsizes used for quantization
("numgbits", ctypes.c_uint32),
# region of interest shift
("roishift", ctypes.c_int32),
# precinct width
("prcw", ctypes.c_uint32 * J2K_MAXRLVLS),
# precinct width
("prch", ctypes.c_uint32 * J2K_MAXRLVLS)]
class TileInfoV2(ctypes.Structure):
"""Tile coding parameters information
Corresponds to tile_info_v2_t type in openjp2 headers.
"""
_fields_ = [
# number (index) of tile
("tileno", ctypes.c_int32),
# coding style
("csty", ctypes.c_uint32),
# progression order
("prg", PROG_ORDER_TYPE),
# number of layers
("numlayers", ctypes.c_uint32),
# multi-component transform identifier
("mct", ctypes.c_uint32),
# information concerning tile component parameters
("tccp_info", ctypes.POINTER(TccpInfo))]
class CodestreamInfoV2(ctypes.Structure):
"""information about the codestream.
Corresponds to codestream_info_v2_t type in openjp2 header files.
"""
_fields_ = [
# tile info
# tile origin in x, y (XTOsiz, YTOsiz)
("tx0", ctypes.c_uint32),
("ty0", ctypes.c_uint32),
# tile size in x, y = XTsiz, YTsiz
("tdx", ctypes.c_uint32),
("tdy", ctypes.c_uint32),
# number of tiles in X, Y
("tw", ctypes.c_uint32),
("th", ctypes.c_uint32),
# number of components
("nbcomps", ctypes.c_uint32),
# default information regarding tiles inside of image
("m_default_tile_info", TileInfoV2),
# information regarding tiles inside of image
("tile_info", ctypes.POINTER(TileInfoV2))]
def check_error(status):
@ -737,6 +755,28 @@ def encode(codec, stream):
OPENJP2.opj_encode(codec, stream)
def get_cstr_info(codec):
"""get the codestream information from the codec
Wraps the openjp2 library function opj_get_cstr_info.
Parameters
----------
codec : CODEC_TYPE
The jpeg2000 codec.
Returns
-------
cstr_info_p : CodestreamInfoV2
Reference to codestream information.
"""
OPENJP2.opj_get_cstr_info.argtypes = [CODEC_TYPE]
OPENJP2.opj_get_cstr_info.restype = ctypes.POINTER(CodestreamInfoV2)
cstr_info_p = OPENJP2.opj_get_cstr_info(codec)
return cstr_info_p
def get_decoded_tile(codec, stream, imagep, tile_index):
"""get the decoded tile from the codec
@ -767,6 +807,23 @@ def get_decoded_tile(codec, stream, imagep, tile_index):
OPENJP2.opj_get_decoded_tile(codec, stream, imagep, tile_index)
def destroy_cstr_info(cstr_info_p):
"""destroy codestream information after compression or decompression
Wraps the openjp2 library function opj_destroy_cstr_info.
Parameters
----------
cstr_info_p : CodestreamInfoV2 pointer
Pointer to codestream info structure.
"""
ARGTYPES = [ctypes.POINTER(ctypes.POINTER(CodestreamInfoV2))]
OPENJP2.opj_destroy_cstr_info.argtypes = ARGTYPES
OPENJP2.opj_destroy_cstr_info.restype = ctypes.c_void_p
OPENJP2.opj_destroy_cstr_info(ctypes.byref(cstr_info_p))
def end_compress(codec, stream):
"""End of compressing the current image.
@ -909,7 +966,7 @@ def read_header(stream, codec):
ARGTYPES = [STREAM_TYPE_P, CODEC_TYPE,
ctypes.POINTER(ctypes.POINTER(ImageType))]
OPENJP2.opj_read_header.argtypes = ARGTYPES
OPENJP2.opj_read_header.restype = check_error
OPENJP2.opj_read_header.restype = check_error
imagep = ctypes.POINTER(ImageType)()
OPENJP2.opj_read_header(stream, codec, ctypes.byref(imagep))
@ -1268,7 +1325,6 @@ def _stream_create_default_file_stream_2p0(fptr, isa_read_stream):
stream = OPENJP2.opj_stream_create_default_file_stream(fptr, read_stream)
return stream
def _stream_create_default_file_stream_2p1(fname, isa_read_stream):
"""Wraps openjp2 library function opj_stream_create_default_vile_stream.
@ -1295,7 +1351,7 @@ def _stream_create_default_file_stream_2p1(fname, isa_read_stream):
stream = OPENJP2.opj_stream_create_default_file_stream(file_argument,
read_stream)
return stream
if re.match(r'''2.0''', version()):
stream_create_default_file_stream = _stream_create_default_file_stream_2p0
else:

View file

@ -1,11 +1,14 @@
"""Wraps library calls to openjpeg.
"""
# pylint: disable=R0903
import ctypes
import sys
from .config import glymur_config
import numpy as np
from .config import glymur_config
_, OPENJPEG = glymur_config()
# Maximum number of tile parts expected by JPWL: increase at your will
@ -55,10 +58,8 @@ class CommonStructType(ctypes.Structure):
("mj2_handle", ctypes.c_void_p)]
STREAM_READ = 0x0001 # The stream was opened for reading.
STREAM_WRITE = 0x0002 # The stream was opened for writing.
STREAM_READ = 0x0001 # The stream was opened for reading.
STREAM_WRITE = 0x0002 # The stream was opened for writing.
class CioType(ctypes.Structure):
"""Byte input-output stream (CIO)
@ -89,57 +90,70 @@ class CompressionInfoType(CommonStructType):
class PocType(ctypes.Structure):
"""Progression order changes."""
_fields_ = [("resno", ctypes.c_int),
# Resolution num start, Component num start, given by POC
("compno0", ctypes.c_int),
# Resolution num start, Component num start, given by POC
("compno0", ctypes.c_int),
# Layer num end,Resolution num end, Component num end, given
# by POC
("layno1", ctypes.c_int),
("resno1", ctypes.c_int),
("compno1", ctypes.c_int),
# Layer num end,Resolution num end, Component num end, given by POC
("layno1", ctypes.c_int),
("resno1", ctypes.c_int),
("compno1", ctypes.c_int),
# Layer num start,Precinct num start, Precinct num end
("layno0", ctypes.c_int),
("precno0", ctypes.c_int),
("precno1", ctypes.c_int),
# Layer num start,Precinct num start, Precinct num end
("layno0", ctypes.c_int),
("precno0", ctypes.c_int),
("precno1", ctypes.c_int),
# Progression order enum
# OPJ_PROG_ORDER prg1,prg;
("prg1", ctypes.c_int),
("prg", ctypes.c_int),
# Progression order enum
# OPJ_PROG_ORDER prg1,prg;
("prg1", ctypes.c_int),
("prg", ctypes.c_int),
# Progression order string
# char progorder[5];
("progorder", ctypes.c_char * 5),
# Progression order string
# char progorder[5];
("progorder", ctypes.c_char * 5),
# Tile number
# int tile;
("tile", ctypes.c_int),
# Tile number
# int tile;
("tile", ctypes.c_int),
("tx0", ctypes.c_int),
("tx1", ctypes.c_int),
("ty0", ctypes.c_int),
("ty1", ctypes.c_int),
("layS", ctypes.c_int),
("resS", ctypes.c_int),
("compS", ctypes.c_int),
("prcS", ctypes.c_int),
("layE", ctypes.c_int),
("resE", ctypes.c_int),
("compE", ctypes.c_int),
("prcE", ctypes.c_int),
("txS", ctypes.c_int),
("txE", ctypes.c_int),
("tyS", ctypes.c_int),
("tyE", ctypes.c_int),
("dx", ctypes.c_int),
("dy", ctypes.c_int),
("lay_t", ctypes.c_int),
("res_t", ctypes.c_int),
("comp_t", ctypes.c_int),
("prc_t", ctypes.c_int),
("tx0_t", ctypes.c_int),
("ty0_t", ctypes.c_int)]
# /** Start and end values for Tile width and height*/
# int tx0,tx1,ty0,ty1;
("tx0", ctypes.c_int),
("tx1", ctypes.c_int),
("ty0", ctypes.c_int),
("ty1", ctypes.c_int),
# /** Start value, initialised in pi_initialise_encode*/
# int layS, resS, compS, prcS;
("layS", ctypes.c_int),
("resS", ctypes.c_int),
("compS", ctypes.c_int),
("prcS", ctypes.c_int),
# /** End value, initialised in pi_initialise_encode */
# int layE, resE, compE, prcE;
("layE", ctypes.c_int),
("resE", ctypes.c_int),
("compE", ctypes.c_int),
("prcE", ctypes.c_int),
# Start and end values of Tile width and height, initialised in
# pi_initialise_encode int txS,txE,tyS,tyE,dx,dy;
("txS", ctypes.c_int),
("txE", ctypes.c_int),
("tyS", ctypes.c_int),
("tyE", ctypes.c_int),
("dx", ctypes.c_int),
("dy", ctypes.c_int),
# Temporary values for Tile parts, initialised in pi_create_encode
# int lay_t, res_t, comp_t, prc_t,tx0_t,ty0_t;
("lay_t", ctypes.c_int),
("res_t", ctypes.c_int),
("comp_t", ctypes.c_int),
("prc_t", ctypes.c_int),
("tx0_t", ctypes.c_int),
("ty0_t", ctypes.c_int)]
class CompressionParametersType(ctypes.Structure):
@ -360,47 +374,48 @@ class DecompressionParametersType(ctypes.Structure):
class ImageComptParmType(ctypes.Structure):
"""Component parameters structure used by the opj_image_create function.
"""
_fields_ = [("dx", ctypes.c_int),
# XRsiz: horizontal separation of a sample of ith component
# with respect to the reference grid
_fields_ = [
# XRsiz: horizontal separation of a sample of ith component with
# respect to the reference grid
("dx", ctypes.c_int),
# YRsiz: vertical separation of a sample of ith component with
# respect to the reference grid */
("dy", ctypes.c_int),
# YRsiz: vertical separation of a sample of ith component with
# respect to the reference grid */
("dy", ctypes.c_int),
# data width, height
("w", ctypes.c_int),
("h", ctypes.c_int),
# data width, height
("w", ctypes.c_int),
("h", ctypes.c_int),
# x component offset compared to the whole image
# y component offset compared to the whole image
("x0", ctypes.c_int),
("y0", ctypes.c_int),
# x component offset compared to the whole image
# y component offset compared to the whole image
("x0", ctypes.c_int),
("y0", ctypes.c_int),
# precision
('prec', ctypes.c_int),
# precision
('prec', ctypes.c_int),
# image depth in bits
('bpp', ctypes.c_int),
# image depth in bits
('bpp', ctypes.c_int),
# signed (1) / unsigned (0)
('sgnd', ctypes.c_int)]
# signed (1) / unsigned (0)
('sgnd', ctypes.c_int)]
class ImageCompType(ctypes.Structure):
"""Defines a single image component. """
_fields_ = [("dx", ctypes.c_int),
("dy", ctypes.c_int),
("w", ctypes.c_int),
("h", ctypes.c_int),
("x0", ctypes.c_int),
("y0", ctypes.c_int),
("prec", ctypes.c_int),
("bpp", ctypes.c_int),
("sgnd", ctypes.c_int),
("resno_decoded", ctypes.c_int),
("factor", ctypes.c_int),
("data", ctypes.POINTER(ctypes.c_int))]
("dy", ctypes.c_int),
("w", ctypes.c_int),
("h", ctypes.c_int),
("x0", ctypes.c_int),
("y0", ctypes.c_int),
("prec", ctypes.c_int),
("bpp", ctypes.c_int),
("sgnd", ctypes.c_int),
("resno_decoded", ctypes.c_int),
("factor", ctypes.c_int),
("data", ctypes.POINTER(ctypes.c_int))]
class ImageType(ctypes.Structure):
@ -452,7 +467,6 @@ def cio_tell(cio):
pos = OPENJPEG.cio_tell(cio)
return pos
def create_compress(fmt):
"""Wrapper for openjpeg library function opj_create_compress.
@ -523,11 +537,56 @@ def destroy_decompress(dinfo):
OPENJPEG.opj_destroy_decompress(dinfo)
def image_cmptparm_t_from_np(np_image):
"""Return appropriate image_cmptparm_t based on given numpy array.
"""
try:
num_comps = np_image.shape[2]
except IndexError:
num_comps = 1
cmpt_parm_array_t = ImageCmptparmType * num_comps
tarr = cmpt_parm_array_t()
if np_image.dtype == np.uint8:
prec = 8
bpp = 8
sgnd = 0
elif np_image.dtype == np.int8:
prec = 8
bpp = 8
sgnd = 1
elif np_image.dtype == np.uint16:
prec = 16
bpp = 16
sgnd = 0
elif np_image.dtype == np.int16:
prec = 16
bpp = 16
sgnd = 1
else:
raise(TypeError("unhandled"))
for j in range(0, num_comps):
tarr[j].dx = 1
tarr[j].dy = 1
tarr[j].w = np_image.shape[1]
tarr[j].h = np_image.shape[0]
tarr[j].x0 = 0
tarr[j].y0 = 0
tarr[j].prec = prec
tarr[j].bpp = bpp
tarr[j].sgnd = sgnd
return(tarr)
def image_create(cmptparms, cspace):
"""Wrapper for openjpeg library function opj_image_create.
"""
lst = [ctypes.c_int, ctypes.POINTER(ImageComptParmType), ctypes.c_int]
OPENJPEG.opj_image_create.argtypes = lst
OPENJPEG.opj_image_create.argtypes = [ctypes.c_int,
ctypes.POINTER(ImageComptParmType),
ctypes.c_int]
OPENJPEG.opj_image_create.restype = ctypes.POINTER(ImageType)
image = OPENJPEG.opj_image_create(len(cmptparms), cmptparms, cspace)

View file

@ -1,144 +0,0 @@
decompression_parameters_type = """<class 'glymur.lib.openjp2.DecompressionParametersType'>:
cp_reduce: 0
cp_layer: 0
infile: b''
outfile: b''
decod_format: -1
cod_format: -1
DA_x0: 0
DA_x1: 0
DA_y0: 0
DA_y1: 0
m_verbose: 0
tile_index: 0
nb_tile_to_decode: 0
jpwl_correct: 0
jpwl_exp_comps: 0
jpwl_max_tiles: 0
flags: 0"""
default_progression_order_changes_type = """<class 'glymur.lib.openjp2.PocType'>:
resno0: 0
compno0: 0
layno1: 0
resno1: 0
compno1: 0
layno0: 0
precno0: 0
precno1: 0
prg1: 0
prg: 0
progorder: b''
tile: 0
tx0: 0
tx1: 0
ty0: 0
ty1: 0
layS: 0
resS: 0
compS: 0
prcS: 0
layE: 0
resE: 0
compE: 0
prcE: 0
txS: 0
txE: 0
tyS: 0
tyE: 0
dx: 0
dy: 0
lay_t: 0
res_t: 0
comp_t: 0
prec_t: 0
tx0_t: 0
ty0_t: 0"""
default_compression_parameters_type = """<class 'glymur.lib.openjp2.CompressionParametersType'>:
tile_size_on: 0
cp_tx0: 0
cp_ty0: 0
cp_tdx: 0
cp_tdy: 0
cp_disto_alloc: 0
cp_fixed_alloc: 0
cp_fixed_quality: 0
cp_matrice: None
cp_comment: None
csty: 0
prog_order: 0
numpocs: 0
numpocs: 0
tcp_numlayers: 0
tcp_rates: []
tcp_distoratio: []
numresolution: 6
cblockw_init: 64
cblockh_init: 64
mode: 0
irreversible: 0
roi_compno: -1
roi_shift: 0
res_spec: 0
prch_init: []
prcw_init: []
infile: b''
outfile: b''
index_on: 0
index: b''
image_offset_x0: 0
image_offset_y0: 0
subsampling_dx: 1
subsampling_dy: 1
decod_format: -1
cod_format: -1
jpwl_epc_on: 0
jpwl_hprot_mh: 0
jpwl_hprot_tph_tileno: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
jpwl_hprot_tph: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
jpwl_pprot_tileno: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
jpwl_pprot_packno: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
jpwl_pprot: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
jpwl_sens_size: 0
jpwl_sens_addr: 0
jpwl_sens_range: 0
jpwl_sens_mh: 0
jpwl_sens_tph_tileno: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
jpwl_sens_tph: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
cp_cinema: 0
max_comp_size: 0
cp_rsiz: 0
tp_on: 0
tp_flag: 0
tcp_mct: 0
jpip_on: 0
mct_data: None
max_cs_size: 0
rsiz: 0"""
default_image_component_parameters = """<class 'glymur.lib.openjp2.ImageComptParmType'>:
dx: 0
dy: 0
w: 0
h: 0
x0: 0
y0: 0
prec: 0
bpp: 0
sgnd: 0"""
# The "icc_profile_buf" field is problematic as it is a pointer value, i.e.
#
# icc_profile_buf: <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.
"""
# R0904: Seems like pylint is fooled in this situation
# W0142: using kwargs is ok in this context
# pylint: disable=R0904,W0142
import os
import re
import sys
import tempfile
import unittest
@ -16,8 +21,8 @@ from glymur.lib import openjp2
@unittest.skipIf(openjp2.OPENJP2 is None,
"Missing openjp2 library.")
@unittest.skipIf(re.match(r'''(1|2.0)''',
glymur.version.openjpeg_version) is not None,
"Not to be run until 2.1.0")
glymur.version.openjpeg_version) is not None,
"Not to be run until 2.1.0")
class TestOpenJP2(unittest.TestCase):
"""Test openjp2 library functionality.
@ -51,6 +56,53 @@ class TestOpenJP2(unittest.TestCase):
self.assertEqual(dparams.DA_x1, 0)
self.assertEqual(dparams.DA_y1, 0)
def tile_macro(self, codec, stream, imagep, tidx):
"""called only by j2k_random_tile_access"""
openjp2.get_decoded_tile(codec, stream, imagep, tidx)
for j in range(imagep.contents.numcomps):
self.assertIsNotNone(imagep.contents.comps[j].data)
def j2k_random_tile_access(self, filename, codec_format=None):
"""fixture called by the test_rtaX methods"""
dparam = openjp2.set_default_decoder_parameters()
infile = filename.encode()
nelts = openjp2.PATH_LEN - len(infile)
infile += b'0' * nelts
dparam.infile = infile
dparam.decod_format = codec_format
codec = openjp2.create_decompress(codec_format)
openjp2.set_info_handler(codec, None)
openjp2.set_warning_handler(codec, None)
openjp2.set_error_handler(codec, None)
stream = openjp2.stream_create_default_file_stream(filename, True)
openjp2.setup_decoder(codec, dparam)
image = openjp2.read_header(stream, codec)
cstr_info = openjp2.get_cstr_info(codec)
tile_ul = 0
tile_ur = cstr_info.contents.tw - 1
tile_lr = cstr_info.contents.tw * cstr_info.contents.th - 1
tile_ll = tile_lr - cstr_info.contents.tw
self.tile_macro(codec, stream, image, tile_ul)
self.tile_macro(codec, stream, image, tile_ur)
self.tile_macro(codec, stream, image, tile_lr)
self.tile_macro(codec, stream, image, tile_ll)
openjp2.destroy_cstr_info(cstr_info)
openjp2.end_decompress(codec, stream)
openjp2.destroy_codec(codec)
openjp2.stream_destroy(stream)
openjp2.image_destroy(image)
def test_tte0(self):
"""Runs test designated tte0 in OpenJPEG test suite."""
with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile:
@ -108,6 +160,15 @@ class TestOpenJP2(unittest.TestCase):
tile_decoder(**kwargs)
self.assertTrue(True)
def test_rta1(self):
"""Runs test designated rta1 in OpenJPEG test suite."""
with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile:
self.xtx1_setup(tfile.name)
codec_format = openjp2.CODEC_J2K
self.j2k_random_tile_access(tfile.name, codec_format)
self.assertTrue(True)
def test_tte2(self):
"""Runs test designated tte2 in OpenJPEG test suite."""
with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile:
@ -129,25 +190,62 @@ class TestOpenJP2(unittest.TestCase):
tile_decoder(**kwargs)
self.assertTrue(True)
def test_rta2(self):
"""Runs test designated rta2 in OpenJPEG test suite."""
with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile:
xtx2_setup(tfile.name)
codec_format = openjp2.CODEC_JP2
self.j2k_random_tile_access(tfile.name, codec_format)
def test_tte3(self):
"""Runs test designated tte3 in OpenJPEG test suite."""
with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile:
xtx3_setup(tfile.name)
self.assertTrue(True)
def test_rta3(self):
"""Runs test designated rta3 in OpenJPEG test suite."""
with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile:
xtx3_setup(tfile.name)
codec_format = openjp2.CODEC_J2K
self.j2k_random_tile_access(tfile.name, codec_format)
self.assertTrue(True)
def test_tte4(self):
"""Runs test designated tte4 in OpenJPEG test suite."""
with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile:
xtx4_setup(tfile.name)
self.assertTrue(True)
def test_rta4(self):
"""Runs test designated rta4 in OpenJPEG test suite."""
with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile:
xtx4_setup(tfile.name)
codec_format = openjp2.CODEC_J2K
self.j2k_random_tile_access(tfile.name, codec_format)
def test_tte5(self):
"""Runs test designated tte5 in OpenJPEG test suite."""
with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile:
xtx5_setup(tfile.name)
self.assertTrue(True)
def test_rta5(self):
"""Runs test designated rta5 in OpenJPEG test suite."""
with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile:
xtx5_setup(tfile.name)
codec_format = openjp2.CODEC_J2K
self.j2k_random_tile_access(tfile.name, codec_format)
#def tile_encoder(num_comps=None, tile_width=None, tile_height=None,
# filename=None, codec=None, comp_prec=None,
# image_width=None, image_height=None,
# irreversible=None):
def tile_encoder(**kwargs):
"""Fixture used by many tests."""
num_tiles = ((kwargs['image_width'] / kwargs['tile_width']) *
@ -211,7 +309,7 @@ def tile_encoder(**kwargs):
openjp2.setup_encoder(codec, l_param, l_image)
stream = openjp2.stream_create_default_file_stream(kwargs['filename'],
False)
False)
openjp2.start_compress(codec, l_image, stream)
for j in np.arange(num_tiles):
@ -222,14 +320,13 @@ def tile_encoder(**kwargs):
openjp2.destroy_codec(codec)
openjp2.image_destroy(l_image)
def tile_decoder(**kwargs):
"""Fixture called with various configurations by many tests.
Reads a tile. That's all it does.
"""
stream = openjp2.stream_create_default_file_stream(kwargs['filename'],
True)
True)
dparam = openjp2.set_default_decoder_parameters()
dparam.decod_format = kwargs['codec_format']
@ -267,7 +364,6 @@ def tile_decoder(**kwargs):
openjp2.stream_destroy(stream)
openjp2.image_destroy(image)
def ttx0_setup(filename):
"""Runs tests tte0, tte0."""
kwargs = {'filename': filename,
@ -281,7 +377,6 @@ def ttx0_setup(filename):
'tile_width': 100}
tile_encoder(**kwargs)
def xtx2_setup(filename):
"""Runs tests rta2, tte2, ttd2."""
kwargs = {'filename': filename,
@ -295,7 +390,6 @@ def xtx2_setup(filename):
'tile_width': 128}
tile_encoder(**kwargs)
def xtx3_setup(filename):
"""Runs tests tte3, rta3."""
kwargs = {'filename': filename,
@ -309,7 +403,6 @@ def xtx3_setup(filename):
'tile_width': 128}
tile_encoder(**kwargs)
def xtx4_setup(filename):
"""Runs tests rta4, tte4."""
kwargs = {'filename': filename,
@ -323,7 +416,6 @@ def xtx4_setup(filename):
'tile_width': 128}
tile_encoder(**kwargs)
def xtx5_setup(filename):
"""Runs tests rta5, tte5."""
kwargs = {'filename': filename,
@ -336,3 +428,6 @@ def xtx5_setup(filename):
'tile_height': 256,
'tile_width': 256}
tile_encoder(**kwargs)
if __name__ == "__main__":
unittest.main()

View file

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

View file

@ -1,75 +0,0 @@
# -*- coding: utf-8 -*-
"""Test suite for printing.
"""
import re
import sys
import unittest
if sys.hexversion < 0x03000000:
from mock import patch
from StringIO import StringIO
else:
from unittest.mock import patch
from io import StringIO
import glymur
from . import fixtures
@unittest.skipIf(sys.hexversion < 0x03000000, "do not care about 2.7 here")
@unittest.skipIf(re.match('0|1|2.0', glymur.version.openjpeg_version),
"Requires openjpeg 2.1.0 or higher")
class TestPrintingOpenjp2(unittest.TestCase):
"""Tests for verifying how printing works on openjp2 library structures."""
def setUp(self):
self.jp2file = glymur.data.nemo()
def tearDown(self):
pass
def test_decompression_parameters(self):
"""printing DecompressionParametersType"""
dparams = glymur.lib.openjp2.set_default_decoder_parameters()
with patch('sys.stdout', new=StringIO()) as fake_out:
print(dparams)
actual = fake_out.getvalue().strip()
expected = fixtures.decompression_parameters_type
self.assertEqual(actual, expected)
def test_progression_order_changes(self):
"""printing PocType"""
ptype = glymur.lib.openjp2.PocType()
with patch('sys.stdout', new=StringIO()) as fake_out:
print(ptype)
actual = fake_out.getvalue().strip()
expected = fixtures.default_progression_order_changes_type
self.assertEqual(actual, expected)
def test_default_compression_parameters(self):
"""printing default compression parameters"""
cparams = glymur.lib.openjp2.set_default_encoder_parameters()
with patch('sys.stdout', new=StringIO()) as fake_out:
print(cparams)
actual = fake_out.getvalue().strip()
expected = fixtures.default_compression_parameters_type
self.assertEqual(actual, expected)
def test_default_component_parameters(self):
"""printing default image component parameters"""
icpt = glymur.lib.openjp2.ImageComptParmType()
with patch('sys.stdout', new=StringIO()) as fake_out:
print(icpt)
actual = fake_out.getvalue().strip()
expected = fixtures.default_image_component_parameters
self.assertEqual(actual, expected)
def test_default_image_type(self):
"""printing default image type"""
it = glymur.lib.openjp2.ImageType()
with patch('sys.stdout', new=StringIO()) as fake_out:
print(it)
actual = fake_out.getvalue().strip()
expected = fixtures.default_image_type
self.assertRegex(actual, expected)

View file

@ -5,7 +5,6 @@ import os
import re
import sys
import textwrap
import unittest
import warnings
import numpy as np
@ -13,14 +12,6 @@ import six
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.
# Only use when the version is 1.7 or higher.
# And moreover, we only test using the 3.x infrastructure, never on 2.x.
@ -31,131 +22,7 @@ if sys.hexversion < 0x03000000:
WARNING_INFRASTRUCTURE_MSG = "3.x warning infrastructure only"
elif re.match('1.[0-6]', six.__version__) is not None:
WARNING_INFRASTRUCTURE_ISSUE = True
msg = "Cannot run test with version {0} of python-six"
WARNING_INFRASTRUCTURE_MSG = msg.format(six.__version__)
# Cannot reopen a named temporary file in windows.
WINDOWS_TMP_FILE_MSG = "cannot use NamedTemporaryFile like this in windows"
class MetadataBase(unittest.TestCase):
"""
Base class for testing metadata.
This class has helper routines defined for testing metadata so that it can
be subclassed and used easily.
"""
def setUp(self):
pass
def tearDown(self):
pass
def verify_codeblock_style(self, actual, style):
"""
Verify the code-block style for SPcod and SPcoc parameters.
This information is stored in a single byte. Please reference
Table A-17 in FCD15444-1
"""
expected = 0
if style[0]:
# Selective arithmetic coding bypass
expected |= 0x01
if style[1]:
# Reset context probabilities
expected |= 0x02
if style[2]:
# Termination on each coding pass
expected |= 0x04
if style[3]:
# Vertically causal context
expected |= 0x08
if style[4]:
# Predictable termination
expected |= 0x10
if style[5]:
# Segmentation symbols
expected |= 0x20
self.assertEqual(actual, expected)
def verifySignatureBox(self, box):
"""
The signature box is a constant.
"""
self.assertEqual(box.signature, (13, 10, 135, 10))
def verify_filetype_box(self, actual, expected):
"""
All JP2 files should have a brand reading 'jp2 ' and just a single
entry in the compatibility list, also 'jp2 '. JPX files can have more
compatibility items.
"""
self.assertEqual(actual.brand, expected.brand)
self.assertEqual(actual.minor_version, expected.minor_version)
self.assertEqual(actual.minor_version, 0)
for cl in expected.compatibility_list:
self.assertIn(cl, actual.compatibility_list)
def verifyRGNsegment(self, actual, expected):
"""
verify the fields of a RGN segment
"""
self.assertEqual(actual.crgn, expected.crgn) # 0 = component
self.assertEqual(actual.srgn, expected.srgn) # 0 = implicit
self.assertEqual(actual.sprgn, expected.sprgn)
def verifySOTsegment(self, actual, expected):
"""
verify the fields of a SOT (start of tile) segment
"""
self.assertEqual(actual.isot, expected.isot)
self.assertEqual(actual.psot, expected.psot)
self.assertEqual(actual.tpsot, expected.tpsot)
self.assertEqual(actual.tnsot, expected.tnsot)
def verifyCMEsegment(self, actual, expected):
"""
verify the fields of a CME (comment) segment
"""
self.assertEqual(actual.rcme, expected.rcme)
self.assertEqual(actual.ccme, expected.ccme)
def verifySizSegment(self, actual, expected):
"""
Verify the fields of the SIZ segment.
"""
for field in ['rsiz', 'xsiz', 'ysiz', 'xosiz', 'yosiz', 'xtsiz',
'ytsiz', 'xtosiz', 'ytosiz', 'bitdepth',
'xrsiz', 'yrsiz']:
self.assertEqual(getattr(actual, field), getattr(expected, field))
def verifyImageHeaderBox(self, box1, box2):
self.assertEqual(box1.height, box2.height)
self.assertEqual(box1.width, box2.width)
self.assertEqual(box1.num_components, box2.num_components)
self.assertEqual(box1.bits_per_component, box2.bits_per_component)
self.assertEqual(box1.signed, box2.signed)
self.assertEqual(box1.compression, box2.compression)
self.assertEqual(box1.colorspace_unknown, box2.colorspace_unknown)
self.assertEqual(box1.ip_provided, box2.ip_provided)
def verifyColourSpecificationBox(self, actual, expected):
"""
Does not currently check icc profiles.
"""
self.assertEqual(actual.method, expected.method)
self.assertEqual(actual.precedence, expected.precedence)
self.assertEqual(actual.approximation, expected.approximation)
if expected.colorspace is None:
self.assertIsNone(actual.colorspace)
self.assertIsNotNone(actual.icc_profile)
else:
self.assertEqual(actual.colorspace, expected.colorspace)
self.assertIsNone(actual.icc_profile)
WARNING_INFRASTRUCTURE_MSG = "Cannot use with this version of six"
# The Python XMP Toolkit may be used for XMP UUIDs, but only if available and
# if the version is at least 2.0.0.
@ -185,7 +52,7 @@ except:
# The Cinema2K/4K tests seem to need the freeimage backend to skimage.io
# in order to work. Unfortunately, scikit-image/freeimage is about as wonky as
# it gets. Anaconda can get totally weirded out on versions up through 3.6.4
# on Python3 with scikit-image up through version 0.10.0.
# on Python3 with scikit-image up through version 0.10.0.
NO_SKIMAGE_FREEIMAGE_SUPPORT = False
try:
import skimage
@ -213,7 +80,7 @@ def _indent(textstr):
String to be indented.
indent_level : str
Number of spaces of indentation to add.
Returns
-------
indented_string : str
@ -236,6 +103,7 @@ try:
# The whole point of trying to import PIL is to determine if it's there
# or not. We won't use it directly.
# pylint: disable=F0401,W0611
import PIL
NO_READ_BACKEND = False
@ -545,7 +413,7 @@ text_gbr_34 = """Colour Specification Box (colr) @ (179, 1339)
dump = r'''JPEG 2000 Signature Box (jP ) @ (0, 12)
Signature: 0d0a870a
File Type Box (ftyp) @ (12, 20)
Brand: jp2
Brand: jp2
Compatibility: ['jp2 ']
JP2 Header Box (jp2h) @ (32, 45)
Image Header Box (ihdr) @ (40, 22)
@ -601,6 +469,7 @@ Contiguous Codestream Box (jp2c) @ (3223, 1132296)
"Created by OpenJPEG version 2.0.0"'''
nemo_with_codestream_header = dump.format(_indent(nemo_xmp))
#nemo_dump_full = dump.format(_indent(nemo_xmp))
nemo_dump_short = r"""JPEG 2000 Signature Box (jP ) @ (0, 12)
File Type Box (ftyp) @ (12, 20)
@ -613,7 +482,7 @@ Contiguous Codestream Box (jp2c) @ (3223, 1132296)"""
nemo_dump_no_xml = '''JPEG 2000 Signature Box (jP ) @ (0, 12)
Signature: 0d0a870a
File Type Box (ftyp) @ (12, 20)
Brand: jp2
Brand: jp2
Compatibility: ['jp2 ']
JP2 Header Box (jp2h) @ (32, 45)
Image Header Box (ihdr) @ (40, 22)
@ -669,7 +538,7 @@ Contiguous Codestream Box (jp2c) @ (3223, 1132296)
dump = r"""JPEG 2000 Signature Box (jP ) @ (0, 12)
Signature: 0d0a870a
File Type Box (ftyp) @ (12, 20)
Brand: jp2
Brand: jp2
Compatibility: ['jp2 ']
JP2 Header Box (jp2h) @ (32, 45)
Image Header Box (ihdr) @ (40, 22)
@ -691,26 +560,6 @@ nemo_dump_no_codestream = dump.format(_indent(nemo_xmp))
nemo_dump_no_codestream_no_xml = r"""JPEG 2000 Signature Box (jP ) @ (0, 12)
Signature: 0d0a870a
File Type Box (ftyp) @ (12, 20)
Brand: jp2
Compatibility: ['jp2 ']
JP2 Header Box (jp2h) @ (32, 45)
Image Header Box (ihdr) @ (40, 22)
Size: [1456 2592 3]
Bitdepth: 8
Signed: False
Compression: wavelet
Colorspace Unknown: False
Colour Specification Box (colr) @ (62, 15)
Method: enumerated colorspace
Precedence: 0
Colorspace: sRGB
UUID Box (uuid) @ (77, 3146)
UUID: be7acfcb-97a9-42e8-9c71-999491e3afac (XMP)
Contiguous Codestream Box (jp2c) @ (3223, 1132296)"""
nemo = """JPEG 2000 Signature Box (jP ) @ (0, 12)
Signature: 0d0a870a
File Type Box (ftyp) @ (12, 20)
Brand: jp2
Compatibility: ['jp2 ']
@ -727,171 +576,7 @@ JP2 Header Box (jp2h) @ (32, 45)
Colorspace: sRGB
UUID Box (uuid) @ (77, 3146)
UUID: be7acfcb-97a9-42e8-9c71-999491e3afac (XMP)
UUID Data:
<?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)"""
Contiguous Codestream Box (jp2c) @ (3223, 1132296)"""
# Output of reader requirements printing for text_GBR.jp2
text_GBR_rreq = r"""Reader Requirements Box (rreq) @ (40, 109)
@ -927,7 +612,7 @@ issue_183_colr = """Colour Specification Box (colr) @ (62, 12)
Method: restricted ICC profile
Precedence: 0
ICC Profile: None"""
# Progression order is invalid.
issue_186_progression_order = """COD marker segment @ (174, 12)

View file

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

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
OPENJP2 may be present in some form or other.
"""
import contextlib
import ctypes
# unittest doesn't work well with R0904.
# pylint: disable=R0904
# tempfile.TemporaryDirectory, unittest.assertWarns introduced in 3.2
# pylint: disable=E1101
# unittest.mock only in Python 3.3 (python2.7/pylint import issue)
# pylint: disable=E0611,F0401
import imp
import os
import sys
@ -17,43 +25,7 @@ else:
import glymur
from glymur import Jp2k
from .fixtures import (WARNING_INFRASTRUCTURE_ISSUE,
WARNING_INFRASTRUCTURE_MSG,
WINDOWS_TMP_FILE_MSG)
def openjpeg_not_found_by_ctypes():
"""
Need to know if openjpeg library can be picked right up by ctypes for one
of the tests.
"""
with patch.dict('os.environ',
{'DYLD_FALLBACK_LIBRARY_PATH': '/opt/local/lib'}):
if ctypes.util.find_library('openjpeg') is None:
return True
else:
return False
@contextlib.contextmanager
def chdir(dirname=None):
"""
This context manager restores the value of the current working directory
(cwd) after the enclosed code block completes or raises an exception. If a
directory name is supplied to the context manager then the cwd is changed
prior to running the code block.
Shamelessly lifted from
http://www.astropython.org/snippet/2009/10/chdir-context-manager
"""
curdir = os.getcwd()
try:
if dirname is not None:
os.chdir(dirname)
yield
finally:
os.chdir(curdir)
from .fixtures import WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG
@unittest.skipIf(sys.hexversion < 0x03020000,
"TemporaryDirectory introduced in 3.2.")
@ -89,6 +61,7 @@ class TestSuite(unittest.TestCase):
# Need to reliably recover the location of the openjp2 library,
# so using '_name' appears to be the only way to do it.
# pylint: disable=W0212
libloc = glymur.lib.openjp2.OPENJP2._name
line = 'openjp2: {0}\n'.format(libloc)
tfile.write(line)
@ -98,7 +71,6 @@ class TestSuite(unittest.TestCase):
Jp2k(self.jp2file)
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
@unittest.skipIf(os.name == "nt", WINDOWS_TMP_FILE_MSG)
def test_xdg_env_config_file_is_bad(self):
"""A non-existant library location should be rejected."""
with tempfile.TemporaryDirectory() as tdir:
@ -117,56 +89,50 @@ class TestSuite(unittest.TestCase):
with self.assertWarnsRegex(UserWarning, regex):
imp.reload(glymur.lib.openjp2)
@unittest.skipIf(glymur.lib.openjp2.OPENJPEG is None,
"Needs openjp2 and openjpeg before this test make sense.")
@unittest.skipIf(os.name == "nt", WINDOWS_TMP_FILE_MSG)
def test_library_specified_as_None(self):
"""Verify that we can stop library from being loaded by using None."""
with tempfile.TemporaryDirectory() as tdir:
configdir = os.path.join(tdir, 'glymur')
os.mkdir(configdir)
fname = os.path.join(configdir, 'glymurrc')
with open(fname, 'w') as fptr:
# Essentially comment out openjp2 and preferentially load
# openjpeg instead.
fptr.write('[library]\n')
fptr.write('openjp2: None\n')
msg = 'openjpeg: {0}\n'
msg = msg.format(glymur.lib.openjp2.OPENJPEG._name)
fptr.write(msg)
fptr.flush()
with patch.dict('os.environ', {'XDG_CONFIG_HOME': tdir}):
imp.reload(glymur.lib.openjp2)
self.assertIsNone(glymur.lib.openjp2.OPENJP2)
self.assertIsNotNone(glymur.lib.openjp2.OPENJPEG)
@unittest.skipIf(glymur.lib.openjp2.OPENJPEG is None,
"Needs openjpeg before this test make sense.")
@unittest.skipIf(openjpeg_not_found_by_ctypes(),
"OpenJPEG must be found before this test can work.")
@unittest.skipIf(os.name == "nt", WINDOWS_TMP_FILE_MSG)
def test_config_dir_but_no_config_file(self):
@unittest.skipIf(glymur.lib.openjp2.OPENJP2 is None and
glymur.lib.openjpeg.OPENJPEG is None,
"Missing openjp2 library.")
class TestConfig(unittest.TestCase):
"""Test suite for reading without proper library in place."""
with tempfile.TemporaryDirectory() as tdir:
configdir = os.path.join(tdir, 'glymur')
os.mkdir(configdir)
with patch.dict('os.environ', {'XDG_CONFIG_HOME': tdir}):
# Should still be able to load openjpeg, despite the
# configuration file not being there
imp.reload(glymur.lib.openjpeg)
self.assertIsNotNone(glymur.lib.openjp2.OPENJPEG)
def setUp(self):
self.jp2file = glymur.data.nemo()
self.j2kfile = glymur.data.goodstuff()
@unittest.skipIf(os.name == "nt", WINDOWS_TMP_FILE_MSG)
def test_config_file_in_current_directory(self):
"""A configuration file in the current directory should be honored."""
libloc = glymur.lib.openjp2.OPENJP2._name
with tempfile.TemporaryDirectory() as tdir1:
fname = os.path.join(tdir1, 'glymurrc')
with open(fname, 'w') as fptr:
fptr.write('[library]\n')
fptr.write('openjp2: {0}\n'.format(libloc))
fptr.flush()
with chdir(tdir1):
# Should be able to load openjp2 as before.
imp.reload(glymur.lib.openjp2)
self.assertEqual(glymur.lib.openjp2.OPENJP2._name, libloc)
def tearDown(self):
pass
def test_read_without_library(self):
"""Don't have either openjp2 or openjpeg libraries? Must error out.
"""
with patch('glymur.lib.openjp2.OPENJP2', new=None):
with patch('glymur.lib.openjpeg.OPENJPEG', new=None):
with self.assertRaises(glymur.jp2k.LibraryNotFoundError):
glymur.Jp2k(self.jp2file).read()
def test_read_bands_without_library(self):
"""Don't have openjp2 library? Must error out.
"""
with patch('glymur.lib.openjp2.OPENJP2', new=None):
with patch('glymur.lib.openjpeg.OPENJPEG', new=None):
with patch('glymur.version.openjpeg_version_tuple',
new=(0, 0, 0)):
with self.assertRaises(glymur.jp2k.LibraryNotFoundError):
glymur.Jp2k(self.jp2file).read_bands()
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
def test_write_without_library(self):
"""Don't have openjpeg libraries? Must error out.
"""
data = glymur.Jp2k(self.j2kfile).read()
with patch('glymur.lib.openjp2.OPENJP2', new=None):
with patch('glymur.lib.openjpeg.OPENJPEG', new=None):
with self.assertRaises(glymur.jp2k.LibraryNotFoundError):
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
ofile = Jp2k(tfile.name, 'wb')
ofile.write(data)
if __name__ == "__main__":
unittest.main()

View file

@ -1,32 +1,34 @@
"""
Test suite for warnings issued by glymur.
"""
# unittest doesn't work well with R0904.
# pylint: disable=R0904
import platform
import os
import re
import struct
import sys
import tempfile
import unittest
import six
from glymur import Jp2k
import glymur
from .fixtures import opj_data_file, OPJ_DATA_ROOT
from .fixtures import WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG
@unittest.skipIf(sys.hexversion < 0x03040000 and platform.system() == 'Linux',
"inexplicable failures on 3.3 and linux")
@unittest.skipIf(OPJ_DATA_ROOT is None,
"OPJ_DATA_ROOT environment variable not set")
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
class TestWarnings(unittest.TestCase):
"""Test suite for warnings issued by glymur."""
def test_invalid_compatibility_list_entry(self):
"""should not error out with invalid compatibility list entry"""
filename = opj_data_file('input/nonregression/issue397.jp2')
with self.assertWarns(UserWarning):
Jp2k(filename)
self.assertTrue(True)
def test_exceeded_box_length(self):
"""
should warn if reading past end of a box
@ -38,9 +40,9 @@ class TestWarnings(unittest.TestCase):
infile = os.path.join(OPJ_DATA_ROOT,
'input/nonregression/mem-b2ace68c-1381.jp2')
regex = re.compile(r'''Encountered\san\sunrecoverable\sValueError\s
while\sparsing\sa\sPalette\sbox\sat\sbyte\s
offset\s\d+\.\s+The\soriginal\serror\smessage\s
was\s"total\ssize\sof\snew\sarray\smust\sbe\s
while\sparsing\sa\spclr\sbox\sat\sbyte\soffset\s
\d+\.\s+The\soriginal\serror\smessage\swas\s
"total\ssize\sof\snew\sarray\smust\sbe\s
unchanged"''',
re.VERBOSE)
with self.assertWarnsRegex(UserWarning, regex):
@ -53,11 +55,12 @@ class TestWarnings(unittest.TestCase):
"""
relpath = 'input/nonregression/issue188_beach_64bitsbox.jp2'
jfile = opj_data_file(relpath)
pattern = r"""Unrecognized\sbox\s\(b'XML\s'\)\sencountered."""
regex = re.compile(pattern, re.VERBOSE)
regex = re.compile(r"""Unrecognized\sbox\s\(b'XML\s'\)\sencountered.""",
re.VERBOSE)
with self.assertWarnsRegex(UserWarning, regex):
Jp2k(jfile)
def test_NR_gdal_fuzzer_unchecked_numresolutions_dump(self):
"""
Has an invalid number of resolutions.
@ -69,7 +72,7 @@ class TestWarnings(unittest.TestCase):
\(\d+\)\.""",
re.VERBOSE)
with self.assertWarnsRegex(UserWarning, regex):
Jp2k(jfile).get_codestream()
Jp2k(jfile)
@unittest.skipIf(re.match("1.5|2.0.0", glymur.version.openjpeg_version),
"Test not passing on 1.5.x, not introduced until 2.x")
@ -84,7 +87,7 @@ class TestWarnings(unittest.TestCase):
\(\d+\)\.""",
re.VERBOSE)
with self.assertWarnsRegex(UserWarning, regex):
Jp2k(jfile).get_codestream()
Jp2k(jfile)
def test_NR_gdal_fuzzer_check_comp_dx_dy_jp2_dump(self):
"""
@ -97,7 +100,7 @@ class TestWarnings(unittest.TestCase):
dx=\d+,\s*dy=\d+""",
re.VERBOSE)
with self.assertWarnsRegex(UserWarning, regex):
Jp2k(jfile).get_codestream()
Jp2k(jfile)
def test_NR_gdal_fuzzer_assert_in_opj_j2k_read_SQcd_SQcc_patch_jp2(self):
lst = ['input', 'nonregression',
@ -107,32 +110,53 @@ class TestWarnings(unittest.TestCase):
number\sof\scomponents\sis\sonly\s\d+""",
re.VERBOSE)
with self.assertWarnsRegex(UserWarning, regex):
Jp2k(jfile).get_codestream()
Jp2k(jfile)
def test_NR_broken_jp2_dump(self):
"""
The colr box has a ridiculously incorrect box length.
"""
jfile = opj_data_file('input/nonregression/broken.jp2')
regex = re.compile(r'''b'colr'\sbox\shas\sincorrect\sbox\slength\s
\(\d+\)''',
re.VERBOSE)
with self.assertWarnsRegex(UserWarning, regex):
jp2 = Jp2k(jfile)
def test_NR_broken2_jp2_dump(self):
"""
Invalid marker ID on codestream.
"""
jfile = opj_data_file('input/nonregression/broken2.jp2')
regex = re.compile(r'''Invalid\smarker\sid\sencountered\sat\sbyte\s
\d+\sin\scodestream:\s*"0x[a-fA-F0-9]{4}"''',
re.VERBOSE)
with self.assertWarnsRegex(UserWarning, regex):
Jp2k(jfile)
def test_bad_rsiz(self):
"""Should warn if RSIZ is bad. Issue196"""
filename = opj_data_file('input/nonregression/edf_c2_1002767.jp2')
with self.assertWarnsRegex(UserWarning, 'Invalid profile'):
Jp2k(filename).get_codestream()
Jp2k(filename)
def test_bad_wavelet_transform(self):
"""Should warn if wavelet transform is bad. Issue195"""
filename = opj_data_file('input/nonregression/edf_c2_10025.jp2')
with self.assertWarnsRegex(UserWarning, 'Invalid wavelet transform'):
Jp2k(filename).get_codestream()
Jp2k(filename)
def test_invalid_progression_order(self):
"""Should still be able to parse even if prog order is invalid."""
jfile = opj_data_file('input/nonregression/2977.pdf.asan.67.2198.jp2')
with self.assertWarnsRegex(UserWarning, 'Invalid progression order'):
Jp2k(jfile).get_codestream()
Jp2k(jfile)
def test_tile_height_is_zero(self):
"""Zero tile height should not cause an exception."""
filename = 'input/nonregression/2539.pdf.SIGFPE.706.1712.jp2'
filename = opj_data_file(filename)
filename = opj_data_file('input/nonregression/2539.pdf.SIGFPE.706.1712.jp2')
with self.assertWarnsRegex(UserWarning, 'Invalid tile dimensions'):
Jp2k(filename).get_codestream()
Jp2k(filename)
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
def test_unknown_marker_segment(self):
@ -155,9 +179,9 @@ class TestWarnings(unittest.TestCase):
read_buffer = ifile.read()
tfile.write(read_buffer)
tfile.flush()
with self.assertWarnsRegex(UserWarning, 'Unrecognized marker'):
Jp2k(tfile.name).get_codestream()
codestream = Jp2k(tfile.name).get_codestream()
if __name__ == "__main__":

View file

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

View file

@ -1,6 +1,18 @@
"""
Test suite specifically targeting JP2 box layout.
"""
# E1103: return value from read may be list or np array
# pylint: disable=E1103
# R0902: More than 7 instance attributes are just fine for testing.
# pylint: disable=R0902
# R0904: Seems like pylint is fooled in this situation
# pylint: disable=R0904
# W0613: load_tests doesn't need to use ignore or loader arguments.
# pylint: disable=W0613
import doctest
import os
import re
@ -8,6 +20,7 @@ import shutil
import struct
import sys
import tempfile
import uuid
from uuid import UUID
import unittest
@ -22,13 +35,13 @@ from glymur.jp2box import JPEG2000SignatureBox
from glymur.core import COLOR, OPACITY
from glymur.core import RED, GREEN, BLUE, GREY, WHOLE_IMAGE
from .fixtures import (WARNING_INFRASTRUCTURE_ISSUE,
WARNING_INFRASTRUCTURE_MSG,
WINDOWS_TMP_FILE_MSG, MetadataBase)
from .fixtures import opj_data_file
from .fixtures import WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG
def docTearDown(doctest_obj):
glymur.set_parseoptions(full_codestream=False)
try:
FORMAT_CORPUS_DATA_ROOT = os.environ['FORMAT_CORPUS_DATA_ROOT']
except KeyError:
FORMAT_CORPUS_DATA_ROOT = None
def load_tests(loader, tests, ignore):
@ -36,35 +49,31 @@ def load_tests(loader, tests, ignore):
if os.name == "nt":
# Can't do it on windows, temporary file issue.
return tests
tests.addTests(doctest.DocTestSuite('glymur.jp2box',
tearDown=docTearDown))
tests.addTests(doctest.DocTestSuite('glymur.jp2box'))
return tests
@unittest.skipIf(os.name == "nt", WINDOWS_TMP_FILE_MSG)
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
class TestDataEntryURL(unittest.TestCase):
"""Test suite for DataEntryURL boxes."""
def setUp(self):
self.jp2file = glymur.data.nemo()
@unittest.skipIf(re.match("1.5|2",
glymur.version.openjpeg_version) is None,
"Must have openjpeg 1.5 or higher to run")
def test_wrap_greyscale(self):
"""A single component should be wrapped as GREYSCALE."""
j = Jp2k(self.jp2file)
data = j[:]
data = j.read()
red = data[:, :, 0]
# Write it back out as a raw codestream.
with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile1:
j2k = glymur.Jp2k(tfile1.name, data=red)
j2k = glymur.Jp2k(tfile1.name, 'wb')
j2k.write(data[:, :, 0])
# Ok, now rewrap it as JP2. The colorspace should be GREYSCALE.
with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile2:
jp2 = j2k.wrap(tfile2.name)
self.assertEqual(jp2.box[2].box[1].colorspace,
glymur.core.GREYSCALE)
glymur.core.GREYSCALE)
def test_basic_url(self):
"""Just your most basic URL box."""
@ -86,7 +95,7 @@ class TestDataEntryURL(unittest.TestCase):
self.assertEqual(jp22.box[4].url, url)
def test_null_termination(self):
"""I.9.3.2 specifies that location field must be null terminated."""
"""I.9.3.2 specifies that the location field must be null terminated."""
jp2 = Jp2k(self.jp2file)
url = 'http://glymur.readthedocs.org'
@ -97,24 +106,21 @@ class TestDataEntryURL(unittest.TestCase):
jp22 = jp2.wrap(tfile.name, boxes=boxes)
self.assertEqual(jp22.box[-1].length, 42)
# Go to the last box. Seek past the L, T, version,
# and flag fields.
# Go to the last box. Seek past the L, T, version, and flag fields.
with open(tfile.name, 'rb') as fptr:
fptr.seek(jp22.box[-1].offset + 4 + 4 + 1 + 3)
nbytes = (jp22.box[-1].offset +
jp22.box[-1].length -
fptr.tell())
nbytes = jp22.box[-1].offset + jp22.box[-1].length - fptr.tell()
read_buffer = fptr.read(nbytes)
read_url = read_buffer.decode('utf-8')
self.assertEqual(url + chr(0), read_url)
@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,
"Not supported until 2.1")
@unittest.skipIf(os.name == "nt", WINDOWS_TMP_FILE_MSG)
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
class TestChannelDefinition(unittest.TestCase):
"""Test suite for channel definition boxes."""
@ -122,21 +128,24 @@ class TestChannelDefinition(unittest.TestCase):
def setUpClass(cls):
"""Need a one_plane plane image for greyscale testing."""
j2k = Jp2k(glymur.data.goodstuff())
data = j2k[:]
data = j2k.read()
# Write the first component back out to file.
with tempfile.NamedTemporaryFile(suffix=".j2k", delete=False) as tfile:
Jp2k(tfile.name, data=data[:, :, 0])
grey_j2k = Jp2k(tfile.name, 'wb')
grey_j2k.write(data[:, :, 0])
cls.one_plane = tfile.name
# Write the first two components back out to file.
with tempfile.NamedTemporaryFile(suffix=".j2k", delete=False) as tfile:
Jp2k(tfile.name, data=data[:, :, 0:1])
grey_j2k = Jp2k(tfile.name, 'wb')
grey_j2k.write(data[:, :, 0:1])
cls.two_planes = tfile.name
# Write four components back out to file.
with tempfile.NamedTemporaryFile(suffix=".j2k", delete=False) as tfile:
rgba_jp2 = Jp2k(tfile.name, 'wb')
shape = (data.shape[0], data.shape[1], 1)
alpha = np.zeros((shape), dtype=data.dtype)
data4 = np.concatenate((data, alpha), axis=2)
Jp2k(tfile.name, data=data4)
rgba_jp2.write(data4)
cls.four_planes = tfile.name
@classmethod
@ -389,7 +398,7 @@ class TestFileTypeBox(unittest.TestCase):
ftyp = glymur.jp2box.FileTypeBox(brand='jp3')
with self.assertRaises(IOError):
with tempfile.TemporaryFile() as tfile:
ftyp.write(tfile)
ftyp.write(tfile)
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_cl_entry_unknown(self):
@ -399,8 +408,7 @@ class TestFileTypeBox(unittest.TestCase):
ftyp = glymur.jp2box.FileTypeBox(compatibility_list=['jp3'])
with self.assertRaises(IOError):
with tempfile.TemporaryFile() as tfile:
ftyp.write(tfile)
ftyp.write(tfile)
class TestColourSpecificationBox(unittest.TestCase):
"""Test suite for colr box instantiation."""
@ -424,7 +432,8 @@ class TestColourSpecificationBox(unittest.TestCase):
def tearDown(self):
pass
@unittest.skipIf(os.name == "nt", WINDOWS_TMP_FILE_MSG)
@unittest.skipIf(os.name == "nt",
"Problems using NamedTemporaryFile on windows.")
def test_colr_with_out_enum_cspace(self):
"""must supply an enumerated colorspace when writing"""
j2k = Jp2k(self.j2kfile)
@ -435,7 +444,7 @@ class TestColourSpecificationBox(unittest.TestCase):
with self.assertRaises(IOError):
j2k.wrap(tfile.name, boxes=boxes)
@unittest.skipIf(os.name == "nt", WINDOWS_TMP_FILE_MSG)
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
def test_missing_colr_box(self):
"""jp2h must have a colr box"""
j2k = Jp2k(self.j2kfile)
@ -445,7 +454,7 @@ class TestColourSpecificationBox(unittest.TestCase):
with self.assertRaises(IOError):
j2k.wrap(tfile.name, boxes=boxes)
@unittest.skipIf(os.name == "nt", WINDOWS_TMP_FILE_MSG)
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
def test_bad_approx_jp2_field(self):
"""JP2 has requirements for approx field"""
j2k = Jp2k(self.j2kfile)
@ -506,7 +515,8 @@ class TestColourSpecificationBox(unittest.TestCase):
colr.write(tfile)
@unittest.skipIf(os.name == "nt", WINDOWS_TMP_FILE_MSG)
@unittest.skipIf(os.name == "nt",
"Problems using NamedTemporaryFile on windows.")
class TestPaletteBox(unittest.TestCase):
"""Test suite for pclr box instantiation."""
@ -523,8 +533,8 @@ class TestPaletteBox(unittest.TestCase):
bps = (8, 8, 8)
signed = (False, False)
with self.assertWarns(UserWarning):
glymur.jp2box.PaletteBox(palette, bits_per_component=bps,
signed=signed)
pclr = glymur.jp2box.PaletteBox(palette, bits_per_component=bps,
signed=signed)
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_mismatched_signed_palette(self):
@ -533,8 +543,8 @@ class TestPaletteBox(unittest.TestCase):
bps = (8, 8, 8, 8)
signed = (False, False, False, False)
with self.assertWarns(UserWarning):
glymur.jp2box.PaletteBox(palette, bits_per_component=bps,
signed=signed)
pclr = glymur.jp2box.PaletteBox(palette, bits_per_component=bps,
signed=signed)
def test_writing_with_different_bitdepths(self):
"""Bitdepths must be the same when writing."""
@ -548,7 +558,7 @@ class TestPaletteBox(unittest.TestCase):
pclr.write(tfile)
@unittest.skipIf(os.name == "nt", WINDOWS_TMP_FILE_MSG)
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
class TestAppend(unittest.TestCase):
"""Tests for append method."""
@ -634,14 +644,14 @@ class TestAppend(unittest.TestCase):
jp2 = Jp2k(tfile.name)
# Make a UUID box. Only XMP UUID boxes can currently be appended.
uuid_instance = UUID('00000000-0000-0000-0000-000000000000')
uuid_instance = uuid.UUID('00000000-0000-0000-0000-000000000000')
data = b'0123456789'
uuidbox = glymur.jp2box.UUIDBox(uuid_instance, data)
with self.assertRaises(IOError):
jp2.append(uuidbox)
@unittest.skipIf(os.name == "nt", WINDOWS_TMP_FILE_MSG)
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
class TestWrap(unittest.TestCase):
"""Tests for wrap method."""
@ -790,7 +800,7 @@ class TestWrap(unittest.TestCase):
# list to trigger the error.
boxes[2].box = []
with self.assertRaises(IOError):
jp2.wrap(tfile.name, boxes=boxes)
jp22 = jp2.wrap(tfile.name, boxes=boxes)
def test_default_layout_with_boxes(self):
"""basic test for rewrapping a jp2 file, boxes specified"""
@ -855,8 +865,8 @@ class TestWrap(unittest.TestCase):
"""A palette box must reside in a JP2 header box."""
palette = np.array([[255, 0, 255], [0, 255, 0]], dtype=np.int32)
bps = (8, 8, 8)
pclr = glymur.jp2box.PaletteBox(palette=palette,
bits_per_component=bps,
signed = (True, False, True)
pclr = glymur.jp2box.PaletteBox(palette=palette, bits_per_component=bps,
signed=(True, False, True))
j2k = Jp2k(self.j2kfile)
@ -968,8 +978,7 @@ class TestWrap(unittest.TestCase):
"""Rewrap a jpx file."""
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile1:
jpx = Jp2k(self.jpxfile)
idx = (list(range(5)) +
list(range(9, 12)) + list(range(6, 9))) + [12]
idx = list(range(5)) + list(range(9, 12)) + list(range(6, 9)) + [12]
boxes = [jpx.box[j] for j in idx]
jpx2 = jpx.wrap(tfile1.name, boxes=boxes)
exp_ids = [box.box_id for box in boxes]
@ -1021,16 +1030,16 @@ class TestJp2Boxes(unittest.TestCase):
"""Raw instantiation should not produce a main_header."""
box = ContiguousCodestreamBox()
self.assertEqual(box.box_id, 'jp2c')
self.assertIsNone(box.codestream)
self.assertIsNone(box.main_header)
def test_codestream_main_header_offset(self):
"""main_header_offset is an attribute of the CCS box"""
j = Jp2k(self.jpxfile)
j = Jp2k(self.jpxfile);
self.assertEqual(j.box[5].main_header_offset,
j.box[5].offset + 8)
class TestRepr(MetadataBase):
class TestRepr(unittest.TestCase):
"""Tests for __repr__ methods."""
def test_default_jp2k(self):
"""Should be able to eval a JPEG2000SignatureBox"""
@ -1100,7 +1109,10 @@ class TestRepr(MetadataBase):
# Test the representation instantiation.
newbox = eval(repr(ftyp))
self.verify_filetype_box(newbox, FileTypeBox())
self.assertTrue(isinstance(newbox, glymur.jp2box.FileTypeBox))
self.assertEqual(newbox.brand, 'jp2 ')
self.assertEqual(newbox.minor_version, 0)
self.assertEqual(newbox.compatibility_list, ['jp2 '])
def test_colourspecification_box(self):
"""Verify __repr__ method on colr box."""
@ -1226,8 +1238,8 @@ class TestRepr(MetadataBase):
def test_uuidlist_box(self):
"""Verify __repr__ method on ulst box."""
uuid1 = UUID('00000000-0000-0000-0000-000000000001')
uuid2 = UUID('00000000-0000-0000-0000-000000000002')
uuid1 = uuid.UUID('00000000-0000-0000-0000-000000000001')
uuid2 = uuid.UUID('00000000-0000-0000-0000-000000000002')
uuids = [uuid1, uuid2]
ulst = glymur.jp2box.UUIDListBox(ulst=uuids)
newbox = eval(repr(ulst))
@ -1235,10 +1247,21 @@ class TestRepr(MetadataBase):
self.assertEqual(newbox.ulst[0], uuid1)
self.assertEqual(newbox.ulst[1], uuid2)
def test_jp2k_box(self):
"""Verify Superclass repr."""
box = glymur.jp2box.Jp2kBox(box_id='one', offset=2, length=3,
longname='four')
newbox = eval(repr(box))
self.assertEqual(newbox.box_id, 'one')
self.assertEqual(newbox.offset, 2)
self.assertEqual(newbox.length, 3)
self.assertEqual(newbox.longname, 'four')
def test_palette_box(self):
"""Verify Palette box repr."""
palette = np.array([[255, 0, 1000], [0, 255, 0]], dtype=np.int32)
bps = (8, 8, 16)
signed = (True, False, True)
box = glymur.jp2box.PaletteBox(palette=palette, bits_per_component=bps,
signed=(True, False, True))
@ -1281,15 +1304,14 @@ class TestRepr(MetadataBase):
def test_uuid_box_generic(self):
"""Verify uuid repr method."""
uuid_instance = UUID('00000000-0000-0000-0000-000000000000')
uuid_instance = uuid.UUID('00000000-0000-0000-0000-000000000000')
data = b'0123456789'
box = glymur.jp2box.UUIDBox(the_uuid=uuid_instance, raw_data=data)
# Since the raw_data parameter is a sequence of bytes which could be
# quite long, don't bother trying to make it conform to eval(repr()).
regexp = r"""glymur.jp2box.UUIDBox\("""
regexp += """the_uuid="""
regexp += """UUID\('00000000-0000-0000-0000-000000000000'\),\s"""
regexp += """the_uuid=UUID\('00000000-0000-0000-0000-000000000000'\),\s"""
regexp += """raw_data=<byte\sarray\s10\selements>\)"""
if sys.hexversion < 0x03000000:
@ -1306,8 +1328,7 @@ class TestRepr(MetadataBase):
# Since the raw_data parameter is a sequence of bytes which could be
# quite long, don't bother trying to make it conform to eval(repr()).
regexp = r"""glymur.jp2box.UUIDBox\("""
regexp += """the_uuid="""
regexp += """UUID\('be7acfcb-97a9-42e8-9c71-999491e3afac'\),\s"""
regexp += """the_uuid=UUID\('be7acfcb-97a9-42e8-9c71-999491e3afac'\),\s"""
regexp += """raw_data=<byte\sarray\s3122\selements>\)"""
if sys.hexversion < 0x03000000:
@ -1323,10 +1344,49 @@ class TestRepr(MetadataBase):
# Difficult to eval(repr()) this, so just match the general pattern.
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]*)>[)]"
if sys.hexversion < 0x03000000:
self.assertRegexpMatches(repr(box), regexp)
else:
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 os
import struct
import sys
import tempfile
import unittest
@ -19,7 +20,6 @@ from glymur.jp2box import ColourSpecificationBox
from .fixtures import WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
class TestJPXWrap(unittest.TestCase):
"""Test suite for wrapping JPX files."""
@ -184,7 +184,7 @@ class TestJPXWrap(unittest.TestCase):
with tempfile.NamedTemporaryFile(suffix=".jpx") as tfile:
with self.assertRaises(IOError):
jp2.wrap(tfile.name, boxes=boxes)
jpx = jp2.wrap(tfile.name, boxes=boxes)
def test_cgrp_neg(self):
"""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 self.assertRaises(IOError):
jp2.wrap(tfile.name, boxes=boxes)
jpx = jp2.wrap(tfile.name, boxes=boxes)
def test_ftbl(self):
"""Write a fragment table box."""
@ -484,7 +484,7 @@ class TestJPX(unittest.TestCase):
ftbl.write(tfile)
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
version = (0, 0, 0)
url1 = 'file:////usr/local/bin'
@ -574,7 +574,7 @@ class TestJPX(unittest.TestCase):
131072, 65536, 32768, 16384, 8192]
for j in range(len(standard_flags)):
mask = (standard_masks[j] >> 16,
standard_masks[j] & 0x0000ffff >> 8,
standard_masks[j] & 0x0000ffff>> 8,
standard_masks[j] & 0x000000ff)
struct.pack_into('>HBBB', rreq_buffer, 17 + j * 5,
standard_flags[j], *mask)
@ -599,6 +599,7 @@ class TestJPX(unittest.TestCase):
self.assertEqual(jpx.box[2].standard_flag,
(5, 42, 45, 2, 18, 19, 1, 8, 12, 31, 20))
def test_nlst(self):
"""Verify that we can handle a number list box."""
j = Jp2k(self.jpxfile)

View file

@ -1,7 +1,17 @@
# -*- coding: utf-8 -*-
"""Test suite for printing.
"""
# C0302: don't care too much about having too many lines in a test module
# pylint: disable=C0302
# E061: unittest.mock introduced in 3.3 (python-2.7/pylint issue)
# pylint: disable=E0611,F0401
# R0904: Not too many methods in unittest.
# pylint: disable=R0904
import os
import re
import shutil
import struct
import sys
@ -13,18 +23,30 @@ if sys.hexversion < 0x02070000:
else:
import unittest
if sys.hexversion < 0x03000000:
from StringIO import StringIO
else:
from io import StringIO
if sys.hexversion <= 0x03030000:
from mock import patch
else:
from unittest.mock import patch
import lxml.etree
from .fixtures import (WARNING_INFRASTRUCTURE_ISSUE,
WARNING_INFRASTRUCTURE_MSG,
WINDOWS_TMP_FILE_MSG)
from .fixtures import HAS_PYTHON_XMP_TOOLKIT, OPJ_DATA_ROOT
from .fixtures import WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG
if HAS_PYTHON_XMP_TOOLKIT:
from libxmp import XMPMeta
import glymur
from glymur import Jp2k
from .fixtures import SimpleRDF
from .fixtures import OPJ_DATA_ROOT, opj_data_file, SimpleRDF
@unittest.skipIf(os.name == "nt", WINDOWS_TMP_FILE_MSG)
@unittest.skipIf(os.name == "nt", "Unexplained failure on windows")
class TestSuite(unittest.TestCase):
"""Tests for XMP, Exif UUIDs."""
@ -79,9 +101,8 @@ class TestSuite(unittest.TestCase):
jp2 = glymur.Jp2k(tfile.name)
self.assertEqual(jp2.box[-1].data['Make'], "HTC")
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
@unittest.skipIf(os.name == "nt", WINDOWS_TMP_FILE_MSG)
@unittest.skipIf(os.name == "nt", "Unexplained failure on windows")
class TestSuiteWarns(unittest.TestCase):
"""Tests for XMP, Exif UUIDs, issues warnings."""
@ -90,7 +111,7 @@ class TestSuiteWarns(unittest.TestCase):
def tearDown(self):
pass
def test_unrecognized_exif_tag(self):
"""Verify warning in case of unrecognized tag."""
with tempfile.NamedTemporaryFile(suffix='.jp2', mode='wb') as tfile:
@ -114,7 +135,7 @@ class TestSuiteWarns(unittest.TestCase):
tfile.flush()
with self.assertWarnsRegex(UserWarning, 'Unrecognized Exif tag'):
glymur.Jp2k(tfile.name)
j = glymur.Jp2k(tfile.name)
def test_bad_tag_datatype(self):
"""Only certain datatypes are allowable"""
@ -170,3 +191,6 @@ class TestSuiteWarns(unittest.TestCase):
jp2 = glymur.Jp2k(tfile.name)
self.assertEqual(jp2.box[-1].box_id, 'uuid')
if __name__ == "__main__":
unittest.main()

View file

@ -2,12 +2,35 @@
"""
Test suite specifically targeting JP2 box layout.
"""
# E1103: return value from read may be list or np array
# pylint: disable=E1103
# R0902: More than 7 instance attributes are just fine for testing.
# pylint: disable=R0902
# R0904: Seems like pylint is fooled in this situation
# pylint: disable=R0904
# W0613: load_tests doesn't need to use ignore or loader arguments.
# pylint: disable=W0613
import os
import re
import struct
import sys
import tempfile
import unittest
if sys.hexversion < 0x03000000:
from StringIO import StringIO
else:
from io import StringIO
if sys.hexversion <= 0x03030000:
from mock import patch
else:
from unittest.mock import patch
import lxml.etree as ET
import glymur
@ -18,10 +41,8 @@ from glymur.jp2box import JPEG2000SignatureBox
from .fixtures import OPJ_DATA_ROOT, opj_data_file
from .fixtures import WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG
from . import fixtures
@unittest.skipIf(os.name == "nt", fixtures.WINDOWS_TMP_FILE_MSG)
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
class TestXML(unittest.TestCase):
"""Test suite for XML boxes."""
@ -145,6 +166,7 @@ class TestXML(unittest.TestCase):
u'<country>Россия</country>')
class TestJp2kBadXmlFile(unittest.TestCase):
"""Test suite for bad XML box situations"""
@ -197,7 +219,7 @@ class TestJp2kBadXmlFile(unittest.TestCase):
self.assertIsNone(jp2k.box[3].xml)
@unittest.skipIf(os.name == "nt", fixtures.WINDOWS_TMP_FILE_MSG)
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
class TestBadButRecoverableXmlFile(unittest.TestCase):
"""Test suite for XML box that is bad, but we can still recover the XML."""
@ -270,19 +292,22 @@ class TestXML_OpjDataRoot(unittest.TestCase):
'nonregression',
'issue171.jp2'))
msg = 'An illegal BOM \(byte order marker\) was detected and removed '
msg += 'from the XML contents in the box starting at byte offset \d+'
msg += 'from the XML contents in the box starting at byte offset \d+'
with self.assertWarnsRegex(UserWarning, re.compile(msg)):
jp2 = Jp2k(filename)
self.assertIsNotNone(jp2.box[3].xml)
def test_invalid_utf8(self):
"""Bad byte sequence that cannot be parsed."""
relname = '26ccf3651020967f7778238ef5af08af.SIGFPE.d25.527.jp2'
filename = opj_data_file(os.path.join('input',
'nonregression',
relname))
'26ccf3651020967f7778238ef5af08af.SIGFPE.d25.527.jp2'))
with self.assertWarns((UserWarning, UserWarning)):
jp2 = Jp2k(filename)
self.assertIsNone(jp2.box[3].box[1].box[1].xml)

File diff suppressed because it is too large Load diff

View file

@ -2,25 +2,45 @@
The tests defined here roughly correspond to what is in the OpenJPEG test
suite.
"""
# Some test names correspond with openjpeg tests. Long names are ok in this
# case.
# pylint: disable=C0103
# All of these tests correspond to tests in openjpeg, so no docstring is really
# needed.
# pylint: disable=C0111
# This module is very long, cannot be helped.
# pylint: disable=C0302
# unittest fools pylint with "too many public methods"
# pylint: disable=R0904
# Some tests use numpy test infrastructure, which means the tests never
# reference "self", so pylint claims it should be a function. No, no, no.
# pylint: disable=R0201
# Many tests are pretty long and that can't be helped.
# pylint: disable=R0915
# asserWarns introduced in python 3.2 (python2.7/pylint issue)
# pylint: disable=E1101
import re
import sys
import unittest
import warnings
import numpy as np
import glymur
from glymur import Jp2k
from glymur.jp2box import FileTypeBox, ImageHeaderBox, ColourSpecificationBox
import glymur
from .fixtures import (OPJ_DATA_ROOT, MetadataBase,
WARNING_INFRASTRUCTURE_ISSUE,
WARNING_INFRASTRUCTURE_MSG,
mse, peak_tolerance, read_pgx, opj_data_file,
OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG)
from .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(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG)
@unittest.skipIf(OPJ_DATA_ROOT is None,
"OPJ_DATA_ROOT environment variable not set")
class TestSuite(unittest.TestCase):
@ -34,7 +54,7 @@ class TestSuite(unittest.TestCase):
def test_ETS_C1P0_p0_01_j2k(self):
jfile = opj_data_file('input/conformance/p0_01.j2k')
jp2k = Jp2k(jfile)
jpdata = jp2k[:]
jpdata = jp2k.read(rlevel=0)
pgxfile = opj_data_file('baseline/conformance/c1p0_01_0.pgx')
pgxdata = read_pgx(pgxfile)
@ -44,7 +64,7 @@ class TestSuite(unittest.TestCase):
def test_ETS_C1P0_p0_03_j2k(self):
jfile = opj_data_file('input/conformance/p0_03.j2k')
jp2k = Jp2k(jfile)
jpdata = jp2k[:]
jpdata = jp2k.read(rlevel=0)
pgxfile = opj_data_file('baseline/conformance/c1p0_03_0.pgx')
pgxdata = read_pgx(pgxfile)
@ -54,7 +74,7 @@ class TestSuite(unittest.TestCase):
def test_ETS_C1P0_p0_04_j2k(self):
jfile = opj_data_file('input/conformance/p0_04.j2k')
jp2k = Jp2k(jfile)
jpdata = jp2k[:]
jpdata = jp2k.read(rlevel=0)
pgxfile = opj_data_file('baseline/conformance/c1p0_04_0.pgx')
pgxdata = read_pgx(pgxfile)
@ -74,7 +94,7 @@ class TestSuite(unittest.TestCase):
def test_ETS_C1P0_p0_08_j2k(self):
jfile = opj_data_file('input/conformance/p0_08.j2k')
jp2k = Jp2k(jfile)
jpdata = jp2k[::2, ::2]
jpdata = jp2k.read(rlevel=1)
pgxfile = opj_data_file('baseline/conformance/c1p0_08_0.pgx')
pgxdata = read_pgx(pgxfile)
@ -91,7 +111,7 @@ class TestSuite(unittest.TestCase):
def test_ETS_C1P0_p0_09_j2k(self):
jfile = opj_data_file('input/conformance/p0_09.j2k')
jp2k = Jp2k(jfile)
jpdata = jp2k[:]
jpdata = jp2k.read(rlevel=0)
pgxfile = opj_data_file('baseline/conformance/c1p0_09_0.pgx')
pgxdata = read_pgx(pgxfile)
@ -100,7 +120,7 @@ class TestSuite(unittest.TestCase):
def test_ETS_C1P0_p0_11_j2k(self):
jfile = opj_data_file('input/conformance/p0_11.j2k')
jp2k = Jp2k(jfile)
jpdata = jp2k[:]
jpdata = jp2k.read(rlevel=0)
pgxfile = opj_data_file('baseline/conformance/c1p0_11_0.pgx')
pgxdata = read_pgx(pgxfile)
@ -109,7 +129,7 @@ class TestSuite(unittest.TestCase):
def test_ETS_C1P0_p0_14_j2k(self):
jfile = opj_data_file('input/conformance/p0_14.j2k')
jp2k = Jp2k(jfile)
jpdata = jp2k[:]
jpdata = jp2k.read(rlevel=0)
pgxfile = opj_data_file('baseline/conformance/c1p0_14_0.pgx')
pgxdata = read_pgx(pgxfile)
@ -126,7 +146,7 @@ class TestSuite(unittest.TestCase):
def test_ETS_C1P0_p0_15_j2k(self):
jfile = opj_data_file('input/conformance/p0_15.j2k')
jp2k = Jp2k(jfile)
jpdata = jp2k[:]
jpdata = jp2k.read(rlevel=0)
pgxfile = opj_data_file('baseline/conformance/c1p0_15_0.pgx')
pgxdata = read_pgx(pgxfile)
@ -135,7 +155,7 @@ class TestSuite(unittest.TestCase):
def test_ETS_C1P0_p0_16_j2k(self):
jfile = opj_data_file('input/conformance/p0_16.j2k')
jp2k = Jp2k(jfile)
jpdata = jp2k[:]
jpdata = jp2k.read(rlevel=0)
pgxfile = opj_data_file('baseline/conformance/c1p0_16_0.pgx')
pgxdata = read_pgx(pgxfile)
@ -144,7 +164,7 @@ class TestSuite(unittest.TestCase):
def test_ETS_C1P1_p1_01_j2k(self):
jfile = opj_data_file('input/conformance/p1_01.j2k')
jp2k = Jp2k(jfile)
jpdata = jp2k[:]
jpdata = jp2k.read(rlevel=0)
pgxfile = opj_data_file('baseline/conformance/c1p1_01_0.pgx')
pgxdata = read_pgx(pgxfile)
@ -153,7 +173,7 @@ class TestSuite(unittest.TestCase):
def test_ETS_C1P1_p1_02_j2k(self):
jfile = opj_data_file('input/conformance/p1_02.j2k')
jp2k = Jp2k(jfile)
jpdata = jp2k[:]
jpdata = jp2k.read(rlevel=0)
pgxfile = opj_data_file('baseline/conformance/c1p1_02_0.pgx')
pgxdata = read_pgx(pgxfile)
@ -173,7 +193,7 @@ class TestSuite(unittest.TestCase):
def test_ETS_C1P1_p1_04_j2k(self):
jfile = opj_data_file('input/conformance/p1_04.j2k')
jp2k = Jp2k(jfile)
jpdata = jp2k[:]
jpdata = jp2k.read()
pgxfile = opj_data_file('baseline/conformance/c1p1_04_0.pgx')
pgxdata = read_pgx(pgxfile)
@ -183,102 +203,102 @@ class TestSuite(unittest.TestCase):
def test_NR_DEC_Bretagne2_j2k_1_decode(self):
jfile = opj_data_file('input/nonregression/Bretagne2.j2k')
jp2 = Jp2k(jfile)
jp2[:]
jp2.read()
self.assertTrue(True)
def test_NR_DEC__00042_j2k_2_decode(self):
jfile = opj_data_file('input/nonregression/_00042.j2k')
jp2 = Jp2k(jfile)
jp2[:]
jp2.read()
self.assertTrue(True)
def test_NR_DEC_buxI_j2k_9_decode(self):
jfile = opj_data_file('input/nonregression/buxI.j2k')
Jp2k(jfile)[:]
Jp2k(jfile).read()
self.assertTrue(True)
def test_NR_DEC_buxR_j2k_10_decode(self):
jfile = opj_data_file('input/nonregression/buxR.j2k')
Jp2k(jfile)[:]
Jp2k(jfile).read()
self.assertTrue(True)
def test_NR_DEC_Cannotreaddatawithnosizeknown_j2k_11_decode(self):
relpath = 'input/nonregression/Cannotreaddatawithnosizeknown.j2k'
jfile = opj_data_file(relpath)
Jp2k(jfile)[:]
Jp2k(jfile).read()
self.assertTrue(True)
def test_NR_DEC_cthead1_j2k_12_decode(self):
jfile = opj_data_file('input/nonregression/cthead1.j2k')
Jp2k(jfile)[:]
Jp2k(jfile).read()
self.assertTrue(True)
def test_NR_DEC_CT_Phillips_JPEG2K_Decompr_Problem_j2k_13_decode(self):
relpath = 'input/nonregression/CT_Phillips_JPEG2K_Decompr_Problem.j2k'
jfile = opj_data_file(relpath)
Jp2k(jfile)[:]
Jp2k(jfile).read()
self.assertTrue(True)
def test_NR_DEC_j2k32_j2k_15_decode(self):
jfile = opj_data_file('input/nonregression/j2k32.j2k')
Jp2k(jfile)[:]
Jp2k(jfile).read()
self.assertTrue(True)
def test_NR_DEC_MarkerIsNotCompliant_j2k_17_decode(self):
jfile = opj_data_file('input/nonregression/MarkerIsNotCompliant.j2k')
Jp2k(jfile)[:]
Jp2k(jfile).read()
self.assertTrue(True)
def test_NR_DEC_Marrin_jp2_18_decode(self):
jfile = opj_data_file('input/nonregression/Marrin.jp2')
Jp2k(jfile)[:]
Jp2k(jfile).read()
self.assertTrue(True)
def test_NR_DEC_movie_00000_j2k_20_decode(self):
jfile = opj_data_file('input/nonregression/movie_00000.j2k')
Jp2k(jfile)[:]
Jp2k(jfile).read()
self.assertTrue(True)
def test_NR_DEC_movie_00001_j2k_21_decode(self):
jfile = opj_data_file('input/nonregression/movie_00001.j2k')
Jp2k(jfile)[:]
Jp2k(jfile).read()
self.assertTrue(True)
def test_NR_DEC_movie_00002_j2k_22_decode(self):
jfile = opj_data_file('input/nonregression/movie_00002.j2k')
Jp2k(jfile)[:]
Jp2k(jfile).read()
self.assertTrue(True)
def test_NR_DEC_orb_blue_lin_j2k_j2k_23_decode(self):
jfile = opj_data_file('input/nonregression/orb-blue10-lin-j2k.j2k')
Jp2k(jfile)[:]
Jp2k(jfile).read()
self.assertTrue(True)
def test_NR_DEC_orb_blue_win_j2k_j2k_24_decode(self):
jfile = opj_data_file('input/nonregression/orb-blue10-win-j2k.j2k')
Jp2k(jfile)[:]
Jp2k(jfile).read()
self.assertTrue(True)
def test_NR_DEC_relax_jp2_27_decode(self):
jfile = opj_data_file('input/nonregression/relax.jp2')
Jp2k(jfile)[:]
Jp2k(jfile).read()
self.assertTrue(True)
def test_NR_DEC_test_lossless_j2k_28_decode(self):
jfile = opj_data_file('input/nonregression/test_lossless.j2k')
Jp2k(jfile)[:]
Jp2k(jfile).read()
self.assertTrue(True)
def test_NR_DEC_pacs_ge_j2k_30_decode(self):
jfile = opj_data_file('input/nonregression/pacs.ge.j2k')
Jp2k(jfile)[:]
Jp2k(jfile).read()
self.assertTrue(True)
@unittest.skipIf(OPJ_DATA_ROOT is None,
"OPJ_DATA_ROOT environment variable not set")
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
class TestSuiteWarns(MetadataBase):
class TestSuiteWarns(unittest.TestCase):
"""
Identical setup to above, but these tests issue warnings.
"""
@ -294,14 +314,14 @@ class TestSuiteWarns(MetadataBase):
with self.assertWarns(UserWarning):
# Bad compatibility list item.
jp2k = Jp2k(jfile)
jpdata = jp2k[:]
jpdata = jp2k.read()
self.assertEqual(jpdata.shape, (512, 768, 3))
def test_ETS_JP2_file2(self):
jfile = opj_data_file('input/conformance/file2.jp2')
with self.assertWarns(UserWarning):
jp2k = Jp2k(jfile)
jpdata = jp2k[:]
jpdata = jp2k.read()
self.assertEqual(jpdata.shape, (640, 480, 3))
@unittest.skipIf(glymur.version.openjpeg_version_tuple[0] < 2,
@ -319,7 +339,7 @@ class TestSuiteWarns(MetadataBase):
jfile = opj_data_file('input/conformance/file4.jp2')
with self.assertWarns(UserWarning):
jp2k = Jp2k(jfile)
jpdata = jp2k[:]
jpdata = jp2k.read()
self.assertEqual(jpdata.shape, (512, 768))
def test_ETS_JP2_file5(self):
@ -328,45 +348,42 @@ class TestSuiteWarns(MetadataBase):
# There's a warning for an unknown compatibility entry.
# Ignore it here.
jp2k = Jp2k(jfile)
jpdata = jp2k[:]
jpdata = jp2k.read()
self.assertEqual(jpdata.shape, (512, 768, 3))
def test_ETS_JP2_file6(self):
jfile = opj_data_file('input/conformance/file6.jp2')
with self.assertWarns(UserWarning):
jp2k = Jp2k(jfile)
jpdata = jp2k[:]
jpdata = jp2k.read()
self.assertEqual(jpdata.shape, (512, 768))
def test_ETS_JP2_file7(self):
jfile = opj_data_file('input/conformance/file7.jp2')
with self.assertWarns(UserWarning):
jp2k = Jp2k(jfile)
jpdata = jp2k[:]
jpdata = jp2k.read()
self.assertEqual(jpdata.shape, (640, 480, 3))
def test_ETS_JP2_file8(self):
jfile = opj_data_file('input/conformance/file8.jp2')
with self.assertWarns(UserWarning):
jp2k = Jp2k(jfile)
jpdata = jp2k[:]
jpdata = jp2k.read()
self.assertEqual(jpdata.shape, (400, 700))
def test_ETS_JP2_file9(self):
jfile = opj_data_file('input/conformance/file9.jp2')
with self.assertWarns(UserWarning):
jp2k = Jp2k(jfile)
jpdata = jp2k[:]
jpdata = jp2k.read()
self.assertEqual(jpdata.shape, (512, 768, 3))
def test_NR_broken1_jp2_dump(self):
jfile = opj_data_file('input/nonregression/broken1.jp2')
def test_NR_broken_jp2_dump(self):
jfile = opj_data_file('input/nonregression/broken.jp2')
# The colr box has a ridiculously incorrect box length.
regex = re.compile(r'''b'colr'\sbox\shas\sincorrect\sbox\slength\s
\(\d+\)''',
re.VERBOSE)
with self.assertWarnsRegex(UserWarning, regex):
with self.assertWarns(UserWarning):
# colr box has bad length.
jp2 = Jp2k(jfile)
ids = [box.box_id for box in jp2.box]
@ -377,32 +394,63 @@ class TestSuiteWarns(MetadataBase):
# Signature box. Check for corruption.
self.assertEqual(jp2.box[0].signature, (13, 10, 135, 10))
self.verify_filetype_box(jp2.box[1], FileTypeBox())
expected = ImageHeaderBox(152, 203, num_components=3)
self.verifyImageHeaderBox(jp2.box[2].box[0], expected)
# File type box.
self.assertEqual(jp2.box[1].brand, 'jp2 ')
self.assertEqual(jp2.box[1].minor_version, 0)
self.assertEqual(jp2.box[1].compatibility_list[0], 'jp2 ')
expected = ColourSpecificationBox(colorspace=glymur.core.SRGB)
self.verifyColourSpecificationBox(jp2.box[2].box[1], expected)
# Jp2 Header
# Image header
self.assertEqual(jp2.box[2].box[0].height, 152)
self.assertEqual(jp2.box[2].box[0].width, 203)
self.assertEqual(jp2.box[2].box[0].num_components, 3)
self.assertEqual(jp2.box[2].box[0].bits_per_component, 8)
self.assertEqual(jp2.box[2].box[0].signed, False)
self.assertEqual(jp2.box[2].box[0].compression, 7) # wavelet
self.assertEqual(jp2.box[2].box[0].colorspace_unknown, False)
self.assertEqual(jp2.box[2].box[0].ip_provided, False)
c = jp2.box[3].codestream
# Jp2 Header
# Colour specification
self.assertEqual(jp2.box[2].box[1].method,
glymur.core.ENUMERATED_COLORSPACE)
self.assertEqual(jp2.box[2].box[1].precedence, 0)
self.assertEqual(jp2.box[2].box[1].approximation, 0) # not allowed?
self.assertEqual(jp2.box[2].box[1].colorspace, glymur.core.SRGB)
c = jp2.box[3].main_header
ids = [x.marker_id for x in c.segment]
expected = ['SOC', 'SIZ', 'CME', 'COD', 'QCD', 'QCC', 'QCC']
self.assertEqual(ids, expected)
kwargs = {'rsiz': 0, 'xysiz': (203, 152), 'xyosiz': (0, 0),
'xytsiz': (203, 152), 'xytosiz': (0, 0),
'bitdepth': (8, 8, 8),
'signed': (False, False, False),
'xyrsiz': [(1, 1, 1), (1, 1, 1)]}
self.verifySizSegment(c.segment[1],
glymur.codestream.SIZsegment(**kwargs))
# SIZ: Image and tile size
# Profile:
self.assertEqual(c.segment[1].rsiz, 0)
# Reference grid size
self.assertEqual(c.segment[1].xsiz, 203)
self.assertEqual(c.segment[1].ysiz, 152)
# Reference grid offset
self.assertEqual((c.segment[1].xosiz, c.segment[1].yosiz), (0, 0))
# Tile size
self.assertEqual((c.segment[1].xtsiz, c.segment[1].ytsiz), (203, 152))
# Tile offset
self.assertEqual((c.segment[1].xtosiz, c.segment[1].ytosiz), (0, 0))
# bitdepth
self.assertEqual(c.segment[1].bitdepth, (8, 8, 8))
# signed
self.assertEqual(c.segment[1].signed, (False, False, False))
# subsampling
self.assertEqual(list(zip(c.segment[1].xrsiz, c.segment[1].yrsiz)),
[(1, 1)] * 3)
pargs = (glymur.core.RCME_ISO_8859_1,
"Creator: JasPer Version 1.701.0".encode())
self.verifyCMEsegment(c.segment[2],
glymur.codestream.CMEsegment(*pargs))
# COM: comment
# Registration
self.assertEqual(c.segment[2].rcme, glymur.core.RCME_ISO_8859_1)
# Comment value
self.assertEqual(c.segment[2].ccme.decode('latin-1'),
"Creator: JasPer Version 1.701.0")
# COD: Coding style default
self.assertFalse(c.segment[3].scod & 2) # no sop
@ -413,8 +461,18 @@ class TestSuiteWarns(MetadataBase):
self.assertEqual(c.segment[3].spcod[4], 5) # level
self.assertEqual(tuple(c.segment[3].code_block_size),
(64, 64)) # cblk
self.verify_codeblock_style(c.segment[3].spcod[7],
[False, False, False, False, False, False])
# Selective arithmetic coding bypass
self.assertFalse(c.segment[3].spcod[7] & 0x01)
# Reset context probabilities
self.assertFalse(c.segment[3].spcod[7] & 0x02)
# Termination on each coding pass
self.assertFalse(c.segment[3].spcod[7] & 0x04)
# Vertically causal context
self.assertFalse(c.segment[3].spcod[7] & 0x08)
# Predictable termination
self.assertFalse(c.segment[3].spcod[7] & 0x0010)
# Segmentation symbols
self.assertFalse(c.segment[3].spcod[7] & 0x0020)
self.assertEqual(c.segment[3].spcod[8],
glymur.core.WAVELET_XFORM_5X3_REVERSIBLE)
self.assertEqual(len(c.segment[3].spcod), 9)
@ -450,19 +508,19 @@ class TestSuiteWarns(MetadataBase):
jfile = opj_data_file('input/nonregression/orb-blue10-lin-jp2.jp2')
with self.assertWarns(UserWarning):
# This file has an invalid ICC profile
Jp2k(jfile)[:]
Jp2k(jfile).read()
self.assertTrue(True)
def test_NR_DEC_orb_blue_win_jp2_26_decode(self):
jfile = opj_data_file('input/nonregression/orb-blue10-win-jp2.jp2')
with self.assertWarns(UserWarning):
Jp2k(jfile)[:]
Jp2k(jfile).read()
self.assertTrue(True)
@unittest.skipIf(OPJ_DATA_ROOT is None,
"OPJ_DATA_ROOT environment variable not set")
@unittest.skipIf(glymur.version.openjpeg_version_tuple[0] != 2,
@unittest.skipIf(glymur.version.openjpeg_version_tuple[0] == 1,
"Feature not supported in glymur until openjpeg 2.0")
class TestSuiteBands(unittest.TestCase):
"""
@ -557,7 +615,7 @@ class TestSuiteBands(unittest.TestCase):
@unittest.skipIf(OPJ_DATA_ROOT is None,
"OPJ_DATA_ROOT environment variable not set")
@unittest.skipIf(glymur.version.openjpeg_version_tuple[0] < 2,
@unittest.skipIf(glymur.version.openjpeg_version_tuple[0] == 1,
"Tests not passing until 2.0")
class TestSuite2point0(unittest.TestCase):
"""Runs tests introduced in version 2.0 or that pass only in 2.0"""
@ -571,7 +629,7 @@ class TestSuite2point0(unittest.TestCase):
def test_ETS_C1P0_p0_10_j2k(self):
jfile = opj_data_file('input/conformance/p0_10.j2k')
jp2k = Jp2k(jfile)
jpdata = jp2k[:]
jpdata = jp2k.read(rlevel=0)
pgxfile = opj_data_file('baseline/conformance/c1p0_10_0.pgx')
pgxdata = read_pgx(pgxfile)
@ -587,16 +645,13 @@ class TestSuite2point0(unittest.TestCase):
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_NR_DEC_broken2_jp2_5_decode(self):
"""
Invalid marker ID on codestream, Null pointer access upon read.
"""
# Null pointer access
jfile = opj_data_file('input/nonregression/broken2.jp2')
regex = re.compile(r'''Invalid\smarker\sid\sencountered\sat\sbyte\s
\d+\sin\scodestream:\s*"0x[a-fA-F0-9]{4}"''',
re.VERBOSE)
with self.assertRaises(IOError):
with self.assertWarnsRegex(UserWarning, regex):
Jp2k(jfile)[:]
with self.assertWarns(UserWarning):
# Invalid marker ID.
Jp2k(jfile).read()
self.assertTrue(True)
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_NR_DEC_broken4_jp2_7_decode(self):
@ -604,7 +659,7 @@ class TestSuite2point0(unittest.TestCase):
with self.assertRaises(IOError):
with self.assertWarns(UserWarning):
# invalid number of subbands, bad marker ID
Jp2k(jfile)[:]
Jp2k(jfile).read()
self.assertTrue(True)
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
@ -616,344 +671,11 @@ class TestSuite2point0(unittest.TestCase):
if glymur.version.openjpeg_version_tuple[0] < 2:
with self.assertWarns(UserWarning):
# Incorrect warning issued about tile parts.
Jp2k(jfile)[:]
Jp2k(jfile).read()
else:
Jp2k(jfile)[:]
Jp2k(jfile).read()
self.assertTrue(True)
@unittest.skipIf(OPJ_DATA_ROOT is None,
"OPJ_DATA_ROOT environment variable not set")
@unittest.skipIf(re.match(r'''0|1|2.0.0''',
glymur.version.openjpeg_version) is not None,
"Only supported in 2.0.1 or higher")
class TestSuite2point1(unittest.TestCase):
"""Runs tests introduced in version 2.0+ or that pass only in 2.0+"""
def setUp(self):
pass
def tearDown(self):
pass
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_NR_DEC_text_GBR_jp2_29_decode(self):
jfile = opj_data_file('input/nonregression/text_GBR.jp2')
with self.assertWarns(UserWarning):
# brand is 'jp2 ', but has any icc profile.
jp2 = Jp2k(jfile)
jp2[:]
self.assertTrue(True)
def test_NR_DEC_kodak_2layers_lrcp_j2c_31_decode(self):
jfile = opj_data_file('input/nonregression/kodak_2layers_lrcp.j2c')
Jp2k(jfile)[:]
self.assertTrue(True)
def test_NR_DEC_kodak_2layers_lrcp_j2c_32_decode(self):
jfile = opj_data_file('input/nonregression/kodak_2layers_lrcp.j2c')
Jp2k(jfile)[::4, ::4]
self.assertTrue(True)
def test_NR_DEC_issue104_jpxstream_jp2_33_decode(self):
jfile = opj_data_file('input/nonregression/issue104_jpxstream.jp2')
Jp2k(jfile)[:]
self.assertTrue(True)
def test_NR_DEC_mem_b2b86b74_2753_jp2_35_decode(self):
jfile = opj_data_file('input/nonregression/mem-b2b86b74-2753.jp2')
Jp2k(jfile)[:]
self.assertTrue(True)
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_NR_DEC_gdal_fuzzer_unchecked_num_resolutions_jp2_36_decode(self):
f = 'input/nonregression/gdal_fuzzer_unchecked_numresolutions.jp2'
jfile = opj_data_file(f)
with self.assertWarns(UserWarning):
# Invalid number of resolutions.
j = Jp2k(jfile)
with self.assertRaises(IOError):
j[:]
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_NR_DEC_gdal_fuzzer_check_number_of_tiles_jp2_38_decode(self):
relpath = 'input/nonregression/gdal_fuzzer_check_number_of_tiles.jp2'
jfile = opj_data_file(relpath)
with self.assertWarns(UserWarning):
# Invalid number of tiles.
j = Jp2k(jfile)
with self.assertRaises(IOError):
j[:]
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_NR_DEC_gdal_fuzzer_check_comp_dx_dy_jp2_39_decode(self):
relpath = 'input/nonregression/gdal_fuzzer_check_comp_dx_dy.jp2'
jfile = opj_data_file(relpath)
with self.assertWarns(UserWarning):
# Invalid subsampling value
with self.assertRaises(IOError):
Jp2k(jfile)[:]
def test_NR_DEC_file_409752_jp2_40_decode(self):
jfile = opj_data_file('input/nonregression/file409752.jp2')
with self.assertRaises(RuntimeError):
Jp2k(jfile)[:]
def test_NR_DEC_issue206_image_000_jp2_42_decode(self):
jfile = opj_data_file('input/nonregression/issue206_image-000.jp2')
Jp2k(jfile)[:]
self.assertTrue(True)
def test_NR_DEC_p1_04_j2k_57_decode(self):
jfile = opj_data_file('input/conformance/p1_04.j2k')
jp2k = Jp2k(jfile)
tdata = jp2k[896:1024, 896:1024] # last tile
odata = jp2k[:]
np.testing.assert_array_equal(tdata, odata[896:1024, 896:1024])
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_NR_DEC_p1_04_j2k_57_decode_0p7_backwards_compatibility(self):
"""
0.7.x usage deprecated
"""
jfile = opj_data_file('input/conformance/p1_04.j2k')
jp2k = Jp2k(jfile)
if sys.hexversion < 0x03000000:
with warnings.catch_warnings():
# Suppress a warning due to deprecated syntax
warnings.simplefilter("ignore")
tdata = jp2k.read(tile=63) # last tile
else:
with self.assertWarns(DeprecationWarning):
tdata = jp2k.read(tile=63) # last tile
odata = jp2k[:]
np.testing.assert_array_equal(tdata, odata[896:1024, 896:1024])
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_NR_DEC_p1_04_j2k_58_decode_0p7_backwards_compatibility(self):
"""
0.7.x usage deprecated
"""
jfile = opj_data_file('input/conformance/p1_04.j2k')
jp2k = Jp2k(jfile)
if sys.hexversion < 0x03000000:
with warnings.catch_warnings():
# Suppress a warning due to deprecated syntax
tdata = jp2k.read(tile=63, rlevel=2) # last tile
else:
with self.assertWarns(DeprecationWarning):
tdata = jp2k.read(tile=63, rlevel=2) # last tile
odata = jp2k[::4, ::4]
np.testing.assert_array_equal(tdata, odata[224:256, 224:256])
def test_NR_DEC_p1_04_j2k_58_decode(self):
jfile = opj_data_file('input/conformance/p1_04.j2k')
jp2k = Jp2k(jfile)
tdata = jp2k[896:1024:4, 896:1024:4] # last tile
odata = jp2k[::4, ::4]
np.testing.assert_array_equal(tdata, odata[224:256, 224:256])
def test_NR_DEC_p1_04_j2k_59_decode(self):
jfile = opj_data_file('input/conformance/p1_04.j2k')
jp2k = Jp2k(jfile)
tdata = jp2k[128:256, 512:640] # 2nd row, 5th column
odata = jp2k[:]
np.testing.assert_array_equal(tdata, odata[128:256, 512:640])
def test_NR_DEC_p1_04_j2k_60_decode(self):
jfile = opj_data_file('input/conformance/p1_04.j2k')
jp2k = Jp2k(jfile)
tdata = jp2k[128:256:2, 512:640:2] # 2nd row, 5th column
odata = jp2k[::2, ::2]
np.testing.assert_array_equal(tdata, odata[64:128, 256:320])
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_NR_DEC_jp2_36_decode(self):
lst = ('input',
'nonregression',
'gdal_fuzzer_assert_in_opj_j2k_read_SQcd_SQcc.patch.jp2')
jfile = opj_data_file('/'.join(lst))
with self.assertWarns(UserWarning):
# Invalid component number.
j = Jp2k(jfile)
with self.assertRaises(IOError):
j[:]
@unittest.skipIf(OPJ_DATA_ROOT is None,
"OPJ_DATA_ROOT environment variable not set")
@unittest.skipIf(re.match(r'''0|1|2.0.0''',
glymur.version.openjpeg_version) is not None,
"Only supported in 2.0.1 or higher")
class TestReadArea(unittest.TestCase):
"""
Runs tests introduced in version 2.0+ or that pass only in 2.0+
Specifically for read method with area parameter.
"""
@classmethod
def setUpClass(self):
jfile = opj_data_file('input/conformance/p1_04.j2k')
self.j2k = Jp2k(jfile)
self.j2k_data = self.j2k[:]
self.j2k_half_data = self.j2k[::2, ::2]
self.j2k_quarter_data = self.j2k[::4, ::4]
jfile = opj_data_file('input/conformance/p1_06.j2k')
self.j2k_p1_06 = Jp2k(jfile)
def test_NR_DEC_p1_04_j2k_43_decode(self):
actual = self.j2k[:1024, :1024]
expected = self.j2k_data
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p1_04_j2k_44_decode(self):
actual = self.j2k[640:768, 512:640]
expected = self.j2k_data[640:768, 512:640]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p1_04_j2k_45_decode(self):
actual = self.j2k[896:1024, 896:1024]
expected = self.j2k_data[896:1024, 896:1024]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p1_04_j2k_46_decode(self):
actual = self.j2k[500:800, 100:300]
expected = self.j2k_data[500:800, 100:300]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p1_04_j2k_47_decode(self):
actual = self.j2k[520:600, 260:360]
expected = self.j2k_data[520:600, 260:360]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p1_04_j2k_48_decode(self):
actual = self.j2k[520:660, 260:360]
expected = self.j2k_data[520:660, 260:360]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p1_04_j2k_49_decode(self):
actual = self.j2k[520:600, 360:400]
expected = self.j2k_data[520:600, 360:400]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p1_04_j2k_50_decode(self):
actual = self.j2k[:1024:4, :1024:4]
expected = self.j2k_quarter_data
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p1_04_j2k_51_decode(self):
actual = self.j2k[640:768:4, 512:640:4]
expected = self.j2k_quarter_data[160:192, 128:160]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p1_04_j2k_52_decode(self):
actual = self.j2k[896:1024:4, 896:1024:4]
expected = self.j2k_quarter_data[224:352, 224:352]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p1_04_j2k_53_decode(self):
actual = self.j2k[500:800:4, 100:300:4]
expected = self.j2k_quarter_data[125:200, 25:75]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p1_04_j2k_54_decode(self):
actual = self.j2k[520:600:4, 260:360:4]
expected = self.j2k_quarter_data[130:150, 65:90]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p1_04_j2k_55_decode(self):
actual = self.j2k[520:660:4, 260:360:4]
expected = self.j2k_quarter_data[130:165, 65:90]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p1_04_j2k_56_decode(self):
actual = self.j2k[520:600:4, 360:400:4]
expected = self.j2k_quarter_data[130:150, 90:100]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p1_06_j2k_70_decode(self):
actual = self.j2k_p1_06[9:12:2, 9:12:2]
self.assertEqual(actual.shape, (1, 1, 3))
def test_NR_DEC_p1_06_j2k_71_decode(self):
actual = self.j2k_p1_06[10:12:2, 4:10:2]
self.assertEqual(actual.shape, (1, 3, 3))
def test_NR_DEC_p1_06_j2k_72_decode(self):
ssdata = self.j2k_p1_06[3:9:2, 3:9:2]
self.assertEqual(ssdata.shape, (3, 3, 3))
def test_NR_DEC_p1_06_j2k_73_decode(self):
ssdata = self.j2k_p1_06[4:7:2, 4:7:2]
self.assertEqual(ssdata.shape, (2, 2, 3))
def test_NR_DEC_p1_06_j2k_74_decode(self):
ssdata = self.j2k_p1_06[4:5:2, 4:5:2]
self.assertEqual(ssdata.shape, (1, 1, 3))
def test_NR_DEC_p1_06_j2k_75_decode(self):
# Image size would be 0 x 0.
with self.assertRaises((IOError, OSError)):
self.j2k_p1_06[9:12:4, 9:12:4]
def test_NR_DEC_p0_04_j2k_85_decode(self):
actual = self.j2k[:256, :256]
expected = self.j2k_data[:256, :256]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p0_04_j2k_86_decode(self):
actual = self.j2k[:128, 128:256]
expected = self.j2k_data[:128, 128:256]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p0_04_j2k_87_decode(self):
actual = self.j2k[10:200, 50:120]
expected = self.j2k_data[10:200, 50:120]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p0_04_j2k_88_decode(self):
actual = self.j2k[150:210, 10:190]
expected = self.j2k_data[150:210, 10:190]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p0_04_j2k_89_decode(self):
actual = self.j2k[80:150, 100:200]
expected = self.j2k_data[80:150, 100:200]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p0_04_j2k_90_decode(self):
actual = self.j2k[20:50, 150:200]
expected = self.j2k_data[20:50, 150:200]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p0_04_j2k_91_decode(self):
actual = self.j2k[:256:4, :256:4]
expected = self.j2k_quarter_data[0:64, 0:64]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p0_04_j2k_92_decode(self):
actual = self.j2k[:128:4, 128:256:4]
expected = self.j2k_quarter_data[:32, 32:64]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p0_04_j2k_93_decode(self):
actual = self.j2k[10:200:4, 50:120:4]
expected = self.j2k_quarter_data[3:50, 13:30]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p0_04_j2k_94_decode(self):
actual = self.j2k[150:210:4, 10:190:4]
expected = self.j2k_quarter_data[38:53, 3:48]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p0_04_j2k_95_decode(self):
actual = self.j2k[80:150:4, 100:200:4]
expected = self.j2k_quarter_data[20:38, 25:50]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p0_04_j2k_96_decode(self):
actual = self.j2k[20:50:4, 150:200:4]
expected = self.j2k_quarter_data[5:13, 38:50]
np.testing.assert_array_equal(actual, expected)
if __name__ == "__main__":
unittest.main()

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
seem like logical negative tests to add.
"""
# E1101: assertWarns introduced in python 3.2
# pylint: disable=E1101
# R0904: Not too many methods in unittest.
# pylint: disable=R0904
import os
import re
import sys
import tempfile
import unittest
@ -17,7 +24,6 @@ from .fixtures import OPJ_DATA_ROOT, opj_data_file, read_image
from .fixtures import NO_READ_BACKEND, NO_READ_BACKEND_MSG
from .fixtures import NO_SKIMAGE_FREEIMAGE_SUPPORT
from .fixtures import WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG
from . import fixtures
from glymur import Jp2k
import glymur
@ -25,7 +31,7 @@ import glymur
@unittest.skipIf(OPJ_DATA_ROOT is None,
"OPJ_OPJ_DATA_ROOT environment variable not set")
class TestSuiteNegativeRead(unittest.TestCase):
class TestSuiteNegative(unittest.TestCase):
"""Test suite for certain negative tests from openjpeg suite."""
def setUp(self):
@ -35,6 +41,33 @@ class TestSuiteNegativeRead(unittest.TestCase):
def tearDown(self):
pass
@unittest.skipIf(NO_SKIMAGE_FREEIMAGE_SUPPORT,
"Cannot read input image without scikit-image/freeimage")
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
def test_cinema2K_bad_frame_rate(self):
"""Cinema2k frame rate must be either 24 or 48."""
relfile = 'input/nonregression/X_5_2K_24_235_CBR_STEM24_000.tif'
infile = opj_data_file(relfile)
data = skimage.io.imread(infile)
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
j = Jp2k(tfile.name, 'wb')
with self.assertRaises(IOError):
j.write(data, cinema2k=36)
@unittest.skipIf(NO_READ_BACKEND, NO_READ_BACKEND_MSG)
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
def test_psnr_with_cratios(self):
"""Using psnr with cratios options is not allowed."""
# Not an OpenJPEG test, but close.
infile = opj_data_file('input/nonregression/Bretagne1.ppm')
data = read_image(infile)
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
j = Jp2k(tfile.name, 'wb')
with self.assertRaises(IOError):
j.write(data, psnr=[30, 35, 40], cratios=[2, 3, 4])
def test_nr_marker_not_compliant(self):
"""non-compliant marker, should still be able to read"""
relpath = 'input/nonregression/MarkerIsNotCompliant.j2k'
@ -65,80 +98,56 @@ class TestSuiteNegativeRead(unittest.TestCase):
jp2k.get_codestream(header_only=False)
self.assertTrue(True)
@unittest.skipIf(re.match("1.5|2", glymur.version.openjpeg_version) is None,
"Must have openjpeg 1.5 or higher to run")
@unittest.skipIf(os.name == "nt", fixtures.WINDOWS_TMP_FILE_MSG)
@unittest.skipIf(OPJ_DATA_ROOT is None,
"OPJ_OPJ_DATA_ROOT environment variable not set")
class TestSuiteNegativeWrite(unittest.TestCase):
"""Test suite for certain negative tests from openjpeg suite."""
def setUp(self):
self.jp2file = glymur.data.nemo()
self.j2kfile = glymur.data.goodstuff()
def tearDown(self):
pass
@unittest.skipIf(NO_SKIMAGE_FREEIMAGE_SUPPORT,
"Cannot read input image without scikit-image/freeimage")
def test_cinema2K_bad_frame_rate(self):
"""Cinema2k frame rate must be either 24 or 48."""
relfile = 'input/nonregression/X_5_2K_24_235_CBR_STEM24_000.tif'
infile = opj_data_file(relfile)
data = skimage.io.imread(infile)
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
with self.assertRaises(IOError):
Jp2k(tfile.name, data=data, cinema2k=36)
@unittest.skipIf(NO_READ_BACKEND, NO_READ_BACKEND_MSG)
def test_psnr_with_cratios(self):
"""Using psnr with cratios options is not allowed."""
# Not an OpenJPEG test, but close.
infile = opj_data_file('input/nonregression/Bretagne1.ppm')
data = read_image(infile)
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
with self.assertRaises(IOError):
Jp2k(tfile.name,
data=data, psnr=[30, 35, 40], cratios=[2, 3, 4])
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
def test_code_block_dimensions(self):
"""don't allow extreme codeblock sizes"""
# opj_compress doesn't allow the dimensions of a codeblock
# to be too small or too big, so neither will we.
data = np.zeros((256, 256), dtype=np.uint8)
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
j = Jp2k(tfile.name, 'wb')
# opj_compress doesn't allow code block area to exceed 4096.
with self.assertRaises(IOError):
Jp2k(tfile.name, data=data, cbsize=(256, 256))
j.write(data, cbsize=(256, 256))
# opj_compress doesn't allow either dimension to be less than 4.
with self.assertRaises(IOError):
Jp2k(tfile.name, data=data, cbsize=(2048, 2))
j.write(data, cbsize=(2048, 2))
with self.assertRaises(IOError):
Jp2k(tfile.name, data=data, cbsize=(2, 2048))
j.write(data, cbsize=(2, 2048))
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
def test_precinct_size_not_p2(self):
"""precinct sizes should be powers of two."""
ifile = Jp2k(self.j2kfile)
data = ifile[::4, ::4]
data = ifile.read(rlevel=2)
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
ofile = Jp2k(tfile.name, 'wb')
with self.assertRaises(IOError):
Jp2k(tfile.name, data=data, psizes=[(13, 13)])
ofile.write(data, psizes=[(13, 13)])
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
def test_cblk_size_not_power_of_two(self):
"""code block sizes should be powers of two."""
ifile = Jp2k(self.j2kfile)
data = ifile[::4, ::4]
data = ifile.read(rlevel=2)
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
ofile = Jp2k(tfile.name, 'wb')
with self.assertRaises(IOError):
Jp2k(tfile.name, data=data, cbsize=(13, 12))
ofile.write(data, cbsize=(13, 12))
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
def test_cblk_size_precinct_size(self):
"""code block sizes should never exceed half that of precinct size."""
ifile = Jp2k(self.j2kfile)
data = ifile[::4, ::4]
data = ifile.read(rlevel=2)
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
ofile = Jp2k(tfile.name, 'wb')
with self.assertRaises(IOError):
Jp2k(tfile.name, data=data, cbsize=(64, 64), psizes=[(64, 64)])
ofile.write(data,
cbsize=(64, 64),
psizes=[(64, 64)])
if __name__ == "__main__":
unittest.main()

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,15 @@
# -*- coding: utf-8 -*-
"""Test suite for printing.
"""
# C0302: don't care too much about having too many lines in a test module
# pylint: disable=C0302
# E061: unittest.mock introduced in 3.3 (python-2.7/pylint issue)
# pylint: disable=E0611,F0401
# R0904: Not too many methods in unittest.
# pylint: disable=R0904
import os
import re
import struct
@ -23,14 +32,12 @@ import lxml.etree as ET
import glymur
from glymur import Jp2k, command_line
from . import fixtures
from .fixtures import (OPJ_DATA_ROOT, opj_data_file,
WARNING_INFRASTRUCTURE_ISSUE,
WARNING_INFRASTRUCTURE_MSG,
WINDOWS_TMP_FILE_MSG,
text_gbr_27, text_gbr_33, text_gbr_34)
from .fixtures import OPJ_DATA_ROOT, opj_data_file
from .fixtures import WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG
from .fixtures import text_gbr_27, text_gbr_33, text_gbr_34
@unittest.skipIf(os.name == "nt", WINDOWS_TMP_FILE_MSG)
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
class TestPrinting(unittest.TestCase):
"""Tests for verifying how printing works."""
def setUp(self):
@ -42,13 +49,25 @@ class TestPrinting(unittest.TestCase):
glymur.set_printoptions(short=False, xml=True, codestream=True)
def tearDown(self):
glymur.set_parseoptions(full_codestream=False)
pass
def test_codestream(self):
"""Should be able to print a raw codestream."""
j = glymur.Jp2k(self.j2kfile)
with patch('sys.stdout', new=StringIO()) as fake_out:
print(j)
actual = fake_out.getvalue().strip()
# Remove the file line, as that is filesystem-dependent.
lines = actual.split('\n')
actual = '\n'.join(lines[1:])
self.assertEqual(actual, fixtures.codestream)
def test_version_info(self):
"""Should be able to print(glymur.version.info)"""
with patch('sys.stdout', new=StringIO()) as fake_out:
print(glymur.version.info)
fake_out.getvalue().strip()
actual = fake_out.getvalue().strip()
self.assertTrue(True)
@ -58,7 +77,7 @@ class TestPrinting(unittest.TestCase):
with tempfile.NamedTemporaryFile(suffix='.jpx') as tfile:
with open(self.jpxfile, 'rb') as ifile:
tfile.write(ifile.read())
# Add the header for an unknown superbox.
write_buffer = struct.pack('>I4s', 20, 'grp '.encode())
tfile.write(write_buffer)
@ -87,17 +106,16 @@ class TestPrinting(unittest.TestCase):
with self.assertRaises(TypeError):
glymur.set_printoptions(hi='low')
@unittest.skipIf(re.match("1.5|2",
glymur.version.openjpeg_version) is None,
"Must have openjpeg 1.5 or higher to run")
def test_asoc_label_box(self):
"""verify printing of asoc, label boxes"""
# Construct a fake file with an asoc and a label box, as
# OpenJPEG doesn't have such a file.
data = glymur.Jp2k(self.jp2file)[::2, ::2]
data = glymur.Jp2k(self.jp2file).read(rlevel=1)
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
j = glymur.Jp2k(tfile.name, 'wb')
j.write(data)
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile2:
glymur.Jp2k(tfile.name, data=data)
# Offset of the codestream is where we start.
wbuffer = tfile.read(77)
@ -399,7 +417,7 @@ class TestPrinting(unittest.TestCase):
@unittest.skipIf(sys.hexversion < 0x03000000,
"Only trusting python3 for printing non-ascii chars")
def test_xml_cyrrilic(self):
"""Should be able to print XMLBox with utf-8 encoding (cyrrillic)."""
"""Should be able to print an XMLBox with utf-8 encoding (cyrrillic)."""
# Seems to be inconsistencies between different versions of python2.x
# as to what gets printed.
#
@ -417,8 +435,7 @@ class TestPrinting(unittest.TestCase):
actual = fake_out.getvalue().strip()
if sys.hexversion < 0x03000000:
lines = ["XML Box (xml ) @ (-1, 0)",
(" <country>&#1056;&#1086;&#1089;&#1089;",
"&#1080;&#1103;</country>")]
" <country>&#1056;&#1086;&#1089;&#1089;&#1080;&#1103;</country>"]
else:
lines = ["XML Box (xml ) @ (-1, 0)",
" <country>Россия</country>"]
@ -594,8 +611,7 @@ class TestPrinting(unittest.TestCase):
lines = ["UUID Box (uuid) @ (1135519, 76)",
" UUID: 4a706754-6966-6645-7869-662d3e4a5032 (EXIF)",
(" UUID Data: OrderedDict([('ImageWidth', 256),"
" ('ImageLength', 512), ('Make', 'HTC')])")]
" UUID Data: OrderedDict([('ImageWidth', 256), ('ImageLength', 512), ('Make', 'HTC')])"]
expected = '\n'.join(lines)
@ -809,7 +825,6 @@ class TestPrintingOpjDataRoot(unittest.TestCase):
expected = '\n'.join(lines)
self.assertEqual(actual, expected)
@unittest.skipIf(OPJ_DATA_ROOT is None,
"OPJ_DATA_ROOT environment variable not set")
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
@ -831,31 +846,21 @@ class TestPrintingOpjDataRootWarns(unittest.TestCase):
def tearDown(self):
pass
def test_invalid_colour_specification_method(self):
"""should not error out with invalid colour specification method"""
# Don't care so much about what the output looks like, just that we
# do not error out.
filename = opj_data_file('input/nonregression/issue397.jp2')
with self.assertWarns(UserWarning):
jp2 = Jp2k(filename)
with patch('sys.stdout', new=StringIO()):
print(jp2)
self.assertTrue(True)
def test_invalid_colorspace(self):
"""An invalid colorspace shouldn't cause an error."""
filename = opj_data_file('input/nonregression/edf_c2_1103421.jp2')
with self.assertWarns(UserWarning):
jp2 = Jp2k(filename)
with patch('sys.stdout', new=StringIO()):
with patch('sys.stdout', new=StringIO()) as fake_out:
print(jp2)
@unittest.skip("unexplained failure")
def test_bad_rsiz(self):
"""Should still be able to print if rsiz is bad, issue196"""
filename = opj_data_file('input/nonregression/edf_c2_1002767.jp2')
with self.assertWarns(UserWarning):
j = Jp2k(filename).get_codestream()
with patch('sys.stdout', new=StringIO()):
j = Jp2k(filename)
with patch('sys.stdout', new=StringIO()) as fake_out:
print(j)
def test_bad_wavelet_transform(self):
@ -863,7 +868,7 @@ class TestPrintingOpjDataRootWarns(unittest.TestCase):
filename = opj_data_file('input/nonregression/edf_c2_10025.jp2')
with self.assertWarns(UserWarning):
jp2 = Jp2k(filename)
with patch('sys.stdout', new=StringIO()):
with patch('sys.stdout', new=StringIO()) as fake_out:
print(jp2)
def test_invalid_progression_order(self):
@ -1024,7 +1029,7 @@ class TestPrintingOpjDataRootWarns(unittest.TestCase):
'issue171.jp2'))
with self.assertWarns(UserWarning):
jp2 = Jp2k(filename)
with patch('sys.stdout', new=StringIO()):
with patch('sys.stdout', new=StringIO()) as fake_out:
# No need to verify, it's enough that we don't error out.
print(jp2)
@ -1040,10 +1045,9 @@ class TestJp2dump(unittest.TestCase):
# Reset printoptions for every test.
glymur.set_printoptions(short=False, xml=True, codestream=True)
glymur.set_parseoptions(full_codestream=False)
def tearDown(self):
glymur.set_parseoptions(full_codestream=False)
pass
def run_jp2dump(self, args):
sys.argv = args
@ -1056,53 +1060,24 @@ class TestJp2dump(unittest.TestCase):
return actual
def test_default_nemo(self):
"""by default one should get the main header"""
"""Should be able to dump a JP2 file's metadata with no codestream."""
actual = self.run_jp2dump(['', self.jp2file])
# shave off the non-main-header segments
lines = fixtures.nemo.split('\n')
expected = lines[0:140]
expected = '\n'.join(expected)
self.assertEqual(actual, expected)
self.assertEqual(actual, fixtures.nemo_dump_no_codestream)
def test_jp2_codestream_0(self):
def test_codestream_0(self):
"""Verify dumping with -c 0, supressing all codestream details."""
actual = self.run_jp2dump(['', '-c', '0', self.jp2file])
# shave off the codestream details
lines = fixtures.nemo.split('\n')
expected = lines[0:105]
expected = '\n'.join(expected)
self.assertEqual(actual, expected)
self.assertEqual(actual, fixtures.nemo_dump_no_codestream)
def test_jp2_codestream_1(self):
def test_codestream_1(self):
"""Verify dumping with -c 1, print just the header."""
actual = self.run_jp2dump(['', '-c', '1', self.jp2file])
# shave off the non-main-header segments
lines = fixtures.nemo.split('\n')
expected = lines[0:140]
expected = '\n'.join(expected)
self.assertEqual(actual, expected)
self.assertEqual(actual, fixtures.nemo_with_codestream_header)
def test_jp2_codestream_2(self):
"""Verify dumping with -c 2, print entire jp2 jacket, codestream."""
actual = self.run_jp2dump(['', '-c', '2', self.jp2file])
# shave off the non-main-header segments
expected = fixtures.nemo
self.assertEqual(actual, expected)
@unittest.skipIf(sys.hexversion < 0x03000000, "assertRegex not in 2.7")
def test_j2k_codestream_0(self):
"""-c 0 should print just a single line when used on a codestream."""
sys.argv = ['', '-c', '0', self.j2kfile]
with patch('sys.stdout', new=StringIO()) as fake_out:
command_line.main()
actual = fake_out.getvalue().strip()
self.assertRegex(actual, "File: .*")
def test_j2k_codestream_2(self):
def test_codestream_2(self):
"""Verify dumping with -c 2, full details."""
with patch('sys.stdout', new=StringIO()) as fake_out:
sys.argv = ['', '-c', '2', self.j2kfile]
@ -1127,9 +1102,4 @@ class TestJp2dump(unittest.TestCase):
"""Verify dumping with -x, suppress XML."""
actual = self.run_jp2dump(['', '-x', self.jp2file])
# shave off the XML and non-main-header segments
lines = fixtures.nemo.split('\n')
expected = lines[0:18]
expected.extend(lines[104:140])
expected = '\n'.join(expected)
self.assertEqual(actual, expected)
self.assertEqual(actual, fixtures.nemo_dump_no_codestream_no_xml)

View file

@ -14,11 +14,12 @@ from distutils.version import LooseVersion
import lxml.etree
import numpy as np
from .lib import openjpeg as opj, openjp2 as opj2
from .lib import openjpeg as opj
from .lib import openjp2 as opj2
# Do not change the format of this next line! Doing so risks breaking
# setup.py
version = "0.8.0"
version = "0.6.0"
_sv = LooseVersion(version)
version_tuple = _sv.version

View file

@ -1,9 +1,9 @@
from setuptools import setup
from setuptools import setup, find_packages
import os
import re
import sys
kwargs = {'name': 'Glymur',
kwargs = {'name': 'glymur',
'description': 'Tools for accessing JPEG2000 files',
'long_description': open('README.md').read(),
'author': 'John Evans',
@ -11,20 +11,18 @@ kwargs = {'name': 'Glymur',
'url': 'https://github.com/quintusdias/glymur',
'packages': ['glymur', 'glymur.data', 'glymur.test', 'glymur.lib',
'glymur.lib.test'],
'package_data': {'glymur': ['data/*.jp2',
'data/*.j2k',
'data/*.jpx']},
'package_data': {'glymur': ['data/*.jp2', 'data/*.j2k', 'data/*.jpx']},
'entry_points': {
'console_scripts': ['jp2dump=glymur.command_line:main'],
},
'license': 'MIT',
'test_suite': 'glymur.test'}
install_requires = ['numpy>=1.7.0', 'lxml>=3.0.0']
instllrqrs = ['numpy>=1.4.1', 'lxml>=2.3.2']
if sys.hexversion < 0x03030000:
install_requires.append('contextlib2>=0.4')
install_requires.append('mock>=1.0.1')
kwargs['install_requires'] = install_requires
instllrqrs.append('contextlib2>=0.4')
instllrqrs.append('mock>=1.0.1')
kwargs['install_requires'] = instllrqrs
clssfrs = ["Programming Language :: Python",
"Programming Language :: Python :: 2.7",