merge branch 'devel' of github.com:quintusdias/glymur into devel

This commit is contained in:
John Evans 2014-11-23 22:22:18 -05:00
commit 39cc8a845a
15 changed files with 302 additions and 278 deletions

View file

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

View file

@ -24,11 +24,10 @@ import warnings
import numpy as np
from .core import (
LRCP, RLCP, RPCL, PCRL, CPRL,
WAVELET_XFORM_9X7_IRREVERSIBLE, WAVELET_XFORM_5X3_REVERSIBLE,
_Keydefaultdict
)
from .core import (LRCP, RLCP, RPCL, PCRL, CPRL,
WAVELET_XFORM_9X7_IRREVERSIBLE,
WAVELET_XFORM_5X3_REVERSIBLE,
_Keydefaultdict)
from .lib import openjp2 as opj2
_factory = lambda x: '{0} (invalid)'.format(x)
@ -57,7 +56,7 @@ _CAPABILITIES_DISPLAY = _Keydefaultdict(_factory,
_PROFILE_0: '0',
_PROFILE_1: '1',
_PROFILE_3: 'Cinema 2K',
_PROFILE_4: 'Cinema 4K'} )
_PROFILE_4: 'Cinema 4K'})
# Need a catch-all list of valid markers.
# See table A-1 in ISO/IEC FCD15444-1.
@ -703,7 +702,6 @@ class Codestream(object):
msg = "Invalid number of tiles ({0}).".format(numtiles)
warnings.warn(msg)
kwargs = {'rsiz': rsiz,
'xysiz': xysiz,
'xyosiz': xyosiz,
@ -1614,6 +1612,7 @@ class SOCsegment(Segment):
msg = "glymur.codestream.SOCsegment()"
return msg
class SODsegment(Segment):
"""Container for Start of Data (SOD) segment information.

View file

@ -8,28 +8,29 @@ import warnings
from . import Jp2k, set_printoptions, lib
def main():
"""
Entry point for console script jp2dump.
"""
description='Print JPEG2000 metadata.'
description = 'Print JPEG2000 metadata.'
parser = argparse.ArgumentParser(description=description)
parser.add_argument('-x', '--noxml',
help='Suppress XML.',
action='store_true')
help='Suppress XML.',
action='store_true')
parser.add_argument('-s', '--short',
help='Only print box id, offset, and length.',
action='store_true')
help='Only print box id, offset, and length.',
action='store_true')
chelp = 'Level of codestream information. 0 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])
help=chelp,
nargs=1,
type=int,
default=[0])
parser.add_argument('filename')
@ -38,7 +39,7 @@ def main():
set_printoptions(xml=False)
if args.short:
set_printoptions(short=True)
codestream_level = args.codestream[0]
if codestream_level not in [0, 1, 2]:
raise ValueError("Invalid level of codestream information specified.")
@ -50,15 +51,16 @@ def main():
print_full_codestream = False
else:
print_full_codestream = True
filename = args.filename
with warnings.catch_warnings(record=True) as wctx:
# JP2 metadata can be extensive, so don't print any warnings until we
# are done with the metadata.
jp2 = Jp2k(filename)
if jp2._codec_format == lib.openjp2.CODEC_J2K and codestream_level == 0:
if (((jp2._codec_format == lib.openjp2.CODEC_J2K) and
(codestream_level == 0))):
print('File: {0}'.format(os.path.basename(filename)))
elif print_full_codestream:
print(jp2.get_codestream(header_only=False))

View file

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

View file

@ -29,13 +29,11 @@ import lxml.etree as ET
import numpy as np
from .codestream import Codestream
from .core import (
_COLORSPACE_MAP_DISPLAY, _COLOR_TYPE_MAP_DISPLAY,
SRGB, GREYSCALE, YCC,
ENUMERATED_COLORSPACE, RESTRICTED_ICC_PROFILE,
ANY_ICC_PROFILE, VENDOR_COLOR_METHOD,
_Keydefaultdict
)
from .core import (_COLORSPACE_MAP_DISPLAY, _COLOR_TYPE_MAP_DISPLAY,
SRGB, GREYSCALE, YCC,
ENUMERATED_COLORSPACE, RESTRICTED_ICC_PROFILE,
ANY_ICC_PROFILE, VENDOR_COLOR_METHOD,
_Keydefaultdict)
from . import _uuid_io
@ -52,6 +50,7 @@ _APPROX_DISPLAY = _Keydefaultdict(_factory,
3: 'approximates correct colorspace definition, reasonable quality',
4: 'approximates correct colorspace definition, poor quality'})
class Jp2kBox(object):
"""Superclass for JPEG 2000 boxes.
@ -109,7 +108,6 @@ class Jp2kBox(object):
msg += '\n' + self._indent(boxstr)
return msg
def _indent(self, textstr, indent_level=4):
"""
Indent a string.
@ -135,7 +133,6 @@ class Jp2kBox(object):
lst = [(' ' * indent_level + x) for x in textstr.split('\n')]
return '\n'.join(lst)
def _write_superbox(self, fptr, box_id):
"""Write a superbox.
@ -191,13 +188,14 @@ class Jp2kBox(object):
try:
box = parser(fptr, start, num_bytes)
except ValueError as err:
msg = "Encountered an unrecoverable ValueError while parsing a {0} "
msg += "box at byte offset {1}. The original error message was "
msg += "\"{2}\""
msg = "Encountered an unrecoverable ValueError while parsing a "
msg += "{0} box at byte offset {1}. The original error message "
msg += "was \"{2}\""
msg = msg.format(_BOX_WITH_ID[box_id].longname, start, str(err))
warnings.warn(msg, UserWarning)
box = UnknownBox(box_id.decode('utf-8'),
length=num_bytes, offset=start, longname='Unknown')
length=num_bytes,
offset=start, longname='Unknown')
return box
@ -299,6 +297,7 @@ class ColourSpecificationBox(Jp2kBox):
"""
longname = 'Colour Specification'
box_id = 'colr'
def __init__(self, method=ENUMERATED_COLORSPACE, precedence=0,
approximation=0, colorspace=None, icc_profile=None,
length=0, offset=-1):
@ -337,16 +336,16 @@ class ColourSpecificationBox(Jp2kBox):
if self.icc_profile is None:
if self.colorspace not in [SRGB, GREYSCALE, YCC]:
msg = "Colorspace should correspond to one of SRGB, GREYSCALE, "
msg += "or YCC."
msg = "Colorspace should correspond to one of SRGB, "
msg += "GREYSCALE, or YCC."
self._dispatch_validation_error(msg, writing=True)
self._validate(writing=True)
def __repr__(self):
msg = "glymur.jp2box.ColourSpecificationBox("
msg += "method={0}, precedence={1}, approximation={2}, colorspace={3}, "
msg += "method={0}, precedence={1}, approximation={2}, "
msg += "colorspace={3}, "
msg += "icc_profile={4})"
msg = msg.format(self.method,
self.precedence,
@ -357,7 +356,7 @@ class ColourSpecificationBox(Jp2kBox):
def __str__(self):
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
if _printoptions['short'] is True:
return msg
msg += '\n Method: {0}'.format(_METHOD_DISPLAY[self.method])
@ -619,10 +618,9 @@ class ChannelDefinitionBox(Jp2kBox):
msg += " 65535 - unspecified"
self._dispatch_validation_error(msg, writing=writing)
def __str__(self):
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
if _printoptions['short'] is True:
return msg
for j in range(len(self.association)):
@ -842,7 +840,7 @@ class CompositingLayerHeaderBox(Jp2kBox):
List of boxes contained in this superbox.
"""
box_id = 'jplh'
longname='Compositing Layer Header'
longname = 'Compositing Layer Header'
def __init__(self, box=None, length=0, offset=-1):
Jp2kBox.__init__(self)
@ -931,7 +929,7 @@ class ComponentMappingBox(Jp2kBox):
def __str__(self):
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
if _printoptions['short'] is True:
return msg
for k in range(len(self.component_index)):
@ -1027,7 +1025,9 @@ class ContiguousCodestreamBox(Jp2kBox):
if self._filename is not None:
with open(self._filename, 'rb') as fptr:
fptr.seek(self.main_header_offset)
main_header = Codestream(fptr, self._length, header_only=True)
main_header = Codestream(fptr,
self._length,
header_only=True)
self._main_header = main_header
return self._main_header
@ -1037,9 +1037,9 @@ class ContiguousCodestreamBox(Jp2kBox):
def __str__(self):
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
if _printoptions['short'] is True:
return msg
if _printoptions['codestream'] == False:
if _printoptions['codestream'] is False:
return msg
msg += '\n Main header:'
@ -1118,7 +1118,8 @@ class DataReferenceBox(Jp2kBox):
"""Verify that the box obeys the specifications for writing.
"""
if len(self.DR) == 0:
msg = "A data reference box cannot be empty when written to a file."
msg = "A data reference box cannot be empty when written to a "
msg += "file."
self._dispatch_validation_error(msg, writing=True)
self._validate(writing=True)
@ -1145,7 +1146,7 @@ class DataReferenceBox(Jp2kBox):
def __str__(self):
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
if _printoptions['short'] is True:
return msg
for box in self.DR:
@ -1248,7 +1249,7 @@ class FileTypeBox(Jp2kBox):
def __str__(self):
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
if _printoptions['short'] is True:
return msg
lst = [msg,
@ -1311,7 +1312,7 @@ class FileTypeBox(Jp2kBox):
brand = brand.decode('utf-8')
# Extract the compatibility list. Each entry has 4 bytes.
num_entries = int((length - 16)/ 4)
num_entries = int((length - 16) / 4)
compatibility_list = []
for j in range(int(num_entries)):
entry, = struct.unpack_from('>4s', read_buffer, 8 + j * 4)
@ -1374,7 +1375,7 @@ class FragmentListBox(Jp2kBox):
def __str__(self):
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
if _printoptions['short'] is True:
return msg
for j in range(len(self.fragment_offset)):
@ -1458,7 +1459,10 @@ class FragmentTableBox(Jp2kBox):
def __repr__(self):
msg = "glymur.jp2box.FragmentTableBox(box={0})"
msg = msg.format(None) if (len(self.box) == 0) else msg.format(self.box)
if len(self.box) == 0:
msg = msg.format(None)
else:
msg = msg.format(self.box)
return msg
def __str__(self):
@ -1505,7 +1509,6 @@ class FragmentTableBox(Jp2kBox):
self._write_superbox(fptr, b'ftbl')
class FreeBox(Jp2kBox):
"""Container for JPX free box information.
@ -1534,7 +1537,7 @@ class FreeBox(Jp2kBox):
def __str__(self):
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
if _printoptions['short'] is True:
return msg
return msg
@ -1630,7 +1633,7 @@ class ImageHeaderBox(Jp2kBox):
def __str__(self):
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
if _printoptions['short'] is True:
return msg
msg = "{0}"
@ -1861,7 +1864,7 @@ class JPEG2000SignatureBox(Jp2kBox):
def __str__(self):
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
if _printoptions['short'] is True:
return msg
msg += '\n Signature: {0:02x}{1:02x}{2:02x}{3:02x}'
@ -1950,7 +1953,7 @@ class PaletteBox(Jp2kBox):
def __str__(self):
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
if _printoptions['short'] is True:
return msg
msg += '\n Size: ({0} x {1})'.format(*self.palette.shape)
@ -2203,7 +2206,7 @@ class ReaderRequirementsBox(Jp2kBox):
def __str__(self):
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
if _printoptions['short'] is True:
return msg
msg += '\n Fully Understands Aspect Mask: 0x{0:x}'.format(self.fuam)
@ -2262,7 +2265,8 @@ class ReaderRequirementsBox(Jp2kBox):
standard_flag, standard_mask = data
nflags = len(standard_flag)
vendor_offset = 1 + 2 * mask_length + 2 + (2 + mask_length) * nflags
vendor_offset = 1 + 2 * mask_length + 2 \
+ (2 + mask_length) * nflags
data = _parse_vendor_features(read_buffer[vendor_offset:],
mask_length)
vendor_feature, vendor_mask = data
@ -2348,14 +2352,11 @@ def _parse_standard_flag(read_buffer, mask_length):
# from the buffer read from file.
mask_format = {1: 'B', 2: 'H', 4: 'I'}[mask_length]
#read_buffer = fptr.read(2)
num_standard_flags, = struct.unpack_from('>H', read_buffer, offset=0)
# Read in standard flags and standard masks. Each standard flag should
# be two bytes, but the standard mask flag is as long as specified by
# the mask length.
#read_buffer = fptr.read(num_standard_flags * (2 + mask_length))
fmt = '>' + ('H' + mask_format) * num_standard_flags
data = struct.unpack_from(fmt, read_buffer, offset=2)
@ -2386,7 +2387,6 @@ def _parse_vendor_features(read_buffer, mask_length):
# Each vendor feature consists of a 16-byte UUID plus a mask whose
# length is specified by, you guessed it, "mask_length".
entry_length = 16 + mask_length
#read_buffer = fptr.read(num_vendor_features * entry_length)
vendor_feature = []
vendor_mask = []
for j in range(num_vendor_features):
@ -2494,7 +2494,7 @@ class CaptureResolutionBox(Jp2kBox):
def __str__(self):
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
if _printoptions['short'] is True:
return msg
msg += '\n VCR: {0}'.format(self.vertical_resolution)
@ -2560,7 +2560,7 @@ class DisplayResolutionBox(Jp2kBox):
def __str__(self):
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
if _printoptions['short'] is True:
return msg
msg += '\n VDR: {0}'.format(self.vertical_resolution)
@ -2620,7 +2620,7 @@ class LabelBox(Jp2kBox):
def __str__(self):
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
if _printoptions['short'] is True:
return msg
msg += '\n Label: {0}'.format(self.label)
@ -2688,7 +2688,7 @@ class NumberListBox(Jp2kBox):
def __str__(self):
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
if _printoptions['short'] is True:
return msg
for j, association in enumerate(self.associations):
@ -2738,7 +2738,8 @@ class NumberListBox(Jp2kBox):
def write(self, fptr):
"""Write a NumberList box to file.
"""
fptr.write(struct.pack('>I4s', len(self.associations) * 4 + 8, b'nlst'))
fptr.write(struct.pack('>I4s',
len(self.associations) * 4 + 8, b'nlst'))
fmt = '>' + 'I' * len(self.associations)
write_buffer = struct.pack(fmt, *self.associations)
@ -2790,9 +2791,9 @@ class XMLBox(Jp2kBox):
def __str__(self):
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
if _printoptions['short'] is True:
return msg
if _printoptions['xml'] == False:
if _printoptions['xml'] is False:
return msg
msg += '\n'
@ -2911,7 +2912,7 @@ class UUIDListBox(Jp2kBox):
def __str__(self):
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
if _printoptions['short'] is True:
return msg
for j, uuid_item in enumerate(self.ulst):
@ -2942,7 +2943,7 @@ class UUIDListBox(Jp2kBox):
ulst = []
for j in range(num_uuids):
uuid_buffer = read_buffer[2 + j * 16 : 2 + (j + 1) * 16]
uuid_buffer = read_buffer[2 + j * 16:2 + (j + 1) * 16]
ulst.append(uuid.UUID(bytes=uuid_buffer))
return cls(ulst, length=length, offset=offset)
@ -3056,7 +3057,6 @@ class DataEntryURLBox(Jp2kBox):
fptr.write(write_buffer)
fptr.write(url)
def __repr__(self):
msg = "glymur.jp2box.DataEntryURLBox({0}, {1}, '{2}')"
msg = msg.format(self.version, self.flag, self.url)
@ -3064,7 +3064,7 @@ class DataEntryURLBox(Jp2kBox):
def __str__(self):
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
if _printoptions['short'] is True:
return msg
msg += '\n '
@ -3216,7 +3216,7 @@ class UUIDBox(Jp2kBox):
def __str__(self):
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
if _printoptions['short'] is True:
return msg
msg = '{0}\n UUID: {1}'.format(msg, self.uuid)
@ -3227,7 +3227,7 @@ class UUIDBox(Jp2kBox):
else:
msg += ' (unknown)'
if (((_printoptions['xml'] == False) and
if (((_printoptions['xml'] is False) and
(self.uuid == uuid.UUID('be7acfcb-97a9-42e8-9c71-999491e3afac')))):
# If it's an XMP UUID, don't print the XML contents.
return msg
@ -3312,6 +3312,7 @@ _BOX_WITH_ID = {
_parseoptions = {'codestream': True}
def set_parseoptions(codestream=True):
"""Set parsing options.
@ -3336,6 +3337,7 @@ def set_parseoptions(codestream=True):
"""
_parseoptions['codestream'] = codestream
def get_parseoptions():
"""Return the current parsing options.
@ -3356,6 +3358,7 @@ def get_parseoptions():
_printoptions = {'short': False, 'xml': True, 'codestream': True}
def set_printoptions(**kwargs):
"""Set printing options.
@ -3365,7 +3368,8 @@ def set_printoptions(**kwargs):
----------
short : bool, optional
When True, only the box ID, offset, and length are displayed. Useful
for displaying only the basic structure or skeleton of a JPEG 2000 file.
for displaying only the basic structure or skeleton of a JPEG 2000
file.
xml : bool, optional
When False, printing of the XML contents of any XML boxes or UUID XMP
boxes is suppressed.
@ -3388,6 +3392,7 @@ def set_printoptions(**kwargs):
raise TypeError('"{0}" not a valid keyword parameter.'.format(key))
_printoptions[key] = value
def get_printoptions():
"""Return the current print options.
@ -3407,5 +3412,3 @@ def get_printoptions():
set_printoptions
"""
return _printoptions

View file

@ -31,12 +31,12 @@ import numpy as np
from .codestream import Codestream
from . import core, version
from .jp2box import (
Jp2kBox, JPEG2000SignatureBox, FileTypeBox, JP2HeaderBox,
ColourSpecificationBox, ContiguousCodestreamBox, ImageHeaderBox
)
from .jp2box import (Jp2kBox, JPEG2000SignatureBox, FileTypeBox,
JP2HeaderBox, ColourSpecificationBox,
ContiguousCodestreamBox, ImageHeaderBox)
from .lib import openjpeg as opj, openjp2 as opj2, c as libc
class Jp2k(Jp2kBox):
"""JPEG 2000 file.
@ -138,7 +138,6 @@ class Jp2k(Jp2kBox):
not (X, Y)
verbose : bool, optional
print informational messages produced by the OpenJPEG library
"""
Jp2kBox.__init__(self)
self.filename = filename
@ -176,8 +175,8 @@ class Jp2k(Jp2kBox):
@layer.setter
def layer(self, layer):
if version.openjpeg_version_tuple[0] < 2:
msg = "Layer property not supported unless the version of OpenJPEG "
msg += "is 2.0 or higher."
msg = "Layer property not supported unless the version of "
msg += "OpenJPEG is 2.0 or higher."
raise RuntimeError(msg)
self._layer = layer
@ -361,8 +360,8 @@ class Jp2k(Jp2kBox):
kwargs : dictionary
non-image keyword inputs provided to write method
"""
if (('cinema2k' in kwargs or 'cinema4k' in kwargs) and
(len(set(kwargs)) > 1)):
if ((('cinema2k' in kwargs or 'cinema4k' in kwargs) and
(len(set(kwargs)) > 1))):
msg = "Cannot specify cinema2k/cinema4k along with other options."
raise IOError(msg)
@ -484,9 +483,10 @@ class Jp2k(Jp2kBox):
This method can only be used to create JPEG 2000 images that can fit
in memory.
"""
if re.match("1.[0-4]", version.openjpeg_version) is not None:
raise RuntimeError("You must have at least version 1.5 of OpenJPEG "
"in order to write images.")
if re.match("0|1.[0-4]", version.openjpeg_version) is not None:
msg = "You must have at least version 1.5 of OpenJPEG "
msg += "in order to write images."
raise RuntimeError(msg)
self._determine_colorspace(**kwargs)
self._populate_cparams(img_array, **kwargs)
@ -518,9 +518,11 @@ class Jp2k(Jp2kBox):
image.contents.x0 = self._cparams.image_offset_x0
image.contents.y0 = self._cparams.image_offset_y0
image.contents.x1 = image.contents.x0 \
+ (numcols - 1) * self._cparams.subsampling_dx + 1
+ (numcols - 1) * self._cparams.subsampling_dx \
+ 1
image.contents.y1 = image.contents.y0 \
+ (numrows - 1) * self._cparams.subsampling_dy + 1
+ (numrows - 1) * self._cparams.subsampling_dy \
+ 1
# Stage the image data to the openjpeg data structure.
for k in range(0, numlayers):
@ -633,7 +635,7 @@ class Jp2k(Jp2kBox):
def _determine_colorspace(self, colorspace=None, **kwargs):
"""Determine the colorspace from the supplied inputs.
Parameters
----------
colorspace : str, optional
@ -658,7 +660,7 @@ class Jp2k(Jp2kBox):
elif colorspace.lower() == 'rgb' and self.shape[2] < 3:
msg = 'RGB colorspace requires at least 3 components.'
raise IOError(msg)
# Turn the colorspace from a string to the enumerated value that
# the library expects.
COLORSPACE_MAP = {'rgb': opj2.CLRSPC_SRGB,
@ -667,8 +669,7 @@ class Jp2k(Jp2kBox):
'ycc': opj2.CLRSPC_YCC}
self._colorspace = COLORSPACE_MAP[colorspace.lower()]
def _write_openjp2(self, img_array, verbose=False):
"""
Write JPEG 2000 file using OpenJPEG 2.x interface.
@ -734,7 +735,8 @@ class Jp2k(Jp2kBox):
if not ((box.box_id == 'xml ') or
(box.box_id == 'uuid' and
box.uuid == UUID('be7acfcb-97a9-42e8-9c71-999491e3afac'))):
msg = "Only XML boxes and XMP UUID boxes can currently be appended."
msg = "Only XML boxes and XMP UUID boxes can currently be "
msg += "appended."
raise IOError(msg)
# Check the last box. If the length field is zero, then rewrite
@ -891,9 +893,9 @@ class Jp2k(Jp2kBox):
Slicing protocol.
"""
if ((isinstance(index, slice) and
(index.start == None and
index.stop == None and
index.step == None)) or (index is Ellipsis)):
(index.start is None and
index.stop is None and
index.step is None)) or (index is Ellipsis)):
# Case of jp2[:] = data, i.e. write the entire image.
#
# Should have a slice object where start = stop = step = None
@ -923,12 +925,14 @@ class Jp2k(Jp2kBox):
return self._read()
if isinstance(pargs, slice):
if pargs.start is None and pargs.stop is None and pargs.step is None:
if (((pargs.start is None) and
(pargs.stop is None) and
(pargs.step is None))):
# Case of jp2[:]
return self._read()
# Corner case of jp2[x] where x is a slice object with non-null
# members. Just augment it with an ellipsis and let the code
# members. Just augment it with an ellipsis and let the code
# below handle it.
pargs = (pargs, Ellipsis)
@ -971,8 +975,7 @@ class Jp2k(Jp2kBox):
# Reduce dimensionality in the scalar dimension.
return np.squeeze(data, axis=idx)
# Assuming pargs is a tuple of slices from now on.
# Assuming pargs is a tuple of slices from now on.
rows = pargs[0]
cols = pargs[1]
if len(pargs) == 2:
@ -989,15 +992,14 @@ class Jp2k(Jp2kBox):
# Ok, reduce layer step is the same in both xy directions, so just take
# one of them.
step = rows_step
# Check if the step size is a power of 2.
if np.abs(np.log2(step) - np.round(np.log2(step))) > 1e-6:
msg = "Row and column strides must be powers of 2."
raise IndexError(msg)
rlevel = np.int(np.round(np.log2(step)))
area = (
0 if rows.start is None else rows.start,
area = (0 if rows.start is None else rows.start,
0 if cols.start is None else cols.start,
numrows if rows.stop is None else rows.stop,
numcols if cols.stop is None else cols.stop
@ -1009,7 +1011,6 @@ class Jp2k(Jp2kBox):
# Ok, 3 arguments in pargs.
return data[:, :, bands]
def _read(self, **kwargs):
"""Read a JPEG 2000 image.
@ -1032,34 +1033,34 @@ class Jp2k(Jp2kBox):
def read(self, **kwargs):
"""
"""
#Read a JPEG 2000 image.
# Read a JPEG 2000 image.
#
#Parameters
#----------
#rlevel : int, optional
# Factor by which to rlevel output resolution. Use -1 to get the
# lowest resolution thumbnail. This is the only keyword option
# available to use when the OpenJPEG version is 1.5 or earlier.
#layer : int, optional
# Number of quality layer to decode.
#area : tuple, optional
# Specifies decoding image area,
# (first_row, first_col, last_row, last_col)
#tile : int, optional
# Number of tile to decode.
#verbose : bool, optional
# Print informational messages produced by the OpenJPEG library.
# Parameters
# ----------
# rlevel : int, optional
# Factor by which to rlevel output resolution. Use -1 to get the
# lowest resolution thumbnail. This is the only keyword option
# available to use when the OpenJPEG version is 1.5 or earlier.
# layer : int, optional
# Number of quality layer to decode.
# area : tuple, optional
# Specifies decoding image area,
# (first_row, first_col, last_row, last_col)
# tile : int, optional
# Number of tile to decode.
# verbose : bool, optional
# Print informational messages produced by the OpenJPEG library.
#
#Returns
#-------
#img_array : ndarray
# The image data.
# Returns
# -------
# img_array : ndarray
# The image data.
#
#Raises
#------
#IOError
# If the image has differing subsample factors.
# Raises
# ------
# IOError
# If the image has differing subsample factors.
if 'ignore_pclr_cmap_cdef' in kwargs:
self.ignore_pclr_cmap_cdef = kwargs['ignore_pclr_cmap_cdef']
warnings.warn("Use array-style slicing instead.", DeprecationWarning)
@ -1163,7 +1164,8 @@ class Jp2k(Jp2kBox):
return data
def _read_openjp2(self, rlevel=0, layer=None, area=None, tile=None, verbose=False):
def _read_openjp2(self, rlevel=0, layer=None, area=None, tile=None,
verbose=False):
"""Read a JPEG 2000 image using libopenjp2.
Parameters
@ -1453,7 +1455,7 @@ class Jp2k(Jp2kBox):
def _populate_image_struct(self, image, imgdata):
"""Populates image struct needed for compression.
Parameters
----------
image : ImageType(ctypes.Structure)
@ -1461,9 +1463,9 @@ class Jp2k(Jp2kBox):
img_array : ndarray
Image data to be written to file.
"""
numrows, numcols, num_comps = imgdata.shape
# set image offset and reference grid
image.contents.x0 = self._cparams.image_offset_x0
image.contents.y0 = self._cparams.image_offset_y0
@ -1471,7 +1473,7 @@ class Jp2k(Jp2kBox):
(numcols - 1) * self._cparams.subsampling_dx + 1)
image.contents.y1 = (image.contents.y0 +
(numrows - 1) * self._cparams.subsampling_dy + 1)
# Stage the image data to the openjpeg data structure.
for k in range(0, num_comps):
if re.match("2.0", version.openjpeg_version) is not None:
@ -1485,19 +1487,19 @@ class Jp2k(Jp2kBox):
core.OPJ_PROFILE_CINEMA_4K):
image.contents.comps[k].prec = 12
image.contents.comps[k].bpp = 12
layer = np.ascontiguousarray(imgdata[:, :, k], dtype=np.int32)
dest = image.contents.comps[k].data
src = layer.ctypes.data
ctypes.memmove(dest, src, layer.nbytes)
return image
def _populate_comptparms(self, img_array):
"""Instantiate and populate comptparms structure.
This structure defines the image components.
Parameters
----------
img_array : ndarray
@ -1526,8 +1528,8 @@ class Jp2k(Jp2kBox):
comptparms[j].sgnd = 0
self._comptparms = comptparms
def _component2dtype(component):
"""Take an OpenJPEG component structure and determine the numpy datatype.
@ -1573,6 +1575,7 @@ JP2_IDS = ['colr', 'cdef', 'cmap', 'jp2c', 'ftyp', 'ihdr', 'jp2h', 'jP ',
'pclr', 'res ', 'resc', 'resd', 'xml ', 'ulst', 'uinf', 'url ',
'uuid']
def _validate_jp2_box_sequence(boxes):
"""Run through series of tests for JP2 box legality.
@ -1588,12 +1591,13 @@ def _validate_jp2_box_sequence(boxes):
count = _collect_box_count(boxes)
for box_id in count.keys():
if box_id not in JP2_IDS:
msg = "The presence of a '{0}' box requires that the file type "
msg += "brand be set to 'jpx '."
msg = "The presence of a '{0}' box requires that the file "
msg += "type brand be set to 'jpx '."
raise IOError(msg.format(box_id))
_validate_jp2_colr(boxes)
def _validate_jp2_colr(boxes):
"""
Validate JP2 requirements on colour specification boxes.
@ -1605,6 +1609,7 @@ def _validate_jp2_colr(boxes):
msg = "A JP2 colr box cannot have a non-zero approximation field."
raise IOError(msg)
def _validate_jpx_box_sequence(boxes):
"""Run through series of tests for JPX box legality."""
_validate_label(boxes)
@ -1613,6 +1618,7 @@ def _validate_jpx_box_sequence(boxes):
_validate_singletons(boxes)
_validate_top_level(boxes)
def _validate_signature_compatibility(boxes):
"""Validate the file signature and compatibility status."""
# Check for a bad sequence of boxes.
@ -1700,6 +1706,8 @@ def _validate_channel_definition(jp2h, colr):
JP2H_CHILDREN = set(['bpcc', 'cdef', 'cmap', 'ihdr', 'pclr'])
def _check_jp2h_child_boxes(boxes, parent_box_name):
"""Certain boxes can only reside in the JP2 header."""
box_ids = set([box.box_id for box in boxes])
@ -1727,6 +1735,7 @@ def _collect_box_count(boxes):
TOP_LEVEL_ONLY_BOXES = set(['dtbl'])
def _check_superbox_for_top_levels(boxes):
"""Several boxes can only occur at the top level."""
# We are only looking at the boxes contained in a superbox, so if any of
@ -1742,6 +1751,7 @@ def _check_superbox_for_top_levels(boxes):
if hasattr(box, 'box'):
_check_superbox_for_top_levels(box.box)
def _validate_top_level(boxes):
"""Several boxes can only occur at the top level."""
# Add the counts in the superboxes.
@ -1761,6 +1771,7 @@ def _validate_top_level(boxes):
msg += 'a fragment table box as well.'
raise IOError(msg)
def _validate_singletons(boxes):
"""Several boxes can only occur once."""
count = _collect_box_count(boxes)
@ -1771,6 +1782,7 @@ def _validate_singletons(boxes):
JPX_IDS = ['asoc', 'nlst']
def _validate_jpx_brand(boxes, brand):
"""
If there is a JPX box then the brand must be 'jpx '.
@ -1785,6 +1797,7 @@ def _validate_jpx_brand(boxes, brand):
# Same set of checks on any child boxes.
_validate_jpx_brand(box.box, brand)
def _validate_jpx_compatibility(boxes, compatibility_list):
"""
If there is a JPX box then the compatibility list must also contain 'jpx '.
@ -1800,6 +1813,7 @@ def _validate_jpx_compatibility(boxes, compatibility_list):
# Same set of checks on any child boxes.
_validate_jpx_compatibility(box.box, compatibility_list)
def _validate_label(boxes):
"""
Label boxes can only be inside association, codestream headers, or
@ -1816,6 +1830,7 @@ def _validate_label(boxes):
# Same set of checks on any child boxes.
_validate_label(box.box)
def extract_image_cube(image):
"""Extract 3D image from openjpeg data structure.
"""
@ -1871,7 +1886,6 @@ def extract_image_bands(image):
return data
# Setup the default callback handlers. See the callback functions subsection
# in the ctypes section of the Python documentation for a solid explanation of
# what's going on here.

View file

@ -144,7 +144,7 @@ def glymur_config():
lst.append(load_openjpeg_library(libname))
if all(handle is None for handle in lst):
msg = "Neither the openjp2 nor the openjpeg library could be loaded. "
raise IOError(msg)
warnings.warn(msg)
return tuple(lst)
def get_configdir():

View file

@ -13,6 +13,7 @@ from .config import glymur_config
OPENJP2, OPENJPEG = glymur_config()
def version():
"""Wrapper for opj_version library routine."""
try:
@ -50,13 +51,6 @@ JPWL_MAX_NO_TILESPECS = 16
TRUE = 1
FALSE = 0
#PROFILE = {'none': 0, # No profile
# 0: 1, # Profile 0
# 1: 2, # Profile 1
# 'part2': 0x8000, # At least one extension
# 'Cinema2K': 0x0003, # 2K cinema profile
# 'Cinema4K': 0x0004, # 4K cinema profile
# supported color spaces
CLRSPC_UNKNOWN = -1
CLRSPC_UNSPECIFIED = 0
@ -548,7 +542,6 @@ class ImageType(ctypes.Structure):
return msg
class ImageComptParmType(ctypes.Structure):
"""Component parameters structure used by image_create function.
@ -958,7 +951,7 @@ def read_header(stream, codec):
ARGTYPES = [STREAM_TYPE_P, CODEC_TYPE,
ctypes.POINTER(ctypes.POINTER(ImageType))]
OPENJP2.opj_read_header.argtypes = ARGTYPES
OPENJP2.opj_read_header.restype = check_error
OPENJP2.opj_read_header.restype = check_error
imagep = ctypes.POINTER(ImageType)()
OPENJP2.opj_read_header(stream, codec, ctypes.byref(imagep))
@ -1317,6 +1310,7 @@ def _stream_create_default_file_stream_2p0(fptr, isa_read_stream):
stream = OPENJP2.opj_stream_create_default_file_stream(fptr, read_stream)
return stream
def _stream_create_default_file_stream_2p1(fname, isa_read_stream):
"""Wraps openjp2 library function opj_stream_create_default_vile_stream.
@ -1343,7 +1337,7 @@ def _stream_create_default_file_stream_2p1(fname, isa_read_stream):
stream = OPENJP2.opj_stream_create_default_file_stream(file_argument,
read_stream)
return stream
if re.match(r'''2.0''', version()):
stream_create_default_file_stream = _stream_create_default_file_stream_2p0
else:

View file

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

View file

@ -17,7 +17,7 @@ import glymur
from . import fixtures
@unittest.skipIf(sys.hexversion < 0x03000000, "do not care about 2.7 here")
@unittest.skipIf(re.match('1|2.0', glymur.version.openjpeg_version),
@unittest.skipIf(re.match('0|1|2.0', glymur.version.openjpeg_version),
"Requires openjpeg 2.1.0 or higher")
class TestPrintingOpenjp2(unittest.TestCase):
"""Tests for verifying how printing works on openjp2 library structures."""

View file

@ -13,6 +13,14 @@ import six
import glymur
# If openjpeg is not installed, many tests cannot be run.
if glymur.version.openjpeg_version == '0.0.0':
OPENJPEG_NOT_AVAILABLE = True
OPENJPEG_NOT_AVAILABLE_MSG = 'OpenJPEG library not installed'
else:
OPENJPEG_NOT_AVAILABLE = False
OPENJPEG_NOT_AVAILABLE_MSG = None
# Some versions of "six" on python3 cause problems when verifying warnings.
# Only use when the version is 1.7 or higher.
# And moreover, we only test using the 3.x infrastructure, never on 2.x.

View file

@ -65,6 +65,8 @@ class TestCallbacks(unittest.TestCase):
expected = '[INFO] tile number 1 / 1'
self.assertEqual(actual, expected)
@unittest.skipIf(glymur.version.openjpeg_version[0] == '0',
"Missing openjpeg/openjp2 library.")
def test_info_callbacks_on_read(self):
"""stdio output when info callback handler is enabled"""

View file

@ -114,7 +114,7 @@ class TestDataEntryURL(unittest.TestCase):
self.assertEqual(url + chr(0), read_url)
@unittest.skipIf(re.match(r'''(1|2.0.0)''',
@unittest.skipIf(re.match(r'''0|1|2.0.0''',
glymur.version.openjpeg_version) is not None,
"Not supported until 2.1")
@unittest.skipIf(os.name == "nt", WINDOWS_TMP_FILE_MSG)

View file

@ -36,6 +36,7 @@ from glymur.version import openjpeg_version
from .fixtures import HAS_PYTHON_XMP_TOOLKIT
from .fixtures import WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG
from .fixtures import OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG
if HAS_PYTHON_XMP_TOOLKIT:
import libxmp
@ -76,6 +77,7 @@ class SliceProtocolBase(unittest.TestCase):
self.j2k_data_r1 = self.j2k[::2, ::2]
self.j2k_data_r5 = self.j2k[::32, ::32]
@unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG)
@unittest.skipIf(re.match("1.5|2", glymur.version.openjpeg_version) is None,
"Must have openjpeg 1.5 or higher to run")
@unittest.skipIf(os.name == "nt", fixtures.WINDOWS_TMP_FILE_MSG)
@ -143,6 +145,7 @@ class TestSliceProtocolBaseWrite(SliceProtocolBase):
j[:25, :45, :] = self.j2k_data[:25, :25, :]
@unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG)
class TestSliceProtocolRead(SliceProtocolBase):
def test_resolution_strides_cannot_differ(self):
@ -251,6 +254,7 @@ class TestJp2k(unittest.TestCase):
def tearDown(self):
pass
@unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG)
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_warn_if_using_read_method(self):
"""Should warn if deprecated read method is called"""
@ -313,6 +317,7 @@ class TestJp2k(unittest.TestCase):
actdata = j2[:]
self.assertTrue(fixtures.mse(actdata[0], expdata[0]) < 0.38)
@unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG)
@unittest.skipIf(re.match('1.[0-4]', openjpeg_version) is not None,
"Not supported with OpenJPEG {0}".format(openjpeg_version))
@unittest.skipIf(re.match('1.5.(1|2)', openjpeg_version) is not None,
@ -345,6 +350,7 @@ class TestJp2k(unittest.TestCase):
self.assertEqual(newjp2.filename, self.j2kfile)
self.assertEqual(len(newjp2.box), 0)
@unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG)
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_rlevel_max_backwards_compatibility(self):
"""
@ -367,6 +373,7 @@ class TestJp2k(unittest.TestCase):
np.testing.assert_array_equal(thumbnail1, thumbnail2)
self.assertEqual(thumbnail1.shape, (25, 15, 3))
@unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG)
def test_rlevel_too_high(self):
"""Should error out appropriately if reduce level too high"""
j = Jp2k(self.jp2file)
@ -518,12 +525,14 @@ class TestJp2k(unittest.TestCase):
self.assertEqual(new_jp2.box[j].length,
baseline_jp2.box[j].length)
@unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG)
def test_basic_jp2(self):
"""Just a very basic test that reading a JP2 file does not error out.
"""
j2k = Jp2k(self.jp2file)
j2k[::2, ::2]
@unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG)
def test_basic_j2k(self):
"""This test is only useful when openjp2 is not available
and OPJ_DATA_ROOT is not set. We need at least one
@ -648,6 +657,7 @@ class TestJp2k(unittest.TestCase):
creator_tool = xmp.get_property(libxmp.consts.XMP_NS_XMP, 'CreatorTool')
self.assertEqual(creator_tool, 'Google')
@unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG)
@unittest.skipIf(re.match(r'''(1|2.0.0)''',
glymur.version.openjpeg_version) is not None,
"Not supported until 2.0.1")
@ -664,6 +674,7 @@ class TestJp2k(unittest.TestCase):
with self.assertRaises(RuntimeError):
glymur.Jp2k(self.jp2file).read_bands()
@unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG)
@unittest.skipIf(re.match('1.[0-4]', openjpeg_version) is not None,
"Not supported with OpenJPEG {0}".format(openjpeg_version))
@unittest.skipIf(os.name == "nt", fixtures.WINDOWS_TMP_FILE_MSG)
@ -869,6 +880,7 @@ class TestJp2k_1_x(unittest.TestCase):
@unittest.skipIf(os.name == "nt", fixtures.WINDOWS_TMP_FILE_MSG)
@unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG)
class Test_2p0_official(unittest.TestCase):
"""Tests specific to v2.0.0"""
@ -962,6 +974,7 @@ class TestJp2k_2_0(unittest.TestCase):
self.assertEqual(jasoc.box[3].box[1].box_id, 'xml ')
@unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG)
@unittest.skipIf(re.match(r'''(1|2.0.0)''',
glymur.version.openjpeg_version) is not None,
"Not to be run until unless 2.0.1 or higher is present")
@ -1135,7 +1148,7 @@ class TestJp2kOpjDataRoot(unittest.TestCase):
actdata = j[:]
self.assertTrue(fixtures.mse(actdata, expdata) < 250)
@unittest.skipIf(WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG)
def test_no_cxform_pclr_jp2(self):
"""Indices for pclr jpxfile if no color transform"""

View file

@ -41,10 +41,12 @@ from glymur.jp2box import FileTypeBox, ImageHeaderBox, ColourSpecificationBox
from .fixtures import (
OPJ_DATA_ROOT, MetadataBase,
WARNING_INFRASTRUCTURE_ISSUE, WARNING_INFRASTRUCTURE_MSG,
mse, peak_tolerance, read_pgx, opj_data_file
mse, peak_tolerance, read_pgx, opj_data_file,
OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG
)
@unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG)
@unittest.skipIf(OPJ_DATA_ROOT is None,
"OPJ_DATA_ROOT environment variable not set")
class TestSuite(unittest.TestCase):
@ -482,7 +484,7 @@ class TestSuiteWarns(MetadataBase):
@unittest.skipIf(OPJ_DATA_ROOT is None,
"OPJ_DATA_ROOT environment variable not set")
@unittest.skipIf(glymur.version.openjpeg_version_tuple[0] == 1,
@unittest.skipIf(glymur.version.openjpeg_version_tuple[0] != 2,
"Feature not supported in glymur until openjpeg 2.0")
class TestSuiteBands(unittest.TestCase):
"""
@ -577,7 +579,7 @@ class TestSuiteBands(unittest.TestCase):
@unittest.skipIf(OPJ_DATA_ROOT is None,
"OPJ_DATA_ROOT environment variable not set")
@unittest.skipIf(glymur.version.openjpeg_version_tuple[0] == 1,
@unittest.skipIf(glymur.version.openjpeg_version_tuple[0] < 2,
"Tests not passing until 2.0")
class TestSuite2point0(unittest.TestCase):
"""Runs tests introduced in version 2.0 or that pass only in 2.0"""
@ -641,7 +643,7 @@ class TestSuite2point0(unittest.TestCase):
@unittest.skipIf(OPJ_DATA_ROOT is None,
"OPJ_DATA_ROOT environment variable not set")
@unittest.skipIf(re.match(r'''(1|2.0.0)''',
@unittest.skipIf(re.match(r'''0|1|2.0.0''',
glymur.version.openjpeg_version) is not None,
"Only supported in 2.0.1 or higher")
class TestSuite2point1(unittest.TestCase):
@ -798,7 +800,7 @@ class TestSuite2point1(unittest.TestCase):
@unittest.skipIf(OPJ_DATA_ROOT is None,
"OPJ_DATA_ROOT environment variable not set")
@unittest.skipIf(re.match(r'''(1|2.0.0)''',
@unittest.skipIf(re.match(r'''0|1|2.0.0''',
glymur.version.openjpeg_version) is not None,
"Only supported in 2.0.1 or higher")
class TestReadArea(unittest.TestCase):