Merge branch 'release-0.7.0rc1'

Conflicts:
	docs/source/conf.py
	glymur/version.py
This commit is contained in:
jevans 2014-10-01 18:54:53 -04:00
commit 074bc588a8
40 changed files with 3118 additions and 4947 deletions

1
.gitignore vendored
View file

@ -1 +1,2 @@
*.pyc *.pyc
*.swp

View file

@ -12,9 +12,9 @@ before_install:
# command to install dependencies # command to install dependencies
install: install:
- if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install lxml contextlib2 mock; fi - if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install lxml contextlib2 mock six; fi
- if [[ $TRAVIS_PYTHON_VERSION == '3.3' ]]; then pip install lxml numpy; fi - if [[ $TRAVIS_PYTHON_VERSION == '3.3' ]]; then pip install lxml numpy six; fi
- if [[ $TRAVIS_PYTHON_VERSION == '3.4' ]]; then pip install lxml numpy; fi - if [[ $TRAVIS_PYTHON_VERSION == '3.4' ]]; then pip install lxml numpy six; fi
# command to run tests # command to run tests
script: script:

View file

@ -1,4 +1,6 @@
Mar 06, 2014 - Added Cinema2K, Cinema4K write support. Oct 01, 2014 - v0.7.0 Added array-style slicing.
August 03, 2014 - v0.6.0 Added Cinema2K, Cinema4K write support.
Changed constructor for ChannelDefinition box. Removed support Changed constructor for ChannelDefinition box. Removed support
for Python 2.6. Added write support for JP2 UUID, DataEntryURL, for Python 2.6. Added write support for JP2 UUID, DataEntryURL,
Palette and Component Mapping boxes, JPX Association, NumberList Palette and Component Mapping boxes, JPX Association, NumberList

View file

@ -1,34 +0,0 @@
#!/usr/bin/env python
import argparse
import sys
import glymur
description='Print JPEG2000 metadata.'
parser = argparse.ArgumentParser(description=description)
parser.add_argument('-x', '--noxml', help='Suppress XML.',
action='store_true')
parser.add_argument('-s', '--short', help='Only print box id, offset, and length.',
action='store_true')
chelp='Level of codestream information. 0 suppressed all details, 1 prints headers, 2 prints the full codestream'
parser.add_argument('-c', '--codestream',
help=chelp,
nargs=1,
type=int,
default=[0])
parser.add_argument('filename')
args = parser.parse_args()
if args.noxml:
glymur.set_printoptions(xml=False)
if args.short:
glymur.set_printoptions(short=True)
if args.codestream[0] == 0:
glymur.set_printoptions(codestream=False)
print_full_codestream = False
elif args.codestream[0] == 1:
print_full_codestream = False
else:
print_full_codestream = True
filename = args.filename
glymur.jp2dump(args.filename, codestream=print_full_codestream)

View file

@ -76,9 +76,9 @@ copyright = u'2013, John Evans'
# built documents. # built documents.
# #
# The short X.Y version. # The short X.Y version.
version = '0.6' version = '0.7'
# The full version, including alpha/beta/rc tags. # The full version, including alpha/beta/rc tags.
release = '0.6.1' release = '0.7.0'
# The language for content autogenerated by Sphinx. Refer to documentation # The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages. # for a list of supported languages.

View file

@ -24,11 +24,11 @@ configuration format is the same as used by Python’s configparser
module, i.e. :: module, i.e. ::
[library] [library]
openjp2: /opt/openjp2-svn/lib/libopenjp2.so openjp2: /somewhere/lib/libopenjp2.so
This assumes, of course, that you've installed OpenJPEG into This assumes, of course, that you've installed OpenJPEG into
/opt/openjp2-svn on a linux system. The location of the configuration file /opt/openjpeg 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 can vary as well. If you use either linux or mac, the path
to the configuration file would normally be :: to the configuration file would normally be ::
$HOME/.config/glymur/glymurrc $HOME/.config/glymur/glymurrc
@ -48,7 +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. :: installed in a non-standard place, i.e. ::
[library] [library]
openjpeg: /not/the/usual/location/lib/libopenjpeg.so openjpeg: /somewhere/lib/libopenjpeg.so
''''''' '''''''
Testing Testing

View file

@ -3,29 +3,51 @@ How do I...?
------------ ------------
... read the lowest resolution thumbnail? ... read images?
========================================= ================
Printing the Jp2k object should reveal the number of resolutions Jp2k implements slicing via the :py:meth:`__getitem__` method, meaning that
(look in the COD segment section of the codestream), but you can multiple resolution imagery in a JPEG 2000 file can
take a shortcut by supplying -1 as the easily be accessed via array-style slicing. For example here's how to
resolution level. :: retrieve a full resolution and first lower-resolution image ::
>>> import glymur >>> import glymur
>>> jp2file = glymur.data.nemo() >>> jp2file = glymur.data.nemo() # just a path to a JPEG2000 file
>>> jp2 = glymur.Jp2k(jp2file) >>> jp2 = glymur.Jp2k(jp2file)
>>> thumbnail = jp2.read(rlevel=-1) >>> fullres = jp2[:]
>>> print(fullres.shape)
(1456, 2592, 3)
>>> thumbnail = jp2[::2, ::2]
>>> print(thumbnail.shape)
(728, 1296, 3)
The :py:meth:`read` method exposes many more options for other JPEG 2000
features such as quality layers.
... write images?
=================
So long as the image data can fit entirely into memory, array-style slicing may
also be used to write JPEG 2000 files.
>>> import glymur, numpy as np
>>> jp2 = glymur.Jp2k('zeros.jp2', mode='wb')
>>> jp2[:] = np.zeros((640, 480), dtype=np.uint8)
The :py:meth:`write` method exposes many more options for other JPEG 2000
features. You should have OpenJPEG version 1.5 or more recent before writing
JPEG 2000 images.
... display metadata? ... display metadata?
===================== =====================
There are two ways. From the unix command line, the script **jp2dump** is There are two ways. From the command line, the console script **jp2dump** is
available. :: available. ::
$ jp2dump /path/to/glymur/installation/data/nemo.jp2 $ jp2dump /path/to/glymur/installation/data/nemo.jp2
From within Python, it is as simple as printing the Jp2k object, i.e. :: From within Python, the same result is obtained simply by printing the Jp2k
object, i.e. ::
>>> import glymur >>> import glymur
>>> jp2file = glymur.data.nemo() >>> jp2file = glymur.data.nemo() # just a path to a JP2 file
>>> jp2 = glymur.Jp2k(jp2file) >>> jp2 = glymur.Jp2k(jp2file)
>>> print(jp2) >>> print(jp2)
File: nemo.jp2 File: nemo.jp2
@ -319,10 +341,10 @@ is currently limited to XML and UUID boxes.
... create an image with an alpha layer? ... create an image with an alpha layer?
======================================== ========================================
OpenJPEG can create JP2 files with more than 3 components (requires OpenJPEG can create JP2 files with more than 3 components (use version 2.1.0+
the development version of OpenJPEG), but by default, any extra components are for this), but by default, any extra components are not described
not described as such. In order to do so, we need to rewrap such as such. In order to do so, we need to rewrap such an image in a
an image in a set of boxes that includes a channel definition box. set of boxes that includes a channel definition box.
This example is based on SciPy example code found at This example is based on SciPy example code found at
http://scipy-lectures.github.io/advanced/image_processing/#basic-manipulations . http://scipy-lectures.github.io/advanced/image_processing/#basic-manipulations .
@ -340,7 +362,7 @@ image isn't square. ::
>>> alpha[mask] = 0 >>> alpha[mask] = 0
>>> rgba = np.concatenate((rgb, alpha), axis=2) >>> rgba = np.concatenate((rgb, alpha), axis=2)
>>> jp2 = Jp2k('tmp.jp2', 'wb') >>> jp2 = Jp2k('tmp.jp2', 'wb')
>>> jp2.write(rgba) >>> jp2[:] = rgba
Next we need to specify what types of channels we have. Next we need to specify what types of channels we have.
The first three channels are color channels, but we identify the fourth as The first three channels are color channels, but we identify the fourth as
@ -380,7 +402,11 @@ Here's how the Preview application on the mac shows the RGBA image.
... work with XMP UUIDs? ... work with XMP UUIDs?
======================== ========================
XMP is metadata on steroids. `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."
The example JP2 file shipped with glymur has an XMP UUID. :: The example JP2 file shipped with glymur has an XMP UUID. ::
@ -440,7 +466,7 @@ http://photojournal.jpl.nasa.gov/tiff/PIA17145.tif info JPEG 2000::
>>> image = skimage.io.imread('PIA17145.tif') >>> image = skimage.io.imread('PIA17145.tif')
>>> from glymur import Jp2k >>> from glymur import Jp2k
>>> jp2 = Jp2k('PIA17145.jp2', 'wb') >>> jp2 = Jp2k('PIA17145.jp2', 'wb')
>>> jp2.write(image) >>> jp2[:] = image
Next you can extract the XMP metadata. Next you can extract the XMP metadata.

View file

@ -15,9 +15,9 @@ Contents:
introduction introduction
detailed_installation detailed_installation
how_do_i how_do_i
api
whatsnew/index whatsnew/index
roadmap roadmap
api
------------------ ------------------
Indices and tables Indices and tables

View file

@ -5,13 +5,16 @@ Glymur: a Python interface for JPEG 2000
**Glymur** is an interface to the OpenJPEG library **Glymur** is an interface to the OpenJPEG library
which allows one to read and write JPEG 2000 files from Python. which allows one to read and write JPEG 2000 files from Python.
Glymur supports both reading and writing of JPEG 2000 images, but writing 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 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.
In regards to metadata, most JP2 boxes are properly interpreted. In regards to metadata, most JP2 boxes are properly interpreted.
Certain optional JP2 boxes can also be written, including XML boxes and Certain optional JP2 boxes can also be written, including XML boxes and
XMP UUIDs. There is incomplete support for reading JPX metadata. XMP UUIDs. There is incomplete support for reading JPX metadata.
Glymur 0.6 works on Python versions 2.7, 3.3 and 3.4. If you have Python 2.6, Glymur 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. you should use the 0.5 series of Glymur.
For more information about OpenJPEG, please consult http://www.openjpeg.org. For more information about OpenJPEG, please consult http://www.openjpeg.org.
@ -27,10 +30,6 @@ but you should also be able to install Glymur via pip ::
$ pip install glymur $ pip install glymur
This will install a script **jp2dump** that can be used from the unix command In addition to the package, this also gives you a command line script
line for dumping JP2 metadata, so you should adjust your **$PATH** **jp2dump** that can be used from the command line line to print JPEG 2000
environment variable to take advantage of it. For example, if you install metadata.
with pip's `--user` option on linux ::
$ export PATH=$HOME/.local/bin:$PATH

View file

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

View file

@ -0,0 +1,11 @@
=====================
Changes in glymur 0.7
=====================
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 (see issue 248)

View file

@ -10,3 +10,4 @@ These document the changes between minor (or major) versions of glymur.
0.5 0.5
0.6 0.6
0.7

View file

@ -7,7 +7,6 @@ from glymur import version
__version__ = version.version __version__ = version.version
from .jp2k import Jp2k from .jp2k import Jp2k
from .jp2dump import jp2dump
from .jp2box import get_printoptions, set_printoptions from .jp2box import get_printoptions, set_printoptions
from .jp2box import get_parseoptions, set_parseoptions from .jp2box import get_parseoptions, set_parseoptions

View file

@ -202,7 +202,7 @@ class Codestream(object):
msg = 'Invalid marker id encountered at byte {0:d} ' msg = 'Invalid marker id encountered at byte {0:d} '
msg += 'in codestream: "0x{1:x}"' msg += 'in codestream: "0x{1:x}"'
msg = msg.format(self._offset, self._marker_id) msg = msg.format(self._offset, self._marker_id)
warnings.warn(msg) warnings.warn(msg, UserWarning)
break break
self.segment.append(segment) self.segment.append(segment)
@ -642,7 +642,7 @@ class Codestream(object):
srgn = data[1] srgn = data[1]
sprgn = data[2] sprgn = data[2]
return RGNsegment(length, offset, crgn, srgn, sprgn) return RGNsegment(crgn, srgn, sprgn, length, offset)
def _parse_siz_segment(self, fptr): def _parse_siz_segment(self, fptr):
"""Parse the SIZ segment. """Parse the SIZ segment.
@ -1074,7 +1074,7 @@ class CMEsegment(Segment):
15444-1:2004 - Information technology -- JPEG 2000 image coding system: 15444-1:2004 - Information technology -- JPEG 2000 image coding system:
Core coding system Core coding system
""" """
def __init__(self, rcme, ccme, length, offset): def __init__(self, rcme, ccme, length=-1, offset=-1):
Segment.__init__(self, marker_id='CME') Segment.__init__(self, marker_id='CME')
self.rcme = rcme self.rcme = rcme
self.ccme = ccme self.ccme = ccme
@ -1463,7 +1463,7 @@ class RGNsegment(Segment):
15444-1:2004 - Information technology -- JPEG 2000 image coding system: 15444-1:2004 - Information technology -- JPEG 2000 image coding system:
Core coding system Core coding system
""" """
def __init__(self, length, offset, crgn, srgn, sprgn): def __init__(self, crgn, srgn, sprgn, length=-1, offset=-1):
Segment.__init__(self, marker_id='RGN') Segment.__init__(self, marker_id='RGN')
self.length = length self.length = length
self.offset = offset self.offset = offset
@ -1726,7 +1726,7 @@ class SOTsegment(Segment):
15444-1:2004 - Information technology -- JPEG 2000 image coding system: 15444-1:2004 - Information technology -- JPEG 2000 image coding system:
Core coding system Core coding system
""" """
def __init__(self, isot, psot, tpsot, tnsot, length, offset): def __init__(self, isot, psot, tpsot, tnsot, length=-1, offset=-1):
Segment.__init__(self, marker_id='SOT') Segment.__init__(self, marker_id='SOT')
self.isot = isot self.isot = isot
self.psot = psot self.psot = psot

71
glymur/command_line.py Normal file
View file

@ -0,0 +1,71 @@
"""
Entry point for console script jp2dump.
"""
import argparse
import sys
import warnings
from . import Jp2k, set_printoptions
def main():
"""
Entry point for console script jp2dump.
"""
description='Print JPEG2000 metadata.'
parser = argparse.ArgumentParser(description=description)
parser.add_argument('-x', '--noxml',
help='Suppress XML.',
action='store_true')
parser.add_argument('-s', '--short',
help='Only print box id, offset, and length.',
action='store_true')
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,
nargs=1,
type=int,
default=[0])
parser.add_argument('filename')
args = parser.parse_args()
if args.noxml:
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)
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.
j = Jp2k(filename)
if print_full_codestream:
print(j.get_codestream(header_only=False))
else:
print(j)
# Re-emit any warnings that may have been suppressed.
if len(wctx) > 0:
print("\n")
for warning in wctx:
print("{0}:{1}: {2}: {3}".format(warning.filename,
warning.lineno,
warning.category.__name__,
warning.message))

View file

@ -62,17 +62,13 @@ class Jp2kBox(object):
length of the box in bytes. length of the box in bytes.
offset : int offset : int
offset of the box from the start of the file. offset of the box from the start of the file.
longname : str
more verbose description of the box.
box : list box : list
List of JPEG 2000 boxes. List of JPEG 2000 boxes.
""" """
def __init__(self, box_id='', offset=0, length=0, longname=''): def __init__(self, offset=0, length=0):
self.box_id = box_id
self.length = length self.length = length
self.offset = offset self.offset = offset
self.longname = longname
self.box = [] self.box = []
def __repr__(self): def __repr__(self):
@ -197,7 +193,7 @@ class Jp2kBox(object):
msg = "Encountered an unrecoverable ValueError while parsing a {0} " msg = "Encountered an unrecoverable ValueError while parsing a {0} "
msg += "box at byte offset {1}. The original error message was " msg += "box at byte offset {1}. The original error message was "
msg += "\"{2}\"" msg += "\"{2}\""
msg = msg.format(box_id.decode('utf-8'), start, str(err)) msg = msg.format(_BOX_WITH_ID[box_id].longname, start, str(err))
warnings.warn(msg, UserWarning) warnings.warn(msg, UserWarning)
box = UnknownBox(box_id.decode('utf-8'), box = UnknownBox(box_id.decode('utf-8'),
length=num_bytes, offset=start, longname='Unknown') length=num_bytes, offset=start, longname='Unknown')
@ -300,11 +296,12 @@ class ColourSpecificationBox(Jp2kBox):
ICC profile header according to ICC profile specification. If ICC profile header according to ICC profile specification. If
colorspace is not None, then icc_profile must be empty. colorspace is not None, then icc_profile must be empty.
""" """
longname = 'Colour Specification'
box_id = 'colr'
def __init__(self, method=ENUMERATED_COLORSPACE, precedence=0, def __init__(self, method=ENUMERATED_COLORSPACE, precedence=0,
approximation=0, colorspace=None, icc_profile=None, approximation=0, colorspace=None, icc_profile=None,
length=0, offset=-1): length=0, offset=-1):
Jp2kBox.__init__(self, box_id='colr', longname='Colour Specification') Jp2kBox.__init__(self)
self.method = method self.method = method
self.precedence = precedence self.precedence = precedence
@ -588,8 +585,11 @@ class ChannelDefinitionBox(Jp2kBox):
association : list association : list
index of the associated color index of the associated color
""" """
box_id = 'cdef'
longname = 'Channel Definition'
def __init__(self, channel_type, association, index=None, **kwargs): def __init__(self, channel_type, association, index=None, **kwargs):
Jp2kBox.__init__(self, box_id='cdef', longname='Channel Definition') Jp2kBox.__init__(self)
if index is None: if index is None:
self.index = tuple(range(len(channel_type))) self.index = tuple(range(len(channel_type)))
@ -704,8 +704,11 @@ class CodestreamHeaderBox(Jp2kBox):
box : list box : list
List of boxes contained in this superbox. List of boxes contained in this superbox.
""" """
box_id = 'jpch'
longname = 'Codestream Header'
def __init__(self, box=None, length=0, offset=-1): def __init__(self, box=None, length=0, offset=-1):
Jp2kBox.__init__(self, box_id='jpch', longname='Codestream Header') Jp2kBox.__init__(self)
self.length = length self.length = length
self.offset = offset self.offset = offset
self.box = box if box is not None else [] self.box = box if box is not None else []
@ -765,8 +768,11 @@ class ColourGroupBox(Jp2kBox):
box : list box : list
List of boxes contained in this superbox. List of boxes contained in this superbox.
""" """
box_id = 'cgrp'
longname = 'Colour Group'
def __init__(self, box=None, length=0, offset=-1): def __init__(self, box=None, length=0, offset=-1):
Jp2kBox.__init__(self, box_id='cgrp', longname='Colour Group') Jp2kBox.__init__(self)
self.length = length self.length = length
self.offset = offset self.offset = offset
self.box = box if box is not None else [] self.box = box if box is not None else []
@ -834,9 +840,11 @@ class CompositingLayerHeaderBox(Jp2kBox):
box : list box : list
List of boxes contained in this superbox. List of boxes contained in this superbox.
""" """
box_id = 'jplh'
longname='Compositing Layer Header'
def __init__(self, box=None, length=0, offset=-1): def __init__(self, box=None, length=0, offset=-1):
Jp2kBox.__init__(self, box_id='jplh', Jp2kBox.__init__(self)
longname='Compositing Layer Header')
self.length = length self.length = length
self.offset = offset self.offset = offset
self.box = box if box is not None else [] self.box = box if box is not None else []
@ -900,9 +908,12 @@ class ComponentMappingBox(Jp2kBox):
palette_index : tuple palette_index : tuple
Index component from palette Index component from palette
""" """
box_id = 'cmap'
longname = 'Component Mapping'
def __init__(self, component_index, mapping_type, palette_index, def __init__(self, component_index, mapping_type, palette_index,
length=0, offset=-1): length=0, offset=-1):
Jp2kBox.__init__(self, box_id='cmap', longname='Component Mapping') Jp2kBox.__init__(self)
self.component_index = component_index self.component_index = component_index
self.mapping_type = mapping_type self.mapping_type = mapping_type
self.palette_index = palette_index self.palette_index = palette_index
@ -995,9 +1006,12 @@ class ContiguousCodestreamBox(Jp2kBox):
main_header_offset : int main_header_offset : int
offset of main header from start of file offset of main header from start of file
""" """
box_id = 'jp2c'
longname = 'Contiguous Codestream'
def __init__(self, main_header=None, main_header_offset=None, length=0, def __init__(self, main_header=None, main_header_offset=None, length=0,
offset=-1): offset=-1):
Jp2kBox.__init__(self, box_id='jp2c', longname='Contiguous Codestream') Jp2kBox.__init__(self)
self._main_header = main_header self._main_header = main_header
self.length = length self.length = length
self.offset = offset self.offset = offset
@ -1078,8 +1092,11 @@ class DataReferenceBox(Jp2kBox):
DR : list DR : list
Data Entry URL boxes. Data Entry URL boxes.
""" """
box_id = 'dtbl'
longname = 'Data Reference'
def __init__(self, data_entry_url_boxes=None, length=0, offset=-1): def __init__(self, data_entry_url_boxes=None, length=0, offset=-1):
Jp2kBox.__init__(self, box_id='dtbl', longname='Data Reference') Jp2kBox.__init__(self)
if data_entry_url_boxes is None: if data_entry_url_boxes is None:
self.DR = [] self.DR = []
else: else:
@ -1205,9 +1222,12 @@ class FileTypeBox(Jp2kBox):
compatibility_list: list compatibility_list: list
List of file conformance profiles. List of file conformance profiles.
""" """
box_id = 'ftyp'
longname = 'File Type'
def __init__(self, brand='jp2 ', minor_version=0, def __init__(self, brand='jp2 ', minor_version=0,
compatibility_list=None, length=0, offset=-1): compatibility_list=None, length=0, offset=-1):
Jp2kBox.__init__(self, box_id='ftyp', longname='File Type') Jp2kBox.__init__(self)
self.brand = brand self.brand = brand
self.minor_version = minor_version self.minor_version = minor_version
if compatibility_list is None: if compatibility_list is None:
@ -1317,9 +1337,12 @@ class FragmentListBox(Jp2kBox):
longname : str longname : str
more verbose description of the box. more verbose description of the box.
""" """
box_id = 'flst'
longname = 'Fragment List'
def __init__(self, fragment_offset, fragment_length, data_reference, def __init__(self, fragment_offset, fragment_length, data_reference,
length=0, offset=-1): length=0, offset=-1):
Jp2kBox.__init__(self, box_id='flst', longname='Fragment List') Jp2kBox.__init__(self)
self.fragment_offset = fragment_offset self.fragment_offset = fragment_offset
self.fragment_length = fragment_length self.fragment_length = fragment_length
self.data_reference = data_reference self.data_reference = data_reference
@ -1423,8 +1446,11 @@ class FragmentTableBox(Jp2kBox):
longname : str longname : str
more verbose description of the box. more verbose description of the box.
""" """
box_id = 'ftbl'
longname = 'Fragment Table'
def __init__(self, box=None, length=0, offset=-1): def __init__(self, box=None, length=0, offset=-1):
Jp2kBox.__init__(self, box_id='ftbl', longname='Fragment Table') Jp2kBox.__init__(self)
self.length = length self.length = length
self.offset = offset self.offset = offset
self.box = box if box is not None else [] self.box = box if box is not None else []
@ -1493,8 +1519,11 @@ class FreeBox(Jp2kBox):
longname : str longname : str
more verbose description of the box. more verbose description of the box.
""" """
box_id = 'free'
longname = 'Free'
def __init__(self, length=0, offset=-1): def __init__(self, length=0, offset=-1):
Jp2kBox.__init__(self, box_id='free', longname='Free') Jp2kBox.__init__(self)
self.length = length self.length = length
self.offset = offset self.offset = offset
@ -1558,6 +1587,9 @@ class ImageHeaderBox(Jp2kBox):
False if the file does not contain intellectual propery rights False if the file does not contain intellectual propery rights
information. information.
""" """
box_id = 'ihdr'
longname = 'Image Header'
def __init__(self, height, width, num_components=1, signed=False, def __init__(self, height, width, num_components=1, signed=False,
bits_per_component=8, compression=7, colorspace_unknown=False, bits_per_component=8, compression=7, colorspace_unknown=False,
ip_provided=False, length=0, offset=-1): ip_provided=False, length=0, offset=-1):
@ -1567,7 +1599,7 @@ class ImageHeaderBox(Jp2kBox):
>>> import glymur >>> import glymur
>>> box = glymur.jp2box.ImageHeaderBox(height=512, width=256) >>> box = glymur.jp2box.ImageHeaderBox(height=512, width=256)
""" """
Jp2kBox.__init__(self, box_id='ihdr', longname='Image Header') Jp2kBox.__init__(self)
self.height = height self.height = height
self.width = width self.width = width
self.num_components = num_components self.num_components = num_components
@ -1686,8 +1718,11 @@ class AssociationBox(Jp2kBox):
box : list box : list
List of boxes contained in this superbox. List of boxes contained in this superbox.
""" """
box_id = 'asoc'
longname = 'Association'
def __init__(self, box=None, length=0, offset=-1): def __init__(self, box=None, length=0, offset=-1):
Jp2kBox.__init__(self, box_id='asoc', longname='Association') Jp2kBox.__init__(self)
self.length = length self.length = length
self.offset = offset self.offset = offset
self.box = box if box is not None else [] self.box = box if box is not None else []
@ -1747,8 +1782,11 @@ class JP2HeaderBox(Jp2kBox):
box : list box : list
List of boxes contained in this superbox. List of boxes contained in this superbox.
""" """
box_id = 'jp2h'
longname = 'JP2 Header'
def __init__(self, box=None, length=0, offset=-1): def __init__(self, box=None, length=0, offset=-1):
Jp2kBox.__init__(self, box_id='jp2h', longname='JP2 Header') Jp2kBox.__init__(self)
self.length = length self.length = length
self.offset = offset self.offset = offset
self.box = box if box is not None else [] self.box = box if box is not None else []
@ -1808,8 +1846,11 @@ class JPEG2000SignatureBox(Jp2kBox):
signature : tuple signature : tuple
Four-byte tuple identifying the file as JPEG 2000. Four-byte tuple identifying the file as JPEG 2000.
""" """
box_id = 'jP '
longname = 'JPEG 2000 Signature'
def __init__(self, signature=(13, 10, 135, 10), length=0, offset=-1): def __init__(self, signature=(13, 10, 135, 10), length=0, offset=-1):
Jp2kBox.__init__(self, box_id='jP ', longname='JPEG 2000 Signature') Jp2kBox.__init__(self)
self.signature = signature self.signature = signature
self.length = length self.length = length
self.offset = offset self.offset = offset
@ -1872,9 +1913,12 @@ class PaletteBox(Jp2kBox):
palette : ndarray palette : ndarray
Colormap array. Colormap array.
""" """
longname = 'Palette'
box_id = 'pclr'
def __init__(self, palette, bits_per_component, signed, length=0, def __init__(self, palette, bits_per_component, signed, length=0,
offset=-1): offset=-1):
Jp2kBox.__init__(self, box_id='pclr', longname='Palette') Jp2kBox.__init__(self)
self.palette = palette self.palette = palette
self.bits_per_component = bits_per_component self.bits_per_component = bits_per_component
self.signed = signed self.signed = signed
@ -2129,9 +2173,12 @@ class ReaderRequirementsBox(Jp2kBox):
Specifies the compatibility mask for each corresponding vendor Specifies the compatibility mask for each corresponding vendor
feature. feature.
""" """
box_id = 'rreq'
longname = 'Reader Requirements'
def __init__(self, fuam, dcm, standard_flag, standard_mask, vendor_feature, def __init__(self, fuam, dcm, standard_flag, standard_mask, vendor_feature,
vendor_mask, length=0, offset=-1): vendor_mask, length=0, offset=-1):
Jp2kBox.__init__(self, box_id='rreq', longname='Reader Requirements') Jp2kBox.__init__(self)
self.fuam = fuam self.fuam = fuam
self.dcm = dcm self.dcm = dcm
self.standard_flag = tuple(standard_flag) self.standard_flag = tuple(standard_flag)
@ -2368,8 +2415,11 @@ class ResolutionBox(Jp2kBox):
box : list box : list
List of boxes contained in this superbox. List of boxes contained in this superbox.
""" """
box_id = 'res '
longname = 'Resolution'
def __init__(self, box=None, length=0, offset=-1): def __init__(self, box=None, length=0, offset=-1):
Jp2kBox.__init__(self, box_id='res ', longname='Resolution') Jp2kBox.__init__(self)
self.length = length self.length = length
self.offset = offset self.offset = offset
self.box = box if box is not None else [] self.box = box if box is not None else []
@ -2425,9 +2475,12 @@ class CaptureResolutionBox(Jp2kBox):
vertical_resolution, horizontal_resolution : float vertical_resolution, horizontal_resolution : float
Vertical, horizontal resolution. Vertical, horizontal resolution.
""" """
box_id = 'resc'
longname = 'Capture Resolution'
def __init__(self, vertical_resolution, horizontal_resolution, length=0, def __init__(self, vertical_resolution, horizontal_resolution, length=0,
offset=-1): offset=-1):
Jp2kBox.__init__(self, box_id='resc', longname='Capture Resolution') Jp2kBox.__init__(self)
self.vertical_resolution = vertical_resolution self.vertical_resolution = vertical_resolution
self.horizontal_resolution = horizontal_resolution self.horizontal_resolution = horizontal_resolution
self.length = length self.length = length
@ -2488,9 +2541,12 @@ class DisplayResolutionBox(Jp2kBox):
vertical_resolution, horizontal_resolution : float vertical_resolution, horizontal_resolution : float
Vertical, horizontal resolution. Vertical, horizontal resolution.
""" """
box_id = 'resd'
longname = 'Display Resolution'
def __init__(self, vertical_resolution, horizontal_resolution, def __init__(self, vertical_resolution, horizontal_resolution,
length=0, offset=-1): length=0, offset=-1):
Jp2kBox.__init__(self, box_id='resd', longname='Display Resolution') Jp2kBox.__init__(self)
self.vertical_resolution = vertical_resolution self.vertical_resolution = vertical_resolution
self.horizontal_resolution = horizontal_resolution self.horizontal_resolution = horizontal_resolution
self.length = length self.length = length
@ -2552,8 +2608,11 @@ class LabelBox(Jp2kBox):
label : str label : str
Textual label. Textual label.
""" """
box_id = 'lbl '
longname = 'Label'
def __init__(self, label, length=0, offset=-1): def __init__(self, label, length=0, offset=-1):
Jp2kBox.__init__(self, box_id='lbl ', longname='Label') Jp2kBox.__init__(self)
self.label = label self.label = label
self.length = length self.length = length
self.offset = offset self.offset = offset
@ -2617,8 +2676,11 @@ class NumberListBox(Jp2kBox):
Descriptors of an entity with which the data contained within the same Descriptors of an entity with which the data contained within the same
Association box is associated. Association box is associated.
""" """
box_id = 'nlst'
longname = 'Number List'
def __init__(self, associations, length=0, offset=-1): def __init__(self, associations, length=0, offset=-1):
Jp2kBox.__init__(self, box_id='nlst', longname='Number List') Jp2kBox.__init__(self)
self.associations = associations self.associations = associations
self.length = length self.length = length
self.offset = offset self.offset = offset
@ -2698,6 +2760,9 @@ class XMLBox(Jp2kBox):
xml : ElementTree object xml : ElementTree object
XML section. XML section.
""" """
box_id = 'xml '
longname = 'XML'
def __init__(self, xml=None, filename=None, length=0, offset=-1): def __init__(self, xml=None, filename=None, length=0, offset=-1):
""" """
Parameters Parameters
@ -2708,7 +2773,7 @@ class XMLBox(Jp2kBox):
File from which to read XML. If filename is not None, then the xml File from which to read XML. If filename is not None, then the xml
keyword argument must be None. keyword argument must be None.
""" """
Jp2kBox.__init__(self, box_id='xml ', longname='XML') Jp2kBox.__init__(self)
if filename is not None and xml is not None: if filename is not None and xml is not None:
msg = "Only one of either filename or xml should be provided." msg = "Only one of either filename or xml should be provided."
raise IOError(msg) raise IOError(msg)
@ -2830,8 +2895,11 @@ class UUIDListBox(Jp2kBox):
ulst : list ulst : list
List of UUIDs. List of UUIDs.
""" """
box_id = 'ulst'
longname = 'UUID List'
def __init__(self, ulst, length=0, offset=-1): def __init__(self, ulst, length=0, offset=-1):
Jp2kBox.__init__(self, box_id='ulst', longname='UUID List') Jp2kBox.__init__(self)
self.ulst = ulst self.ulst = ulst
self.length = length self.length = length
self.offset = offset self.offset = offset
@ -2895,8 +2963,11 @@ class UUIDInfoBox(Jp2kBox):
box : list box : list
List of boxes contained in this superbox. List of boxes contained in this superbox.
""" """
box_id = 'uinf'
longname = 'UUIDInfo'
def __init__(self, box=None, length=0, offset=-1): def __init__(self, box=None, length=0, offset=-1):
Jp2kBox.__init__(self, box_id='uinf', longname='UUIDInfo') Jp2kBox.__init__(self)
self.length = length self.length = length
self.offset = offset self.offset = offset
self.box = box if box is not None else [] self.box = box if box is not None else []
@ -2956,8 +3027,11 @@ class DataEntryURLBox(Jp2kBox):
URL : str URL : str
Associated URL. Associated URL.
""" """
box_id = 'url '
longname = 'Data Entry URL'
def __init__(self, version, flag, url, length=0, offset=-1): def __init__(self, version, flag, url, length=0, offset=-1):
Jp2kBox.__init__(self, box_id='url ', longname='Data Entry URL') Jp2kBox.__init__(self)
self.version = version self.version = version
self.flag = flag self.flag = flag
self.url = url self.url = url
@ -3045,7 +3119,9 @@ class UnknownBox(Jp2kBox):
more verbose description of the box. more verbose description of the box.
""" """
def __init__(self, box_id, length=0, offset=-1, longname=''): def __init__(self, box_id, length=0, offset=-1, longname=''):
Jp2kBox.__init__(self, box_id=box_id, longname=longname) Jp2kBox.__init__(self)
self.longname = longname
self.box_id = box_id
self.length = length self.length = length
self.offset = offset self.offset = offset
@ -3089,6 +3165,9 @@ class UUIDBox(Jp2kBox):
16684-1:2012 - Graphic technology -- Extensible metadata platform (XMP) 16684-1:2012 - Graphic technology -- Extensible metadata platform (XMP)
specification -- Part 1: Data model, serialization and core properties specification -- Part 1: Data model, serialization and core properties
""" """
box_id = 'uuid'
longname = 'UUID'
def __init__(self, the_uuid, raw_data, length=0, offset=-1): def __init__(self, the_uuid, raw_data, length=0, offset=-1):
""" """
Parameters Parameters
@ -3102,7 +3181,7 @@ class UUIDBox(Jp2kBox):
offset : int offset : int
offset of the box from the start of the file. offset of the box from the start of the file.
""" """
Jp2kBox.__init__(self, box_id='uuid', longname='UUID') Jp2kBox.__init__(self)
self.uuid = the_uuid self.uuid = the_uuid
self.raw_data = raw_data self.raw_data = raw_data
self.length = length self.length = length

View file

@ -1,36 +0,0 @@
"""
Entry point for jp2dump script.
"""
import warnings
from .jp2k import Jp2k
def jp2dump(filename, codestream=False):
"""Prints JPEG2000 metadata.
Parameters
----------
filename : string
The input JPEG2000 file.
codestream : optional, logical scalar
Whether or not to dump codestream contents.
"""
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.
j = Jp2k(filename)
if codestream:
print(j.get_codestream(header_only=False))
else:
print(j)
# Re-emit any warnings that may have been suppressed.
if len(wctx) > 0:
print("\n")
for warning in wctx:
print("{0}:{1}: {2}: {3}".format(warning.filename,
warning.lineno,
warning.category.__name__,
warning.message))

View file

@ -759,6 +759,114 @@ class Jp2k(Jp2kBox):
return boxes return boxes
def __setitem__(self, index, data):
"""
Slicing protocol.
"""
if isinstance(index, slice) and (
index.start == None and
index.stop == None and
index.step == None):
# Case of jp2[:] = data, i.e. write the entire image.
#
# Should have a slice object where start = stop = step = None
self.write(data)
else:
msg = "Partial write operations are currently not allowed."
raise TypeError(msg)
def __getitem__(self, pargs):
"""
Slicing protocol.
"""
codestream = self.get_codestream(header_only=True)
if isinstance(pargs, int):
# Not a very good use of this protocol, but technically legal.
# This retrieves a single row.
row = pargs
area = (row, 0, row + 1, codestream.segment[1].xsiz)
return self.read(area=area).squeeze()
if isinstance(pargs, slice):
# Case of jp2[:], i.e. retrieve the entire image.
#
# Should have a slice object where start = stop = step = None
return self.read()
if isinstance(pargs, tuple) and all(isinstance(x, int) for x in pargs):
# Retrieve a single pixel.
# Something like jp2[r, c]
row = pargs[0]
col = pargs[1]
area = (row, col, row + 1, col + 1)
pixel = self.read(area=area).squeeze()
if len(pargs) == 2:
return pixel
elif len(pargs) == 3:
return pixel[pargs[2]]
# Assuming pargs is a tuple of slices from now on.
rows = pargs[0]
cols = pargs[1]
if len(pargs) == 2:
bands = slice(None, None, None)
else:
bands = pargs[2]
if rows.step is None:
rows_step = 1
else:
rows_step = rows.step
if cols.step is None:
cols_step = 1
else:
cols_step = cols.step
if rows_step != cols_step:
msg = "Row and column strides must be the same."
raise IndexError(msg)
# Ok, reduce layer step is the same in both xy directions, so just take
# one of them.
step = rows_step
# Check if the step size is a power of 2.
if np.abs(np.log2(step) - np.round(np.log2(step))) > 1e-6:
msg = "Row and column strides must be powers of 2."
raise IndexError(msg)
rlevel = np.int(np.round(np.log2(step)))
if rows.start is None:
rows_start = 0
else:
rows_start = rows.start
if rows.stop is None:
rows_stop = codestream.segment[1].ysiz
else:
rows_stop = rows.stop
if cols.start is None:
cols_start = 0
else:
cols_start = cols.start
if cols.stop is None:
cols_stop = codestream.segment[1].xsiz
else:
cols_stop = cols.stop
area = (rows_start, cols_start, rows_stop, cols_stop)
data = self.read(area=area, rlevel=rlevel)
if len(pargs) == 2:
return data
# Ok, 3 arguments in pargs.
return data[:, :, bands]
def read(self, **kwargs): def read(self, **kwargs):
"""Read a JPEG 2000 image. """Read a JPEG 2000 image.
@ -767,7 +875,7 @@ class Jp2k(Jp2kBox):
rlevel : int, optional rlevel : int, optional
Factor by which to rlevel output resolution. Use -1 to get the Factor by which to rlevel output resolution. Use -1 to get the
lowest resolution thumbnail. This is the only keyword option lowest resolution thumbnail. This is the only keyword option
available to use when only the OpenJPEG version 1.5.1 is present. available to use when the OpenJPEG version is 1.5 or earlier.
layer : int, optional layer : int, optional
Number of quality layer to decode. Number of quality layer to decode.
area : tuple, optional area : tuple, optional
@ -833,7 +941,7 @@ class Jp2k(Jp2kBox):
raise RuntimeError(msg) raise RuntimeError(msg)
def _read_openjpeg(self, rlevel=0, ignore_pclr_cmap_cdef=False, def _read_openjpeg(self, rlevel=0, ignore_pclr_cmap_cdef=False,
verbose=False): verbose=False, area=None):
"""Read a JPEG 2000 image using libopenjpeg. """Read a JPEG 2000 image using libopenjpeg.
Parameters Parameters
@ -846,6 +954,9 @@ class Jp2k(Jp2kBox):
color transformation. Defaults to False. color transformation. Defaults to False.
verbose : bool, optional verbose : bool, optional
Print informational messages produced by the OpenJPEG library. Print informational messages produced by the OpenJPEG library.
area : tuple, optional
Specifies decoding image area,
(first_row, first_col, last_row, last_col)
Returns Returns
------- -------
@ -898,6 +1009,18 @@ class Jp2k(Jp2kBox):
# data 2D instead of 3D. # data 2D instead of 3D.
data.shape = data.shape[0:2] data.shape = data.shape[0:2]
if area is not None:
x0, y0, x1, y1 = area
extent = 2 ** rlevel
if x1 - x0 < extent or y1 - y0 < extent:
msg = "Decoded area is too small."
raise IOError(msg)
area = [int(round(float(x)/extent + 2 ** -20)) for x in area]
rows = slice(area[0], area[2], None)
cols = slice(area[1], area[3], None)
data = data[rows, cols]
return data return data
def _read_openjp2(self, rlevel=0, layer=0, area=None, tile=None, def _read_openjp2(self, rlevel=0, layer=0, area=None, tile=None,

View file

@ -73,7 +73,6 @@ def load_openjpeg(path):
return load_library_handle(path) return load_library_handle(path)
def load_openjp2(path): def load_openjp2(path):
"""Load the openjp2 library, falling back on defaults if necessary. """Load the openjp2 library, falling back on defaults if necessary.
""" """
@ -100,7 +99,7 @@ def load_openjp2(path):
def load_library_handle(path): def load_library_handle(path):
"""Load the library, return the ctypes handle.""" """Load the library, return the ctypes handle."""
if path is None: if path is None or path in ['None', 'none']:
# Either could not find a library via ctypes or user-configuration-file, # 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 we could not find it in any of the default locations.
# This is probably a very old linux. # This is probably a very old linux.
@ -130,14 +129,11 @@ def read_config_file():
# Read the configuration file for the library location. # Read the configuration file for the library location.
parser = ConfigParser() parser = ConfigParser()
parser.read(filename) parser.read(filename)
try: for name in ['openjp2', 'openjpeg']:
lib['openjp2'] = parser.get('library', 'openjp2') try:
except NoOptionError: lib[name] = parser.get('library', name)
pass except NoOptionError:
try: pass
lib['openjpeg'] = parser.get('library', 'openjpeg')
except NoOptionError:
pass
return lib return lib
@ -150,8 +146,7 @@ def glymur_config():
libopenjpeg_handle = load_openjpeg(libs['openjpeg']) libopenjpeg_handle = load_openjpeg(libs['openjpeg'])
if libopenjp2_handle is None and libopenjpeg_handle is None: if libopenjp2_handle is None and libopenjpeg_handle is None:
msg = "Neither the openjp2 nor the openjpeg library could be loaded. " msg = "Neither the openjp2 nor the openjpeg library could be loaded. "
msg += "Operating in severely degraded mode." raise IOError(msg)
warnings.warn(msg, UserWarning)
return libopenjp2_handle, libopenjpeg_handle return libopenjp2_handle, libopenjpeg_handle

View file

@ -5,12 +5,144 @@ import os
import re import re
import sys import sys
import textwrap import textwrap
import unittest
import warnings import warnings
import numpy as np import numpy as np
import six
import glymur import glymur
# 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.
WARNING_INFRASTRUCTURE_ISSUE = False
WARNING_INFRASTRUCTURE_MSG = ""
if sys.hexversion < 0x03000000:
WARNING_INFRASTRUCTURE_ISSUE = True
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__)
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)
# The Python XMP Toolkit may be used for XMP UUIDs, but only if available and # The Python XMP Toolkit may be used for XMP UUIDs, but only if available and
# if the version is at least 2.0.0. # if the version is at least 2.0.0.
@ -37,6 +169,24 @@ except:
raise raise
# 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.
NO_SKIMAGE_FREEIMAGE_SUPPORT = False
try:
import skimage
import skimage.io
if (((sys.hexversion >= 0x03000000) and
('Anaconda' in sys.version) and
(re.match('0.10', skimage.__version__)))):
NO_SKIMAGE_FREEIMAGE_SUPPORT = True
else:
skimage.io.use_plugin('freeimage', 'imread')
except ((ImportError, RuntimeError)):
NO_SKIMAGE_FREEIMAGE_SUPPORT = True
def _indent(textstr): def _indent(textstr):
""" """
Indent a string. Indent a string.
@ -437,7 +587,9 @@ Contiguous Codestream Box (jp2c) @ (3223, 1132296)
Step size: [(0, 8), (0, 9), (0, 9), (0, 10)] Step size: [(0, 8), (0, 9), (0, 9), (0, 10)]
CME marker segment @ (3305, 37) CME marker segment @ (3305, 37)
"Created by OpenJPEG version 2.0.0"''' "Created by OpenJPEG version 2.0.0"'''
nemo_dump_full = dump.format(_indent(nemo_xmp))
nemo_with_codestream_header = dump.format(_indent(nemo_xmp))
#nemo_dump_full = dump.format(_indent(nemo_xmp))
nemo_dump_short = r"""JPEG 2000 Signature Box (jP ) @ (0, 12) nemo_dump_short = r"""JPEG 2000 Signature Box (jP ) @ (0, 12)
File Type Box (ftyp) @ (12, 20) File Type Box (ftyp) @ (12, 20)
@ -633,7 +785,7 @@ number_list_box = r"""Number List Box (nlst) @ (-1, 0)
Association[2]: compositing layer 0""" Association[2]: compositing layer 0"""
goodstuff = r"""Codestream: goodstuff_codestream_header = r"""Codestream:
SOC marker segment @ (0, 0) SOC marker segment @ (0, 0)
SIZ marker segment @ (2, 47) SIZ marker segment @ (2, 47)
Profile: no profile Profile: no profile
@ -668,3 +820,80 @@ goodstuff = r"""Codestream:
Quantization style: no quantization, 2 guard bits Quantization style: no quantization, 2 guard bits
Step size: [(0, 8), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), (0, 10)]""" Step size: [(0, 8), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), (0, 10)]"""
goodstuff_with_full_header = r"""Codestream:
SOC marker segment @ (0, 0)
SIZ marker segment @ (2, 47)
Profile: no profile
Reference Grid Height, Width: (800 x 480)
Vertical, Horizontal Reference Grid Offset: (0 x 0)
Reference Tile Height, Width: (800 x 480)
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 @ (51, 12)
Coding style:
Entropy coder, without partitions
SOP marker segments: False
EPH marker segments: False
Coding style parameters:
Progression order: LRCP
Number of layers: 1
Multiple component transformation usage: reversible
Number of resolutions: 6
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 @ (65, 19)
Quantization style: no quantization, 2 guard bits
Step size: [(0, 8), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), (0, 10)]
SOT marker segment @ (86, 10)
Tile part index: 0
Tile part length: 115132
Tile part instance: 0
Number of tile parts: 1
COC marker segment @ (98, 9)
Associated component: 1
Coding style for this component: Entropy coder, PARTITION = 0
Coding style parameters:
Number of resolutions: 6
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 @ (109, 20)
Associated Component: 1
Quantization style: no quantization, 2 guard bits
Step size: [(0, 8), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), (0, 10)]
COC marker segment @ (131, 9)
Associated component: 2
Coding style for this component: Entropy coder, PARTITION = 0
Coding style parameters:
Number of resolutions: 6
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 @ (142, 20)
Associated Component: 2
Quantization style: no quantization, 2 guard bits
Step size: [(0, 8), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), (0, 10)]
SOD marker segment @ (164, 0)
EOC marker segment @ (115218, 0)"""

View file

@ -11,7 +11,6 @@ import os
import re import re
import sys import sys
import tempfile import tempfile
import warnings
import unittest import unittest
@ -24,6 +23,7 @@ else:
import glymur import glymur
from .fixtures import WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG
@unittest.skipIf(glymur.lib.openjp2.OPENJP2 is None, @unittest.skipIf(glymur.lib.openjp2.OPENJP2 is None,
"Missing openjp2 library.") "Missing openjp2 library.")
@ -37,12 +37,12 @@ class TestCallbacks(unittest.TestCase):
def tearDown(self): def tearDown(self):
pass pass
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.") @unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
def test_info_callback_on_write(self): def test_info_callback_on_write(self):
"""Verify messages printed when writing an image in verbose mode.""" """Verify messages printed when writing an image in verbose mode."""
j = glymur.Jp2k(self.jp2file) j = glymur.Jp2k(self.jp2file)
with warnings.catch_warnings(): with self.assertWarns(UserWarning):
warnings.simplefilter("ignore")
tiledata = j.read(tile=0) tiledata = j.read(tile=0)
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile: with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
j = glymur.Jp2k(tfile.name, 'wb') j = glymur.Jp2k(tfile.name, 'wb')

View file

@ -5,15 +5,11 @@ Test suite for codestream parsing.
# unittest doesn't work well with R0904. # unittest doesn't work well with R0904.
# pylint: disable=R0904 # pylint: disable=R0904
# tempfile.TemporaryDirectory, unittest.assertWarns introduced in 3.2
# pylint: disable=E1101
import os import os
import struct import struct
import sys import sys
import tempfile import tempfile
import unittest import unittest
import warnings
from glymur import Jp2k from glymur import Jp2k
import glymur import glymur

View file

@ -1,100 +0,0 @@
"""
Test suite for codestream parsing.
"""
# unittest doesn't work well with R0904.
# pylint: disable=R0904
# tempfile.TemporaryDirectory, unittest.assertWarns introduced in 3.2
# pylint: disable=E1101
import os
import struct
import sys
import tempfile
import unittest
import warnings
from glymur import Jp2k
import glymur
from .fixtures import opj_data_file, OPJ_DATA_ROOT
@unittest.skipIf(sys.platform.startswith('linux'), 'warnings failing on linux')
@unittest.skipIf(OPJ_DATA_ROOT is None,
"OPJ_DATA_ROOT environment variable not set")
class TestCodestreamOpjDataWarnings(unittest.TestCase):
"""Test suite for unusual codestream cases. Uses OPJ_DATA_ROOT"""
def test_bad_rsiz(self):
"""Should warn if RSIZ is bad. Issue196"""
filename = opj_data_file('input/nonregression/edf_c2_1002767.jp2')
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter('always')
j = Jp2k(filename)
self.assertEqual(len(w), 3)
self.assertTrue(issubclass(w[0].category, UserWarning))
self.assertTrue('Invalid profile' in str(w[0].message))
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 warnings.catch_warnings(record=True) as w:
warnings.simplefilter('always')
j = Jp2k(filename)
self.assertTrue(issubclass(w[0].category, UserWarning))
self.assertTrue('Invalid wavelet transform' in str(w[0].message))
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 warnings.catch_warnings(record=True) as w:
warnings.simplefilter('always')
Jp2k(jfile)
self.assertTrue(issubclass(w[0].category, UserWarning))
self.assertTrue('Invalid progression order' in str(w[0].message))
def test_tile_height_is_zero(self):
"""Zero tile height should not cause an exception."""
filename = opj_data_file('input/nonregression/2539.pdf.SIGFPE.706.1712.jp2')
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter('always')
Jp2k(filename)
self.assertTrue(issubclass(w[0].category, UserWarning))
self.assertTrue('Invalid tile dimensions' in str(w[0].message))
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
def test_unknown_marker_segment(self):
"""Should warn for an unknown marker."""
# Let's inject a marker segment whose marker does not appear to
# be valid. We still parse the file, but warn about the offending
# marker.
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, 0xff79 = 65401
read_buffer = struct.pack('>HHB', int(65401), 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()
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter('always')
codestream = Jp2k(tfile.name).get_codestream()
self.assertTrue(issubclass(w[0].category, UserWarning))
self.assertTrue('Unrecognized marker' in str(w[0].message))
self.assertEqual(codestream.segment[2].marker_id, '0xff79')
self.assertEqual(codestream.segment[2].length, 3)
self.assertEqual(codestream.segment[2].data, b'\x00')
if __name__ == "__main__":
unittest.main()

View file

@ -16,7 +16,6 @@ import os
import sys import sys
import tempfile import tempfile
import unittest import unittest
import warnings
if sys.hexversion <= 0x03030000: if sys.hexversion <= 0x03030000:
from mock import patch from mock import patch
@ -26,6 +25,7 @@ else:
import glymur import glymur
from glymur import Jp2k from glymur import Jp2k
from .fixtures import WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG
@unittest.skipIf(sys.hexversion < 0x03020000, @unittest.skipIf(sys.hexversion < 0x03020000,
"TemporaryDirectory introduced in 3.2.") "TemporaryDirectory introduced in 3.2.")
@ -70,6 +70,8 @@ class TestSuite(unittest.TestCase):
imp.reload(glymur.lib.openjp2) imp.reload(glymur.lib.openjp2)
Jp2k(self.jp2file) Jp2k(self.jp2file)
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
@unittest.skipIf(os.name == "nt", 'named temporary file issue on windows')
def test_xdg_env_config_file_is_bad(self): def test_xdg_env_config_file_is_bad(self):
"""A non-existant library location should be rejected.""" """A non-existant library location should be rejected."""
with tempfile.TemporaryDirectory() as tdir: with tempfile.TemporaryDirectory() as tdir:
@ -84,11 +86,9 @@ class TestSuite(unittest.TestCase):
with patch.dict('os.environ', {'XDG_CONFIG_HOME': tdir}): with patch.dict('os.environ', {'XDG_CONFIG_HOME': tdir}):
# Misconfigured new configuration file should # Misconfigured new configuration file should
# be rejected. # be rejected.
with warnings.catch_warnings(record=True) as w: regex = 'could not be loaded'
warnings.simplefilter('always') with self.assertWarnsRegex(UserWarning, regex):
imp.reload(glymur.lib.openjp2) imp.reload(glymur.lib.openjp2)
self.assertTrue(issubclass(w[0].category,UserWarning))
self.assertTrue('could not be loaded' in str(w[0].message))
@unittest.skipIf(glymur.lib.openjp2.OPENJP2 is None and @unittest.skipIf(glymur.lib.openjp2.OPENJP2 is None and

View file

@ -0,0 +1,188 @@
"""
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_exceeded_box_length(self):
"""
should warn if reading past end of a box
Verify that a warning is issued if we read past the end of a box
This file has a palette (pclr) box whose length is impossibly
short.
"""
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
unchanged"''',
re.VERBOSE)
with self.assertWarnsRegex(UserWarning, regex):
Jp2k(infile)
def test_NR_DEC_issue188_beach_64bitsbox_jp2_41_decode(self):
"""
Has an 'XML ' box instead of 'xml '. Yes that is pedantic, but it
really does deserve a warning.
"""
relpath = 'input/nonregression/issue188_beach_64bitsbox.jp2'
jfile = opj_data_file(relpath)
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.
"""
lst = ['input', 'nonregression',
'gdal_fuzzer_unchecked_numresolutions.jp2']
jfile = opj_data_file('/'.join(lst))
regex = re.compile(r"""Invalid\snumber\sof\sresolutions\s
\(\d+\)\.""",
re.VERBOSE)
with self.assertWarnsRegex(UserWarning, regex):
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")
def test_NR_gdal_fuzzer_check_number_of_tiles(self):
"""
Has an impossible tiling setup.
"""
lst = ['input', 'nonregression',
'gdal_fuzzer_check_number_of_tiles.jp2']
jfile = opj_data_file('/'.join(lst))
regex = re.compile(r"""Invalid\snumber\sof\stiles\s
\(\d+\)\.""",
re.VERBOSE)
with self.assertWarnsRegex(UserWarning, regex):
Jp2k(jfile)
def test_NR_gdal_fuzzer_check_comp_dx_dy_jp2_dump(self):
"""
Invalid subsampling value.
"""
lst = ['input', 'nonregression', 'gdal_fuzzer_check_comp_dx_dy.jp2']
jfile = opj_data_file('/'.join(lst))
regex = re.compile(r"""Invalid\ssubsampling\svalue\sfor\scomponent\s
\d+:\s+
dx=\d+,\s*dy=\d+""",
re.VERBOSE)
with self.assertWarnsRegex(UserWarning, regex):
Jp2k(jfile)
def test_NR_gdal_fuzzer_assert_in_opj_j2k_read_SQcd_SQcc_patch_jp2(self):
lst = ['input', 'nonregression',
'gdal_fuzzer_assert_in_opj_j2k_read_SQcd_SQcc.patch.jp2']
jfile = opj_data_file('/'.join(lst))
regex = re.compile(r"""Invalid\scomponent\snumber\s\(\d+\),\s
number\sof\scomponents\sis\sonly\s\d+""",
re.VERBOSE)
with self.assertWarnsRegex(UserWarning, regex):
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)
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)
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)
def test_tile_height_is_zero(self):
"""Zero tile height should not cause an exception."""
filename = opj_data_file('input/nonregression/2539.pdf.SIGFPE.706.1712.jp2')
with self.assertWarnsRegex(UserWarning, 'Invalid tile dimensions'):
Jp2k(filename)
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
def test_unknown_marker_segment(self):
"""Should warn for an unknown marker."""
# Let's inject a marker segment whose marker does not appear to
# be valid. We still parse the file, but warn about the offending
# marker.
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, 0xff79 = 65401
read_buffer = struct.pack('>HHB', int(65401), 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()
with self.assertWarnsRegex(UserWarning, 'Unrecognized marker'):
codestream = Jp2k(tfile.name).get_codestream()
if __name__ == "__main__":
unittest.main()

View file

@ -9,14 +9,15 @@ import datetime
import os import os
import sys import sys
import unittest import unittest
import warnings
import numpy as np import numpy as np
from glymur import Jp2k from glymur import Jp2k
from .fixtures import OPJ_DATA_ROOT, opj_data_file from .fixtures import OPJ_DATA_ROOT, opj_data_file
from .fixtures import WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
@unittest.skipIf(OPJ_DATA_ROOT is None, @unittest.skipIf(OPJ_DATA_ROOT is None,
"OPJ_DATA_ROOT environment variable not set") "OPJ_DATA_ROOT environment variable not set")
class TestICC(unittest.TestCase): class TestICC(unittest.TestCase):
@ -31,9 +32,8 @@ class TestICC(unittest.TestCase):
def test_file5(self): def test_file5(self):
"""basic ICC profile""" """basic ICC profile"""
filename = opj_data_file('input/conformance/file5.jp2') filename = opj_data_file('input/conformance/file5.jp2')
with warnings.catch_warnings(): with self.assertWarns(UserWarning):
# The file has a bad compatibility list entry. Not important here. # The file has a bad compatibility list entry. Not important here.
warnings.simplefilter("ignore")
j = Jp2k(filename) j = Jp2k(filename)
profile = j.box[3].box[1].icc_profile profile = j.box[3].box[1].icc_profile
self.assertEqual(profile['Size'], 546) self.assertEqual(profile['Size'], 546)
@ -62,18 +62,15 @@ class TestICC(unittest.TestCase):
self.assertEqual(profile['Creator'], 'JPEG') self.assertEqual(profile['Creator'], 'JPEG')
@unittest.skipIf(sys.platform.startswith('linux'), 'Failing on linux')
def test_invalid_profile_header(self): def test_invalid_profile_header(self):
"""invalid ICC header data should cause UserWarning""" """invalid ICC header data should cause UserWarning"""
jfile = opj_data_file('input/nonregression/orb-blue10-lin-jp2.jp2') jfile = opj_data_file('input/nonregression/orb-blue10-lin-jp2.jp2')
# assertWarns in Python 3.3 (python2.7/pylint issue) # assertWarns in Python 3.3 (python2.7/pylint issue)
# pylint: disable=E1101 # pylint: disable=E1101
with warnings.catch_warnings(record=True) as w: regex = 'ICC profile header is corrupt'
warnings.simplefilter('always') with self.assertWarnsRegex(UserWarning, regex):
Jp2k(jfile) Jp2k(jfile)
self.assertTrue(issubclass(w[0].category,UserWarning))
self.assertTrue('ICC profile header is corrupt' in str(w[0].message))
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

View file

@ -23,7 +23,6 @@ import tempfile
import uuid import uuid
from uuid import UUID from uuid import UUID
import unittest import unittest
import warnings
import lxml.etree as ET import lxml.etree as ET
import numpy as np import numpy as np
@ -36,7 +35,10 @@ from glymur.jp2box import JPEG2000SignatureBox
from glymur.core import COLOR, OPACITY from glymur.core import COLOR, OPACITY
from glymur.core import RED, GREEN, BLUE, GREY, WHOLE_IMAGE from glymur.core import RED, GREEN, BLUE, GREY, WHOLE_IMAGE
from .fixtures import opj_data_file from .fixtures import (
WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG,
MetadataBase
)
try: try:
FORMAT_CORPUS_DATA_ROOT = os.environ['FORMAT_CORPUS_DATA_ROOT'] FORMAT_CORPUS_DATA_ROOT = os.environ['FORMAT_CORPUS_DATA_ROOT']
@ -357,6 +359,7 @@ class TestChannelDefinition(unittest.TestCase):
with self.assertRaises((IOError, OSError)): with self.assertRaises((IOError, OSError)):
j2k.wrap(tfile.name, boxes=boxes) j2k.wrap(tfile.name, boxes=boxes)
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_bad_type(self): def test_bad_type(self):
"""Channel types are limited to 0, 1, 2, 65535 """Channel types are limited to 0, 1, 2, 65535
Should reject if not all of index, channel_type, association the Should reject if not all of index, channel_type, association the
@ -365,25 +368,20 @@ class TestChannelDefinition(unittest.TestCase):
channel_type = (COLOR, COLOR, 3) channel_type = (COLOR, COLOR, 3)
association = (RED, GREEN, BLUE) association = (RED, GREEN, BLUE)
with warnings.catch_warnings(record=True) as w: with self.assertWarns(UserWarning):
warnings.simplefilter('always')
glymur.jp2box.ChannelDefinitionBox(channel_type=channel_type, glymur.jp2box.ChannelDefinitionBox(channel_type=channel_type,
association=association) association=association)
self.assertEqual(len(w), 1)
self.assertTrue(issubclass(w[0].category, UserWarning))
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_wrong_lengths(self): def test_wrong_lengths(self):
"""Should reject if not all of index, channel_type, association the """Should reject if not all of index, channel_type, association the
same length. same length.
""" """
channel_type = (COLOR, COLOR) channel_type = (COLOR, COLOR)
association = (RED, GREEN, BLUE) association = (RED, GREEN, BLUE)
with warnings.catch_warnings(record=True) as w: with self.assertWarns(UserWarning):
warnings.simplefilter('always')
glymur.jp2box.ChannelDefinitionBox(channel_type=channel_type, glymur.jp2box.ChannelDefinitionBox(channel_type=channel_type,
association=association) association=association)
self.assertEqual(len(w), 1)
self.assertTrue(issubclass(w[0].category, UserWarning))
class TestFileTypeBox(unittest.TestCase): class TestFileTypeBox(unittest.TestCase):
@ -395,20 +393,20 @@ class TestFileTypeBox(unittest.TestCase):
def tearDown(self): def tearDown(self):
pass pass
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_brand_unknown(self): def test_brand_unknown(self):
"""A ftyp box brand must be 'jp2 ' or 'jpx '.""" """A ftyp box brand must be 'jp2 ' or 'jpx '."""
with warnings.catch_warnings(): with self.assertWarns(UserWarning):
warnings.simplefilter("ignore")
ftyp = glymur.jp2box.FileTypeBox(brand='jp3') ftyp = glymur.jp2box.FileTypeBox(brand='jp3')
with self.assertRaises(IOError): with self.assertRaises(IOError):
with tempfile.TemporaryFile() as tfile: with tempfile.TemporaryFile() as tfile:
ftyp.write(tfile) ftyp.write(tfile)
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_cl_entry_unknown(self): def test_cl_entry_unknown(self):
"""A ftyp box cl list can only contain 'jp2 ', 'jpx ', or 'jpxb'.""" """A ftyp box cl list can only contain 'jp2 ', 'jpx ', or 'jpxb'."""
with warnings.catch_warnings(): with self.assertWarns(UserWarning):
# Bad compatibility list item. # Bad compatibility list item.
warnings.simplefilter("ignore")
ftyp = glymur.jp2box.FileTypeBox(compatibility_list=['jp3']) ftyp = glymur.jp2box.FileTypeBox(compatibility_list=['jp3'])
with self.assertRaises(IOError): with self.assertRaises(IOError):
with tempfile.TemporaryFile() as tfile: with tempfile.TemporaryFile() as tfile:
@ -479,41 +477,34 @@ class TestColourSpecificationBox(unittest.TestCase):
self.assertEqual(colr.colorspace, glymur.core.SRGB) self.assertEqual(colr.colorspace, glymur.core.SRGB)
self.assertIsNone(colr.icc_profile) self.assertIsNone(colr.icc_profile)
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_colr_with_cspace_and_icc(self): def test_colr_with_cspace_and_icc(self):
"""Colour specification boxes can't have both.""" """Colour specification boxes can't have both."""
with warnings.catch_warnings(record=True) as w: regex = 'Colorspace and icc_profile cannot both be set'
warnings.simplefilter('always') with self.assertWarnsRegex(UserWarning, regex):
colorspace = glymur.core.SRGB colorspace = glymur.core.SRGB
rawb = b'\x01\x02\x03\x04' rawb = b'\x01\x02\x03\x04'
glymur.jp2box.ColourSpecificationBox(colorspace=colorspace, glymur.jp2box.ColourSpecificationBox(colorspace=colorspace,
icc_profile=rawb) icc_profile=rawb)
self.assertTrue(issubclass(w[0].category,UserWarning))
msg = 'Colorspace and icc_profile cannot both be set'
self.assertTrue(msg in str(w[0].message))
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_colr_with_bad_method(self): def test_colr_with_bad_method(self):
"""colr must have a valid method field""" """colr must have a valid method field"""
colorspace = glymur.core.SRGB colorspace = glymur.core.SRGB
method = -1 method = -1
with warnings.catch_warnings(record=True) as w: regex = 'Invalid method'
warnings.simplefilter('always') with self.assertWarnsRegex(UserWarning, regex):
glymur.jp2box.ColourSpecificationBox(colorspace=colorspace, glymur.jp2box.ColourSpecificationBox(colorspace=colorspace,
method=method) method=method)
self.assertTrue(issubclass(w[0].category,UserWarning))
msg = 'Invalid method'
self.assertTrue(msg in str(w[0].message))
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_colr_with_bad_approx(self): def test_colr_with_bad_approx(self):
"""colr should have a valid approximation field""" """colr should have a valid approximation field"""
colorspace = glymur.core.SRGB colorspace = glymur.core.SRGB
approx = -1 approx = -1
with warnings.catch_warnings(record=True) as w: with self.assertWarnsRegex(UserWarning, 'Invalid approximation'):
warnings.simplefilter('always')
glymur.jp2box.ColourSpecificationBox(colorspace=colorspace, glymur.jp2box.ColourSpecificationBox(colorspace=colorspace,
approximation=approx) approximation=approx)
self.assertTrue(issubclass(w[0].category,UserWarning))
msg = 'Invalid approximation'
self.assertTrue(msg in str(w[0].message))
def test_colr_with_bad_color(self): def test_colr_with_bad_color(self):
"""colr must have a valid color, strange as though that may sound.""" """colr must have a valid color, strange as though that may sound."""
@ -537,29 +528,25 @@ class TestPaletteBox(unittest.TestCase):
def tearDown(self): def tearDown(self):
pass pass
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_mismatched_bitdepth_signed(self): def test_mismatched_bitdepth_signed(self):
"""bitdepth and signed arguments must have equal length""" """bitdepth and signed arguments must have equal length"""
palette = np.array([[255, 0, 255], [0, 255, 0]], dtype=np.uint8) palette = np.array([[255, 0, 255], [0, 255, 0]], dtype=np.uint8)
bps = (8, 8, 8) bps = (8, 8, 8)
signed = (False, False) signed = (False, False)
with warnings.catch_warnings(record=True) as w: with self.assertWarns(UserWarning):
warnings.simplefilter('always')
pclr = glymur.jp2box.PaletteBox(palette, bits_per_component=bps, pclr = glymur.jp2box.PaletteBox(palette, bits_per_component=bps,
signed=signed) signed=signed)
self.assertEqual(len(w), 1)
self.assertTrue(issubclass(w[0].category, UserWarning))
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_mismatched_signed_palette(self): def test_mismatched_signed_palette(self):
"""bitdepth and signed arguments must have equal length""" """bitdepth and signed arguments must have equal length"""
palette = np.array([[255, 0, 255], [0, 255, 0]], dtype=np.uint8) palette = np.array([[255, 0, 255], [0, 255, 0]], dtype=np.uint8)
bps = (8, 8, 8, 8) bps = (8, 8, 8, 8)
signed = (False, False, False, False) signed = (False, False, False, False)
with warnings.catch_warnings(record=True) as w: with self.assertWarns(UserWarning):
warnings.simplefilter('always')
pclr = glymur.jp2box.PaletteBox(palette, bits_per_component=bps, pclr = glymur.jp2box.PaletteBox(palette, bits_per_component=bps,
signed=signed) signed=signed)
self.assertEqual(len(w), 1)
self.assertTrue(issubclass(w[0].category, UserWarning))
def test_writing_with_different_bitdepths(self): def test_writing_with_different_bitdepths(self):
"""Bitdepths must be the same when writing.""" """Bitdepths must be the same when writing."""
@ -1054,7 +1041,7 @@ class TestJp2Boxes(unittest.TestCase):
j.box[5].offset + 8) j.box[5].offset + 8)
class TestRepr(unittest.TestCase): class TestRepr(MetadataBase):
"""Tests for __repr__ methods.""" """Tests for __repr__ methods."""
def test_default_jp2k(self): def test_default_jp2k(self):
"""Should be able to eval a JPEG2000SignatureBox""" """Should be able to eval a JPEG2000SignatureBox"""
@ -1124,10 +1111,7 @@ class TestRepr(unittest.TestCase):
# Test the representation instantiation. # Test the representation instantiation.
newbox = eval(repr(ftyp)) newbox = eval(repr(ftyp))
self.assertTrue(isinstance(newbox, glymur.jp2box.FileTypeBox)) self.verify_filetype_box(newbox, FileTypeBox())
self.assertEqual(newbox.brand, 'jp2 ')
self.assertEqual(newbox.minor_version, 0)
self.assertEqual(newbox.compatibility_list, ['jp2 '])
def test_colourspecification_box(self): def test_colourspecification_box(self):
"""Verify __repr__ method on colr box.""" """Verify __repr__ method on colr box."""
@ -1262,16 +1246,6 @@ class TestRepr(unittest.TestCase):
self.assertEqual(newbox.ulst[0], uuid1) self.assertEqual(newbox.ulst[0], uuid1)
self.assertEqual(newbox.ulst[1], uuid2) 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): def test_palette_box(self):
"""Verify Palette box repr.""" """Verify Palette box repr."""
palette = np.array([[255, 0, 1000], [0, 255, 0]], dtype=np.int32) palette = np.array([[255, 0, 1000], [0, 255, 0]], dtype=np.int32)

View file

@ -9,7 +9,6 @@ import struct
import sys import sys
import tempfile import tempfile
import unittest import unittest
import warnings
import lxml.etree as ET import lxml.etree as ET
@ -19,6 +18,8 @@ from glymur.jp2box import DataEntryURLBox, FileTypeBox, JPEG2000SignatureBox
from glymur.jp2box import DataReferenceBox, FragmentListBox, FragmentTableBox from glymur.jp2box import DataReferenceBox, FragmentListBox, FragmentTableBox
from glymur.jp2box import ColourSpecificationBox from glymur.jp2box import ColourSpecificationBox
from .fixtures import WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.") @unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
class TestJPXWrap(unittest.TestCase): class TestJPXWrap(unittest.TestCase):
"""Test suite for wrapping JPX files.""" """Test suite for wrapping JPX files."""
@ -305,17 +306,15 @@ class TestJPXWrap(unittest.TestCase):
with self.assertRaises(IOError): with self.assertRaises(IOError):
jp2.wrap(tfile.name, boxes=boxes) jp2.wrap(tfile.name, boxes=boxes)
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_deurl_child_of_dtbl(self): def test_deurl_child_of_dtbl(self):
"""Data reference boxes can only contain data entry url boxes.""" """Data reference boxes can only contain data entry url boxes."""
jp2 = Jp2k(self.jp2file) jp2 = Jp2k(self.jp2file)
boxes = [jp2.box[idx] for idx in [0, 1, 2, 4]] boxes = [jp2.box[idx] for idx in [0, 1, 2, 4]]
ftyp = glymur.jp2box.FileTypeBox() ftyp = glymur.jp2box.FileTypeBox()
with warnings.catch_warnings(record=True) as w: with self.assertWarns(UserWarning):
warnings.simplefilter('always')
dref = glymur.jp2box.DataReferenceBox([ftyp]) dref = glymur.jp2box.DataReferenceBox([ftyp])
self.assertEqual(len(w), 1)
self.assertTrue(issubclass(w[0].category, UserWarning))
# Try to get around it by appending the ftyp box after creation. # Try to get around it by appending the ftyp box after creation.
dref = glymur.jp2box.DataReferenceBox() dref = glymur.jp2box.DataReferenceBox()
@ -433,37 +432,37 @@ class TestJPX(unittest.TestCase):
def tearDown(self): def tearDown(self):
pass pass
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_flst_lens_not_the_same(self): def test_flst_lens_not_the_same(self):
"""A fragment list box items must be the same length.""" """A fragment list box items must be the same length."""
offset = [89] offset = [89]
length = [1132288] length = [1132288]
reference = [0, 0] reference = [0, 0]
with warnings.catch_warnings(): with self.assertWarns(UserWarning):
warnings.simplefilter("ignore")
flst = glymur.jp2box.FragmentListBox(offset, length, reference) flst = glymur.jp2box.FragmentListBox(offset, length, reference)
with self.assertRaises(IOError): with self.assertRaises(IOError):
with tempfile.TemporaryFile() as tfile: with tempfile.TemporaryFile() as tfile:
flst.write(tfile) flst.write(tfile)
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_flst_offsets_not_positive(self): def test_flst_offsets_not_positive(self):
"""A fragment list box offsets must be positive.""" """A fragment list box offsets must be positive."""
offset = [0] offset = [0]
length = [1132288] length = [1132288]
reference = [0] reference = [0]
with warnings.catch_warnings(): with self.assertWarns(UserWarning):
warnings.simplefilter("ignore")
flst = glymur.jp2box.FragmentListBox(offset, length, reference) flst = glymur.jp2box.FragmentListBox(offset, length, reference)
with self.assertRaises((IOError, OSError)): with self.assertRaises((IOError, OSError)):
with tempfile.TemporaryFile() as tfile: with tempfile.TemporaryFile() as tfile:
flst.write(tfile) flst.write(tfile)
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_flst_lengths_not_positive(self): def test_flst_lengths_not_positive(self):
"""A fragment list box lengths must be positive.""" """A fragment list box lengths must be positive."""
offset = [89] offset = [89]
length = [0] length = [0]
reference = [0] reference = [0]
with warnings.catch_warnings(): with self.assertWarns(UserWarning):
warnings.simplefilter("ignore")
flst = glymur.jp2box.FragmentListBox(offset, length, reference) flst = glymur.jp2box.FragmentListBox(offset, length, reference)
with self.assertRaises(IOError): with self.assertRaises(IOError):
with tempfile.TemporaryFile() as tfile: with tempfile.TemporaryFile() as tfile:
@ -471,9 +470,7 @@ class TestJPX(unittest.TestCase):
def test_ftbl_boxes_empty(self): def test_ftbl_boxes_empty(self):
"""A fragment table box must have at least one child box.""" """A fragment table box must have at least one child box."""
with warnings.catch_warnings(): ftbl = glymur.jp2box.FragmentTableBox()
warnings.simplefilter("ignore")
ftbl = glymur.jp2box.FragmentTableBox()
with self.assertRaises(IOError): with self.assertRaises(IOError):
with tempfile.TemporaryFile() as tfile: with tempfile.TemporaryFile() as tfile:
ftbl.write(tfile) ftbl.write(tfile)

View file

@ -17,7 +17,6 @@ import struct
import sys import sys
import tempfile import tempfile
import uuid import uuid
import warnings
if sys.hexversion < 0x02070000: if sys.hexversion < 0x02070000:
import unittest2 as unittest import unittest2 as unittest
@ -37,6 +36,8 @@ else:
import lxml.etree import lxml.etree
from .fixtures import HAS_PYTHON_XMP_TOOLKIT, OPJ_DATA_ROOT from .fixtures import HAS_PYTHON_XMP_TOOLKIT, OPJ_DATA_ROOT
from .fixtures import WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG
if HAS_PYTHON_XMP_TOOLKIT: if HAS_PYTHON_XMP_TOOLKIT:
from libxmp import XMPMeta from libxmp import XMPMeta
@ -46,8 +47,8 @@ from .fixtures import OPJ_DATA_ROOT, opj_data_file, SimpleRDF
@unittest.skipIf(os.name == "nt", "Unexplained failure on windows") @unittest.skipIf(os.name == "nt", "Unexplained failure on windows")
class TestUUIDXMP(unittest.TestCase): class TestSuite(unittest.TestCase):
"""Tests for UUIDs of XMP type.""" """Tests for XMP, Exif UUIDs."""
def setUp(self): def setUp(self):
self.jp2file = glymur.data.nemo() self.jp2file = glymur.data.nemo()
@ -55,7 +56,7 @@ class TestUUIDXMP(unittest.TestCase):
def tearDown(self): def tearDown(self):
pass pass
def test_append(self): def test_append_xmp_uuid(self):
"""Should be able to append an XMP UUID box.""" """Should be able to append an XMP UUID box."""
the_uuid = uuid.UUID('be7acfcb-97a9-42e8-9c71-999491e3afac') the_uuid = uuid.UUID('be7acfcb-97a9-42e8-9c71-999491e3afac')
raw_data = SimpleRDF.encode('utf-8') raw_data = SimpleRDF.encode('utf-8')
@ -75,16 +76,42 @@ class TestUUIDXMP(unittest.TestCase):
self.assertTrue(isinstance(jp2.box[-1].data, self.assertTrue(isinstance(jp2.box[-1].data,
lxml.etree._ElementTree)) lxml.etree._ElementTree))
def test_big_endian_exif(self):
"""Verify read of Exif big-endian IFD."""
with tempfile.NamedTemporaryFile(suffix='.jp2', mode='wb') as tfile:
with open(self.jp2file, 'rb') as ifptr:
tfile.write(ifptr.read())
# Write L, T, UUID identifier.
tfile.write(struct.pack('>I4s', 52, b'uuid'))
tfile.write(b'JpgTiffExif->JP2')
tfile.write(b'Exif\x00\x00')
xbuffer = struct.pack('>BBHI', 77, 77, 42, 8)
tfile.write(xbuffer)
# We will write just a single tag.
tfile.write(struct.pack('>H', 1))
# The "Make" tag is tag no. 271.
tfile.write(struct.pack('>HHI4s', 271, 2, 3, b'HTC\x00'))
tfile.flush()
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", "Unexplained failure on windows") @unittest.skipIf(os.name == "nt", "Unexplained failure on windows")
class TestUUIDExif(unittest.TestCase): class TestSuiteWarns(unittest.TestCase):
"""Tests for UUIDs of Exif type.""" """Tests for XMP, Exif UUIDs, issues warnings."""
def setUp(self): def setUp(self):
self.jp2file = glymur.data.nemo() self.jp2file = glymur.data.nemo()
def tearDown(self): def tearDown(self):
pass pass
def test_unrecognized_exif_tag(self): def test_unrecognized_exif_tag(self):
"""Verify warning in case of unrecognized tag.""" """Verify warning in case of unrecognized tag."""
with tempfile.NamedTemporaryFile(suffix='.jp2', mode='wb') as tfile: with tempfile.NamedTemporaryFile(suffix='.jp2', mode='wb') as tfile:
@ -107,12 +134,8 @@ class TestUUIDExif(unittest.TestCase):
tfile.write(struct.pack('<HHI4s', 171, 2, 3, b'HTC\x00')) tfile.write(struct.pack('<HHI4s', 171, 2, 3, b'HTC\x00'))
tfile.flush() tfile.flush()
with warnings.catch_warnings(record=True) as w: with self.assertWarnsRegex(UserWarning, 'Unrecognized Exif tag'):
warnings.simplefilter('always')
j = glymur.Jp2k(tfile.name) j = glymur.Jp2k(tfile.name)
self.assertTrue(issubclass(w[0].category, UserWarning))
msg = 'Unrecognized Exif tag'
self.assertTrue(msg in str(w[0].message))
def test_bad_tag_datatype(self): def test_bad_tag_datatype(self):
"""Only certain datatypes are allowable""" """Only certain datatypes are allowable"""
@ -136,12 +159,8 @@ class TestUUIDExif(unittest.TestCase):
tfile.write(struct.pack('<HHI4s', 271, 2000, 3, b'HTC\x00')) tfile.write(struct.pack('<HHI4s', 271, 2000, 3, b'HTC\x00'))
tfile.flush() tfile.flush()
with warnings.catch_warnings(record=True) as w: with self.assertWarnsRegex(UserWarning, 'Invalid TIFF tag'):
warnings.simplefilter('always')
j = glymur.Jp2k(tfile.name) j = glymur.Jp2k(tfile.name)
self.assertTrue(issubclass(w[0].category, UserWarning))
msg = 'Invalid TIFF tag'
self.assertTrue(msg in str(w[0].message))
self.assertEqual(j.box[-1].box_id, 'uuid') self.assertEqual(j.box[-1].box_id, 'uuid')
@ -167,45 +186,11 @@ class TestUUIDExif(unittest.TestCase):
tfile.write(struct.pack('<HHI4s', 271, 2, 3, b'HTC\x00')) tfile.write(struct.pack('<HHI4s', 271, 2, 3, b'HTC\x00'))
tfile.flush() tfile.flush()
with warnings.catch_warnings(record=True) as w: regex = 'The byte order indication in the TIFF header '
warnings.simplefilter('always') with self.assertWarnsRegex(UserWarning, regex):
j = glymur.Jp2k(tfile.name) jp2 = glymur.Jp2k(tfile.name)
self.assertTrue(issubclass(w[0].category, UserWarning))
msg = 'The byte order indication in the TIFF header '
if sys.hexversion < 0x03000000:
msg += "(JI) is invalid. "
msg += "It should be either [73, 73] or [77, 77]."
else:
msg += "(b'JI') is invalid. "
msg += "It should be either b'II' or b'MM'."
self.assertTrue(msg in str(w[0].message))
self.assertEqual(j.box[-1].box_id, 'uuid') self.assertEqual(jp2.box[-1].box_id, 'uuid')
def test_big_endian(self):
"""Verify read of big-endian IFD."""
with tempfile.NamedTemporaryFile(suffix='.jp2', mode='wb') as tfile:
with open(self.jp2file, 'rb') as ifptr:
tfile.write(ifptr.read())
# Write L, T, UUID identifier.
tfile.write(struct.pack('>I4s', 52, b'uuid'))
tfile.write(b'JpgTiffExif->JP2')
tfile.write(b'Exif\x00\x00')
xbuffer = struct.pack('>BBHI', 77, 77, 42, 8)
tfile.write(xbuffer)
# We will write just a single tag.
tfile.write(struct.pack('>H', 1))
# The "Make" tag is tag no. 271.
tfile.write(struct.pack('>HHI4s', 271, 2, 3, b'HTC\x00'))
tfile.flush()
jp2 = glymur.Jp2k(tfile.name)
self.assertEqual(jp2.box[-1].data['Make'], "HTC")
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

View file

@ -15,11 +15,11 @@ Test suite specifically targeting JP2 box layout.
# pylint: disable=W0613 # pylint: disable=W0613
import os import os
import re
import struct import struct
import sys import sys
import tempfile import tempfile
import unittest import unittest
import warnings
if sys.hexversion < 0x03000000: if sys.hexversion < 0x03000000:
from StringIO import StringIO from StringIO import StringIO
@ -40,6 +40,7 @@ from glymur.jp2box import FileTypeBox, ImageHeaderBox, JP2HeaderBox
from glymur.jp2box import JPEG2000SignatureBox from glymur.jp2box import JPEG2000SignatureBox
from .fixtures import OPJ_DATA_ROOT, opj_data_file from .fixtures import OPJ_DATA_ROOT, opj_data_file
from .fixtures import WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.") @unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
class TestXML(unittest.TestCase): class TestXML(unittest.TestCase):
@ -166,7 +167,6 @@ class TestXML(unittest.TestCase):
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
class TestJp2kBadXmlFile(unittest.TestCase): class TestJp2kBadXmlFile(unittest.TestCase):
"""Test suite for bad XML box situations""" """Test suite for bad XML box situations"""
@ -207,14 +207,11 @@ class TestJp2kBadXmlFile(unittest.TestCase):
def tearDown(self): def tearDown(self):
pass pass
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_invalid_xml_box(self): def test_invalid_xml_box(self):
"""Should be able to recover info from xml box with bad xml.""" """Should be able to recover info from xml box with bad xml."""
with warnings.catch_warnings(record=True) as w: with self.assertWarns(UserWarning):
warnings.simplefilter("always")
jp2k = Jp2k(self._bad_xml_file) jp2k = Jp2k(self._bad_xml_file)
self.assertTrue(issubclass(w[0].category, UserWarning))
msg = 'No XML was retrieved'
self.assertTrue(msg in str(w[0].message))
self.assertEqual(jp2k.box[3].box_id, 'xml ') self.assertEqual(jp2k.box[3].box_id, 'xml ')
self.assertEqual(jp2k.box[3].offset, 77) self.assertEqual(jp2k.box[3].offset, 77)
@ -263,19 +260,17 @@ class TestBadButRecoverableXmlFile(unittest.TestCase):
def tearDownClass(cls): def tearDownClass(cls):
os.unlink(cls._bad_xml_file) os.unlink(cls._bad_xml_file)
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_bad_xml_box_warning(self): def test_bad_xml_box_warning(self):
"""Should warn in case of bad XML""" """Should warn in case of bad XML"""
with warnings.catch_warnings(record=True) as w: regex = 'A UnicodeDecodeError was encountered parsing an XML box'
warnings.simplefilter('always') with self.assertWarnsRegex(UserWarning, regex):
Jp2k(self._bad_xml_file) Jp2k(self._bad_xml_file)
self.assertTrue(issubclass(w[0].category, UserWarning))
msg = 'A UnicodeDecodeError was encountered parsing an XML box'
self.assertTrue(msg in str(w[0].message))
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_recover_from_bad_xml(self): def test_recover_from_bad_xml(self):
"""Should be able to recover info from xml box with bad xml.""" """Should be able to recover info from xml box with bad xml."""
with warnings.catch_warnings(): with self.assertWarns(UserWarning):
warnings.simplefilter("ignore")
jp2 = Jp2k(self._bad_xml_file) jp2 = Jp2k(self._bad_xml_file)
self.assertEqual(jp2.box[3].box_id, 'xml ') self.assertEqual(jp2.box[3].box_id, 'xml ')
@ -285,23 +280,21 @@ class TestBadButRecoverableXmlFile(unittest.TestCase):
b'<test>this is a test</test>') b'<test>this is a test</test>')
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
@unittest.skipIf(OPJ_DATA_ROOT is None, @unittest.skipIf(OPJ_DATA_ROOT is None,
"OPJ_DATA_ROOT environment variable not set") "OPJ_DATA_ROOT environment variable not set")
class TestXML_OpjDataRoot(unittest.TestCase): class TestXML_OpjDataRoot(unittest.TestCase):
"""Test suite for XML boxes, requires OPJ_DATA_ROOT.""" """Test suite for XML boxes, requires OPJ_DATA_ROOT."""
@unittest.skipIf(sys.platform.startswith('linux'), 'Failing on linux')
def test_bom(self): def test_bom(self):
"""Byte order markers are illegal in UTF-8. Issue 185""" """Byte order markers are illegal in UTF-8. Issue 185"""
filename = opj_data_file(os.path.join('input', filename = opj_data_file(os.path.join('input',
'nonregression', 'nonregression',
'issue171.jp2')) 'issue171.jp2'))
with warnings.catch_warnings(record=True) as w: msg = 'An illegal BOM \(byte order marker\) was detected and removed '
warnings.simplefilter("always") msg += 'from the XML contents in the box starting at byte offset \d+'
with self.assertWarnsRegex(UserWarning, re.compile(msg)):
jp2 = Jp2k(filename) jp2 = Jp2k(filename)
self.assertTrue(issubclass(w[0].category, UserWarning))
msg = 'An illegal BOM (byte order marker) was detected and removed'
self.assertTrue(msg in str(w[0].message))
self.assertIsNotNone(jp2.box[3].xml) self.assertIsNotNone(jp2.box[3].xml)
@ -311,10 +304,8 @@ class TestXML_OpjDataRoot(unittest.TestCase):
filename = opj_data_file(os.path.join('input', filename = opj_data_file(os.path.join('input',
'nonregression', 'nonregression',
'26ccf3651020967f7778238ef5af08af.SIGFPE.d25.527.jp2')) '26ccf3651020967f7778238ef5af08af.SIGFPE.d25.527.jp2'))
with warnings.catch_warnings(record=True) as w: with self.assertWarns((UserWarning, UserWarning)):
warnings.simplefilter("always")
jp2 = Jp2k(filename) jp2 = Jp2k(filename)
self.assertTrue(issubclass(w[0].category, UserWarning))
self.assertIsNone(jp2.box[3].box[1].box[1].xml) self.assertIsNone(jp2.box[3].box[1].box[1].xml)

View file

@ -19,17 +19,19 @@ import sys
import tempfile import tempfile
import unittest import unittest
import uuid import uuid
from xml.etree import cElementTree as ET
import warnings import warnings
from xml.etree import cElementTree as ET
import numpy as np import numpy as np
import pkg_resources import pkg_resources
import glymur import glymur
from glymur import Jp2k from glymur import Jp2k
from glymur.version import openjpeg_version
from .fixtures import HAS_PYTHON_XMP_TOOLKIT from .fixtures import HAS_PYTHON_XMP_TOOLKIT
from .fixtures import WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG
if HAS_PYTHON_XMP_TOOLKIT: if HAS_PYTHON_XMP_TOOLKIT:
import libxmp import libxmp
from libxmp import XMPMeta from libxmp import XMPMeta
@ -52,6 +54,366 @@ def load_tests(loader, tests, ignore):
return tests return tests
class SliceProtocolBase(unittest.TestCase):
"""
Test slice protocol, i.e. when using [ ] to read image data.
"""
@classmethod
def setUpClass(self):
self.jp2 = Jp2k(glymur.data.nemo())
self.jp2_data = self.jp2.read()
self.j2k = Jp2k(glymur.data.goodstuff())
self.j2k_data = self.j2k.read()
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
class TestSliceProtocolBaseWrite(SliceProtocolBase):
def test_basic_write(self):
expected = self.j2k_data
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
j = Jp2k(tfile.name, 'wb')
j[:] = self.j2k_data
actual = j.read()
np.testing.assert_array_equal(actual, expected)
def test_cannot_write_with_non_default_single_slice(self):
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
j = Jp2k(tfile.name, 'wb')
with self.assertRaises(TypeError):
j[slice(None, 0)] = self.j2k_data
with self.assertRaises(TypeError):
j[slice(0, None)] = self.j2k_data
with self.assertRaises(TypeError):
j[slice(0, 0, None)] = self.j2k_data
with self.assertRaises(TypeError):
j[slice(0, 640)] = self.j2k_data
def test_cannot_write_a_row(self):
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
j = Jp2k(tfile.name, 'wb')
with self.assertRaises(TypeError):
j[5] = self.j2k_data
def test_cannot_write_a_pixel(self):
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
j = Jp2k(tfile.name, 'wb')
with self.assertRaises(TypeError):
j[25, 35] = self.j2k_data[25, 35]
def test_cannot_write_a_column(self):
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
j = Jp2k(tfile.name, 'wb')
with self.assertRaises(TypeError):
j[:, 25, :] = self.j2k_data[:, :25, :]
def test_cannot_write_a_band(self):
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
j = Jp2k(tfile.name, 'wb')
with self.assertRaises(TypeError):
j[:, :, 0] = self.j2k_data[:, :, 0]
def test_cannot_write_a_subarray(self):
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
j = Jp2k(tfile.name, 'wb')
with self.assertRaises(TypeError):
j[:25, :45, :] = self.j2k_data[:25, :25, :]
class TestSliceProtocolRead(SliceProtocolBase):
def test_resolution_strides_cannot_differ(self):
with self.assertRaises(IndexError):
# Strides in x/y directions cannot differ.
self.j2k[::2, ::3]
def test_resolution_strides_cannot_differ(self):
with self.assertRaises(IndexError):
# Strides in x/y directions cannot differ.
self.j2k[::2, ::3]
def test_resolution_strides_must_be_powers_of_two(self):
with self.assertRaises(IndexError):
self.j2k[::3, ::3]
def test_integer_index_in_3d(self):
for j in [0, 1, 2]:
band = self.j2k[:, :, j]
np.testing.assert_array_equal(self.j2k_data[:, :, j], band)
def test_slice_in_third_dimension(self):
actual = self.j2k[:,:,1:3]
expected = self.j2k_data[:,:,1:3]
np.testing.assert_array_equal(actual, expected)
def test_reduce_resolution_and_slice_in_third_dimension(self):
d = self.j2k[::2, ::2, 1:3]
all = self.j2k.read(rlevel=1)
np.testing.assert_array_equal(all[:,:,1:3], d)
def test_retrieve_single_row(self):
actual = self.jp2[0]
expected = self.jp2_data[0]
np.testing.assert_array_equal(actual, expected)
def test_retrieve_single_pixel(self):
actual = self.jp2[0,0]
expected = self.jp2_data[0, 0]
np.testing.assert_array_equal(actual, expected)
def test_retrieve_single_component(self):
actual = self.jp2[20,20,2]
expected = self.jp2_data[20, 20, 2]
np.testing.assert_array_equal(actual, expected)
def test_full_resolution_slicing_by_quarters_upper_left(self):
actual = self.jp2[:728, :1296]
expected = self.jp2_data[:728, :1296]
np.testing.assert_array_equal(actual, expected)
def test_full_resolution_slicing_by_quarters_lower_left(self):
actual = self.jp2[728:, :1296]
expected = self.jp2_data[728:, :1296]
np.testing.assert_array_equal(actual, expected)
def test_full_resolution_slicing_by_quarters_upper_right(self):
actual = self.jp2[:728, 1296:]
expected = self.jp2_data[:728, 1296:]
np.testing.assert_array_equal(actual, expected)
def test_full_resolution_slicing_by_quarters_lower_right(self):
actual = self.jp2[728:, 1296:]
expected = self.jp2_data[728:, 1296:]
np.testing.assert_array_equal(actual, expected)
def test_full_resolution_slicing_by_quarters_center(self):
actual = self.jp2[364:1092, 648:1942]
expected = self.jp2_data[364:1092, 648:1942]
np.testing.assert_array_equal(actual, expected)
def test_full_resolution_slicing_by_halves_left(self):
actual = self.jp2[:, :1296]
expected = self.jp2_data[:, :1296]
np.testing.assert_array_equal(actual, expected)
def test_full_resolution_slicing_by_right_half(self):
actual = self.jp2[:, 1296:]
expected = self.jp2_data[:, 1296:]
np.testing.assert_array_equal(actual, expected)
def test_full_resolution_slicing_by_top_half(self):
actual = self.jp2[:728, :]
expected = self.jp2_data[:728, :]
np.testing.assert_array_equal(actual, expected)
def test_full_resolution_slicing_by_bottom_half(self):
actual = self.jp2[728:, :]
expected = self.jp2_data[728:, :]
np.testing.assert_array_equal(actual, expected)
def test_region_rlevel1(self):
actual = self.jp2[0:201:2, 0:201:2]
expected = self.jp2.read(area=(0, 0, 201, 201), rlevel=1)
np.testing.assert_array_equal(actual, expected)
def test_region_rlevel1_slice_start_is_none(self):
actual = self.jp2[:201:2, :201:2]
expected = self.jp2.read(area=(0, 0, 201, 201), rlevel=1)
np.testing.assert_array_equal(actual, expected)
def test_region_rlevel1_slice_stop_is_none(self):
actual = self.jp2[201::2, 201::2]
expected = self.jp2.read(area=(201, 201, 1456, 2592), rlevel=1)
np.testing.assert_array_equal(actual, expected)
def test_region_rlevel1(self):
actual = self.jp2[0:202:2, 0:202:2]
expected = self.jp2.read(area=(0, 0, 202, 202), rlevel=1)
np.testing.assert_array_equal(actual, expected)
def test_slice_protocol_2d_reduce_resolution(self):
d = self.j2k[:]
self.assertEqual(d.shape, (800, 480, 3))
d = self.j2k[::1, ::1]
self.assertEqual(d.shape, (800, 480, 3))
d = self.j2k[::2, ::2]
self.assertEqual(d.shape, (400, 240, 3))
d = self.j2k[::4, ::4]
self.assertEqual(d.shape, (200, 120, 3))
d = self.j2k[::8, ::8]
self.assertEqual(d.shape, (100, 60, 3))
d = self.j2k[::16, ::16]
self.assertEqual(d.shape, (50, 30, 3))
d = self.j2k[::32, ::32]
self.assertEqual(d.shape, (25, 15, 3))
def test_region_rlevel5(self):
actual = self.j2k[5:533:32, 27:423:32]
expected = self.j2k.read(area=(5, 27, 533, 423), rlevel=5)
np.testing.assert_array_equal(actual, expected)
@unittest.skipIf(OPJ_DATA_ROOT is None,
"OPJ_DATA_ROOT environment variable not set")
class TestSliceProtocolOpjData(unittest.TestCase):
"""
Test slice protocol, i.e. when using [ ] to read image data.
These correspond to tests for the read method with the area parameter.
"""
@classmethod
def setUpClass(self):
jfile = opj_data_file('input/conformance/p1_04.j2k')
self.j2k = Jp2k(jfile)
self.j2k_data = self.j2k.read()
self.j2k_half_data = self.j2k.read(rlevel=1)
self.j2k_quarter_data = self.j2k.read(rlevel=2)
def test_NR_DEC_p1_04_j2k_43_decode(self):
actual = self.j2k[:1024, :1024]
expected = self.j2k_data
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p1_04_j2k_44_decode(self):
actual = self.j2k[640:768, 512:640]
expected = self.j2k_data[640:768, 512:640]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p1_04_j2k_45_decode(self):
actual = self.j2k[896:1024, 896:1024]
expected = self.j2k_data[896:1024, 896:1024]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p1_04_j2k_46_decode(self):
actual = self.j2k[500:800, 100:300]
expected = self.j2k_data[500:800, 100:300]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p1_04_j2k_47_decode(self):
actual = self.j2k[520:600, 260:360]
expected = self.j2k_data[520:600, 260:360]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p1_04_j2k_48_decode(self):
actual = self.j2k[520:660, 260:360]
expected = self.j2k_data[520:660, 260:360]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p1_04_j2k_49_decode(self):
actual = self.j2k[520:600, 360:400]
expected = self.j2k_data[520:600, 360:400]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p1_04_j2k_50_decode(self):
actual = self.j2k[:1024:4, :1024:4]
expected = self.j2k_quarter_data[:256, :256]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p1_04_j2k_51_decode(self):
actual = self.j2k[640:768:4, 512:640:4]
expected = self.j2k_quarter_data[160:192, 128:160]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p1_04_j2k_52_decode(self):
actual = self.j2k[896:1024:4, 896:1024:4]
expected = self.j2k_quarter_data[224:352, 224:352]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p1_04_j2k_53_decode(self):
actual = self.j2k[500:800:4, 100:300:4]
expected = self.j2k_quarter_data[125:200, 25:75]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p1_04_j2k_54_decode(self):
actual = self.j2k[520:600:4, 260:360:4]
expected = self.j2k_quarter_data[130:150, 65:90]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p1_04_j2k_55_decode(self):
actual = self.j2k[520:660:4, 260:360:4]
expected = self.j2k_quarter_data[130:165, 65:90]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p1_04_j2k_56_decode(self):
actual = self.j2k[520:600:4, 360:400:4]
expected = self.j2k_quarter_data[130:150, 90:100]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p1_06_j2k_75_decode(self):
# Image size would be 0 x 0.
with self.assertRaises((IOError, OSError)):
self.j2k[9:12:4, 9:12:4]
def test_NR_DEC_p0_04_j2k_85_decode(self):
actual = self.j2k[:256, :256]
expected = self.j2k_data[:256, :256]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p0_04_j2k_86_decode(self):
actual = self.j2k[:128, 128:256]
expected = self.j2k_data[:128, 128:256]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p0_04_j2k_87_decode(self):
actual = self.j2k[10:200, 50:120]
expected = self.j2k_data[10:200, 50:120]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p0_04_j2k_88_decode(self):
actual = self.j2k[150:210, 10:190]
expected = self.j2k_data[150:210, 10:190]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p0_04_j2k_89_decode(self):
actual = self.j2k[80:150, 100:200]
expected = self.j2k_data[80:150, 100:200]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p0_04_j2k_90_decode(self):
actual = self.j2k[20:50, 150:200]
expected = self.j2k_data[20:50, 150:200]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p0_04_j2k_91_decode(self):
actual = self.j2k[:256:4, :256:4]
expected = self.j2k_quarter_data[0:64, 0:64]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p0_04_j2k_92_decode(self):
actual = self.j2k[:128:4, 128:256:4]
expected = self.j2k_quarter_data[:32, 32:64]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p0_04_j2k_93_decode(self):
actual = self.j2k[10:200:4, 50:120:4]
expected = self.j2k_quarter_data[3:50, 13:30]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p0_04_j2k_94_decode(self):
actual = self.j2k[150:210:4, 10:190:4]
expected = self.j2k_quarter_data[38:53, 3:48]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p0_04_j2k_95_decode(self):
actual = self.j2k[80:150:4, 100:200:4]
expected = self.j2k_quarter_data[20:38, 25:50]
np.testing.assert_array_equal(actual, expected)
def test_NR_DEC_p0_04_j2k_96_decode(self):
actual = self.j2k[20:50:4, 150:200:4]
expected = self.j2k_quarter_data[5:13, 38:50]
np.testing.assert_array_equal(actual, expected)
class TestJp2k(unittest.TestCase): class TestJp2k(unittest.TestCase):
"""These tests should be run by just about all configuration.""" """These tests should be run by just about all configuration."""
@ -63,6 +425,7 @@ class TestJp2k(unittest.TestCase):
def tearDown(self): def tearDown(self):
pass pass
@unittest.skipIf(os.name == "nt", "Unexplained failure on windows") @unittest.skipIf(os.name == "nt", "Unexplained failure on windows")
def test_irreversible(self): def test_irreversible(self):
"""Irreversible""" """Irreversible"""
@ -79,8 +442,9 @@ class TestJp2k(unittest.TestCase):
actdata = j2.read() actdata = j2.read()
self.assertTrue(fixtures.mse(actdata[0], expdata[0]) < 0.38) self.assertTrue(fixtures.mse(actdata[0], expdata[0]) < 0.38)
@unittest.skipIf(re.match('1.5.(1|2)', @unittest.skipIf(re.match('1.[0-4]', openjpeg_version) is not None,
glymur.version.openjpeg_version) is not None, "Not supported with OpenJPEG {0}".format(openjpeg_version))
@unittest.skipIf(re.match('1.5.(1|2)', openjpeg_version) is not None,
"Mysteriously fails in 1.5.1 and 1.5.2") "Mysteriously fails in 1.5.1 and 1.5.2")
def test_no_cxform_pclr_jpx(self): def test_no_cxform_pclr_jpx(self):
"""Indices for pclr jpxfile if no color transform""" """Indices for pclr jpxfile if no color transform"""
@ -410,6 +774,8 @@ class TestJp2k(unittest.TestCase):
self.assertEqual(data.shape, (1024, 1024, 3)) self.assertEqual(data.shape, (1024, 1024, 3))
@unittest.skipIf(re.match('1.[0-4]', openjpeg_version) is not None,
"Not supported with OpenJPEG {0}".format(openjpeg_version))
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows") @unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
class TestJp2k_write(unittest.TestCase): class TestJp2k_write(unittest.TestCase):
"""Write tests, can be run by versions 1.5+""" """Write tests, can be run by versions 1.5+"""
@ -566,13 +932,6 @@ class TestJp2k_1_x(unittest.TestCase):
def tearDown(self): def tearDown(self):
pass pass
def test_area(self):
"""Area option not allowed for 1.x.
"""
j2k = Jp2k(self.j2kfile)
with self.assertRaises(TypeError):
j2k.read(area=(0, 0, 100, 100))
def test_tile(self): def test_tile(self):
"""tile option not allowed for 1.x. """tile option not allowed for 1.x.
""" """
@ -725,6 +1084,7 @@ class TestJp2k_2_1(unittest.TestCase):
self.assertEqual(j.box[2].box[0].num_components, 4) self.assertEqual(j.box[2].box[0].num_components, 4)
self.assertEqual(j.box[2].box[1].colorspace, glymur.core.SRGB) self.assertEqual(j.box[2].box[1].colorspace, glymur.core.SRGB)
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
@unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows") @unittest.skipIf(os.name == "nt", "NamedTemporaryFile issue on windows")
def test_openjpeg_library_message(self): def test_openjpeg_library_message(self):
"""Verify the error message produced by the openjpeg library""" """Verify the error message produced by the openjpeg library"""
@ -750,7 +1110,7 @@ class TestJp2k_2_1(unittest.TestCase):
#tfile.write(data[3186:]) #tfile.write(data[3186:])
tfile.flush() tfile.flush()
with warnings.catch_warnings(): with warnings.catch_warnings():
warnings.simplefilter("ignore") warnings.simplefilter('ignore')
j = Jp2k(tfile.name) j = Jp2k(tfile.name)
regexp = re.compile(r'''OpenJPEG\slibrary\serror:\s+ regexp = re.compile(r'''OpenJPEG\slibrary\serror:\s+
Invalid\svalues\sfor\scomp\s=\s0\s+ Invalid\svalues\sfor\scomp\s=\s0\s+
@ -772,24 +1132,20 @@ class TestParsing(unittest.TestCase):
glymur.set_parseoptions(codestream=True) glymur.set_parseoptions(codestream=True)
def tearDown(self): def tearDown(self):
pass glymur.set_parseoptions(codestream=True)
@unittest.skipIf(sys.platform.startswith('linux'), 'Failing on linux') @unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_bad_rsiz(self): def test_bad_rsiz(self):
"""Should not warn if RSIZ when parsing is turned off.""" """Should not warn if RSIZ when parsing is turned off."""
# Actually there are three warning triggered by this codestream.
filename = opj_data_file('input/nonregression/edf_c2_1002767.jp2') filename = opj_data_file('input/nonregression/edf_c2_1002767.jp2')
glymur.set_parseoptions(codestream=False) glymur.set_parseoptions(codestream=False)
with warnings.catch_warnings(record=True) as w: j = Jp2k(filename)
j = Jp2k(filename)
self.assertEqual(len(w), 0)
glymur.set_parseoptions(codestream=True) glymur.set_parseoptions(codestream=True)
with warnings.catch_warnings(record=True) as w: with self.assertWarnsRegex(UserWarning, 'Invalid profile'):
jp2 = Jp2k(filename) jp2 = Jp2k(filename)
self.assertTrue(issubclass(w[0].category, UserWarning))
self.assertTrue('Invalid profile' in str(w[0].message))
#@unittest.skip('trouble is a brewing...')
def test_main_header(self): def test_main_header(self):
"""Verify that the main header is not loaded when parsing turned off.""" """Verify that the main header is not loaded when parsing turned off."""
# The hidden _main_header attribute should show up after accessing it. # The hidden _main_header attribute should show up after accessing it.
@ -800,6 +1156,7 @@ class TestParsing(unittest.TestCase):
main_header = jp2c.main_header main_header = jp2c.main_header
self.assertIsNotNone(jp2c._main_header) self.assertIsNotNone(jp2c._main_header)
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
@unittest.skipIf(OPJ_DATA_ROOT is None, @unittest.skipIf(OPJ_DATA_ROOT is None,
"OPJ_DATA_ROOT environment variable not set") "OPJ_DATA_ROOT environment variable not set")
class TestJp2kOpjDataRootWarnings(unittest.TestCase): class TestJp2kOpjDataRootWarnings(unittest.TestCase):
@ -808,11 +1165,8 @@ class TestJp2kOpjDataRootWarnings(unittest.TestCase):
def test_undecodeable_box_id(self): def test_undecodeable_box_id(self):
"""Should warn in case of undecodeable box ID but not error out.""" """Should warn in case of undecodeable box ID but not error out."""
filename = opj_data_file('input/nonregression/edf_c2_1013627.jp2') filename = opj_data_file('input/nonregression/edf_c2_1013627.jp2')
with warnings.catch_warnings(record=True) as w: with self.assertWarnsRegex(UserWarning, 'Unrecognized box'):
warnings.simplefilter('always')
jp2 = Jp2k(filename) jp2 = Jp2k(filename)
self.assertTrue(issubclass(w[0].category, UserWarning))
self.assertTrue('Unrecognized box' in str(w[0].message))
# Now make sure we got all of the boxes. Ignore the last, which was # Now make sure we got all of the boxes. Ignore the last, which was
# bad. # bad.
@ -822,37 +1176,30 @@ class TestJp2kOpjDataRootWarnings(unittest.TestCase):
def test_bad_ftyp_brand(self): def test_bad_ftyp_brand(self):
"""Should warn in case of bad ftyp brand.""" """Should warn in case of bad ftyp brand."""
filename = opj_data_file('input/nonregression/edf_c2_1000290.jp2') filename = opj_data_file('input/nonregression/edf_c2_1000290.jp2')
with warnings.catch_warnings(record=True) as w: with self.assertWarns(UserWarning):
warnings.simplefilter('always')
jp2 = Jp2k(filename) jp2 = Jp2k(filename)
self.assertTrue(issubclass(w[0].category, UserWarning))
def test_invalid_approximation(self): def test_invalid_approximation(self):
"""Should warn in case of invalid approximation.""" """Should warn in case of invalid approximation."""
filename = opj_data_file('input/nonregression/edf_c2_1015644.jp2') filename = opj_data_file('input/nonregression/edf_c2_1015644.jp2')
with warnings.catch_warnings(record=True) as w: with self.assertWarnsRegex(UserWarning, 'Invalid approximation'):
warnings.simplefilter('always')
jp2 = Jp2k(filename) jp2 = Jp2k(filename)
self.assertTrue(issubclass(w[0].category, UserWarning))
self.assertTrue('Invalid approximation' in str(w[0].message))
@unittest.skipIf(sys.platform.startswith('linux'), 'Failing on linux')
def test_invalid_colorspace(self): def test_invalid_colorspace(self):
"""Should warn in case of invalid colorspace.""" """
Should warn in case of invalid colorspace.
There are multiple warnings, so there's no good way to regex them all.
"""
filename = opj_data_file('input/nonregression/edf_c2_1103421.jp2') filename = opj_data_file('input/nonregression/edf_c2_1103421.jp2')
with warnings.catch_warnings(record=True) as w: with self.assertWarns(UserWarning):
warnings.simplefilter('always')
jp2 = Jp2k(filename) jp2 = Jp2k(filename)
self.assertTrue(issubclass(w[1].category, UserWarning))
self.assertTrue('Unrecognized colorspace' in str(w[1].message))
def test_stupid_windows_eol_at_end(self): def test_stupid_windows_eol_at_end(self):
"""Garbage characters at the end of the file.""" """Garbage characters at the end of the file."""
filename = opj_data_file('input/nonregression/issue211.jp2') filename = opj_data_file('input/nonregression/issue211.jp2')
with warnings.catch_warnings(record=True) as w: with self.assertWarns(UserWarning):
warnings.simplefilter('always')
jp2 = Jp2k(filename) jp2 = Jp2k(filename)
self.assertTrue(issubclass(w[1].category, UserWarning))
@unittest.skipIf(OPJ_DATA_ROOT is None, @unittest.skipIf(OPJ_DATA_ROOT is None,
@ -877,10 +1224,12 @@ class TestJp2kOpjDataRoot(unittest.TestCase):
actdata = j.read() actdata = j.read()
self.assertTrue(fixtures.mse(actdata, expdata) < 250) self.assertTrue(fixtures.mse(actdata, expdata) < 250)
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_no_cxform_pclr_jp2(self): def test_no_cxform_pclr_jp2(self):
"""Indices for pclr jpxfile if no color transform""" """Indices for pclr jpxfile if no color transform"""
filename = opj_data_file('input/conformance/file9.jp2') filename = opj_data_file('input/conformance/file9.jp2')
j = Jp2k(filename) with self.assertWarns(UserWarning):
j = Jp2k(filename)
rgb = j.read() rgb = j.read()
idx = j.read(ignore_pclr_cmap_cdef=True) idx = j.read(ignore_pclr_cmap_cdef=True)
self.assertEqual(rgb.shape, (512, 768, 3)) self.assertEqual(rgb.shape, (512, 768, 3))
@ -905,15 +1254,15 @@ class TestJp2kOpjDataRoot(unittest.TestCase):
j = Jp2k(filename) j = Jp2k(filename)
with self.assertRaises(RuntimeError): with self.assertRaises(RuntimeError):
j.read() j.read()
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_no_cxform_cmap(self): def test_no_cxform_cmap(self):
"""Bands as physically ordered, not as physically intended""" """Bands as physically ordered, not as physically intended"""
# This file has the components physically reversed. The cmap box # This file has the components physically reversed. The cmap box
# tells the decoder how to order them, but this flag prevents that. # tells the decoder how to order them, but this flag prevents that.
filename = opj_data_file('input/conformance/file2.jp2') filename = opj_data_file('input/conformance/file2.jp2')
with warnings.catch_warnings(): with self.assertWarns(UserWarning):
# The file has a bad compatibility list entry. Not important here. # The file has a bad compatibility list entry. Not important here.
warnings.simplefilter("ignore")
j = Jp2k(filename) j = Jp2k(filename)
ycbcr = j.read() ycbcr = j.read()
crcby = j.read(ignore_pclr_cmap_cdef=True) crcby = j.read(ignore_pclr_cmap_cdef=True)

View file

@ -31,20 +31,19 @@ import re
import sys import sys
import unittest import unittest
import warnings
import numpy as np import numpy as np
from glymur import Jp2k
import glymur import glymur
from glymur import Jp2k
from glymur.jp2box import FileTypeBox, ImageHeaderBox, ColourSpecificationBox
from .fixtures import OPJ_DATA_ROOT from .fixtures import (
from .fixtures import mse, peak_tolerance, read_pgx, opj_data_file OPJ_DATA_ROOT, MetadataBase,
WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG,
mse, peak_tolerance, read_pgx, opj_data_file
)
@unittest.skipIf(glymur.lib.openjp2.OPENJP2 is None and
glymur.lib.openjpeg.OPENJPEG is None,
"Missing openjpeg libraries.")
@unittest.skipIf(OPJ_DATA_ROOT is None, @unittest.skipIf(OPJ_DATA_ROOT is None,
"OPJ_DATA_ROOT environment variable not set") "OPJ_DATA_ROOT environment variable not set")
class TestSuite(unittest.TestCase): class TestSuite(unittest.TestCase):
@ -95,24 +94,6 @@ class TestSuite(unittest.TestCase):
self.assertTrue(peak_tolerance(jpdata[:, :, 2], pgxdata) < 6) self.assertTrue(peak_tolerance(jpdata[:, :, 2], pgxdata) < 6)
self.assertTrue(mse(jpdata[:, :, 2], pgxdata) < 1.07) self.assertTrue(mse(jpdata[:, :, 2], pgxdata) < 1.07)
@unittest.skip("Known failure in OPENJPEG test suite operation.")
def test_ETS_C1P0_p0_07_j2k(self):
jfile = opj_data_file('input/conformance/p0_07.j2k')
jp2k = Jp2k(jfile)
jpdata = jp2k.read()
pgxfile = opj_data_file('baseline/conformance/c1p0_07_0.pgx')
pgxdata = read_pgx(pgxfile)
np.testing.assert_array_equal(jpdata[:, :, 0], pgxdata)
pgxfile = opj_data_file('baseline/conformance/c1p0_07_1.pgx')
pgxdata = read_pgx(pgxfile)
np.testing.assert_array_equal(jpdata[:, : 1], pgxdata)
pgxfile = opj_data_file('baseline/conformance/c1p0_07_2.pgx')
pgxdata = read_pgx(pgxfile)
np.testing.assert_array_equal(jpdata[:, : 2], pgxdata)
def test_ETS_C1P0_p0_08_j2k(self): def test_ETS_C1P0_p0_08_j2k(self):
jfile = opj_data_file('input/conformance/p0_08.j2k') jfile = opj_data_file('input/conformance/p0_08.j2k')
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
@ -222,71 +203,6 @@ class TestSuite(unittest.TestCase):
self.assertTrue(peak_tolerance(jpdata, pgxdata) < 624) self.assertTrue(peak_tolerance(jpdata, pgxdata) < 624)
self.assertTrue(mse(jpdata, pgxdata) < 3080) self.assertTrue(mse(jpdata, pgxdata) < 3080)
def test_ETS_JP2_file1(self):
jfile = opj_data_file('input/conformance/file1.jp2')
with warnings.catch_warnings():
# Bad compatibility list item.
warnings.simplefilter("ignore")
jp2k = Jp2k(jfile)
jpdata = jp2k.read()
self.assertEqual(jpdata.shape, (512, 768, 3))
def test_ETS_JP2_file2(self):
jfile = opj_data_file('input/conformance/file2.jp2')
jp2k = Jp2k(jfile)
jpdata = jp2k.read()
self.assertEqual(jpdata.shape, (640, 480, 3))
@unittest.skipIf(glymur.version.openjpeg_version_tuple[0] < 2,
"Functionality not implemented for 1.x")
def test_ETS_JP2_file3(self):
jfile = opj_data_file('input/conformance/file3.jp2')
jp2k = Jp2k(jfile)
jpdata = jp2k.read_bands()
self.assertEqual(jpdata[0].shape, (640, 480))
self.assertEqual(jpdata[1].shape, (320, 240))
self.assertEqual(jpdata[2].shape, (320, 240))
def test_ETS_JP2_file4(self):
jfile = opj_data_file('input/conformance/file4.jp2')
jp2k = Jp2k(jfile)
jpdata = jp2k.read()
self.assertEqual(jpdata.shape, (512, 768))
def test_ETS_JP2_file5(self):
jfile = opj_data_file('input/conformance/file5.jp2')
with warnings.catch_warnings():
# There's a warning for an unknown compatibility entry.
# Ignore it here.
warnings.simplefilter("ignore")
jp2k = Jp2k(jfile)
jpdata = jp2k.read()
self.assertEqual(jpdata.shape, (512, 768, 3))
def test_ETS_JP2_file6(self):
jfile = opj_data_file('input/conformance/file6.jp2')
jp2k = Jp2k(jfile)
jpdata = jp2k.read()
self.assertEqual(jpdata.shape, (512, 768))
def test_ETS_JP2_file7(self):
jfile = opj_data_file('input/conformance/file7.jp2')
jp2k = Jp2k(jfile)
jpdata = jp2k.read()
self.assertEqual(jpdata.shape, (640, 480, 3))
def test_ETS_JP2_file8(self):
jfile = opj_data_file('input/conformance/file8.jp2')
jp2k = Jp2k(jfile)
jpdata = jp2k.read()
self.assertEqual(jpdata.shape, (400, 700))
def test_ETS_JP2_file9(self):
jfile = opj_data_file('input/conformance/file9.jp2')
jp2k = Jp2k(jfile)
jpdata = jp2k.read()
self.assertEqual(jpdata.shape, (512, 768, 3))
def test_NR_DEC_Bretagne2_j2k_1_decode(self): def test_NR_DEC_Bretagne2_j2k_1_decode(self):
jfile = opj_data_file('input/nonregression/Bretagne2.j2k') jfile = opj_data_file('input/nonregression/Bretagne2.j2k')
jp2 = Jp2k(jfile) jp2 = Jp2k(jfile)
@ -366,19 +282,6 @@ class TestSuite(unittest.TestCase):
Jp2k(jfile).read() Jp2k(jfile).read()
self.assertTrue(True) self.assertTrue(True)
def test_NR_DEC_orb_blue_lin_jp2_25_decode(self):
jfile = opj_data_file('input/nonregression/orb-blue10-lin-jp2.jp2')
with warnings.catch_warnings():
# This file has an invalid ICC profile
warnings.simplefilter("ignore")
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')
Jp2k(jfile).read()
self.assertTrue(True)
def test_NR_DEC_relax_jp2_27_decode(self): def test_NR_DEC_relax_jp2_27_decode(self):
jfile = opj_data_file('input/nonregression/relax.jp2') jfile = opj_data_file('input/nonregression/relax.jp2')
Jp2k(jfile).read() Jp2k(jfile).read()
@ -395,15 +298,194 @@ class TestSuite(unittest.TestCase):
self.assertTrue(True) 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):
"""
Identical setup to above, but these tests issue warnings.
"""
def setUp(self):
pass
def tearDown(self):
pass
def test_ETS_JP2_file1(self):
jfile = opj_data_file('input/conformance/file1.jp2')
with self.assertWarns(UserWarning):
# Bad compatibility list item.
jp2k = Jp2k(jfile)
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.read()
self.assertEqual(jpdata.shape, (640, 480, 3))
@unittest.skipIf(glymur.version.openjpeg_version_tuple[0] < 2,
"Functionality not implemented for 1.x")
def test_ETS_JP2_file3(self):
jfile = opj_data_file('input/conformance/file3.jp2')
with self.assertWarns(UserWarning):
jp2k = Jp2k(jfile)
jpdata = jp2k.read_bands()
self.assertEqual(jpdata[0].shape, (640, 480))
self.assertEqual(jpdata[1].shape, (320, 240))
self.assertEqual(jpdata[2].shape, (320, 240))
def test_ETS_JP2_file4(self):
jfile = opj_data_file('input/conformance/file4.jp2')
with self.assertWarns(UserWarning):
jp2k = Jp2k(jfile)
jpdata = jp2k.read()
self.assertEqual(jpdata.shape, (512, 768))
def test_ETS_JP2_file5(self):
jfile = opj_data_file('input/conformance/file5.jp2')
with self.assertWarns(UserWarning):
# There's a warning for an unknown compatibility entry.
# Ignore it here.
jp2k = Jp2k(jfile)
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.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.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.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.read()
self.assertEqual(jpdata.shape, (512, 768, 3))
def test_NR_broken_jp2_dump(self):
jfile = opj_data_file('input/nonregression/broken.jp2')
with self.assertWarns(UserWarning):
# colr box has bad length.
jp2 = Jp2k(jfile)
ids = [box.box_id for box in jp2.box]
self.assertEqual(ids, ['jP ', 'ftyp', 'jp2h', 'jp2c'])
ids = [box.box_id for box in jp2.box[2].box]
self.assertEqual(ids, ['ihdr', 'colr'])
# 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)
expected = ColourSpecificationBox(colorspace=glymur.core.SRGB)
self.verifyColourSpecificationBox(jp2.box[2].box[1], expected)
c = jp2.box[3].main_header
ids = [x.marker_id for x in c.segment]
expected = ['SOC', 'SIZ', 'CME', 'COD', 'QCD', 'QCC', 'QCC']
self.assertEqual(ids, expected)
kwargs = {'rsiz': 0, 'xysiz': (203, 152), 'xyosiz': (0, 0),
'xytsiz': (203, 152), 'xytosiz': (0, 0), 'bitdepth': (8, 8, 8),
'signed': (False, False, False),
'xyrsiz': [(1, 1, 1), (1, 1, 1)]}
self.verifySizSegment(c.segment[1],
glymur.codestream.SIZsegment(**kwargs))
pargs = (glymur.core.RCME_ISO_8859_1,
"Creator: JasPer Version 1.701.0".encode())
self.verifyCMEsegment(c.segment[2],
glymur.codestream.CMEsegment(*pargs))
# COD: Coding style default
self.assertFalse(c.segment[3].scod & 2) # no sop
self.assertFalse(c.segment[3].scod & 4) # no eph
self.assertEqual(c.segment[3].spcod[0], glymur.core.LRCP)
self.assertEqual(c.segment[3].layers, 1) # layers = 1
self.assertEqual(c.segment[3].spcod[3], 1) # mct
self.assertEqual(c.segment[3].spcod[4], 5) # level
self.assertEqual(tuple(c.segment[3].code_block_size),
(64, 64)) # cblk
self.verify_codeblock_style(c.segment[3].spcod[7],
[False, False, False, False, False, False])
self.assertEqual(c.segment[3].spcod[8],
glymur.core.WAVELET_XFORM_5X3_REVERSIBLE)
self.assertEqual(len(c.segment[3].spcod), 9)
# QCD: Quantization default
self.assertEqual(c.segment[4].sqcd & 0x1f, 0)
self.assertEqual(c.segment[4].guard_bits, 2)
self.assertEqual(c.segment[4].mantissa, [0] * 16)
self.assertEqual(c.segment[4].exponent,
[8] + [9, 9, 10] * 5)
# QCC: Quantization component
# associated component
self.assertEqual(c.segment[5].cqcc, 1)
self.assertEqual(c.segment[5].guard_bits, 2)
# quantization type
self.assertEqual(c.segment[5].sqcc & 0x1f, 0) # none
self.assertEqual(c.segment[5].mantissa, [0] * 16)
self.assertEqual(c.segment[5].exponent,
[8] + [9, 9, 10] * 5)
# QCC: Quantization component
# associated component
self.assertEqual(c.segment[6].cqcc, 2)
self.assertEqual(c.segment[6].guard_bits, 2)
# quantization type
self.assertEqual(c.segment[6].sqcc & 0x1f, 0) # none
self.assertEqual(c.segment[6].mantissa, [0] * 16)
self.assertEqual(c.segment[6].exponent,
[8] + [9, 9, 10] * 5)
def test_NR_DEC_orb_blue_lin_jp2_25_decode(self):
jfile = opj_data_file('input/nonregression/orb-blue10-lin-jp2.jp2')
with self.assertWarns(UserWarning):
# This file has an invalid ICC profile
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).read()
self.assertTrue(True)
@unittest.skipIf(OPJ_DATA_ROOT is None, @unittest.skipIf(OPJ_DATA_ROOT is None,
"OPJ_DATA_ROOT environment variable not set") "OPJ_DATA_ROOT environment variable not set")
@unittest.skipIf(glymur.version.openjpeg_version_tuple[0] == 1, @unittest.skipIf(glymur.version.openjpeg_version_tuple[0] == 1,
"Feature not supported in glymur until openjpeg 2.0") "Feature not supported in glymur until openjpeg 2.0")
class TestSuite_bands(unittest.TestCase): class TestSuiteBands(unittest.TestCase):
"""Runs tests introduced in version 1.x but only pass in glymur with 2.0 """
Test the read_bands method.
The deal here is that the feature works with 1.x, but glymur only supports
it with version 2.0.
""" """
def setUp(self): def setUp(self):
@ -522,34 +604,34 @@ class TestSuite2point0(unittest.TestCase):
pgxdata = read_pgx(pgxfile) pgxdata = read_pgx(pgxfile)
np.testing.assert_array_equal(jpdata[:, :, 2], pgxdata) np.testing.assert_array_equal(jpdata[:, :, 2], pgxdata)
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_NR_DEC_broken2_jp2_5_decode(self): def test_NR_DEC_broken2_jp2_5_decode(self):
# Null pointer access # Null pointer access
jfile = opj_data_file('input/nonregression/broken2.jp2') jfile = opj_data_file('input/nonregression/broken2.jp2')
with self.assertRaises(IOError): with self.assertRaises(IOError):
with warnings.catch_warnings(): with self.assertWarns(UserWarning):
# Invalid marker ID. # Invalid marker ID.
warnings.simplefilter("ignore")
Jp2k(jfile).read() Jp2k(jfile).read()
self.assertTrue(True) self.assertTrue(True)
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_NR_DEC_broken4_jp2_7_decode(self): def test_NR_DEC_broken4_jp2_7_decode(self):
jfile = opj_data_file('input/nonregression/broken4.jp2') jfile = opj_data_file('input/nonregression/broken4.jp2')
with self.assertRaises(IOError): with self.assertRaises(IOError):
with warnings.catch_warnings(): with self.assertWarns(UserWarning):
# invalid number of subbands, bad marker ID # invalid number of subbands, bad marker ID
warnings.simplefilter("ignore")
Jp2k(jfile).read() Jp2k(jfile).read()
self.assertTrue(True) self.assertTrue(True)
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_NR_DEC_kakadu_v4_4_openjpegv2_broken_j2k_16_decode(self): def test_NR_DEC_kakadu_v4_4_openjpegv2_broken_j2k_16_decode(self):
# This test actually passes in 1.5, but produces unpleasant warning # This test actually passes in 1.5, but produces unpleasant warning
# messages that cannot be turned off? # messages that cannot be turned off?
relpath = 'input/nonregression/kakadu_v4-4_openjpegv2_broken.j2k' relpath = 'input/nonregression/kakadu_v4-4_openjpegv2_broken.j2k'
jfile = opj_data_file(relpath) jfile = opj_data_file(relpath)
if glymur.version.openjpeg_version_tuple[0] < 2: if glymur.version.openjpeg_version_tuple[0] < 2:
with warnings.catch_warnings(): with self.assertWarns(UserWarning):
# Incorrect warning issued about tile parts. # Incorrect warning issued about tile parts.
warnings.simplefilter("ignore")
Jp2k(jfile).read() Jp2k(jfile).read()
else: else:
Jp2k(jfile).read() Jp2k(jfile).read()

View file

@ -31,14 +31,13 @@ import re
import sys import sys
import unittest import unittest
import warnings
import numpy as np import numpy as np
from glymur import Jp2k from glymur import Jp2k
import glymur import glymur
from .fixtures import OPJ_DATA_ROOT 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 from .fixtures import mse, peak_tolerance, read_pgx, opj_data_file
@ -56,11 +55,11 @@ class TestSuite2point1(unittest.TestCase):
def tearDown(self): def tearDown(self):
pass pass
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_NR_DEC_text_GBR_jp2_29_decode(self): def test_NR_DEC_text_GBR_jp2_29_decode(self):
jfile = opj_data_file('input/nonregression/text_GBR.jp2') jfile = opj_data_file('input/nonregression/text_GBR.jp2')
with warnings.catch_warnings(): with self.assertWarns(UserWarning):
# brand is 'jp2 ', but has any icc profile. # brand is 'jp2 ', but has any icc profile.
warnings.simplefilter("ignore")
jp2 = Jp2k(jfile) jp2 = Jp2k(jfile)
jp2.read() jp2.read()
self.assertTrue(True) self.assertTrue(True)
@ -85,32 +84,32 @@ class TestSuite2point1(unittest.TestCase):
Jp2k(jfile).read() Jp2k(jfile).read()
self.assertTrue(True) self.assertTrue(True)
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_NR_DEC_gdal_fuzzer_unchecked_num_resolutions_jp2_36_decode(self): def test_NR_DEC_gdal_fuzzer_unchecked_num_resolutions_jp2_36_decode(self):
f = 'input/nonregression/gdal_fuzzer_unchecked_numresolutions.jp2' f = 'input/nonregression/gdal_fuzzer_unchecked_numresolutions.jp2'
jfile = opj_data_file(f) jfile = opj_data_file(f)
with warnings.catch_warnings(): with self.assertWarns(UserWarning):
# Invalid number of resolutions. # Invalid number of resolutions.
warnings.simplefilter("ignore")
j = Jp2k(jfile) j = Jp2k(jfile)
with self.assertRaises(IOError): with self.assertRaises(IOError):
j.read() j.read()
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_NR_DEC_gdal_fuzzer_check_number_of_tiles_jp2_38_decode(self): def test_NR_DEC_gdal_fuzzer_check_number_of_tiles_jp2_38_decode(self):
relpath = 'input/nonregression/gdal_fuzzer_check_number_of_tiles.jp2' relpath = 'input/nonregression/gdal_fuzzer_check_number_of_tiles.jp2'
jfile = opj_data_file(relpath) jfile = opj_data_file(relpath)
with warnings.catch_warnings(): with self.assertWarns(UserWarning):
# Invalid number of tiles. # Invalid number of tiles.
warnings.simplefilter("ignore")
j = Jp2k(jfile) j = Jp2k(jfile)
with self.assertRaises(IOError): with self.assertRaises(IOError):
j.read() j.read()
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_NR_DEC_gdal_fuzzer_check_comp_dx_dy_jp2_39_decode(self): def test_NR_DEC_gdal_fuzzer_check_comp_dx_dy_jp2_39_decode(self):
relpath = 'input/nonregression/gdal_fuzzer_check_comp_dx_dy.jp2' relpath = 'input/nonregression/gdal_fuzzer_check_comp_dx_dy.jp2'
jfile = opj_data_file(relpath) jfile = opj_data_file(relpath)
with warnings.catch_warnings(): with self.assertWarns(UserWarning):
# Invalid subsampling value # Invalid subsampling value
warnings.simplefilter("ignore")
with self.assertRaises(IOError): with self.assertRaises(IOError):
Jp2k(jfile).read() Jp2k(jfile).read()
@ -124,105 +123,6 @@ class TestSuite2point1(unittest.TestCase):
Jp2k(jfile).read() Jp2k(jfile).read()
self.assertTrue(True) self.assertTrue(True)
def test_NR_DEC_p1_04_j2k_43_decode(self):
jfile = opj_data_file('input/conformance/p1_04.j2k')
jp2k = Jp2k(jfile)
ssdata = jp2k.read(area=(0, 0, 1024, 1024))
odata = jp2k.read()
np.testing.assert_array_equal(ssdata, odata)
def test_NR_DEC_p1_04_j2k_44_decode(self):
jfile = opj_data_file('input/conformance/p1_04.j2k')
jp2k = Jp2k(jfile)
ssdata = jp2k.read(area=(640, 512, 768, 640))
odata = jp2k.read()
np.testing.assert_array_equal(ssdata, odata[640:768, 512:640])
def test_NR_DEC_p1_04_j2k_45_decode(self):
jfile = opj_data_file('input/conformance/p1_04.j2k')
jp2k = Jp2k(jfile)
ssdata = jp2k.read(area=(896, 896, 1024, 1024))
odata = jp2k.read()
np.testing.assert_array_equal(ssdata, odata[896:1024, 896:1024])
def test_NR_DEC_p1_04_j2k_46_decode(self):
jfile = opj_data_file('input/conformance/p1_04.j2k')
jp2k = Jp2k(jfile)
ssdata = jp2k.read(area=(500, 100, 800, 300))
odata = jp2k.read()
np.testing.assert_array_equal(ssdata, odata[500:800, 100:300])
def test_NR_DEC_p1_04_j2k_47_decode(self):
jfile = opj_data_file('input/conformance/p1_04.j2k')
jp2k = Jp2k(jfile)
ssdata = jp2k.read(area=(520, 260, 600, 360))
odata = jp2k.read()
np.testing.assert_array_equal(ssdata, odata[520:600, 260:360])
def test_NR_DEC_p1_04_j2k_48_decode(self):
jfile = opj_data_file('input/conformance/p1_04.j2k')
jp2k = Jp2k(jfile)
ssdata = jp2k.read(area=(520, 260, 660, 360))
odata = jp2k.read()
np.testing.assert_array_equal(ssdata, odata[520:660, 260:360])
def test_NR_DEC_p1_04_j2k_49_decode(self):
jfile = opj_data_file('input/conformance/p1_04.j2k')
jp2k = Jp2k(jfile)
ssdata = jp2k.read(area=(520, 360, 600, 400))
odata = jp2k.read()
np.testing.assert_array_equal(ssdata, odata[520:600, 360:400])
def test_NR_DEC_p1_04_j2k_50_decode(self):
jfile = opj_data_file('input/conformance/p1_04.j2k')
jp2k = Jp2k(jfile)
ssdata = jp2k.read(area=(0, 0, 1024, 1024), rlevel=2)
odata = jp2k.read(rlevel=2)
np.testing.assert_array_equal(ssdata, odata[0:256, 0:256])
def test_NR_DEC_p1_04_j2k_51_decode(self):
jfile = opj_data_file('input/conformance/p1_04.j2k')
jp2k = Jp2k(jfile)
ssdata = jp2k.read(area=(640, 512, 768, 640), rlevel=2)
odata = jp2k.read(rlevel=2)
np.testing.assert_array_equal(ssdata, odata[160:192, 128:160])
def test_NR_DEC_p1_04_j2k_52_decode(self):
jfile = opj_data_file('input/conformance/p1_04.j2k')
jp2k = Jp2k(jfile)
ssdata = jp2k.read(area=(896, 896, 1024, 1024), rlevel=2)
odata = jp2k.read(rlevel=2)
np.testing.assert_array_equal(ssdata, odata[224:352, 224:352])
def test_NR_DEC_p1_04_j2k_53_decode(self):
jfile = opj_data_file('input/conformance/p1_04.j2k')
jp2k = Jp2k(jfile)
ssdata = jp2k.read(area=(500, 100, 800, 300), rlevel=2)
odata = jp2k.read(rlevel=2)
np.testing.assert_array_equal(ssdata, odata[125:200, 25:75])
def test_NR_DEC_p1_04_j2k_54_decode(self):
jfile = opj_data_file('input/conformance/p1_04.j2k')
jp2k = Jp2k(jfile)
ssdata = jp2k.read(area=(520, 260, 600, 360), rlevel=2)
odata = jp2k.read(rlevel=2)
np.testing.assert_array_equal(ssdata, odata[130:150, 65:90])
def test_NR_DEC_p1_04_j2k_55_decode(self):
jfile = opj_data_file('input/conformance/p1_04.j2k')
jp2k = Jp2k(jfile)
ssdata = jp2k.read(area=(520, 260, 660, 360), rlevel=2)
odata = jp2k.read(rlevel=2)
np.testing.assert_array_equal(ssdata, odata[130:165, 65:90])
def test_NR_DEC_p1_04_j2k_56_decode(self):
jfile = opj_data_file('input/conformance/p1_04.j2k')
jp2k = Jp2k(jfile)
ssdata = jp2k.read(area=(520, 360, 600, 400), rlevel=2)
odata = jp2k.read(rlevel=2)
np.testing.assert_array_equal(ssdata, odata[130:150, 90:100])
def test_NR_DEC_p1_04_j2k_57_decode(self): def test_NR_DEC_p1_04_j2k_57_decode(self):
jfile = opj_data_file('input/conformance/p1_04.j2k') jfile = opj_data_file('input/conformance/p1_04.j2k')
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
@ -251,139 +151,192 @@ class TestSuite2point1(unittest.TestCase):
odata = jp2k.read(rlevel=1) odata = jp2k.read(rlevel=1)
np.testing.assert_array_equal(tdata, odata[64:128, 256:320]) 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): def test_NR_DEC_jp2_36_decode(self):
lst = ('input', lst = ('input',
'nonregression', 'nonregression',
'gdal_fuzzer_assert_in_opj_j2k_read_SQcd_SQcc.patch.jp2') 'gdal_fuzzer_assert_in_opj_j2k_read_SQcd_SQcc.patch.jp2')
jfile = opj_data_file('/'.join(lst)) jfile = opj_data_file('/'.join(lst))
with warnings.catch_warnings(): with self.assertWarns(UserWarning):
# Invalid component number. # Invalid component number.
warnings.simplefilter("ignore")
j = Jp2k(jfile) j = Jp2k(jfile)
with self.assertRaises(IOError): with self.assertRaises(IOError):
j.read() j.read()
def test_NR_DEC_p1_06_j2k_70_decode(self): @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') jfile = opj_data_file('input/conformance/p1_06.j2k')
jp2k = Jp2k(jfile) self.j2k_p1_06 = Jp2k(jfile)
ssdata = jp2k.read(area=(9, 9, 12, 12), rlevel=1)
self.assertEqual(ssdata.shape, (1, 1, 3)) 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): def test_NR_DEC_p1_06_j2k_71_decode(self):
jfile = opj_data_file('input/conformance/p1_06.j2k') actual = self.j2k_p1_06.read(area=(10, 4, 12, 10), rlevel=1)
jp2k = Jp2k(jfile) self.assertEqual(actual.shape, (1, 3, 3))
ssdata = jp2k.read(area=(10, 4, 12, 10), rlevel=1)
self.assertEqual(ssdata.shape, (1, 3, 3))
def test_NR_DEC_p1_06_j2k_72_decode(self): def test_NR_DEC_p1_06_j2k_72_decode(self):
jfile = opj_data_file('input/conformance/p1_06.j2k') ssdata = self.j2k_p1_06.read(area=(3, 3, 9, 9), rlevel=1)
jp2k = Jp2k(jfile)
ssdata = jp2k.read(area=(3, 3, 9, 9), rlevel=1)
self.assertEqual(ssdata.shape, (3, 3, 3)) self.assertEqual(ssdata.shape, (3, 3, 3))
def test_NR_DEC_p1_06_j2k_73_decode(self): def test_NR_DEC_p1_06_j2k_73_decode(self):
jfile = opj_data_file('input/conformance/p1_06.j2k') ssdata = self.j2k_p1_06.read(area=(4, 4, 7, 7), rlevel=1)
jp2k = Jp2k(jfile)
ssdata = jp2k.read(area=(4, 4, 7, 7), rlevel=1)
self.assertEqual(ssdata.shape, (2, 2, 3)) self.assertEqual(ssdata.shape, (2, 2, 3))
def test_NR_DEC_p1_06_j2k_74_decode(self): def test_NR_DEC_p1_06_j2k_74_decode(self):
jfile = opj_data_file('input/conformance/p1_06.j2k') ssdata = self.j2k_p1_06.read(area=(4, 4, 5, 5), rlevel=1)
jp2k = Jp2k(jfile)
ssdata = jp2k.read(area=(4, 4, 5, 5), rlevel=1)
self.assertEqual(ssdata.shape, (1, 1, 3)) self.assertEqual(ssdata.shape, (1, 1, 3))
def test_NR_DEC_p1_06_j2k_75_decode(self): def test_NR_DEC_p1_06_j2k_75_decode(self):
# Image size would be 0 x 0. # Image size would be 0 x 0.
jfile = opj_data_file('input/conformance/p1_06.j2k')
jp2k = Jp2k(jfile)
with self.assertRaises((IOError, OSError)): with self.assertRaises((IOError, OSError)):
jp2k.read(area=(9, 9, 12, 12), rlevel=2) self.j2k_p1_06.read(area=(9, 9, 12, 12), rlevel=2)
def test_NR_DEC_p0_04_j2k_85_decode(self): def test_NR_DEC_p0_04_j2k_85_decode(self):
jfile = opj_data_file('input/conformance/p0_04.j2k') actual = self.j2k.read(area=(0, 0, 256, 256))
jp2k = Jp2k(jfile) expected = self.j2k_data[:256, :256]
ssdata = jp2k.read(area=(0, 0, 256, 256)) np.testing.assert_array_equal(actual, expected)
fulldata = jp2k.read()
np.testing.assert_array_equal(fulldata[0:256, 0:256], ssdata)
def test_NR_DEC_p0_04_j2k_86_decode(self): def test_NR_DEC_p0_04_j2k_86_decode(self):
jfile = opj_data_file('input/conformance/p0_04.j2k') actual = self.j2k.read(area=(0, 128, 128, 256))
jp2k = Jp2k(jfile) expected = self.j2k_data[:128, 128:256]
ssdata = jp2k.read(area=(0, 128, 128, 256)) np.testing.assert_array_equal(actual, expected)
fulldata = jp2k.read()
np.testing.assert_array_equal(fulldata[0:128, 128:256], ssdata)
def test_NR_DEC_p0_04_j2k_87_decode(self): def test_NR_DEC_p0_04_j2k_87_decode(self):
jfile = opj_data_file('input/conformance/p0_04.j2k') actual = self.j2k.read(area=(10, 50, 200, 120))
jp2k = Jp2k(jfile) expected = self.j2k_data[10:200, 50:120]
ssdata = jp2k.read(area=(10, 50, 200, 120)) np.testing.assert_array_equal(actual, expected)
fulldata = jp2k.read()
np.testing.assert_array_equal(fulldata[10:200, 50:120], ssdata)
def test_NR_DEC_p0_04_j2k_88_decode(self): def test_NR_DEC_p0_04_j2k_88_decode(self):
jfile = opj_data_file('input/conformance/p0_04.j2k') actual = self.j2k.read(area=(150, 10, 210, 190))
jp2k = Jp2k(jfile) expected = self.j2k_data[150:210, 10:190]
ssdata = jp2k.read(area=(150, 10, 210, 190)) np.testing.assert_array_equal(actual, expected)
fulldata = jp2k.read()
np.testing.assert_array_equal(fulldata[150:210, 10:190], ssdata)
def test_NR_DEC_p0_04_j2k_89_decode(self): def test_NR_DEC_p0_04_j2k_89_decode(self):
jfile = opj_data_file('input/conformance/p0_04.j2k') actual = self.j2k.read(area=(80, 100, 150, 200))
jp2k = Jp2k(jfile) expected = self.j2k_data[80:150, 100:200]
ssdata = jp2k.read(area=(80, 100, 150, 200)) np.testing.assert_array_equal(actual, expected)
fulldata = jp2k.read()
np.testing.assert_array_equal(fulldata[80:150, 100:200], ssdata)
def test_NR_DEC_p0_04_j2k_90_decode(self): def test_NR_DEC_p0_04_j2k_90_decode(self):
jfile = opj_data_file('input/conformance/p0_04.j2k') actual = self.j2k.read(area=(20, 150, 50, 200))
jp2k = Jp2k(jfile) expected = self.j2k_data[20:50, 150:200]
ssdata = jp2k.read(area=(20, 150, 50, 200)) np.testing.assert_array_equal(actual, expected)
fulldata = jp2k.read()
np.testing.assert_array_equal(fulldata[20:50, 150:200], ssdata)
def test_NR_DEC_p0_04_j2k_91_decode(self): def test_NR_DEC_p0_04_j2k_91_decode(self):
jfile = opj_data_file('input/conformance/p0_04.j2k') actual = self.j2k.read(area=(0, 0, 256, 256), rlevel=2)
jp2k = Jp2k(jfile) expected = self.j2k_quarter_data[0:64, 0:64]
ssdata = jp2k.read(area=(0, 0, 256, 256), rlevel=2) np.testing.assert_array_equal(actual, expected)
fulldata = jp2k.read(rlevel=2)
np.testing.assert_array_equal(fulldata[0:64, 0:64], ssdata)
def test_NR_DEC_p0_04_j2k_92_decode(self): def test_NR_DEC_p0_04_j2k_92_decode(self):
jfile = opj_data_file('input/conformance/p0_04.j2k') actual = self.j2k.read(area=(0, 128, 128, 256), rlevel=2)
jp2k = Jp2k(jfile) expected = self.j2k_quarter_data[:32, 32:64]
ssdata = jp2k.read(area=(0, 128, 128, 256), rlevel=2) np.testing.assert_array_equal(actual, expected)
fulldata = jp2k.read(rlevel=2)
np.testing.assert_array_equal(fulldata[0:32, 32:64], ssdata)
def test_NR_DEC_p0_04_j2k_93_decode(self): def test_NR_DEC_p0_04_j2k_93_decode(self):
jfile = opj_data_file('input/conformance/p0_04.j2k') actual = self.j2k.read(area=(10, 50, 200, 120), rlevel=2)
jp2k = Jp2k(jfile) expected = self.j2k_quarter_data[3:50, 13:30]
ssdata = jp2k.read(area=(10, 50, 200, 120), rlevel=2) np.testing.assert_array_equal(actual, expected)
fulldata = jp2k.read(rlevel=2)
np.testing.assert_array_equal(fulldata[3:50, 13:30], ssdata)
def test_NR_DEC_p0_04_j2k_94_decode(self): def test_NR_DEC_p0_04_j2k_94_decode(self):
jfile = opj_data_file('input/conformance/p0_04.j2k') actual = self.j2k.read(area=(150, 10, 210, 190), rlevel=2)
jp2k = Jp2k(jfile) expected = self.j2k_quarter_data[38:53, 3:48]
ssdata = jp2k.read(area=(150, 10, 210, 190), rlevel=2) np.testing.assert_array_equal(actual, expected)
fulldata = jp2k.read(rlevel=2)
np.testing.assert_array_equal(fulldata[38:53, 3:48], ssdata)
def test_NR_DEC_p0_04_j2k_95_decode(self): def test_NR_DEC_p0_04_j2k_95_decode(self):
jfile = opj_data_file('input/conformance/p0_04.j2k') actual = self.j2k.read(area=(80, 100, 150, 200), rlevel=2)
jp2k = Jp2k(jfile) expected = self.j2k_quarter_data[20:38, 25:50]
ssdata = jp2k.read(area=(80, 100, 150, 200), rlevel=2) np.testing.assert_array_equal(actual, expected)
fulldata = jp2k.read(rlevel=2)
np.testing.assert_array_equal(fulldata[20:38, 25:50], ssdata)
def test_NR_DEC_p0_04_j2k_96_decode(self): def test_NR_DEC_p0_04_j2k_96_decode(self):
jfile = opj_data_file('input/conformance/p0_04.j2k') actual = self.j2k.read(area=(20, 150, 50, 200), rlevel=2)
jp2k = Jp2k(jfile) expected = self.j2k_quarter_data[5:13, 38:50]
ssdata = jp2k.read(area=(20, 150, 50, 200), rlevel=2) np.testing.assert_array_equal(actual, expected)
fulldata = jp2k.read(rlevel=2)
np.testing.assert_array_equal(fulldata[5:13, 38:50], ssdata)
if __name__ == "__main__":
unittest.main()

File diff suppressed because it is too large Load diff

View file

@ -13,19 +13,17 @@ import re
import sys import sys
import tempfile import tempfile
import unittest import unittest
import warnings
import numpy as np import numpy as np
try: try:
import skimage.io import skimage.io
skimage.io.use_plugin('freeimage', 'imread') except ImportError:
_HAS_SKIMAGE_FREEIMAGE_SUPPORT = True pass
except ((ImportError, RuntimeError)):
_HAS_SKIMAGE_FREEIMAGE_SUPPORT = False
from .fixtures import OPJ_DATA_ROOT, opj_data_file, read_image from .fixtures import OPJ_DATA_ROOT, opj_data_file, read_image
from .fixtures import NO_READ_BACKEND, NO_READ_BACKEND_MSG from .fixtures import NO_READ_BACKEND, NO_READ_BACKEND_MSG
from .fixtures import NO_SKIMAGE_FREEIMAGE_SUPPORT
from .fixtures import WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG
from glymur import Jp2k from glymur import Jp2k
import glymur import glymur
@ -44,7 +42,7 @@ class TestSuiteNegative(unittest.TestCase):
pass pass
@unittest.skipIf(not _HAS_SKIMAGE_FREEIMAGE_SUPPORT, @unittest.skipIf(NO_SKIMAGE_FREEIMAGE_SUPPORT,
"Cannot read input image without scikit-image/freeimage") "Cannot read input image without scikit-image/freeimage")
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.") @unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
def test_cinema2K_bad_frame_rate(self): def test_cinema2K_bad_frame_rate(self):
@ -78,13 +76,13 @@ class TestSuiteNegative(unittest.TestCase):
jp2k.get_codestream(header_only=False) jp2k.get_codestream(header_only=False)
self.assertTrue(True) self.assertTrue(True)
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_nr_illegalclrtransform(self): def test_nr_illegalclrtransform(self):
"""EOC marker is bad""" """EOC marker is bad"""
relpath = 'input/nonregression/illegalcolortransform.j2k' relpath = 'input/nonregression/illegalcolortransform.j2k'
jfile = opj_data_file(relpath) jfile = opj_data_file(relpath)
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
with warnings.catch_warnings(record=True) as w: with self.assertWarns(UserWarning):
warnings.simplefilter('ignore')
codestream = jp2k.get_codestream(header_only=False) codestream = jp2k.get_codestream(header_only=False)
# Verify that the last segment returned in the codestream is SOD, # Verify that the last segment returned in the codestream is SOD,
@ -119,17 +117,6 @@ class TestSuiteNegative(unittest.TestCase):
with self.assertRaises(IOError): with self.assertRaises(IOError):
j.write(data, cbsize=(2, 2048)) j.write(data, cbsize=(2, 2048))
def test_exceeded_box(self):
"""should warn if reading past end of a box"""
# Verify that a warning is issued if we read past the end of a box
# This file has a palette (pclr) box whose length is impossibly
# short.
infile = os.path.join(OPJ_DATA_ROOT,
'input/nonregression/mem-b2ace68c-1381.jp2')
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter('ignore')
Jp2k(infile)
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.") @unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
def test_precinct_size_not_p2(self): def test_precinct_size_not_p2(self):
"""precinct sizes should be powers of two.""" """precinct sizes should be powers of two."""

View file

@ -1,376 +0,0 @@
"""
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
from glymur import Jp2k
import glymur
from .fixtures import OPJ_DATA_ROOT
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")
class TestSuiteDumpWarnings(unittest.TestCase):
def setUp(self):
pass
def tearDown(self):
pass
def test_NR_broken_jp2_dump(self):
jfile = opj_data_file('input/nonregression/broken.jp2')
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter('ignore')
# colr box has bad length.
jp2 = Jp2k(jfile)
ids = [box.box_id for box in jp2.box]
self.assertEqual(ids, ['jP ', 'ftyp', 'jp2h', 'jp2c'])
ids = [box.box_id for box in jp2.box[2].box]
self.assertEqual(ids, ['ihdr', 'colr'])
# Signature box. Check for corruption.
self.assertEqual(jp2.box[0].signature, (13, 10, 135, 10))
# 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 ')
# 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)
# 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)
# 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)
# 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
self.assertFalse(c.segment[3].scod & 4) # no eph
self.assertEqual(c.segment[3].spcod[0], glymur.core.LRCP)
self.assertEqual(c.segment[3].layers, 1) # layers = 1
self.assertEqual(c.segment[3].spcod[3], 1) # mct
self.assertEqual(c.segment[3].spcod[4], 5) # level
self.assertEqual(tuple(c.segment[3].code_block_size),
(64, 64)) # cblk
# 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)
# QCD: Quantization default
self.assertEqual(c.segment[4].sqcd & 0x1f, 0)
self.assertEqual(c.segment[4].guard_bits, 2)
self.assertEqual(c.segment[4].mantissa, [0] * 16)
self.assertEqual(c.segment[4].exponent,
[8] + [9, 9, 10] * 5)
# QCC: Quantization component
# associated component
self.assertEqual(c.segment[5].cqcc, 1)
self.assertEqual(c.segment[5].guard_bits, 2)
# quantization type
self.assertEqual(c.segment[5].sqcc & 0x1f, 0) # none
self.assertEqual(c.segment[5].mantissa, [0] * 16)
self.assertEqual(c.segment[5].exponent,
[8] + [9, 9, 10] * 5)
# QCC: Quantization component
# associated component
self.assertEqual(c.segment[6].cqcc, 2)
self.assertEqual(c.segment[6].guard_bits, 2)
# quantization type
self.assertEqual(c.segment[6].sqcc & 0x1f, 0) # none
self.assertEqual(c.segment[6].mantissa, [0] * 16)
self.assertEqual(c.segment[6].exponent,
[8] + [9, 9, 10] * 5)
def test_NR_broken2_jp2_dump(self):
# Invalid marker ID on codestream.
jfile = opj_data_file('input/nonregression/broken2.jp2')
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter('ignore')
jp2 = Jp2k(jfile)
self.assertEqual(jp2.box[-1].main_header.segment[-1].marker_id, 'QCC')
@unittest.skipIf(sys.maxsize < 2**32, 'Do not run on 32-bit platforms')
def test_NR_broken3_jp2_dump(self):
"""
NR_broken3_jp2_dump
The file in question here has a colr box with an erroneous box
length of over 1GB. Don't run it on 32-bit platforms.
"""
jfile = opj_data_file('input/nonregression/broken3.jp2')
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter('ignore')
jp2 = Jp2k(jfile)
ids = [box.box_id for box in jp2.box]
self.assertEqual(ids, ['jP ', 'ftyp', 'jp2h', 'jp2c'])
ids = [box.box_id for box in jp2.box[2].box]
self.assertEqual(ids, ['ihdr', 'colr'])
# Signature box. Check for corruption.
self.assertEqual(jp2.box[0].signature, (13, 10, 135, 10))
# 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 ')
# 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)
# 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) # JP2
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)
# 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)
# 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 Vers)on 1.701.0")
# COD: Coding style default
self.assertFalse(c.segment[3].scod & 2) # no sop
self.assertFalse(c.segment[3].scod & 4) # no eph
self.assertEqual(c.segment[3].spcod[0], glymur.core.LRCP)
self.assertEqual(c.segment[3].layers, 1) # layers = 1
self.assertEqual(c.segment[3].spcod[3], 1) # mct
self.assertEqual(c.segment[3].spcod[4], 5) # level
self.assertEqual(tuple(c.segment[3].code_block_size),
(64, 64)) # cblk
# 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)
# QCD: Quantization default
self.assertEqual(c.segment[4].sqcd & 0x1f, 0)
self.assertEqual(c.segment[4].guard_bits, 2)
self.assertEqual(c.segment[4].mantissa, [0] * 16)
self.assertEqual(c.segment[4].exponent,
[8] + [9, 9, 10] * 5)
# QCC: Quantization component
# associated component
self.assertEqual(c.segment[5].cqcc, 1)
self.assertEqual(c.segment[5].guard_bits, 2)
# quantization type
self.assertEqual(c.segment[5].sqcc & 0x1f, 0) # none
self.assertEqual(c.segment[5].mantissa, [0] * 16)
self.assertEqual(c.segment[5].exponent,
[8] + [9, 9, 10] * 5)
# QCC: Quantization component
# associated component
self.assertEqual(c.segment[6].cqcc, 2)
self.assertEqual(c.segment[6].guard_bits, 2)
# quantization type
self.assertEqual(c.segment[6].sqcc & 0x1f, 0) # none
self.assertEqual(c.segment[6].mantissa, [0] * 16)
self.assertEqual(c.segment[6].exponent,
[8] + [9, 9, 10] * 5)
def test_NR_broken4_jp2_dump(self):
# Has an invalid marker in the main header
jfile = opj_data_file('input/nonregression/broken4.jp2')
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter('ignore')
jp2 = Jp2k(jfile)
self.assertEqual(jp2.box[-1].main_header.segment[-1].marker_id, 'QCC')
def test_NR_gdal_fuzzer_assert_in_opj_j2k_read_SQcd_SQcc_patch_jp2(self):
lst = ['input', 'nonregression',
'gdal_fuzzer_assert_in_opj_j2k_read_SQcd_SQcc.patch.jp2']
jfile = opj_data_file('/'.join(lst))
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter('ignore')
Jp2k(jfile)
def test_NR_gdal_fuzzer_check_comp_dx_dy_jp2_dump(self):
lst = ['input', 'nonregression', 'gdal_fuzzer_check_comp_dx_dy.jp2']
jfile = opj_data_file('/'.join(lst))
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter('ignore')
Jp2k(jfile)
def test_NR_gdal_fuzzer_check_number_of_tiles(self):
# Has an impossible tiling setup.
lst = ['input', 'nonregression',
'gdal_fuzzer_check_number_of_tiles.jp2']
jfile = opj_data_file('/'.join(lst))
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter('ignore')
Jp2k(jfile)
def test_NR_gdal_fuzzer_unchecked_numresolutions_dump(self):
# Has an invalid number of resolutions.
lst = ['input', 'nonregression',
'gdal_fuzzer_unchecked_numresolutions.jp2']
jfile = opj_data_file('/'.join(lst))
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter('ignore')
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")
def test_NR_DEC_issue188_beach_64bitsbox_jp2_41_decode(self):
# Has an 'XML ' box instead of 'xml '. Yes that is pedantic, but it
# really does deserve a warning.
relpath = 'input/nonregression/issue188_beach_64bitsbox.jp2'
jfile = opj_data_file(relpath)
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter('ignore')
j = Jp2k(jfile)
d = j.read()
if __name__ == "__main__":
unittest.main()

File diff suppressed because it is too large Load diff

View file

@ -11,10 +11,10 @@
# pylint: disable=R0904 # pylint: disable=R0904
import os import os
import re
import struct import struct
import sys import sys
import tempfile import tempfile
import warnings
import unittest import unittest
if sys.hexversion < 0x03000000: if sys.hexversion < 0x03000000:
@ -30,9 +30,10 @@ else:
import lxml.etree as ET import lxml.etree as ET
import glymur import glymur
from glymur import Jp2k from glymur import Jp2k, command_line
from . import fixtures from . import fixtures
from .fixtures import OPJ_DATA_ROOT, opj_data_file 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 from .fixtures import text_gbr_27, text_gbr_33, text_gbr_34
@ -70,13 +71,14 @@ class TestPrinting(unittest.TestCase):
self.assertTrue(True) self.assertTrue(True)
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_unknown_superbox(self): def test_unknown_superbox(self):
"""Verify that we can handle an unknown superbox.""" """Verify that we can handle an unknown superbox."""
with tempfile.NamedTemporaryFile(suffix='.jpx') as tfile: with tempfile.NamedTemporaryFile(suffix='.jpx') as tfile:
with open(self.jpxfile, 'rb') as ifile: with open(self.jpxfile, 'rb') as ifile:
tfile.write(ifile.read()) tfile.write(ifile.read())
# Add the header for an unknwon superbox. # Add the header for an unknown superbox.
write_buffer = struct.pack('>I4s', 20, 'grp '.encode()) write_buffer = struct.pack('>I4s', 20, 'grp '.encode())
tfile.write(write_buffer) tfile.write(write_buffer)
@ -86,10 +88,8 @@ class TestPrinting(unittest.TestCase):
tfile.write(write_buffer) tfile.write(write_buffer)
tfile.flush() tfile.flush()
with warnings.catch_warnings(record=True) as w: with self.assertWarns(UserWarning):
warnings.simplefilter('always')
jpx = Jp2k(tfile.name) jpx = Jp2k(tfile.name)
self.assertTrue(len(w), 1)
glymur.set_printoptions(short=True) glymur.set_printoptions(short=True)
with patch('sys.stdout', new=StringIO()) as fake_out: with patch('sys.stdout', new=StringIO()) as fake_out:
@ -106,74 +106,6 @@ class TestPrinting(unittest.TestCase):
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
glymur.set_printoptions(hi='low') glymur.set_printoptions(hi='low')
def test_propts_no_codestream_then_no_xml(self):
"""Verify printed output when codestream=False and xml=False, #162"""
# The print options should be persistent across invocations.
glymur.set_printoptions(codestream=False)
glymur.set_printoptions(xml=False)
with patch('sys.stdout', new=StringIO()) as fake_out:
glymur.jp2dump(self.jp2file)
actual = fake_out.getvalue().strip()
# Get rid of the filename line, as it is not set in stone.
lst = actual.split('\n')
lst = lst[1:]
actual = '\n'.join(lst)
self.assertEqual(actual, fixtures.nemo_dump_no_codestream_no_xml)
def test_printopt_no_codestr_or_xml(self):
"""Verify printed output when codestream=False and xml=False"""
glymur.set_printoptions(codestream=False, xml=False)
with patch('sys.stdout', new=StringIO()) as fake_out:
glymur.jp2dump(self.jp2file)
actual = fake_out.getvalue().strip()
# Get rid of the filename line, as it is not set in stone.
lst = actual.split('\n')
lst = lst[1:]
actual = '\n'.join(lst)
self.assertEqual(actual, fixtures.nemo_dump_no_codestream_no_xml)
def test_printoptions_no_codestream(self):
"""Verify printed output when codestream=False"""
glymur.set_printoptions(codestream=False)
with patch('sys.stdout', new=StringIO()) as fake_out:
glymur.jp2dump(self.jp2file)
actual = fake_out.getvalue().strip()
# Get rid of the filename line, as it is not set in stone.
lst = actual.split('\n')
lst = lst[1:]
actual = '\n'.join(lst)
self.assertEqual(actual, fixtures.nemo_dump_no_codestream)
def test_printoptions_no_xml(self):
"""Verify printed output when xml=False"""
glymur.set_printoptions(xml=False)
with patch('sys.stdout', new=StringIO()) as fake_out:
glymur.jp2dump(self.jp2file)
actual = fake_out.getvalue().strip()
# Get rid of the filename line, as it is not set in stone.
lst = actual.split('\n')
lst = lst[1:]
actual = '\n'.join(lst)
expected = fixtures.nemo_dump_no_xml
self.assertEqual(actual, expected)
def test_printoptions_short(self):
"""Verify printed output when short=True"""
glymur.set_printoptions(short=True)
with patch('sys.stdout', new=StringIO()) as fake_out:
glymur.jp2dump(self.jp2file)
actual = fake_out.getvalue().strip()
# Get rid of the filename line, as it is not set in stone.
lst = actual.split('\n')
lst = lst[1:]
actual = '\n'.join(lst)
self.assertEqual(actual, fixtures.nemo_dump_short)
def test_asoc_label_box(self): def test_asoc_label_box(self):
"""verify printing of asoc, label boxes""" """verify printing of asoc, label boxes"""
# Construct a fake file with an asoc and a label box, as # Construct a fake file with an asoc and a label box, as
@ -227,32 +159,6 @@ class TestPrinting(unittest.TestCase):
expected = '\n'.join(lines) expected = '\n'.join(lines)
self.assertEqual(actual, expected) self.assertEqual(actual, expected)
def test_jp2dump(self):
"""basic jp2dump test"""
with patch('sys.stdout', new=StringIO()) as fake_out:
glymur.jp2dump(self.jp2file)
actual = fake_out.getvalue().strip()
# Get rid of the filename line, as it is not set in stone.
lst = actual.split('\n')
lst = lst[1:]
actual = '\n'.join(lst)
self.assertEqual(actual, fixtures.nemo_dump_full)
def test_entire_file(self):
"""verify output from printing entire file"""
j = glymur.Jp2k(self.jp2file)
with patch('sys.stdout', new=StringIO()) as fake_out:
print(j)
actual = fake_out.getvalue().strip()
# Get rid of the filename line, as it is not set in stone.
lst = actual.split('\n')
lst = lst[1:]
actual = '\n'.join(lst)
self.assertEqual(actual, fixtures.nemo_dump_full)
def test_coc_segment(self): def test_coc_segment(self):
"""verify printing of COC segment""" """verify printing of COC segment"""
j = glymur.Jp2k(self.jp2file) j = glymur.Jp2k(self.jp2file)
@ -725,8 +631,6 @@ class TestPrintingOpjDataRoot(unittest.TestCase):
# Reset printoptions for every test. # Reset printoptions for every test.
glymur.set_printoptions(short=False, xml=True, codestream=True) glymur.set_printoptions(short=False, xml=True, codestream=True)
warnings.resetwarnings()
def tearDown(self): def tearDown(self):
pass pass
@ -740,45 +644,6 @@ class TestPrintingOpjDataRoot(unittest.TestCase):
actual = fake_out.getvalue().strip() actual = fake_out.getvalue().strip()
self.assertEqual(actual, fixtures.cinema2k_profile) self.assertEqual(actual, fixtures.cinema2k_profile)
def test_invalid_colorspace(self):
"""An invalid colorspace shouldn't cause an error."""
filename = opj_data_file('input/nonregression/edf_c2_1103421.jp2')
with warnings.catch_warnings():
warnings.simplefilter("ignore")
jp2 = Jp2k(filename)
with patch('sys.stdout', new=StringIO()) as fake_out:
print(jp2)
def test_bad_rsiz(self):
"""Should still be able to print if rsiz is bad, issue196"""
filename = opj_data_file('input/nonregression/edf_c2_1002767.jp2')
with warnings.catch_warnings():
warnings.simplefilter("ignore")
j = Jp2k(filename)
with patch('sys.stdout', new=StringIO()) as fake_out:
print(j)
def test_bad_wavelet_transform(self):
"""Should still be able to print if wavelet xform is bad, issue195"""
filename = opj_data_file('input/nonregression/edf_c2_10025.jp2')
with warnings.catch_warnings():
warnings.simplefilter("ignore")
j = Jp2k(filename)
with patch('sys.stdout', new=StringIO()) as fake_out:
print(j)
def test_invalid_progression_order(self):
"""Should still be able to print even if prog order is invalid."""
jfile = opj_data_file('input/nonregression/2977.pdf.asan.67.2198.jp2')
with warnings.catch_warnings():
warnings.simplefilter("ignore")
jp2 = Jp2k(jfile)
codestream = jp2.get_codestream()
with patch('sys.stdout', new=StringIO()) as fake_out:
print(codestream.segment[2])
actual = fake_out.getvalue().strip()
self.assertEqual(actual, fixtures.issue_186_progression_order)
def test_crg(self): def test_crg(self):
"""verify printing of CRG segment""" """verify printing of CRG segment"""
filename = opj_data_file('input/conformance/p0_03.j2k') filename = opj_data_file('input/conformance/p0_03.j2k')
@ -928,46 +793,6 @@ class TestPrintingOpjDataRoot(unittest.TestCase):
expected = '\n'.join(lines) expected = '\n'.join(lines)
self.assertEqual(actual, expected) self.assertEqual(actual, expected)
def test_xml(self):
"""verify printing of XML box"""
filename = opj_data_file('input/conformance/file1.jp2')
j = glymur.Jp2k(filename)
with patch('sys.stdout', new=StringIO()) as fake_out:
print(j.box[2])
actual = fake_out.getvalue().strip()
self.assertEqual(actual, fixtures.file1_xml)
def test_channel_definition(self):
"""verify printing of cdef box"""
filename = opj_data_file('input/conformance/file2.jp2')
with warnings.catch_warnings():
# Bad compatibility list item.
warnings.simplefilter("ignore")
j = glymur.Jp2k(filename)
with patch('sys.stdout', new=StringIO()) as fake_out:
print(j.box[2].box[2])
actual = fake_out.getvalue().strip()
lines = ['Channel Definition Box (cdef) @ (81, 28)',
' Channel 0 (color) ==> (3)',
' Channel 1 (color) ==> (2)',
' Channel 2 (color) ==> (1)']
expected = '\n'.join(lines)
self.assertEqual(actual, expected)
def test_component_mapping(self):
"""verify printing of cmap box"""
filename = opj_data_file('input/conformance/file9.jp2')
j = glymur.Jp2k(filename)
with patch('sys.stdout', new=StringIO()) as fake_out:
print(j.box[2].box[2])
actual = fake_out.getvalue().strip()
lines = ['Component Mapping Box (cmap) @ (848, 20)',
' Component 0 ==> palette column 0',
' Component 0 ==> palette column 1',
' Component 0 ==> palette column 2']
expected = '\n'.join(lines)
self.assertEqual(actual, expected)
def test_componentmapping_box_alpha(self): def test_componentmapping_box_alpha(self):
"""Verify __repr__ method on cmap box.""" """Verify __repr__ method on cmap box."""
cmap = glymur.jp2box.ComponentMappingBox(component_index=(0, 0, 0), cmap = glymur.jp2box.ComponentMappingBox(component_index=(0, 0, 0),
@ -979,27 +804,6 @@ class TestPrintingOpjDataRoot(unittest.TestCase):
self.assertEqual(newbox.mapping_type, (1, 1, 1)) self.assertEqual(newbox.mapping_type, (1, 1, 1))
self.assertEqual(newbox.palette_index, (0, 1, 2)) self.assertEqual(newbox.palette_index, (0, 1, 2))
def test_palette7(self):
"""verify printing of pclr box"""
filename = opj_data_file('input/conformance/file9.jp2')
j = glymur.Jp2k(filename)
with patch('sys.stdout', new=StringIO()) as fake_out:
print(j.box[2].box[1])
actual = fake_out.getvalue().strip()
lines = ['Palette Box (pclr) @ (66, 782)',
' Size: (256 x 3)']
expected = '\n'.join(lines)
self.assertEqual(actual, expected)
def test_rreq(self):
"""verify printing of reader requirements box"""
filename = opj_data_file('input/nonregression/text_GBR.jp2')
j = glymur.Jp2k(filename)
with patch('sys.stdout', new=StringIO()) as fake_out:
print(j.box[2])
actual = fake_out.getvalue().strip()
self.assertEqual(actual, fixtures.text_GBR_rreq)
def test_differing_subsamples(self): def test_differing_subsamples(self):
"""verify printing of SIZ with different subsampling... Issue 86.""" """verify printing of SIZ with different subsampling... Issue 86."""
filename = opj_data_file('input/conformance/p0_05.j2k') filename = opj_data_file('input/conformance/p0_05.j2k')
@ -1021,10 +825,132 @@ class TestPrintingOpjDataRoot(unittest.TestCase):
expected = '\n'.join(lines) expected = '\n'.join(lines)
self.assertEqual(actual, expected) self.assertEqual(actual, expected)
@unittest.skipIf(OPJ_DATA_ROOT is None,
"OPJ_DATA_ROOT environment variable not set")
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
class TestPrintingOpjDataRootWarns(unittest.TestCase):
"""
Tests for verifying printing. restricted to OPJ_DATA_ROOT files.
These tests issue warnings.
"""
def setUp(self):
self.jpxfile = glymur.data.jpxfile()
self.jp2file = glymur.data.nemo()
self.j2kfile = glymur.data.goodstuff()
# Reset printoptions for every test.
glymur.set_printoptions(short=False, xml=True, codestream=True)
def tearDown(self):
pass
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()) as fake_out:
print(jp2)
def test_bad_rsiz(self):
"""Should still be able to print if rsiz is bad, issue196"""
filename = opj_data_file('input/nonregression/edf_c2_1002767.jp2')
with self.assertWarns(UserWarning):
j = Jp2k(filename)
with patch('sys.stdout', new=StringIO()) as fake_out:
print(j)
def test_bad_wavelet_transform(self):
"""Should still be able to print if wavelet xform is bad, issue195"""
filename = opj_data_file('input/nonregression/edf_c2_10025.jp2')
with self.assertWarns(UserWarning):
jp2 = Jp2k(filename)
with patch('sys.stdout', new=StringIO()) as fake_out:
print(jp2)
def test_invalid_progression_order(self):
"""Should still be able to print even if prog order is invalid."""
jfile = opj_data_file('input/nonregression/2977.pdf.asan.67.2198.jp2')
with self.assertWarns(UserWarning):
# Multiple warnings, actually.
jp2 = Jp2k(jfile)
codestream = jp2.get_codestream()
with patch('sys.stdout', new=StringIO()) as fake_out:
print(codestream.segment[2])
actual = fake_out.getvalue().strip()
self.assertEqual(actual, fixtures.issue_186_progression_order)
def test_xml(self):
"""verify printing of XML box"""
filename = opj_data_file('input/conformance/file1.jp2')
with self.assertWarns(UserWarning):
j = glymur.Jp2k(filename)
with patch('sys.stdout', new=StringIO()) as fake_out:
print(j.box[2])
actual = fake_out.getvalue().strip()
self.assertEqual(actual, fixtures.file1_xml)
def test_channel_definition(self):
"""verify printing of cdef box"""
filename = opj_data_file('input/conformance/file2.jp2')
with self.assertWarns(UserWarning):
# Bad compatibility list item.
j = glymur.Jp2k(filename)
with patch('sys.stdout', new=StringIO()) as fake_out:
print(j.box[2].box[2])
actual = fake_out.getvalue().strip()
lines = ['Channel Definition Box (cdef) @ (81, 28)',
' Channel 0 (color) ==> (3)',
' Channel 1 (color) ==> (2)',
' Channel 2 (color) ==> (1)']
expected = '\n'.join(lines)
self.assertEqual(actual, expected)
def test_component_mapping(self):
"""verify printing of cmap box"""
filename = opj_data_file('input/conformance/file9.jp2')
with self.assertWarns(UserWarning):
j = glymur.Jp2k(filename)
with patch('sys.stdout', new=StringIO()) as fake_out:
print(j.box[2].box[2])
actual = fake_out.getvalue().strip()
lines = ['Component Mapping Box (cmap) @ (848, 20)',
' Component 0 ==> palette column 0',
' Component 0 ==> palette column 1',
' Component 0 ==> palette column 2']
expected = '\n'.join(lines)
self.assertEqual(actual, expected)
def test_palette7(self):
"""verify printing of pclr box"""
filename = opj_data_file('input/conformance/file9.jp2')
with self.assertWarns(UserWarning):
j = glymur.Jp2k(filename)
with patch('sys.stdout', new=StringIO()) as fake_out:
print(j.box[2].box[1])
actual = fake_out.getvalue().strip()
lines = ['Palette Box (pclr) @ (66, 782)',
' Size: (256 x 3)']
expected = '\n'.join(lines)
self.assertEqual(actual, expected)
def test_rreq(self):
"""verify printing of reader requirements box"""
filename = opj_data_file('input/nonregression/text_GBR.jp2')
with self.assertWarns(UserWarning):
j = glymur.Jp2k(filename)
with patch('sys.stdout', new=StringIO()) as fake_out:
print(j.box[2])
actual = fake_out.getvalue().strip()
self.assertEqual(actual, fixtures.text_GBR_rreq)
def test_palette_box(self): def test_palette_box(self):
"""Verify that palette (pclr) boxes are printed without error.""" """Verify that palette (pclr) boxes are printed without error."""
filename = opj_data_file('input/conformance/file9.jp2') filename = opj_data_file('input/conformance/file9.jp2')
j = glymur.Jp2k(filename) with self.assertWarns(UserWarning):
j = glymur.Jp2k(filename)
with patch('sys.stdout', new=StringIO()) as fake_out: with patch('sys.stdout', new=StringIO()) as fake_out:
print(j.box[2].box[1]) print(j.box[2].box[1])
actual = fake_out.getvalue().strip() actual = fake_out.getvalue().strip()
@ -1038,9 +964,8 @@ class TestPrintingOpjDataRoot(unittest.TestCase):
# ICC profiles may be used in JP2, but the approximation field should # ICC profiles may be used in JP2, but the approximation field should
# be zero unless we have jpx. This file does both. # be zero unless we have jpx. This file does both.
filename = opj_data_file('input/nonregression/text_GBR.jp2') filename = opj_data_file('input/nonregression/text_GBR.jp2')
with warnings.catch_warnings(): with self.assertWarns(UserWarning):
# brand is 'jp2 ', but has any icc profile. # brand is 'jp2 ', but has any icc profile.
warnings.simplefilter("ignore")
jp2 = Jp2k(filename) jp2 = Jp2k(filename)
with patch('sys.stdout', new=StringIO()) as fake_out: with patch('sys.stdout', new=StringIO()) as fake_out:
@ -1058,9 +983,7 @@ class TestPrintingOpjDataRoot(unittest.TestCase):
def test_uuid(self): def test_uuid(self):
"""verify printing of UUID box""" """verify printing of UUID box"""
filename = opj_data_file('input/nonregression/text_GBR.jp2') filename = opj_data_file('input/nonregression/text_GBR.jp2')
with warnings.catch_warnings(): with self.assertWarns(UserWarning):
# brand is 'jp2 ', but has any icc profile.
warnings.simplefilter("ignore")
jp2 = Jp2k(filename) jp2 = Jp2k(filename)
with patch('sys.stdout', new=StringIO()) as fake_out: with patch('sys.stdout', new=StringIO()) as fake_out:
@ -1079,9 +1002,8 @@ class TestPrintingOpjDataRoot(unittest.TestCase):
# Format strings like %d were showing up in the output. # Format strings like %d were showing up in the output.
filename = opj_data_file('input/nonregression/mem-b2ace68c-1381.jp2') filename = opj_data_file('input/nonregression/mem-b2ace68c-1381.jp2')
with warnings.catch_warnings(): with self.assertWarns(UserWarning):
# Ignore warning about bad pclr box. # Ignore warning about bad pclr box.
warnings.simplefilter("ignore")
jp2 = Jp2k(filename) jp2 = Jp2k(filename)
with patch('sys.stdout', new=StringIO()) as fake_out: with patch('sys.stdout', new=StringIO()) as fake_out:
print(jp2.box[3].box[3]) print(jp2.box[3].box[3])
@ -1091,9 +1013,8 @@ class TestPrintingOpjDataRoot(unittest.TestCase):
def test_issue183(self): def test_issue183(self):
filename = opj_data_file('input/nonregression/orb-blue10-lin-jp2.jp2') filename = opj_data_file('input/nonregression/orb-blue10-lin-jp2.jp2')
with warnings.catch_warnings(): with self.assertWarns(UserWarning):
# Ignore warning about bad pclr box. # Ignore warning about bad pclr box.
warnings.simplefilter("ignore")
jp2 = Jp2k(filename) jp2 = Jp2k(filename)
with patch('sys.stdout', new=StringIO()) as fake_out: with patch('sys.stdout', new=StringIO()) as fake_out:
print(jp2.box[2].box[1]) print(jp2.box[2].box[1])
@ -1105,8 +1026,7 @@ class TestPrintingOpjDataRoot(unittest.TestCase):
filename = opj_data_file(os.path.join('input', filename = opj_data_file(os.path.join('input',
'nonregression', 'nonregression',
'issue171.jp2')) 'issue171.jp2'))
with warnings.catch_warnings(): with self.assertWarns(UserWarning):
warnings.simplefilter("ignore")
jp2 = Jp2k(filename) jp2 = Jp2k(filename)
with patch('sys.stdout', new=StringIO()) as fake_out: with patch('sys.stdout', new=StringIO()) as fake_out:
# No need to verify, it's enough that we don't error out. # No need to verify, it's enough that we don't error out.
@ -1114,5 +1034,71 @@ class TestPrintingOpjDataRoot(unittest.TestCase):
self.assertTrue(True) self.assertTrue(True)
if __name__ == "__main__":
unittest.main() class TestJp2dump(unittest.TestCase):
"""Tests for verifying how jp2dump console script works."""
def setUp(self):
self.jpxfile = glymur.data.jpxfile()
self.jp2file = glymur.data.nemo()
self.j2kfile = glymur.data.goodstuff()
# Reset printoptions for every test.
glymur.set_printoptions(short=False, xml=True, codestream=True)
def tearDown(self):
pass
def run_jp2dump(self, args):
sys.argv = args
with patch('sys.stdout', new=StringIO()) as fake_out:
command_line.main()
actual = fake_out.getvalue().strip()
# Remove the file line, as that is filesystem-dependent.
lines = actual.split('\n')
actual = '\n'.join(lines[1:])
return actual
def test_default_nemo(self):
"""Should be able to dump a JP2 file's metadata with no codestream."""
actual = self.run_jp2dump(['', self.jp2file])
self.assertEqual(actual, fixtures.nemo_dump_no_codestream)
def test_codestream_0(self):
"""Verify dumping with -c 0, supressing all codestream details."""
actual = self.run_jp2dump(['', '-c', '0', self.jp2file])
self.assertEqual(actual, fixtures.nemo_dump_no_codestream)
def test_codestream_1(self):
"""Verify dumping with -c 1, print just the header."""
actual = self.run_jp2dump(['', '-c', '1', self.jp2file])
self.assertEqual(actual, fixtures.nemo_with_codestream_header)
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]
command_line.main()
actual = fake_out.getvalue().strip()
self.assertIn(fixtures.goodstuff_with_full_header, actual)
def test_codestream_invalid(self):
"""Verify dumping with -c 3, not allowd."""
with self.assertRaises(ValueError):
sys.argv = ['', '-c', '3', self.jp2file]
command_line.main()
def test_short(self):
"""Verify dumping with -s, short option."""
actual = self.run_jp2dump(['', '-s', self.jp2file])
self.assertEqual(actual, fixtures.nemo_dump_short)
def test_suppress_xml(self):
"""Verify dumping with -x, suppress XML."""
actual = self.run_jp2dump(['', '-x', self.jp2file])
self.assertEqual(actual, fixtures.nemo_dump_no_codestream_no_xml)

View file

@ -19,7 +19,11 @@ from .lib import openjp2 as opj2
# Do not change the format of this next line! Doing so risks breaking # Do not change the format of this next line! Doing so risks breaking
# setup.py # setup.py
<<<<<<< HEAD
version = "0.6.1" version = "0.6.1"
=======
version = "0.7.0"
>>>>>>> release-0.7.0rc1
_sv = LooseVersion(version) _sv = LooseVersion(version)
version_tuple = _sv.version version_tuple = _sv.version

View file

@ -12,7 +12,9 @@ kwargs = {'name': 'Glymur',
'packages': ['glymur', 'glymur.data', 'glymur.test', 'glymur.lib', 'packages': ['glymur', 'glymur.data', 'glymur.test', 'glymur.lib',
'glymur.lib.test'], 'glymur.lib.test'],
'package_data': {'glymur': ['data/*.jp2', 'data/*.j2k', 'data/*.jpx']}, 'package_data': {'glymur': ['data/*.jp2', 'data/*.j2k', 'data/*.jpx']},
'scripts': ['bin/jp2dump'], 'entry_points': {
'console_scripts': ['jp2dump=glymur.command_line:main'],
},
'license': 'MIT', 'license': 'MIT',
'test_suite': 'glymur.test'} 'test_suite': 'glymur.test'}